Java框架之spring AOP 和 IOC

news2024/12/24 9:09:28

写在前面

本文一起看下spring aop 和 IOC相关的内容。

1:spring bean核心原理

1.1:spring bean的生命周期

在这里插入图片描述

spring bean生命周期,参考下图:
在这里插入图片描述

我们来一步步的看下。

  • 1
    其中1构造函数就是执行类的构造函数完成对象的创建,如下:
public User() {
    System.out.println("User 被实例化");
}
  • 2
    2是通过xml配置或者是@Resource等相关注入bean的注解,从容器中获取相关的spring bean进行赋值,如下:

在这里插入图片描述

在这里插入图片描述

  • 3
    3BeanNameAware,其中BeanNameAware是一个接口,只定义了一个唯一的方法void setBeanName(String name);,如果bean实现了该接口,则会回调setBeanName方法并将当前bean的beanName作为参数传递进去,这样,bean就能够获取自己的名称了,如下:
@Component
public class TestAware implements BeanNameAware {
    private String name;
    
    @Override
    public void setBeanName(String name) {
        this.name = name;
        System.out.println("TestAware.setBeanName, name is: " + this.name);
    }
}
  • 4,5
    4,5类似于3,也都分别定义了相关的xxxAware接口,以及对应的setXxx抽象方法,如下:
public class TestAware implements BeanFactoryAware, ApplicationContextAware {
    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("TestAware.setBeanFactory");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        System.out.println("TestAware.setApplicationContext");
    }
}
  • 6
    6是spring留给我们的一个扩展的口,对应的接口是BeanPostProcessor,定义了初始化前执行的方法postProcessBeforeInitailization和初始化后执行的方法postProcessAfterInitialization如下:
public interface BeanPostProcessor {
    // 1:在InitailizingBean的afterProperties方法执行之前执行
    // 2:在自定义的init方法执行之前执行
    // 3:接收的参数是bean本身以及bean的名称
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	// 1:在InitailizingBean的afterProperties方法执行之后执行
    // 2:在自定义的init方法执行之后执行
    // 3:接收的参数是bean本身以及bean的名称
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

使用BeanPostProcessor的方式也比较简单,只需要实现该接口,对前置bean处理和后置bean处理两个方法提供具体实现后,将实现类注册为spring bean就可以了,如下可能实现:

public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("before--实例化的bean对象:"+bean+"\t"+beanName);
		// 可以根据beanName不同执行不同的处理操作
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("after...实例化的bean对象:"+bean+"\t"+beanName);
		// 可以根据beanName不同执行不同的处理操作
		return bean;
	}
}
  • 7
    InitializingBean也是一个扩展接口,定义了一个方法afterProperties,是在bean的属性都设置完毕后调用的方法,可以在该接口检测不可为空属性是否有值等操作,接口定义如下:
package org.springframework.beans.factory;

// 1:在所有属性设置完毕后执行
// 2:可以检测强制属性是否设置完成
// 3:可以使用自定义的init方法作为该方式的替代品
public interface InitializingBean {
	void afterPropertiesSet() throws Exception;

}

使用实例如下:

public class MyInitializingBeanTest implements InitializingBean {
    private String name;
    private int age;

    @Override
    public void afterPropertiesSet() throws Exception {
        String newName = "李四的歌";
        System.out.println("修改name属性值从 " + this.name + " -> " + newName);
        // 修改name属性的值
        this.name = newName;
    }
    ...
}
  • 8
    自定义的初始化方法,在bean标签中通过init-method设置,如下:
public class MyBeanWithInitMethod {
    private String name;

    public void changeNameProp() {
        String newName = "李四的歌";
        System.out.println("通过init-method修改name属性值从 "
                + this.name + " -> " + newName);
        this.name = newName;
    }
    ...
}

<bean class="yudaosourcecode.studyhelp.MyBeanWithInitMethod"
        id="myBeanWithInitMethod" init-method="changeNameProp">
    <property name="name" value="张三的歌"/>
</bean>
  • 9
    参考6,只不过是在InitialiBean和自定义init初始化方法执行完毕之后执行。
  • 10
    使用期。
  • 11
    当容器关闭时执行操作的接口规范,一般用来执行一些释放资源,收尾等操作,接口定义如下
package org.springframework.beans.factory;

// 1:当bean销毁时想要释放资源可以让bean实现该接口
// 2:可以使用自定义销毁方法作为另一个选择
public interface DisposableBean {
	void destroy() throws Exception;
}

实例如下:

public class MyLifeCycleBean implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy被调用了。。。");
    }
}
  • 12
    类似于11,自定义销毁方法,如下:
public class MyLifeCycleBean {

    public void destroyMethdo(){
        System.out.println("destroy-method 被调用...");
    }
}

<bean id="lifeCycle" 
      class="yudaosourcecode.studyhelp.MyLifeCycleBean"
      destroy-method="destroyMethdo"/>

1.2:spring bean生命周期完整实例

  • BeanPostProcessor
public class BeanPostProcessor123 implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("6:BeanPostProcessor#postProcessBeforeInitialization被调用了。。。");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("9:BeanPostProcessor#postProcessAfterInitialization被调用了。。。");
        return bean;
    }

}
  • bean
public class MyLifeCycleBean implements BeanNameAware,
        BeanFactoryAware,
        ApplicationContextAware,
//        BeanClassLoaderAware,
//        BeanPostProcessor,
        InitializingBean,
        DisposableBean {
    private String test;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("5:ApplicationContextAware 被调用。。。");
    }

    // 普通方法
    public void display(){
//        System.out.println("普通类实例方法调用...");
        System.out.println("10:使用中。。。");
    }

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        System.out.println("2:依赖注入");
        this.test = test;
    }

    public MyLifeCycleBean() {
        System.out.println("1:构造函数");
    }
//    @Override
//    public void setBeanClassLoader(ClassLoader classLoader) {
//        System.out.println("BeanClassLoaderAware 被调用了。。。");
//    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4:BeanFactoryAware 被调用了。。。");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("3:BeanNameAware 被调用了。。。");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("11:DisposableBean#destroy被调用了。。。");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("7:InitializingBean#afterPropertiesSet被调用了。。。");
    }
//
//    @Override
//    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//        System.out.println("BeanPostProcessor#postProcessBeforeInitialization被调用了。。。");
//        return bean;
//    }
//
//    @Override
//    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//        System.out.println("BeanPostProcessor#postProcessAfterInitialization被调用了。。。");
//        return bean;
//    }

    public void initMethod(){
        System.out.println("8: init-method 被调用...");
    }

    public void destroyMethdo(){
        System.out.println("12:destroy-method 被调用...");
    }
}
  • xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="lifeCycle" class="yudaosourcecode.studyhelp.MyLifeCycleBean"
          init-method="initMethod" destroy-method="destroyMethdo">
        <property name="test" value="test"/>
    </bean>

    <bean class="yudaosourcecode.studyhelp.BeanPostProcessor123"/>
</beans>
  • 测试
@Test
public void mylifecyclebeantest111() {
//        ClassPathResource resource
//                = new ClassPathResource("mylifecyclebeantest.xml");
//        XmlBeanFactory xbf = new XmlBeanFactory(resource);
    AbstractApplicationContext ac = new ClassPathXmlApplicationContext("mylifecyclebeantest.xml");
//        Closeable
    // 手动添加后置bean处理器
//        xbf.addBeanPostProcessor(new MyLifeCycleBean());
//        ac.addBeanPostProcessor(new BeanPostProcessor123());
//        ac.
//        MyLifeCycleBean lifeCycle
//                =
    ac.getBean("lifeCycle", MyLifeCycleBean.class).display();
//        lifeCycle.display();
    // 销毁容器
    ac.close();
}

运行:

1:构造函数
2:依赖注入
3:BeanNameAware 被调用了。。。
4:BeanFactoryAware 被调用了。。。
5:ApplicationContextAware 被调用。。。
6:BeanPostProcessor#postProcessBeforeInitialization被调用了。。。
7:InitializingBean#afterPropertiesSet被调用了。。。
8: init-method 被调用...
9:BeanPostProcessor#postProcessAfterInitialization被调用了。。。
10:使用中。。。
11:DisposableBean#destroy被调用了。。。
12:destroy-method 被调用...

对比如下图:

在这里插入图片描述

1.3:spring xml配置原理

这部分来看看下我们在xml配置的<bean>标签是如何解析为spring bean的,我们会定义如下的xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="lifeCycle" class="yudaosourcecode.studyhelp.MyLifeCycleBean"
          init-method="initMethod" destroy-method="destroyMethdo">
        <property name="test" value="test"/>
    </bean>

    <bean class="yudaosourcecode.studyhelp.BeanPostProcessor123"/>
</beans>

其中在<beans中声明的我们叫做是命名空间,然后在命名空间中就可以定义各种各样的标签,并定义了校验xml的xsd,为了解决通过互联网直接加载xsd慢,或者是加载不到的问题,定义了配置文件spring.schemas,配置了命名空间和对应的本地xsd文件的对应关系:

在这里插入图片描述

这样就可以直接从jar包中获取xsd文件了,那么标签如何解析呢?需要使用到spring.handlers,如下:

在这里插入图片描述

当然我们也可以自定义标签,同样也需要在spring.schemas中配置我们自己的xsd,以及并实现并配置解析对应标签的handler到spring.handles配置文件中,具体可以参考spring的自定义标签 一文。

1.4:spring bean配置方式演进

1.0/2.0 XML配置bean,@Autowire注解注入bean
2.5 @Service xml配置和注解配置bean方式混用的半自动注解配置
3.0 @Bean,@Condition Javaconfig方式配置bean
4.0 @Condition,@AutoConfigureX 全自动注解配置

在这里插入图片描述

2:spring AOP

通过前面分析的spring bean的功能,我们已经能够灵活地定义各种对象实例,并设置其各种依赖关系了,此时假定我们现在有类似于下面这样的需求:

1:怀疑某个方法执行的慢,影响性能,但又无法直接修改源代码看其耗时
2:想要记录某一批方法的调用日志,因为方法很多,一个个改的话非常麻烦
3:程序有bug,但是程序年代久远,源程序早已经遗失,但又必须解决

该怎么做呢?在计算机领域有这样一个至理名言,计算机领域所有的问题都可以通过加一层来解决,这里的问题可以吗?可以的,我们只要在方法执行的前和后都加一层就行了,如下:

在这里插入图片描述

spring为了实现这个加一层的功能就提供了对应的功能实现,首先我们需要定义要对那些方法加一层,通过如下红框方式来表示:

在这里插入图片描述

spring给其起个一个专门的名称切点,即要切的点,要拦截的方法,这样还不够,知道要对哪些方法加一层了,但是需要做什么呢,这个时候就可以这样定义:

在这里插入图片描述

spring也给其起了一个专门的名称通知,其中在方法执行前执行的叫做前置通知,在方法执行后执行的叫做后置通知,所有的同志加在一起,spring也起了一个专门的名称切面

以上这个实现加一层的功能,spring也提供了一个专门的名称,aspect oriented programming,即面向切面编程,即AOP。

写在后面

参考文章列表

Java 操作 XML(11)–XMLBeans 使用 。

spring的自定义标签 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/686892.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第八十四天学习记录:Linux基础:初识Linux

流行的Linux发行版&#xff1a; 任何人都可以封装Linux&#xff0c;目前市面上有非常多的Linux发行版&#xff0c;常用的知名的如下&#xff1a; VMware WorkStations安装 安装完成后&#xff0c;要通过下图方式查看网络适配器是否正常配置&#xff1a; 配置成功&#xff1a…

软件需求分析文档怎么写?

什么是软件需求规范文档 &#xff08;SRS&#xff09;&#xff1f; 软件需求规范 &#xff08;SRS&#xff09; 文档列出了未来项目的需求、期望、设计和标准。其中包括规定项目目标的高级业务需求、最终用户要求和需求以及产品在技术方面的功能。简而言之&#xff0c;SRS 提供…

Vue-Element-Admin项目学习笔记(8)配置表单校验规则

前情回顾&#xff1a; vue-element-admin项目学习笔记&#xff08;1&#xff09;安装、配置、启动项目 vue-element-admin项目学习笔记&#xff08;2&#xff09;main.js 文件分析 vue-element-admin项目学习笔记&#xff08;3&#xff09;路由分析一:静态路由 vue-element-adm…

软考:中级软件设计师:校验码,汉明码纠错,信息位L和校验位r的关系

软考&#xff1a;中级软件设计师:校验码&#xff0c;汉明码纠错 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是…

Linux通过crontab定时执行脚本任务

Linux通过crontab定时执行脚本任务 前言1. 创建写入脚本2. 设置执行权限3. 添加定时任务定时任务语法格式每分钟写入一条信息到指定文件 4. 查看日志文件5. 定时执行脚本的作用和用途 前言 在Linux中可以使用crontab来定时执行脚本。crontab是一个用于管理定时任务的工具&…

我这样回答多线程并发,面试官直接惊叹!

目录 前言&#xff1a; 1.单线程执行 2、多线程执行 3.守护线程 4.阻塞线程 前言&#xff1a; 多线程并发是一种处理任务的方式&#xff0c;它可以在同一时间内执行多个任务。多线程并发通常应用于需要同时处理多个任务或同时运行多个程序的情况下。 1.单线程执行 Pyth…

便携式水污染检测设备可以分析多少项污水指标

便携式水污染检测设备可以分析多少项污水指标&#xff08;以下只是一部分&#xff09; 水质检测仪可检测范围 1、饮用水检测&#xff1a;生活用水&#xff08;自来水&#xff09;、&#xff08;瓶、桶装&#xff09;矿泉水、天然矿泉水等&#xff1b; 2、工业用水检测&#xf…

人机融合智能的现状与展望

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在学习摘录和笔记专…

【开源库剖析】Shadow v2.3.0 源码解析

作者&#xff1a;Stan_Z 一、框架介绍 Shadow是19年腾讯开源的自研Android插件化框架&#xff0c;经过线上亿级用户量检验。 Shadow不仅开源分享了插件技术的关键代码&#xff0c;还完整的分享了上线部署所需要的所有设计。 优点&#xff1a; 1&#xff09;复用独立安装app源…

Python可视化库之Matplotlib详解及使用方法

Matplotlib是Python中最常用的可视化工具之一,可以非常方便地创建海量类型的2D图表和一些基本的3D图表。本文主要推荐一个学习使用Matplotlib的步骤。 基本前提 如果你除了本文之外没有任何基础,建议用以下几个步骤学习如何使用matplotlib: 学习基本的matplotlib术语,尤其是…

第二十二章Java一维数组的定义、赋值和初始化

当数组中每个元素都只带有一个下标时&#xff0c;这种数组就是“一维数组”。一维数组&#xff08;one-dimensional array&#xff09;实质上是一组相同类型数据的线性集合&#xff0c;是数组中最简单的一种数组。 数组是引用数据类型&#xff0c;引用数据类型在使用之前一定要…

Reactor的概念

一、Reactor的概念 ​ Reactor模式是一种事件驱动模式&#xff0c;由一个或多个并发输入源&#xff08;input)&#xff0c;一个消息分发处理器&#xff08;Initiation Dispatcher&#xff09;&#xff0c;以及每个消息对应的处理器&#xff08;Request Handler&#xff09;构成…

Linux安装nodejs

一、下载包 https://registry.npmmirror.com/binary.html?pathnode/ 比如&#xff1a;10.9.0 https://registry.npmmirror.com/binary.html?pathnode/v10.9.0/ 按需下载 https://registry.npmmirror.com/-/binary/node/v10.9.0/node-v10.9.0-linux-x64.tar.gz 二、上传到…

使用Nginx+Lua实现自定义WAF(Web application firewall)

转载https://github.com/unixhot/waf WAF 使用NginxLua实现自定义WAF&#xff08;Web application firewall&#xff09; 功能列表&#xff1a; 支持IP白名单和黑名单功能&#xff0c;直接将黑名单的IP访问拒绝。 支持URL白名单&#xff0c;将不需要过滤的URL进行定义。 支持…

解析vcruntime140.dll文件,缺失了要怎么去修复?

在计算机的世界中&#xff0c;vcruntime140.dll是一个重要的动态链接库文件。然而&#xff0c;有时候这个文件可能会引发一系列问题&#xff0c;影响应用程序的正常运行。如果你缺少了vcruntime140.dll&#xff0c;那么你的程序就会打不开&#xff0c;今天我们一起来聊聊vcrunt…

408数据结构第四章

串 定义存储结构模式匹配 小题形式考&#xff0c;比较简单&#xff0c;拿两个题来练手就会了 定义 字符串简称串 由零个或多个字符组成的有限序列 S是串名n称为串的长度&#xff0c;n0称为空串 串中多个连续的字符组成的子序列称为该串的子串 串的逻辑结构和线性表极为相似&am…

ByteHouse+Apache Airflow:高效简化数据管理流程

Apache Airflow 与 ByteHouse 相结合&#xff0c;为管理和执行数据流程提供了强大而高效的解决方案。本文突出了使用 Apache Airflow 与 ByteHouse 的主要优势和特点&#xff0c;展示如何简化数据工作流程并推动业务成功。 主要优势 可扩展可靠的数据流程&#xff1a;Apache Ai…

使用MASA Stack+.Net 从零开始搭建IoT平台 第五章 使用时序库存储上行数据

目录 前言分析实施步骤时序库的安装解决playload没有时间戳问题代码编写 总结 前言 我们可以将设备上行数据存储到关系型数据库中&#xff0c;我们需要两张带有时间戳的表&#xff08;最新数据表 和 历史数据表&#xff09;&#xff0c;历史数据表存储所有设备上报的数据&…

iptables详解

iptables简介 netfilter/iptables&#xff08;简称为iptables&#xff09;组成Linux平台下的包过滤防火墙&#xff0c;完成封包过滤、封包重定向和网络地址转换&#xff08;NAT&#xff09;等功能。 iptables 规则&#xff08;rules&#xff09;其实就是网络管理员预定义的条…

神通数据库X86架构适配DJANGO317指南

制作神通数据库镜像 1&#xff09;、下载docker.io/centos:7.9.2009镜像&#xff0c;docker pull docker.io/centos:7.9.2009 2)、运行一个容器&#xff0c;docker run -itd --name shentong -p 2003:2003 --privilegedtrue --restartalways -v /sys/fs/cgroup:/sys/fs/cgrou…