深入spring源码分析(上)

spring

Spring的本质是一个bean工厂(beanFactory)或者说bean容器,它按照我们的要求,生产我们需要的各种各样的bean,提供给我们使用。只是在生产bean的过程中,需要解决bean之间的依赖问题,才引入了依赖注入(DI)这种技术。也就是说依赖注入是beanFactory生产bean时为了解决bean之间的依赖的一种技术而已。

beanFactory会在bean的生命周期的各个阶段中对bean进行各种管理,并且spring将这些阶段通过各种接口暴露给我们,让我们可以对bean进行各种处理,我们只要让bean实现对应的接口,那么spring就会在bean的生命周期调用我们实现的接口来处理该bean.

BeanFactory

是什么

  • ApplicationContext的父接口
  • 是Spring的核心容器,主要的ApplicationContext实现都组合了他的功能

查看类图,ctrl + alt + u

image-20230627151047791

可以看到ApplicationContext的有个父接口是BeanFactory

打印context.getClass(),可以看到SpringBoot的启动程序返回的ConfigurableApplicationContext的具体的实现类是AnnotationConfigServletWebServerApplicationContext

调用run方法返回的就是spring的核心容器

ConfigurableApplicationContext context = SpringApplication.run(SpringbootFirstApplication.class, args);

System.out.println(context.getClass());

输出

class org.springframework.boot.web.servlet.context.
    AnnotationConfigServletWebServerApplicationContext

可以做什么

  • 表面只能getBean
  • 实际上,控制反转、基本的依赖注入、甚至Bean的生命周期的各种功能,都由他的实现类提供

查看BeanFactory接口中的所有方法,ctrl + F12

image-20230627152249604

查看springboot默认的ConfigurableApplicationContext类中的BeanFactory的实际类型

ConfigurableApplicationContext context = SpringApplication.run(SpringbootFirstApplication.class, args);

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

System.out.println(beanFactory.getClass());

输出

class org.springframework.beans.factory.support.DefaultListableBeanFactory

从打印结果可以了解到实际类型为DefaultListableBeanFactory,这是整个Bean加载的核心部分,是Spring注册及加载Bean的默认实现。

所以这里以DefaultListableBeanFactory作为出发点,进行分析。

查看DefaultListableBeanFactory的类图

image-20230627153004647

这里我们暂且不细看DefaultListableBeanFactory,先看DefaultListableBeanFactory的父类DefaultSingletonBeanRegistry,先选中它,然后双击,即可跳转到对应的源码,可以看到有个私有的成员变量singletonObjects

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

singletonObjects它是我们最熟悉的朋友,俗称“单例池”、“容器”、缓存创建完成单例Bean的地方。

我们知道Spring容器的 Bean 可以是单例 (把 scope 配置为 singleton 或者不设置 scope,即默认是单例),这些单例的 Bean 被初始化之后就被Spring注册到BeanFactory管理了起来,方便下次直接使用。除了配置的单例Bean,我们还可以调用BeanFactory的Bean直接注册一个单例Bean。

这里可以通过反射的方法来获取该成员变量,进行分析

Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
singletonObjects.setAccessible(true);
Map<String,Object> o = (Map<String, Object>) singletonObjects.get(beanFactory);

o.forEach((k,v) -> {
    System.out.println("key: "+ k + " value: " + v);
});

遍历出的map里面就是BeanFactory管理的单例bean

ApplicationContext 比 BeanFactory 多了什么

  • MessageSource: 国际化功能,支持多种语言
  • ResourcePatternResolver: 通配符匹配资源路径
  • EnvironmentCapable: 环境信息,系统环境变量,properties、yml等配置文件中的值
  • ApplicationEventPublisher: 发布事件对象

容器实现

BeanFactory

DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现

  • beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象
    • 我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中
    • bean definition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
  • beanFactory 需要手动调用 beanFactory 后处理器对它做增强
    • 例如通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition
  • beanFactory 需要手动添加 bean 后处理器,以便对后续 bean 的创建过程提供增强
    • 例如 @Autowired,@Resource 等注解的解析都是 bean 后处理器完成的
    • bean 后处理的添加顺序会对解析结果有影响,见视频中同时加 @Autowired,@Resource 的例子
  • beanFactory 需要手动调用方法来初始化单例
  • beanFactory 需要额外设置才能解析 ${} 与 #{}

由上一章的内容,我们了解到,ConfigurableApplicationContext类内部组合的BeanFactory实际类型为DefaultListableBeanFactory,spring底层创建实体类就是依赖于这个类,所以它是BeanFactory接口最重要的一个实现类,下面使用这个类,模拟一下spring使用DefaultListableBeanFactory类创建其他实体类对象的过程。

@SpringBootTest
class SpringbootFirstApplicationTests {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //bean的定义(class、scope、初始化、销毁)

        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
            .setScope("singleton").getBeanDefinition();

        beanFactory.registerBeanDefinition("config",beanDefinition);

        for (String name: beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration
    static class Config{

        @Bean
        public Bean1 bean1(){
            return new Bean1();
        }

        @Bean
        public Bean2 bean2(){
            return new Bean2();
        }

    }

    @Slf4j
    static class Bean1 {

        public Bean1(){
            log.debug("构造 Bean1()");
        }

        @Autowired
        private Bean2 bean2;
        
        public Bean2 getBean2(){
            return bean2;
        }
    }

    @Slf4j
    static class Bean2 {

        public Bean2(){
            log.debug("构造 Bean2()");
        }
    }

}

结果,容器中现在只有config的bean,而没有bean1和bean2

所以,得知,现在的这个beanFactory并没有解析@Configuration和@Bean注解的能力

加上这个方法

//给beanFactory添加常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

结果

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory

结果还是没有bean1、bean2,只是多了几个后处理器,因为我们只是加入了后处理器,现在还并没有调用

添加方法

//拿到所有的bean工厂后处理器,是一个map集合,key是bean的名字,value是对象
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
    .stream().forEach( beanFactoryPostProcessor -> {
    //执行后处理器
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});

结果

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2

其中,bean1、bean2是由internalConfigurationAnnotationProcessor这个后处理器补充的

beanFactory后处理器主要功能就是,补充了一些bean的定义

在bean1中我们用了@Autowired注解注入了bean2,但是却在beanFactory中不能通过bean1来得到bean2

System.out.println(beanFactory.getBean(Bean1.class).getBean2());

结果为null

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
17:49:44.939 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
17:49:44.941 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
17:49:44.953 [main] DEBUG com.kuang.SpringbootFirstApplicationTests$Bean1 - 构造 Bean1()
null

Bean的后处理器,针对Bean的生命周期的各个阶段提供扩展,例如@Autowired@Resource

添加bean的后处理器

//bean的后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
    .forEach(beanFactory::addBeanPostProcessor);//方法引用,等价于

/*
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
                .forEach(beanPostProcessor -> {
                    beanFactory.addBeanPostProcessor(beanPostProcessor);
                });
*/

结果

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
17:59:17.945 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
17:59:17.947 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
17:59:17.968 [main] DEBUG com.kuang.SpringbootFirstApplicationTests$Bean1 - 构造 Bean1()
17:59:17.979 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
17:59:17.980 [main] DEBUG com.kuang.SpringbootFirstApplicationTests$Bean2 - 构造 Bean2()
com.feng.SpringbootFirstApplicationTests$Bean2@5ef60048

查看bean的构建时机,在调用bean1上面加上一行分割线

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
>>>>>>>>>>>>>>>>>>>>>>>>>>
18:17:49.504 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
18:17:49.506 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
18:17:49.523 [main] DEBUG com.feng.SpringbootFirstApplicationTests$Bean1 - 构造 Bean1()
18:17:49.532 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
18:17:49.532 [main] DEBUG com.feng.SpringbootFirstApplicationTests$Bean2 - 构造 Bean2()
com.feng.SpringbootFirstApplicationTests$Bean2@5ef60048

发现,只有在首次调用bean的时候,才会被初始化,刚开始,bean工厂中有的只是bean的描述信息,并没有被初始化

可以调用方法实现,预先初始化所有的单例bean

//单例bean预先初始化
beanFactory.preInstantiateSingletons();

总结:

beanFactory不会做的事

  • 不会主动调用beanFactory后处理器
  • 不会主动添加bean后处理器
  • 不会主动初始化单例bean
  • 不会解析beanFactory还不会解析${} 与 #{}

bean后处理器会有排序的逻辑

  • 默认是AutowiredAnnotationBeanPostProcessor(@Autowired)后处理器在前,CommonAnnotationBeanPostProcessor(@Resource)后处理器在后
  • 可以通过设置beanFactory的排序依据来设置,例如beanFactory.getDependencyComparator()
  • 排序的依据是BeanPostProcessor内部的order属性,其中internalAutowiredAnnotationProcessor的order属性的值为Ordered.LOWEST_PRECEDENCE - 2,internalCommonAnnotationProcessor的order属性的值为Ordered.LOWEST_PRECEDENCE - 3

ApplicationContext的常见实现和用法

测试代码

public class TestApplication {

    public static void main(String[] args) {
        testClassPathXmlApplicationContext();
        //testFileSystemXmlApplicationContext();
        
        //这个方法会帮我们自动加上beanFactory的后处理器
        //testAnnotationConfigApplicationContext();
        
        //这个方法是基于web环境,所以需要添加几个额外的bean定义
        //testAnnotationConfigServletWebServerApplicationContext();

    }

    // ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
    private static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("a02.xml");

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    // ⬇️基于磁盘路径下 xml 格式的配置文件来创建
    private static void testFileSystemXmlApplicationContext() {
        FileSystemXmlApplicationContext context =
            new FileSystemXmlApplicationContext(
            //绝对路径相对路径都可以
            "src\\main\\resources\\a02.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    // ⬇️较为经典的容器, 基于 java 配置类来创建
    private static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(Config.class);

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    // ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebServerApplicationContext context =
            new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration
    static class WebConfig {
        
        //tomcat服务器
        @Bean
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }
        
        //dispatcherServlet
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
        
        //将dispatcherServlet注册到tomcat服务器
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }
        
        @Bean("/hello")
        public Controller controller1() {
            return (request, response) -> {
                response.getWriter().print("hello");
                return null;
            };
        }
        
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        //因为bean2里面有bean1,所以需要加上参数,并用set方法注入进去
        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }

    static class Bean1 {
    }

    static class Bean2 {

        private Bean1 bean1;

        public void setBean1(Bean1 bean1) {
            this.bean1 = bean1;
        }

        public Bean1 getBean1() {
            return bean1;
        }
    }
}

a02.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">

    <!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
    <bean id="bean1" class="com.feng.TestApplication.Bean1"/>

    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.feng.TestApplication.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <!--需要set方法-->
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>

基于xml配置文件的bean工厂创建,实际上内部还是DefaultListableBeanFactory来管理的,使用了XmlBeanDefinitionReader来读取xml文件

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前");
for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
}
System.out.println("读取之后");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("a02.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
}

常见的spring容器

Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考

  • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现
  • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来

bean的生命周期

那么Spring到底是如何来创建一个Bean的呢,这个就是Bean创建的生命周期,大致过程如下

  1. 利用该类的构造方法来实例化得到一个对象(但是如何一个类中有多个构造方法,Spring则会进行选择,这个叫做推断构造方法
  2. 得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解的属性,把这些属性找出来并由Spring进行赋值(依赖注入
  3. 依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数(Aware回调
  4. Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法(初始化前
  5. 紧接着,Spring会判断该对象是否实现了InitializingBean接口,如果实现了,就表示当前对象必须实现该接口中的afterPropertiesSet()方法,那Spring就会调用当前对象中的afterPropertiesSet()方法(初始化
  6. 最后,Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean(初始化后

来看图的方式

创建
依赖注入
初始化
可用
销毁

创建前后的增强

  • postProcessBeforeInstantiation
    • 这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程
  • postProcessAfterInstantiation
    • 这里如果返回 false 会跳过依赖注入阶段

依赖注入前的增强

  • postProcessProperties
    • 如 @Autowired、@Value、@Resource

初始化前后的增强

  • postProcessBeforeInitialization
    • 这里返回的对象会替换掉原本的 bean
    • 如 @PostConstruct、@ConfigurationProperties
  • postProcessAfterInitialization
    • 这里返回的对象会替换掉原本的 bean
    • 如代理增强

销毁之前的增强

  • postProcessBeforeDestruction
    • 如 @PreDestroy

用代码的方式查看一个bean的生命周期

springboot启动类的编写

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ConfigurableApplicationContext context = SpringApplication.
            run(SpringbootFirstApplication.class, args);
        context.close();
    }

}

bean的编写

@Slf4j
@Component
public class LifeCycleBean {

    public LifeCycleBean() {
        log.error("构造");
    }

    @Autowired
    public void autowire(@Value("${JAVA_HOME}") String home) {
        log.error("依赖注入: {}", home);
    }

    @PostConstruct
    public void init() {
        log.error("初始化");
    }

    @PreDestroy
    public void destroy() {
        log.error("销毁");
    }
}

自定义bean的后处理器的编写

@Slf4j
@Component
public class MybeanPostProcess implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }
}

输出

06-29 12:09:19:797 ERROR 11708 --- [           main] com.feng.config.MybeanPostProcess        : <<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean
06-29 12:09:19:798 ERROR 11708 --- [           main] com.feng.bean.LifeCycleBean              : 构造
06-29 12:09:19:803 ERROR 11708 --- [           main] com.feng.config.MybeanPostProcess        : <<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段
06-29 12:09:19:803 ERROR 11708 --- [           main] com.feng.config.MybeanPostProcess        : <<<<<< 依赖注入阶段执行,@Autowired@Value@Resource
06-29 12:09:19:805 ERROR 11708 --- [           main] com.feng.bean.LifeCycleBean              : 依赖注入: D:\jdk1.8.0_131
06-29 12:09:19:807 ERROR 11708 --- [           main] com.feng.config.MybeanPostProcess        : <<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean,@PostConstruct@ConfigurationProperties
06-29 12:09:19:807 ERROR 11708 --- [           main] com.feng.bean.LifeCycleBean              : 初始化
06-29 12:09:19:807 ERROR 11708 --- [           main] com.feng.config.MybeanPostProcess        : <<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强
06-29 12:09:20:010  INFO 11708 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
06-29 12:09:20:099  INFO 11708 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
06-29 12:09:20:293  INFO 11708 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
06-29 12:09:20:297  INFO 11708 --- [           main] com.feng.SpringbootFirstApplication      : Started SpringbootFirstApplication in 2.562 seconds (JVM running for 3.9)
06-29 12:09:20:302  INFO 11708 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
06-29 12:09:20:303 ERROR 11708 --- [           main] com.feng.config.MybeanPostProcess        : <<<<<< 销毁之前执行,@PreDestroy
06-29 12:09:20:303 ERROR 11708 --- [           main] com.feng.bean.LifeCycleBean              : 销毁

实例化和初始化的区别

区别

「实例化」:实例化是创建类的实例的过程。在Spring中,当一个Bean被定义在配置文件中(或者通过其他方式如注解或Java配置),Spring IoC容器就会实例化这个Bean。实例化通常通过调用类的无参数构造函数来完成。这个步骤产生了一个Bean的实例,但是这个实例的属性尚未被设置。

「初始化」:初始化是在Bean实例化后、使用前的一个阶段,主要是对Bean进行一些定制化的设置,比如设置属性的值、执行某些方法等。在Spring中,你可以通过实现InitializingBean接口,或者使用@PostConstruct注解,或者在XML配置中定义<bean init-method="">,来定义Bean的初始化逻辑。

模板方法设计模式

模板方法模式定义了一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

简单的来说,比如①我们大家平时看病的时候,肯定会先挂号→排队等叫号→看病→付款→拿药。其中这个过程中,其实只有看病的过程是取决于我们得了什么病,是不确定的,而其他过程都是固定的流程,因此就可以用模板方法模式。②又比如我们设置闹钟的时候,通常会解锁手机→打开“时钟”→设置闹钟时间→关闭手机。这个过程实际上只有“设置闹钟时间”环节是不确定的,其他流程是固定的,因此也可以用模板方法模式。

模板方法结构

模板方法(Template Method)模式包含以下主要角色:

①抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
②具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级
逻辑的组成步骤。

模板方法模式举例分析

例如,一个spring工厂生产bean的步骤是固定的

// 模板方法  Template Method Pattern
static class MyBeanFactory {
    public Object getBean() {
        Object bean = new Object();
        System.out.println("构造 " + bean);
        System.out.println("依赖注入 " + bean); // @Autowired, @Resource
        System.out.println("初始化 " + bean);
        return bean;
    }

但是依赖注入阶段还可以选择进行增强,为了代码的可扩展性,所以可以选择使用模板方法

public class TestMethodTemplate {

    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.getBean();
    }

    // 模板方法  Template Method Pattern
    static class MyBeanFactory {
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            for (BeanPostProcessor processor : processors) {
                processor.inject(bean);
            }
            System.out.println("初始化 " + bean);
            return bean;
        }

        private List<BeanPostProcessor> processors = new ArrayList<>();

        public void addBeanPostProcessor(BeanPostProcessor processor) {
            processors.add(processor);
        }

    }

    static interface BeanPostProcessor {
        public void inject(Object bean); // 对依赖注入阶段的扩展
    }

}

Spring的后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:

  • BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行
  • BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行

BeanFactoryPostProcessor只执行一次,是在所有BeanDefinition填充到BeanDefinitionMap时

BeanPostProcessor会执行多次,每个bean实例化之后都会执行

Bean后处理器

Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

先自定义一个没有加bean后处理器的spring容器

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) {
        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();

        // ⬇️用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        // ⬇️初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

        System.out.println(context.getBean(Bean1.class));

        // ⬇️销毁容器
        context.close();

    }

}

bean1、bean2、bean3、bean4的定义如下:

//bean1
public class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    private Bean2 bean2;

    @Autowired
    public void setBean2(Bean2 bean2) {
        log.error("@Autowired 生效: {}", bean2);
        this.bean2 = bean2;
    }

    @Autowired
    private Bean3 bean3;

    @Resource
    public void setBean3(Bean3 bean3) {
        log.error("@Resource 生效: {}", bean3);
        this.bean3 = bean3;
    }

    private String home;

    @Autowired
    public void setHome(@Value("${JAVA_HOME}") String home) {
        log.error("@Value 生效: {}", home);
        this.home = home;
    }

    @PostConstruct
    public void init() {
        log.error("@PostConstruct 生效");
    }

    @PreDestroy
    public void destroy() {
        log.error("@PreDestroy 生效");
    }

    @Override
    public String toString() {
        return "Bean1{" +
               "bean2=" + bean2 +
               ", bean3=" + bean3 +
               ", home='" + home + '\'' +
               '}';
    }
}



//bean2
public class Bean2 {
}


//bean3
public class Bean2 {
}



//bean4
@ConfigurationProperties(prefix = "java")
public class Bean4 {

    private String home;

    private String version;

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "Bean4{" +
               "home='" + home + '\'' +
               ", version='" + version + '\'' +
               '}';
    }
}

在未添加bean的后处理器时,控制台上只有spring容器的信息,加上bean的后处理器

public static void main(String[] args) {
    // ⬇️GenericApplicationContext 是一个【干净】的容器
    GenericApplicationContext context = new GenericApplicationContext();

    // ⬇️用原始方法注册三个 bean
    context.registerBean("bean1", Bean1.class);
    context.registerBean("bean2", Bean2.class);
    context.registerBean("bean3", Bean3.class);
    context.registerBean("bean4", Bean4.class);

    context.getDefaultListableBeanFactory().
        setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
    
    context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired、@Value

    context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy

    ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

    // ⬇️初始化容器
    context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例

    System.out.println(context.getBean(Bean1.class));

    // ⬇️销毁容器
    context.close();

}

输出

13:08:26.998 [main] ERROR com.feng.bean.Bean1 - @Resource 生效: com.feng.bean.Bean3@87f383f
13:08:27.008 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
13:08:27.021 [main] ERROR com.feng.bean.Bean1 - @Value 生效: D:\jdk1.8.0_131
13:08:27.021 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
13:08:27.021 [main] ERROR com.feng.bean.Bean1 - @Autowired 生效: com.feng.bean.Bean2@4d1b0d2a
13:08:27.021 [main] ERROR com.feng.bean.Bean1 - @PostConstruct 生效
13:08:27.021 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean4'
13:08:27.159 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'spring.liveBeansView.mbeanDomain' in PropertySource 'systemProperties' with value of type String
Bean1{bean2=com.feng.bean.Bean2@4d1b0d2a, bean3=com.feng.bean.Bean3@87f383f, home='D:\jdk1.8.0_131'}
13:08:27.164 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@7006c658, started on Thu Jun 29 13:08:26 CST 2023
13:08:27.164 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'spring.liveBeansView.mbeanDomain' in PropertySource 'systemProperties' with value of type String
13:08:27.164 [main] ERROR com.feng.bean.Bean1 - @PreDestroy 生效
  • @Autowired对应的后处理器是AutowiredAnnotationBeanPostProcessor
  • @Value需要配合@Autowired一起使用,所以也用到了AutowiredAnnotationBeanPostProcessor后处理器,然后@Value还需要再用到ContextAnnotationAutowireCandidateResolver解析器,否则会报错
  • @Resource、@PostConstruct、@PreDestroy对应的后处理CommonAnnotationBeanPostProcessor
  • @ConfigurationProperties对应的后处理器是ConfigurationPropertiesBindingPostProcessor

分析AutowiredAnnotationBeanPostProcessor

public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean3", new Bean3());
        
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value解析
        
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
        processor.setBeanFactory(beanFactory);

        Bean1 bean1 = new Bean1();
System.out.println("注入前>>>>>>>>>");
        Bean1 bean1 = new Bean1();
        System.out.println(bean1);
        System.out.println("注入后>>>>>>>>>");
        processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入@Autowired、@Value
        System.out.println(bean1);

    }
}

结果

注入前>>>>>>>>>
Bean1{bean2=null, bean3=null, home='null'}
注入后>>>>>>>>>
15:14:04.200 [main] ERROR com.feng.bean.Bean1 - @Autowired 生效: com.feng.bean.Bean2@20322d26
15:14:04.212 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
15:14:04.214 [main] ERROR com.feng.bean.Bean1 - @Value 生效: D:\jdk1.8.0_131
Bean1{bean2=com.feng.bean.Bean2@20322d26, bean3=com.feng.bean.Bean3@5383967b, home='D:\jdk1.8.0_131'}

AutowiredAnnotationBeanPostProcessor的工作原理,它是通过postProcessProperties方法来实现注解的解析和注入的,而这个方法中,又调用了findAutowiringMetadata这个方法来查找有AutoWired注解的属性,然后会调用InjectionMetadata.inject(bean1, "bean1", null)进行依赖注入。

下面通过反射来获取findAutowiringMetadata方法,然后再调用inject方法

Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor
    .class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, 	           PropertyValues.class);
//这个方法是私有的,需要加上setAccessible(true)
findAutowiringMetadata.setAccessible(true);
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
System.out.println(metadata);
// 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
metadata.inject(bean1, "bean1", null);
System.out.println(bean1);

结果,通过debug断点调试,发现metadata里面的injectedElements集合里面就是被@Autowired注解的属性,并且,也成功注入了bean

image-20230629152137425

它是怎么来通过类型查找的

  • 成员变量:例bean1中用@Autowired注入了bean3
Field bean3 = Bean1.class.getDeclaredField("bean3");
//工具类DependencyDescriptor描述一个用于注入的依赖。
//第二个boolean类型的参数,true代表必须注入,false代表找不到的话不注入也不会报错
DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
//找到要注入的对象
Object o = beanFactory.doResolveDependency(dd1, null, null, null);

System.out.println(o);

inject会把拿到的beanName,通过反射来获取对应的属性,然后用DependencyDescriptor封装成一个成员属性依赖,接着用doResolveDependency方法,通过封装好的成员变量,来获取要注入的bean。

  • 方法:例bean1中用@Autowired注解注入了bean2
@Autowired
public void setBean2(Bean2 bean2) {
    log.error("@Autowired 生效: {}", bean2);
    this.bean2 = bean2;
}
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
//是以参数为单位进行封装的,第一个参数是要注入的方法信息,里面的第一个值为方法名,第二个为参数下标,从0开始
DependencyDescriptor dd2 =
    new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
//找到要注入的对象,第二个参数是指要装配到哪
Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);

System.out.println(o1);

和成员变量注入的过程类似,这里就不再赘述了

  • 值注入:例bean1中用@Autowired@Value注解注入了环境变量${JAVA_HOME}
Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(o2);

过程和成员方法的过程类似,不再赘续

BeanFactory后处理器

BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) throws IOException {

        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        // ⬇️销毁容器
        context.close();

    }

}
@Configuration
@ComponentScan("com.feng.component")
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/reggie");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

}



//bean1的定义如下
public class Bean1 {

    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    public Bean1() {
        log.debug("我被 Spring 管理啦");
    }
}

component包下面有三个bean,分别是,bean2、bean3

@Component
public class Bean2 {

    private static final Logger log = LoggerFactory.getLogger(Bean2.class);

    public Bean2() {
        log.debug("我被 Spring 管理啦");
    }
}



@Controller
public class Bean3 {

    private static final Logger log = LoggerFactory.getLogger(Bean3.class);

    public Bean3() {
        log.debug("我被 Spring 管理啦");
    }
}

上述代码,在springboot的启动类上使用了一个干净的容器,GenericApplicationContext,这个容器不会自动注入bean工厂的后处理器、bean的后处理器,然后注入了一个Config,同时这个Config是一个配置类,并且指定了扫描包,在属性中又注入了bean1,测试结果。

image-20230629164006326

容器中只有config,没有其他的bean,为什么会这样呢?

@ComponentScan("com.feng.component")@bean注解没有被扫描

加上这个bean工厂后处理器

context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource

结果成功加上了其他的几个bean

image-20230629164623836

加上一个扫描mybatis的mapper接口的bean工厂后处理器

//和mybatis整合时扫描的mapper接口,需要指定扫描包
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScanner
    bd.getPropertyValues().add("basePackage", "com.feng.mapper");
});



//对应的mapper包下的为
@Mapper
public interface Mapper1 {
}



@Mapper
public interface Mapper2 {
}

结果,加上了这两个mapper

image-20230629170814041

模拟@Component的实现

编写一个自定义的bean工厂后处理器,来模拟Component注解的处理

public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    
    
    @Override // context.refresh后会调用这个方法
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        
        try {
            
            //spring提供的一个工具类AnnotationUtils,查找某个类上是否加了某注解
            //会返回这个注解对象,找不到就返回空
            ComponentScan componentScan = AnnotationUtils
                .findAnnotation(Config.class, ComponentScan.class);
            
            //判断是否为空
            if (componentScan != null) {
                //遍历要扫描的包,先得到basePackages属性,componentScan.basePackages()
                for (String p : componentScan.basePackages()) {
                    System.out.println(p);
                    // com.feng.component -> classpath*:com/feng/component/**/*.class
                    //将包名转化成路径的格式
                    //因为spring是根据文件路径来判断的类上是否加了该注解
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    System.out.println(path);
                    
                    //注1
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    
                    //getResource()方法来从文件系统,类路径或URL得到资源。
                    Resource[] resources = new PathMatchingResourcePatternResolver()
                        .getResources(path);
                    
                    //用于根据注解生成一个bean的名字
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    
                    for (Resource resource : resources) {
                        // System.out.println(resource);
                        
                        //注1
                        MetadataReader reader = factory.getMetadataReader(resource);
                        // System.out.println("类名:" + reader.getClassMetadata().getClassName());
                        
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        
                        // System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                        // System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                        
                        //判断这个类上面有没有Component这个注解
                        //或者是Component的派生注解,例如@Controller
                        if (annotationMetadata
                            .hasAnnotation(Component.class.getName())
                            ||
                            annotationMetadata
                            .hasMetaAnnotation(Component.class.getName())) {
                            
                            //通过类的源信息,来获取到全类名,来获取BeanDefinitionBuilder的对象
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                .getBeanDefinition();
                            
                            //根据给定的BeanDefinition和BeanFactory生成一个唯一的Bean名称
                            String name = generator.generateBeanName(bd, beanFactory);
                            //将这个bean注册到bean工厂中
                            beanFactory.registerBeanDefinition(name, bd);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

CachingMetadataReaderFactory是Spring框架中的一个类,用于提供缓存功能,用于缓存已解析的类元数据信息。

  • 在Spring框架中,当加载类的时候,会解析类的元数据信息,比如类的注解、字段、方法等信息。如果应用程序中需要频繁地读取类的元数据信息,解析的过程可能会导致性能问题。
  • CachingMetadataReaderFactory就是为了解决这个性能问题而引入的。它通过缓存已解析的类元数据,避免每次都进行解析操作。这样,在应用程序中多次读取同一个类的元数据时,就可以直接从缓存中获取,避免重复解析,提高了应用程序的性能。
  • 用户可以通过在Spring的配置文件中配置CachingMetadataReaderFactory的相关参数,来控制缓存的使用。默认情况下,Spring会自动配置CachingMetadataReaderFactory,并启用缓存功能。

getMetadataReader方法是该类中的一个方法,用于获取指定类的元数据读取器。

  • 参数说明:

    • Resource:要获取元数据读取器的资源
  • 返回值:

    • MetadataReader:指定资源的元数据读取器

ConfigurationClassPostProcessor的大概流程:

  1. 先拿到某个类上的某个注解
  2. 然后拿到这个注解中要扫描的包
  3. 拿到包之后,再获取包下面类的二进制资源
  4. 得到资源后再对资源进行分析,主要分析类名,和上面是否加了某注解
  5. 如果加了对应的注解,就生成BeanDefinitionBuilder对象,并且生成bean名
  6. 最后将这个bean注册到bean工厂中

模拟@Bean的实现

public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            
            //获取这个类的元数据
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/feng/config/Config.class"));
            //getAnnotationMetadata()返回这个元数据的注解信息,getAnnotatedMethods(Bean.class.getName())获取带有Bean注解的方法元数据,Bean.class.getName()获取Bean注解的全限定名
            Set<MethodMetadata> methods = reader.getAnnotationMetadata()
                .getAnnotatedMethods(Bean.class.getName());
            //遍历所有的源信息
            for (MethodMetadata method : methods) {
                System.out.println(method);
                
                //获取注解的属性信息
                String initMethod = method.getAnnotationAttributes(Bean.class.getName())
                    .get("initMethod").toString();
                
                BeanDefinitionBuilder builder = BeanDefinitionBuilder
                    .genericBeanDefinition();
                //通过工厂方法来构造这个beanDefinition
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                //因为bean1中的一个方法public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource),它需要自动装配所以需要设置,根据构造函数自动装配
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                
                if (initMethod.length() > 0) {
                    builder.setInitMethodName(initMethod);
                }
                //注册bean
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                beanFactory.registerBeanDefinition(method.getMethodName(), bd);
                
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

模拟@MapperScanner的实现

基本方式添加的Mapper接口

@Configuration
@ComponentScan("com.feng.component")
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/reggie");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    @Bean
    public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

    @Bean
    public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }
}




//mapper接口
@Mapper
public interface Mapper1 {
}



@Mapper
public interface Mapper2 {
}

模拟实现

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            //读取源信息
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.
                getResources("classpath:com/feng/mapper/**/*.class");
            
            //生成唯一的beanName
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            //换成已经解析的类源信息
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            //遍历每个源信息
            for (Resource resource : resources) {
                //放入缓存
                MetadataReader reader = factory.getMetadataReader(resource);
                //读取类元信息
                ClassMetadata classMetadata = reader.getClassMetadata();
                //只处理是接口的
                if (classMetadata.isInterface()) {
                    //生成BeanDefinition,并且设置自动装配
                    AbstractBeanDefinition bd = BeanDefinitionBuilder
                        .genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    
                    //根据类名生成beanName
                    AbstractBeanDefinition bd2 = BeanDefinitionBuilder
                        .genericBeanDefinition(classMetadata.getClassName())
                        .getBeanDefinition();
                    
                    String name = generator.generateBeanName(bd2, beanFactory);
                    beanFactory.registerBeanDefinition(name, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

bean实例化的三种常用方式:

  1. 构造函数实例化:使用构造函数来实例化Bean对象。可以在Bean的定义中使用构造函数参数来传递依赖项。
  2. 静态工厂方法:通过调用一个静态方法来实例化Bean对象。在Bean的定义中,可以使用"factory-method"属性来指定静态工厂方法的名称。
  3. 实例工厂方法:通过调用一个实例方法来实例化Bean对象。在Bean的定义中,可以使用"factory-bean"和"factory-method"属性来指定实例工厂方法所在的Bean和方法名称。

什么是BeanDefinition

spring IoC 容器中的每一个 bean 都会有一个对应的 BeanDefinition 实例,该实例负责保存 bean 对象的所有必要信息,如下所示:

属性(property)说明
classbean 全类名,必须是具体类,不能是抽象类或接口(因为抽象类或接口不能实例化)
namebean 的名称或 id
scopebean 的作用域
constructor argumentsbean 构造器参数(用于依赖注入)
propertiesbean 属性设置(用于依赖注入)
autowiring modebean 自动绑定模式(例如通过名称 byName)
lazy initialization modebean 延迟初始化模式(延迟和非延迟)
initialization methodbean 初始化回调方法名称
destruction methodbean 销毁回调方法名称
AbstractBeanDefinition

BeanDefinition 是个接口,AbstractBeanDefinition 是这个接口的实现类,很多常见的 bean 属性在 AbstractBeanDefinition 中。这是经典的工厂模式,抽象出接口去规范工厂生产的实体类的行为。

AbstractBeanDefinition 定义了一系列描述 bean 的属性,可以看到 bean 属性的某些默认值(例如默认为单例)。这个类的属性部分可以在 xml 配置文件中找到相应的属性或是标签(例如 AbstractBeanDefinition 的 scope 属性对应 xml 配置文件中的 scope 属性)。

BeanDefinition 的构造方式
  1. 通过 BeanDefinitionBuilder
  2. 通过 AbstractBeanDefinition 及其派生类
public static void main(String[] args) {
    // 1. 通过 BeanDefinitionBuilder 构建,参数是为其创建定义的bean的类名
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
    // 设置 bean 属性,builder 模式可以做到链式调用
    beanDefinitionBuilder.addPropertyValue("id", 1).addPropertyValue("name", "小明");
    // 获取 BeanDefinition 实例
    BeanDefinition abstractBeanDefinition = beanDefinitionBuilder.getBeanDefinition();

    // BeanDefinition 不是 Bean 的终态,可以自定义修改

    // 2. 通过 AbstractBeanDefinition 及其派生类构建
    AbstractBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
    // 设置 bean 类型
    genericBeanDefinition.setBeanClass(User.class);
    // 通过 MutablePropertyValues 批量操作属性,这也是一种链式调用
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    // propertyValues.add("id", 1);
    // propertyValues.add("name", "小明");
    propertyValues.add("id", 1).add("name", "小明");
    genericBeanDefinition.setPropertyValues(propertyValues);
}




在默认的情况下,当我们启动Spring,不管我们有没有使用到Bean,Spring总是在创建完IOC容器后马上向IOC容器中添加我们定义的Bean

测试

@Configuration
public class MyConfiguration {

    @Bean
    public Person person() {
        System.out.println("加载Bean对象");
        return new Person(18, "wang");
    }

}


@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
        System.out.println("启动Spring");
    }

}

结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nv72QYIj-1688899739942)(C:\Users\86136\AppData\Roaming\Typora\typora-user-images\image-20230429132008543.png)]

有时候我们并不希望Spring加载所有的Bean,而是当我们需要用到Bean时,再加载Bean,即懒加载,Spring提供了一个注解@Lazy来支持这个功能,只需要将这个注解添加在需要懒加载的Bean上即可

当我们在Sping运行时第一次从IOC容器中获取这个Bean时,Sping才会加载这个Bean,这样做节约了系统开销

懒加载只针对单例的bean才生效

Aware接口

Aware是一个具有标识作用的超级接口,具体实现是有子接口去决定的,但是子接口至少要有一个带一个参数的且返回是空的方法。实现该接口的bean是具有被spring 容器通知的能力的,而被通知的方式就是通过回调。也就是说:直接或间接实现了这个接口的类,都具有被spring容器通知的能力。

Aware翻译过来是adj. 知道的,明白的,察觉到的,意识到的,所以这些接口从字面意思应该是能感知到所有Aware前面的含义。比如实现了ApplicationContextAware接口的类,能够获取到ApplicationContext,实现了BeanFactoryAware接口的类,能够获取到BeanFactory对象。

有些时候,在 Bean 的初始化中,需要使用 Spring 框架自身的对象来执行一些操作,比如获取 ServletContext 的一些参数,获取 ApplicaitionContext 中的 BeanDefinition 的名字,获取 Bean 在容器中的名字等等。为了让 Bean 可以获取到框架自身的一些对象,Spring 提供了一组以 Aware 为结尾的接口。

这些接口均继承于 org.springframework.beans.factory.Aware 标记接口,并提供了由 Bean 实现的 set 方法,Spring 通过基于 setter 的依赖注入方式,使相应的对象可以被 Bean 使用。以下是一些重要的 Aware 接口:

  • ApplicationContextAware:获得 ApplicationContext 对象,可以用来获取所有 Bean definition 的名字;
  • BeanFactoryAware:获得 BeanFactory 对象,可以用来检测 Bean 的作用域;
  • BeanNameAware:获得 Bean 在配置文件中定义的名字;
  • ResourceLoaderAware:获得 ResourceLoader 对象,可以获得 classpath 中的某个文件;
  • ServletContextAware:在 MVC 应用中,可以获取 ServletContext 对象,可以读取 Context 中的参数;
  • ServletConfigAware: 在 MVC 应用中,可以获取 ServletConfig 对象,可以读取 Config 中的参数。

接口实现

@Slf4j
public class MyBean implements BeanNameAware , ApplicationContextAware , BeanFactoryAware {

    @Override
    public void setBeanName(String name) {
        log.error("当前bean"+ this +"名字叫:" + name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.error("当前bean"+ this +"容器是:" + applicationContext);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.error("当前bean"+ this +"工厂是:" + beanFactory);
    }
}

启动类

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) throws IOException {

        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myBean",MyBean.class);
        context.refresh();
        context.close();
    }

}

结果

[ERROR] 18:45:40.280 [main] com.feng.bean.MyBean                - 当前beancom.feng.bean.MyBean@8646db9名字叫:myBean 
[ERROR] 18:45:40.285 [main] com.feng.bean.MyBean                - 当前beancom.feng.bean.MyBean@8646db9工厂是:org.springframework.beans.factory.support.DefaultListableBeanFactory@13eb8acf: defining beans [myBean]; root of factory hierarchy 
[ERROR] 18:45:40.286 [main] com.feng.bean.MyBean                - 当前beancom.feng.bean.MyBean@8646db9资源路径是:class path resource [com/feng/mapper/**/*.class] 
[ERROR] 18:45:40.288 [main] com.feng.bean.MyBean                - 当前beancom.feng.bean.MyBean@8646db9容器是:org.springframework.context.support.GenericApplicationContext@5700d6b1, started on Fri Jun 30 18:45:40 CST 2023 

在这里,我们可能会有一个问题,上面这些功能,有的注解也能完成,例如可以使用@Autowired注解来注入ApplicationContext,为什么还要用Aware接口呢?

简单的说,@Autowired的解析需要用到bean后处理器,属于扩展功能,而Aware接口属于内置功能,不加任何扩展,spring就能识别,而在某些情况下,扩展功能会失效,内置功能不会失效

那么,什么情况下,这些扩展功能会失效呢?

来看这样一种情况

@Slf4j
@Configuration
public class MyConfig1 {

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.error("注入 ApplicationContext");
    }

    @PostConstruct
    public void init() {
        log.error("初始化");
    }

    @Bean //  beanFactory 后处理器
    public BeanFactoryPostProcessor processor1() {
        return beanFactory -> {
            log.error("执行 processor1");
        };
    }
}



//启动类
@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) throws IOException {

        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myConfig1", MyConfig1.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        context.refresh();
        context.close();
    }

}

结果

[INFO ] 19:03:42.389 [main] o.s.c.a.ConfigurationClassEnhancer  - @Bean method MyConfig1.processor1 is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details. 
[ERROR] 19:03:42.400 [main] com.feng.config.MyConfig1           - 执行 processor1

结果在myConfig1中加上了bean工厂后处理器之后,@Autowired@PostConstruct注解就失效了

流程分析

配置类不包含 BeanFactoryPostProcessor 的情况

ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor 3. 创建和初始化 3.1 依赖注入扩展(如 @Value 和 @Autowired) 3.2 初始化扩展(如 @PostConstruct) 3.3 执行 Aware 及 InitializingBean 3.4 创建成功 ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类

配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效

ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 3. 创建和初始化 3.1 执行 Aware 及 InitializingBean 3.2 创建成功 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类

正常情况下,BeanFactoryPostProcessor会在Java配置类初始化之前执行,而Java配置类里面却定义了一个BeanFactoryPostProcessor,要创建其中的 BeanFactoryPostProcessor ,必须提前创建 Java 配置类,这样BeanFactoryPostProcessor就会在Java配置类初始化后执行了,而此时的 BeanFactoryPostProcessorBeanPostProcess还未准备好,导致 @Autowired 等注解失效,扩展功能失效,只能有内置的Aware功能。

来看一下解决方法

使用内置的Aware接口,来实现上面的功能

@Slf4j
@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
    
    @Override
    public void afterPropertiesSet() throws Exception {
        log.error("初始化");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.error("注入 ApplicationContext");
    }

    @Bean //  beanFactory 后处理器
    public BeanFactoryPostProcessor processor2() {
        return beanFactory -> {
            log.error("执行 processor2");
        };
    }
}

结果

[ERROR] 10:12:51.655 [main] com.feng.config.MyConfig2           - 注入 ApplicationContext 
[ERROR] 10:12:51.660 [main] com.feng.config.MyConfig2           - 初始化 
[INFO ] 10:12:51.663 [main] o.s.c.a.ConfigurationClassEnhancer  - @Bean method MyConfig2.processor2 is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details. 
[ERROR] 10:12:51.672 [main] com.feng.config.MyConfig2           - 执行 processor2

InitializingBean接口

1、InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

2、spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。

3、在Spring初始化bean的时候,如果该bean实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertieSet()方法,然后再调用init-method中指定的方法。

1、Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用。

2、实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。

3、如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

Bean的初始化和销毁

启动类

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootFirstApplication.class, args);
        context.close();
    }

    @Bean(initMethod = "init3")
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean(destroyMethod = "destroy3")
    public Bean2 bean2() {
        return new Bean2();
    }

}

bean类

//bean1
@Slf4j
public class Bean1 implements InitializingBean {

    @PostConstruct
    public void init1() {
        log.error("初始化1");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.error("初始化2");
    }

    public void init3() {
        log.error("初始化3");
    }
}


//bean2
@Slf4j
public class Bean2 implements DisposableBean {

    @PreDestroy
    public void destroy1() {
        log.error("销毁1");
    }

    @Override
    public void destroy() throws Exception {
        log.error("销毁2");
    }

    public void destroy3() {
        log.error("销毁3");
    }
}

结果

初始化方法的顺序为

[ERROR] 10:26:19.588 [main] com.feng.config.Bean1               - 初始化1 
[ERROR] 10:26:19.588 [main] com.feng.config.Bean1               - 初始化2 
[ERROR] 10:26:19.588 [main] com.feng.config.Bean1               - 初始化3

销毁方法的顺序为

[ERROR] 10:31:08.412 [main] com.feng.config.Bean2               - 销毁1 
[ERROR] 10:31:08.412 [main] com.feng.config.Bean2               - 销毁2 
[ERROR] 10:31:08.412 [main] com.feng.config.Bean2               - 销毁3
  • 三个初始化方法的执行顺序是

    • 扩展功能@PostConstruct -> InitializingBean接口 -> @Bean的initMethod
  • 三个销毁方法的执行顺序是

    • 扩展功能@PreDestroy -> DisposableBean接口 -> @Bean的destroy

Bean的作用范围Scope

spring的scope类型:

  • singleton:单例
  • prototype:多例
  • request:存在request域中,生命周期也是和request相关
  • session:存在session中,会话开始,bean被创建,会话结束被销毁
  • application:web的ServletContext启动,bean被创建,结束被销毁

这里来测试一下,request、session、application的作用域

//启动类
@SpringBootApplication
public class SpringbootFirstApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootFirstApplication.class, args);
    }
}


//application
@Slf4j
@Scope("application")
@Component
public class BeanForApplication {

    @PreDestroy
    public void destroy() {
        log.error("destroy");
    }
}


//request
@Slf4j
@Scope("request")
@Component
public class BeanForRequest {
    @PreDestroy
    public void destroy() {
        log.error("destroy");
    }
}


//session
@Slf4j
@Scope("session")
@Component
public class BeanForSession {
    @PreDestroy
    public void destroy() {
        log.error("destroy");
    }
}



//controller
@RestController
public class MyController {

    //MyController是单例,在单例中使用其他作用域的都要加上@Lazy
    @Lazy
    @Autowired
    private BeanForRequest beanForRequest;

    @Lazy
    @Autowired
    private BeanForSession beanForSession;

    @Lazy
    @Autowired
    private BeanForApplication beanForApplication;

    @GetMapping(value = "/test", produces = "text/html")
    public String test(HttpServletRequest request, HttpSession session) {
        ServletContext sc = request.getServletContext();
        String sb = "<ul>" +
                    "<li>" + "request scope:" + beanForRequest + "</li>" +
                    "<li>" + "session scope:" + beanForSession + "</li>" +
                    "<li>" + "application scope:" + beanForApplication + "</li>" +
                    "</ul>";
        return sb;
    }

}

浏览器上查看

image-20230701110443340

控制台上的结果

[ERROR] 10:57:59.624 [http-nio-8080-exec-5] com.feng.config.BeanForRequest      - destroy 
[DEBUG] 10:57:59.624 [http-nio-8080-exec-5] o.s.web.servlet.DispatcherServlet   - Completed 200 OK, headers={masked} 
[INFO ] 10:58:28.227 [SpringContextShutdownHook] o.s.s.c.ThreadPoolTaskExecutor      - Shutting down ExecutorService 'applicationTaskExecutor' 
[ERROR] 10:58:28.461 [SpringContextShutdownHook] com.feng.config.BeanForSession      - destroy

结果每刷新一次浏览器,request的对象都会发生变化,但是session和application的对象却不会变化,因为每次刷新都是一次新的请求,而session是作用在浏览器上的,只要浏览器不关闭,session就不会变,切换浏览器,session也会发生变化,application是web应用程序的对象,即使是不同浏览器,application对象也是一个。request请求来了被创建,请求结束被销毁。session超时也会被销毁。

scope失效问题

以单例注入多例为例,单例注入其他的解决方法也类似

@Slf4j
@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(SpringbootFirstApplication.class);

        E e = context.getBean(E.class);
        log.error("{}", e.getF1().getClass());
        log.error("{}", e.getF1());
        log.error("{}", e.getF1());
        log.error("{}", e.getF1());
        context.close();
    }
}



//E单例,注入了一个F1
@Component
public class E {

    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}



//F1是多例
@Scope("prototype")
@Component
public class F1 {
}

结果

[ERROR] 11:22:16.004 [main] com.feng.SpringbootFirstApplication - class com.feng.sub.F1 
[ERROR] 11:22:16.004 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F1@3d526ad9 
[ERROR] 11:22:16.004 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F1@3d526ad9 
[ERROR] 11:22:16.004 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F1@3d526ad9

拿到的F1都是同一个对象

为什么会出现这种情况呢,来看一张图

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F

e 创建
e set 注入 f
f 创建

解决方案1

  • 仍然使用 @Lazy 生成代理
  • 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的 F对象
使用f方法
使用f方法
使用f方法
E 创建
E set 注入 F代理
F 创建
F 创建
F 创建
//加上@Lazy注解
@Lazy
@Autowired
private F1 f1;

结果

[ERROR] 11:27:30.389 [main] com.feng.SpringbootFirstApplication - class com.feng.sub.F1$$EnhancerBySpringCGLIB$$35294557 
[ERROR] 11:27:30.390 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F1@58cd06cb 
[ERROR] 11:27:30.397 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F1@64b31700 
[ERROR] 11:27:30.397 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F1@bae47a0

并且,注入的F1已经是被spring的代理增强的了

解决方案2

  • 在要注入的类上添加注解属性@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
  • 配置代理模式,原理也是生成代理

例,E中添加一个新的依赖注入F2

@Autowired
private F2 f2;

public F2 getF2() {
    return f2;
}



//F2
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}

添加属性前

[ERROR] 11:33:59.697 [main] com.feng.SpringbootFirstApplication - class com.feng.sub.F2 
[ERROR] 11:33:59.697 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F2@26f143ed 
[ERROR] 11:33:59.697 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F2@26f143ed 
[ERROR] 11:33:59.697 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F2@26f143ed

添加属性后

[ERROR] 11:33:29.934 [main] com.feng.SpringbootFirstApplication - class com.feng.sub.F2$$EnhancerBySpringCGLIB$$2920a0c0 
[ERROR] 11:33:29.934 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F2@6548bb7d 
[ERROR] 11:33:29.936 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F2@e27ba81 
[ERROR] 11:33:29.936 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F2@54336c81

解决方案3

  • 在注入的时候,不直接注入,而是注入一个对象工厂
  • 在获取对象的时候就会先获取工厂,然后再用工厂来获取F3

例,E中注入F3

@Autowired
private ObjectFactory<F3> f3;

public F3 getF3() {
    return f3.getObject();
}


//F3
@Scope("prototype")
@Component
public class F3 {
}

结果

[ERROR] 11:39:07.544 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F3@2fb68ec6 
[ERROR] 11:39:07.544 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F3@3add81c4

解决方案4

  • 注入ApplicationContext容器,通过容器来获取想要注入的对象

例,在E中想注入F4,可以用注入ApplicationContext容器的方式

@Autowired
private ApplicationContext context;

public F4 getF4() {
    return context.getBean(F4.class);
}




//F4
@Scope("prototype")
@Component
public class F4 {
}

结果

[ERROR] 11:43:42.654 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F4@2fb68ec6 
[ERROR] 11:43:42.654 [main] com.feng.SpringbootFirstApplication - com.feng.sub.F4@d71adc2

这几种解决方法都是殊途同归,都是延迟单例对其他作用范围的获取,因为单例只注入一次,所以要在运行时获取。

Aop

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中

  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能

  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

ajc 编译器增强

待增强类

@Service
public class MyService {

    private static final Logger log = LoggerFactory.getLogger(MyService.class);

    public void foo(){
        log.error("foo()");
    }
}

切面

@Aspect//注意,此切面并没有被spring管理
public class MyAspect {

    private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

    @Before("execution(* com.feng.service.MyService.foo())")
    public void before(){
        log.error("before()");
    }
}

启动类

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootFirstApplication.class, args);
        MyService service = context.getBean(MyService.class);

        final Logger log = LoggerFactory.getLogger(SpringbootFirstApplication.class);


        log.error("service class: {}", service.getClass());
        service.foo();

        context.close();

        //new MyService().foo();

    }

结果

[ERROR] 14:35:37.138 [main] com.feng.SpringbootFirstApplication - service class: class com.feng.service.MyService 
[ERROR] 14:35:37.139 [main] com.feng.aop.MyAspect               - before() 
[ERROR] 14:35:37.139 [main] com.feng.service.MyService          - foo()

结果还是原始类型,并不是被增强类型,下面查看MyService的字节码文件

找到target目录下面的MyService文件,双击查看编译的字节码文件,idea中打开,已经反编译

@Service
public class MyService {
    private static final Logger log = LoggerFactory.getLogger(MyService.class);

    public MyService() {
    }

    public void foo() {
        MyAspect.aspectOf().before();
        log.error("foo()");
    }
}

说明Aop的这个增强是通过修改MyService的源代码来实现增强的,并且不需要被spring的管理,上面代码中,可以直接new一个MyService()对象然后在调用foo方法也可以实现增强。

它是通过一个ajc编译器来实现增强的,需要在maven中加上一个插件,加上maven依赖

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.14.0</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>8</source>
        <target>8</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <Xlint>ignore</Xlint>
        <encoding>UTF-8</encoding>
    </configuration>
    <executions>
        <execution>
            <goals>
                <!-- use this goal to weave all your main classes -->
                <goal>compile</goal>
                <!-- use this goal to weave all your test classes -->
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>



<!--aspectj的依赖-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
</dependency>

用ajc增强的好处是,可以突破一些代理的限制,因为代理本质上是通过方法重写实现的,如果想要增强的是目标类的静态方法,代理就增强不了,但是编译器增强是可以实现的。

agent增强

需要增强的类

@Service
public class MyService {

    private static final Logger log = LoggerFactory.getLogger(MyService.class);

    final public void foo() {
        log.error("foo()");
        this.bar();
    }

    public void bar() {
        log.error("bar()");
    }
}

切面

@Aspect//注意,此切面并没有被spring管理
public class MyAspect {

    private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

    @Before("execution(* com.feng.service.MyService.*())")
    public void before() {
        log.error("before()");
    }
}

启动类

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication
            .run(SpringbootFirstApplication.class, args);
        MyService service = context.getBean(MyService.class);

        final Logger log = LoggerFactory.getLogger(SpringbootFirstApplication.class);

        log.error("service class: {}", service.getClass());
        service.foo();
        //context.close();
        //new MyService().foo();
    }

}

结果

[ERROR] 14:52:42.836 [main] com.feng.SpringbootFirstApplication - service class: class com.feng.service.MyService
[ERROR] 14:52:42.836 [main] com.feng.aop.MyAspect               - before()
[ERROR] 14:52:42.836 [main] com.feng.service.MyService          - foo()
[ERROR] 14:52:42.836 [main] com.feng.aop.MyAspect               - before()
[ERROR] 14:52:42.836 [main] com.feng.service.MyService          - bar()

需要加上虚拟机参数

-javaagent:D:/apache-maven-3.6.0-bin/apache-maven-3.6.0-bin/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar

其中D:/apache-maven-3.6.0-bin/apache-maven-3.6.0-bin/repository是maven的仓库地址

它是在类加载阶段进行修改字节码增强的,在编译后的字节码文件中并没有像ajc一样加上要增强的代码

Aop之proxy

jdk动态实现

JDK的代理只能是针对接口代理

public class JdkProxyDemo {

    interface Foo {
        void foo();
    }

    static final class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }
    }

    // jdk 只能针对接口代理
    // cglib
    public static void main(String[] param) throws IOException {
        // 目标对象
        Target target = new Target();
		
        // 用来加载在运行期间动态生成的字节码
        ClassLoader loader = JdkProxyDemo.class.getClassLoader(); 
        //代理类和普通类有一个差别,普通类是先写java源代码,然后把源代码编译成字节码,经过类加载之后使用
        //代理类没有源码,它是在运行期间,直接生成类的字节码,生成的字节码也需要被加载后才能运行,谁来做这个加载操作呢?
        //就是上面的这个ClassLoader
        //参数2是要实现的接口,它是一个数组,代表可以实现多个接口
        //参数3是要执行的行为,代理类被执行时,就会调用InvocationHandler的invoke方法 
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
            //这三个参数分别代表,代理对象自己,正在执行的方法对象,方法传过来的实际参数
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("before...");
                // 目标.方法(参数)
                // 方法.invoke(目标, 参数);
                Object result = method.invoke(target, args);
                System.out.println("after....");
                return result; // 让代理也返回目标方法执行的结果
            }
        });

        System.out.println(proxy.getClass());

        proxy.foo();
    }
}

结果

class com.feng.bean.$Proxy0
before...
target foo
after....

jdk生成的代理对象和目标对象是兄弟关系,都实现了Foo接口,但是他们两个不能相互转换

这个目标对象可以是final的

模拟jdk动态代理

要被增强的方法

public class A12 {

    interface Foo {
        void foo();
        int bar();
    }

    //要被代理的类
    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }

        public int bar() {
            System.out.println("target bar");
            return 100;
        }
    }

    public static void main(String[] param) {
        // ⬇️1. 创建代理,这时传入 InvocationHandler
        Foo proxy = new $Proxy0(new InvocationHandler() {    
            // ⬇️5. 进入 InvocationHandler
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
                // ⬇️6. 功能增强
                System.out.println("before...");
                // ⬇️7. 通过反射来调用目标方法,代理对象调用哪个方法,这个method就对应的哪个方法
                return method.invoke(new Target(), args);
            }
        });
        // ⬇️2. 调用代理方法
        proxy.foo();
        proxy.bar();
    }
}

模拟jdk动态生成的代理对象

// ⬇️这就是 jdk 代理类的源码, 秘密都在里面
public class $Proxy0 extends Proxy implements A12.Foo {

    public $Proxy0(InvocationHandler h) {
        super(h);
    }
    // ⬇️3. 进入代理方法
    public void foo() {
        try {
            // ⬇️4. 回调 InvocationHandler,这个method就是
            //public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
                //System.out.println("before...");
                //return method.invoke(new Target(), args);
            //}
            //foo = A12.Foo.class.getMethod("foo");
            h.invoke(this, foo, new Object[0]);
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
            Object result = h.invoke(this, bar, new Object[0]);
            return (int) result;
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    static Method foo;
    static Method bar;
    static {
        try {
            foo = A12.Foo.class.getMethod("foo");
            bar = A12.Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }
}

cjlib动态代理

public class CglibProxyDemo {

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    
    public static void main(String[] param) {
		Target target = new Target();

      	// 代理是子类型, 目标是父类型
        //第二个参数和jdk代理类似,是代理类中方法执行时的行为CallBack
        //我们一般用它的子接口MethodInterceptor
        Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
            
            //参数1代表代理对象自己
            //参数2代表当前代理类中执行的方法
            //参数3方法执行时的参数
            //参数4
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
				Object result = method.invoke(target, args); 
                System.out.println("after...");
                return result;
            }
        });

        proxy.foo();

    }
}

结果

class com.feng.bean.$Proxy0
before...
target foo
after....

这个方法是用方法反射来调用目标实现的,而methodProxy 它可以避免反射调用

实现方案一

//spring用的是这种方式
Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标对象

实现方案二

//注意,这个传入的是自身,而不是代理对象,上面的Target target = new Target();就可以被省略了
Object result = methodProxy.invokeSuper(o, args); // 内部没有用反射, 需要代理自己
模拟cjlib动态代理

要被增强的方法

public class Target {
    public void save() {
        System.out.println("save()");
    }

    public void save(int i) {
        System.out.println("save(int)");
    }

    public void save(long j) {
        System.out.println("save(long)");
    }
}

模拟cjlib动态生成的代理类

public class Proxy extends Target {

    private MethodInterceptor methodInterceptor;

    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    static Method save0;
    static Method save1;
    static Method save2;
    
    static MethodProxy save0Proxy;
    static MethodProxy save1Proxy;
    static MethodProxy save2Proxy;
    static {
        try {
            save0 = Target.class.getMethod("save");
            save1 = Target.class.getMethod("save", int.class);
            save2 = Target.class.getMethod("save", long.class);
            //参数1原始类型,参数2增强类型,参数3返回值类型和方法参数,V返回值void,参数4增强方法名,参数5原始方法名
            save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
            //(I)V代表参数是整型,返回值void
            save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
            //(J)V代表参数是长整型,返回值void
            save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
    public void saveSuper() {
        super.save();
    }
    public void saveSuper(int i) {
        super.save(i);
    }
    public void saveSuper(long j) {
        super.save(j);
    }
    
    
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
    @Override
    public void save() {
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(int i) {
        try {
            methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(long j) {
        try {
            methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}

测试

public class A13 {

    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        Target target = new Target();
        proxy.setMethodInterceptor(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
				//return method.invoke(target, args); // 反射调用
				//return methodProxy.invoke(target, args); // 内部无反射, 但需要结合目标用
                return methodProxy.invokeSuper(o, args); // 内部无反射, 但需要结合代理用
            }
        });

        proxy.save();
        proxy.save(1);
        proxy.save(2L);
    }
}
cjlib中是如何避免反射调用的

当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 还会动态生成两个类,这两个类是FastClass的子类

  • ProxyFastClass 配合代理对象一起使用, 避免反射
  • TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)

FastClass有两个重要的抽象方法:

public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;

public abstract int getIndex(Signature var1);

这里因为继承FastClass类需要实现的方法比较多,所以我们不真正的继承,而是选这两个比较重要的方法来实现。

TargetFastClass 记录了 Target 中方法与编号的对应关系

  • save(long) 编号 2
  • save(int) 编号 1
  • save() 编号 0
  • 首先根据方法名和参数个数、类型, 用 switch 和 if 找到这些方法编号
  • 然后再根据编号去调用目标方法, 又用了一大堆 switch 和 if, 但避免了反射

ProxyFastClass 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法

  • saveSuper(long) 编号 2,不增强,仅是调用 super.save(long)
  • saveSuper(int) 编号 1,不增强, 仅是调用 super.save(int)
  • saveSuper() 编号 0,不增强, 仅是调用 super.save()
  • 查找方式与 TargetFastClass 类似

为什么有这么麻烦的一套东西呢?

  • 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法
  • 用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
//TargetFastClass是首次使用MethodProxy.create()时候被创建出来
public class TargetFastClass {
    //方法签名信息
    static Signature s0 = new Signature("save", "()V");
    static Signature s1 = new Signature("save", "(I)V");
    static Signature s2 = new Signature("save", "(J)V");

    // 获取目标方法的编号
    /*
        Target
            save()              0
            save(int)           1
            save(long)          2
        signature是根据方法的签名来获取对应的编号   签名信息有包括方法名字、参数返回值
        事实上我们在模拟cjlib动态生成的代理对象时,"()V", "save"就是签名信息
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    //在methodProxy.invoke时,内部就会让这个代理对象TargetFastClass调用invoke方法
    // 根据返回的方法编号, 正常调用目标对象方法
    //index方法编号,target目标对象,args参数
    public Object invoke(int index, Object target, Object[] args) {
        if (index == 0) {
            ((Target) target).save();
            return null;
        } else if (index == 1) {
            ((Target) target).save((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Target) target).save((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    //测试
    public static void main(String[] args) {
        TargetFastClass fastClass = new TargetFastClass();
        int index = fastClass.getIndex(new Signature("save", "(I)V"));
        System.out.println(index);
        fastClass.invoke(index, new Target(), new Object[]{100});
    }
}
public class ProxyFastClass {
    static Signature s0 = new Signature("saveSuper", "()V");
    static Signature s1 = new Signature("saveSuper", "(I)V");
    static Signature s2 = new Signature("saveSuper", "(J)V");

    // 获取代理方法的编号,只能是原始功能的方法,如果调用增强功能的方法,不仅没有效果,而且会陷入死循环
    /*
        Proxy
            saveSuper()              0
            saveSuper(int)           1
            saveSuper(long)          2
        signature 包括方法名字、参数返回值
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    // 根据方法编号, 正常调用代理对象方法
    public Object invoke(int index, Object proxy, Object[] args) {
        if (index == 0) {
            ((Proxy) proxy).saveSuper();
            return null;
        } else if (index == 1) {
            ((Proxy) proxy).saveSuper((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Proxy) proxy).saveSuper((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        ProxyFastClass fastClass = new ProxyFastClass();
        int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
        System.out.println(index);

        fastClass.invoke(index, new Proxy(), new Object[0]);
    }
}

spring是如何统一jdk动态代理和cjlib动态代理的

来回顾一下AOP的操作

  • 连接点:指类里面可以被增强的方法
  • 切入点:指实际被增强的方法
  • 通知:指实际增强的逻辑部分
  • 切面:把通知应用到切入点的过程

切点相当于匹配规则。并不是所有方法都要增强,符合切点的方法才会增强。

通知就是增强的逻辑。

切面 = 切点 + 通知。

两个切面的概念

  • aspect:可以包含多个切面(多组切点 + 通知)

  • advisor:更细粒度的切面。包含一个通知和一个切点

aspect在底层逻辑执行过程中也是拆分为多个advisor进行处理的,所以我们这里就直接学习advisor。

来看一下Advisor的类图

«interface»
Advice
«interface»
MethodInterceptor
«interface»
Advisor
«interface»
PointcutAdvisor
«interface»
Pointcut
AspectJExpressionPointcut

这里我们选用比较常用的AspectJExpressionPointuct (根据AspectJ表达式的切点)来实现

再来看一下通知,我们选取 MethodInterceptor (环绕通知)来实现,它是最基本也是最重要的一个,其他通知最终都是会被转换成这个通知来执行。

注意不要和cjlib的MethodInterceptor弄混淆,他们两个不是同一个。

实现步骤:

  1. 准备好切点对象
  2. 准备好通知
  3. 准备好切面
  4. 创建代理
public class A15 {
    public static void main(String[] args) {

        // 1. 备好切点对象
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        //1.1设置好切点表达式
        pointcut.setExpression("execution(* foo())");
        // 2. 备好通知
        MethodInterceptor advice = invocation -> {
            System.out.println("before...");
            Object result = invocation.proceed(); // 调用目标
            System.out.println("after...");
            return result;
        };
        // 3. 备好切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        /*
           4. 创建代理
           spring在创建代理时,用的是哪种实现类呢?遵循下面这几种规律:
           ptoxyFactory会读取它的一个属性proxyTargetClass,这个属性是在它的父类ProxyConfig中
                a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
                b. proxyTargetClass = false,  目标没有实现接口, 用 cglib 实现
                c. proxyTargetClass = true, 总是使用 cglib 实现
         */
        Target1 target = new Target1();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);//设置目标对象
        factory.addAdvisor(advisor);//设置切面
        factory.setInterfaces(target.getClass().getInterfaces());//设置目标上实现哪些接口
        factory.setProxyTargetClass(false);
        I1 proxy = (I1) factory.getProxy();
        
        //代理对象的类型
        /*
          没有加实现接口的时候
          class com.feng.aop.A15$Target2$$EnhancerBySpringCGLIB$$aa211dad
          说明采用的是cglib增强的方式
          加上了实现接口
          class com.feng.aop.$Proxy2
          需要加上设置
          factory.setInterfaces(target.getClass().getInterfaces());
         */
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
    }

    interface I1 {
        void foo();

        void bar();
    }

    static class Target1 implements I1 {
        public void foo() {
            System.out.println("target1 foo");
        }

        public void bar() {
            System.out.println("target1 bar");
        }
    }

    static class Target2 {
        public void foo() {
            System.out.println("target2 foo");
        }

        public void bar() {
            System.out.println("target2 bar");
        }
    }
}

切点匹配

常见 aspectj 切点用法

public class A16 {
    public static void main(String[] args) throws NoSuchMethodException {
		//创建切点对象
        AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
        pt1.setExpression("execution(* bar())");
        //它是通过什么方法来匹配的?1.通过方法的名字来匹配
        //参数1是要判断的哪个方法,参数2是目标是哪个类
        //pt1.matches(T1.class.getMethod("foo"), T1.class)
        System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
        
		System.out.println("-----------------------------------");
        //2.通过方法上是否有某个注解来匹配
        AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut(); 		 pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));
        
		System.out.println("-----------------------------------");
        //查看是否有@Transactional注解
        //spring中不是用AspectJExpressionPointcut使用setExpression来匹配@Transactional注解的
        //AspectJExpressionPointcut只能是匹配方法上的,而如果加在类上的@Transactional,就匹配不了
        StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 检查方法上是否加了 Transactional 注解
                MergedAnnotations annotations = MergedAnnotations.from(method);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                // 查看类上是否加了 Transactional 注解
                //默认情况下只会找一层,加上参数2的匹配策略
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                return false;
            }
        };

        System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
        System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
        System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class)); 
    }

//@Transactional注解可以加在方法上、类上、或者是接口上,如果加在类上说明所有类中的方法都需要添加事务,
//加在接口上是接口中定义的方法,将来实现后都需要添加事务
    static class T1 {
        @Transactional
        public void foo() {
        }
        public void bar() {
        }
    }

    @Transactional
    static class T2 {
        public void foo() {
        }
    }

    @Transactional
    interface I3 {
        void foo();
    }
    static class T3 implements I3 {
        public void foo() {
        }
    }
}

从 @Aspect 到 Advisor

先来定义一个高级切面类@Aspect和一个低级切面类Advisor

@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
    @Before("execution(* foo())")
    public void before1() {
        System.out.println("aspect1 before1...");
    }

    @Before("execution(* foo())")
    public void before2() {
        System.out.println("aspect1 before2...");
    }
}

@Configuration
static class Config {
    @Bean // 低级切面
    public Advisor advisor3(MethodInterceptor advice3) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
        return advisor;
    }
    @Bean
    public MethodInterceptor advice3() {
        return invocation -> {
            System.out.println("advice3 before...");
            Object result = invocation.proceed();
            System.out.println("advice3 after...");
            return result;
        };
    }
}

下面来了解一下AnnotationAwareAspectJAutoProxyCreator,它是用来找到容器中所有的高级切面和低级切面,并且,它会把这些高级切面转换为低级切面,最后根据这些切面创建代理对象,根据ProxyFactory 创建代理对象

image-20230702163021705

通过AnnotationAwareAspectJAutoProxyCreator继承树可以看出AnnotationAwareAspectJAutoProxyCreator继承了Aware接口和BeanPostProcessor接口,相当于AnnotationAwareAspectJAutoProxyCreator既是一个后置处理器,也是一个Aware接口BeanFactoryAware的实现类,可以自动装配。
因为它是框架内自动完成的,我们看不到具体的细节,下面我们手动来调用AnnotationAwareAspectJAutoProxyCreator内部的方法来查看具体过程。因为这两个方法是受保护的,所以为了方便,我们把测试类放在同一个包下。

@SpringBootApplication
public class A17 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        //它是一个bean后处理器
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
        // 它是一个BeanPostProcessor,作用阶段有两种情况
        // 创建 -> (*) 前依赖注入 -> 初始化后 (*)

        context.refresh();
        /*
            第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
                a. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3
                b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 @Aspect 后获得
         */
        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        //就是把所有可以用于Target2的advisor收集起来然后返回
        List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }
		System.out.println("-----------------------------");
        /*
            第二个重要方法 wrapIfNecessary
                a. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
         */
        Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
        System.out.println(o1.getClass());
        Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
        System.out.println(o2.getClass());

        ((Target1) o1).foo();
    }

    static class Target1 {
        public void foo() {
            System.out.println("target1 foo");
        }
    }

    static class Target2 {
        public void bar() {
            System.out.println("target2 bar");
        }
    }

    @Aspect // 高级切面类
    @Order(1)//高级切面和低级切面在一起执行的时候会有顺序问题,默认是低级切面的优先级高
    static class Aspect1 {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("aspect1 before1...");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("aspect1 before2...");
        }
    }

    @Configuration
    static class Config {
        @Bean // 低级切面
        public Advisor advisor3(MethodInterceptor advice3) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
            //低级切面可用setOrder方法来修改执行的优先级顺序数值越小优先级越高
            advisor.setOrfer(2);
            return advisor;
        }
        @Bean
        public MethodInterceptor advice3() {
            return invocation -> {
                System.out.println("advice3 before...");
                Object result = invocation.proceed();
                System.out.println("advice3 after...");
                return result;
            };
        }

第一个方法结果,有四个切面被匹配,第一个是spring给所有方法都设置的,如果换成Target2.class将不会有

org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
//两个高级切面    
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before1()]; perClauseKind=SINGLETON
    
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before2()]; perClauseKind=SINGLETON
//低级切面    
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.framework.autoproxy.A17$Config$$Lambda$58/758119607@37858383]

第二个方法结果Target1是代理,Target2不是代理

class org.springframework.aop.framework.autoproxy.A17$Target1$$EnhancerBySpringCGLIB$$391d9240
class org.springframework.aop.framework.autoproxy.A17$Target2
aspect1 before1...
aspect1 before2...
advice3 before...
target1 foo
advice3 after...

代理对象的创建时机

public class A17_1 {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(Config.class);
        context.refresh();
        context.close();
    }

    @Configuration
    static class Config {
        @Bean // 解析 @Aspect、产生代理(将高级切面转为低级切面)
        public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean // 解析 @Autowired
        public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
            return new AutowiredAnnotationBeanPostProcessor();
        }

        @Bean // 解析 @PostConstruct
        public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
            return new CommonAnnotationBeanPostProcessor();
        }

        @Bean
        public Advisor advisor(MethodInterceptor advice) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            return new DefaultPointcutAdvisor(pointcut, advice);
        }

        @Bean
        public MethodInterceptor advice() {
            return (MethodInvocation invocation) -> {
                System.out.println("before...");
                return invocation.proceed();
            };
        }

        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    //有foo方法,会被代理
    static class Bean1 {
        public void foo() {

        }
        public Bean1() {
            System.out.println("Bean1()");
        }
        //bean1先不要注入bean2
        //@Autowired public void setBean2(Bean2 bean2) {
            //System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
        //}
        @PostConstruct public void init() {
            System.out.println("Bean1 init()");
        }
    }

    //没有foo方法,不会被代理
    static class Bean2 {
        public Bean2() {
            System.out.println("Bean2()");
        }
        //bean2里面注入了bean1
        @Autowired public void setBean1(Bean1 bean1) {
            System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
        }
        @PostConstruct public void init() {
            System.out.println("Bean2 init()");
        }
    }
}

来看一下现在的结果

Bean1()
Bean1 init()
[TRACE] 17:59:16.703 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors 
Bean2()
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$a4191dc7
Bean2 init()

可以看到bean1的代理是在bean1被初始化后创建的,bean2的依赖注入,注入的是bean1的代理对象

下面让bean1里面也注入bean2,循环依赖,结果为

Bean1()
Bean2()
[TRACE] 18:03:21.686 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors 
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$8aad41fc
Bean2 init()
Bean1 setBean2(bean2) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean2
Bean1 init()

bean1的代理是在构造方法之后,初始化方法之前,创建。因为,bean1中注入需要bean2,而bean2又需要注入bean1的代理对象,所以bean1的代理被提前创建了

结论:
代理的创建时机

  • 初始化之后 (无循环依赖时)
  • 创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存

依赖注入与初始化不应该被增强, 用的仍是原始对象

高级切面转换为低级切面的过程

public class A17_2 {

    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        public void after() {
            System.out.println("after");
        }

        public void afterReturning() {
            System.out.println("afterReturning");
        }

        public void afterThrowing() {
            System.out.println("afterThrowing");
        }

        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            try {
                System.out.println("around...before");
                return pjp.proceed();
            } finally {
                System.out.println("around...after");
            }
        }
    }

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    @SuppressWarnings("all")
    public static void main(String[] args) throws Throwable {

        //获取Aspect工厂
        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
        // 高级切面转低级切面类
        List<Advisor> list = new ArrayList<>();
        //拿到高级切面类的所有方法
        for (Method method : Aspect.class.getDeclaredMethods()) {
            //方法上是否加了@before注解
            if (method.isAnnotationPresent(Before.class)) {
                // 拿到切点表达式,解析切点
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                
                // 拿到通知类,前置通知
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
                // 转化为低级切面
                Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            }
        }
        for (Advisor advisor : list) {
            System.out.println(advisor);
        }
        /*
            @Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
                a. 通知代码从哪儿来
                b. 切点是什么(这里为啥要切点, 后面解释)
                c. 通知对象如何创建, 本例共用同一个 Aspect 对象
            类似的通知还有
                1. AspectJAroundAdvice (环绕通知)
                2. AspectJAfterReturningAdvice
                3. AspectJAfterThrowingAdvice (环绕通知)
                4. AspectJAfterAdvice (环绕通知)
         */

    }
}

结果

org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.autoproxy.A17_2$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.autoproxy.A17_2$Aspect.before2()]; aspect name '']

总结:

  1. AnnotationAwareAspectJAutoProxyCreator 的作用
    • 将高级 @Aspect 切面统一为低级 Advisor 切面
    • 在合适的时机创建代理
  2. findEligibleAdvisors 找到有【资格】的 Advisors
    • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3
    • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
  3. wrapIfNecessary
    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
    • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

静态通知调用

来接着上次的代码

public class A18 {

    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        public void after() {
            System.out.println("after");
        }

        @AfterReturning("execution(* foo())")
        public void afterReturning() {
            System.out.println("afterReturning");
        }

        @AfterThrowing("execution(* foo())")
        public void afterThrowing(Exception e) {
            System.out.println("afterThrowing " + e.getMessage());
        }

        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            try {
                System.out.println("around...before");
                return pjp.proceed();
            } finally {
                System.out.println("around...after");
            }
        }
    }

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    @SuppressWarnings("all")
    public static void main(String[] args) throws Throwable {

        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
        // 1. 高级切面转低级切面类
        List<Advisor> list = new ArrayList<>();
        for (Method method : Aspect.class.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Before.class)) {
                // 解析切点
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                // 通知类
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
                // 切面
                Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            } else if (method.isAnnotationPresent(AfterReturning.class)) {
                // 解析切点
                String expression = method.getAnnotation(AfterReturning.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                // 通知类
                AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
                // 切面
                Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            } else if (method.isAnnotationPresent(Around.class)) {
                // 解析切点
                String expression = method.getAnnotation(Around.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                // 通知类
                AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
                // 切面
                Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                list.add(advisor);
            }
        }
        for (Advisor advisor : list) {
            System.out.println(advisor);
        }
		 /*
                1. AspectJAroundAdvice (环绕通知)
                2.AspectJMethodBeforeAdvice
                3. AspectJAfterReturningAdvice
                4. AspectJAfterThrowingAdvice (环绕通知)
                5. AspectJAfterAdvice (环绕通知)
         */
        
        // 2. 通知统一转换为环绕通知 MethodInterceptor,已经是环绕通知的就不用再转换了
        /*

            其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象
                a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocation
                b. MethodInvocation 要知道 advice 有哪些, 还要知道目标, 调用次序如下

                将 MethodInvocation 放入当前线程
                    |-> before1 ----------------------------------- 从当前线程获取 MethodInvocation
                    |                                             |
                    |   |-> before2 --------------------          | 从当前线程获取 MethodInvocation
                    |   |                              |          |
                    |   |   |-> target ------ 目标   advice2    advice1
                    |   |                              |          |
                    |   |-> after2 ---------------------          |
                    |                                             |
                    |-> after1 ------------------------------------
                c. 从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知
                d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式
                    - 对外是为了方便使用要区分 before、afterReturning
                    - 对内统一都是环绕通知, 统一用 MethodInterceptor 表示
            此步获取所有执行时需要的 advice (静态)
                a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上
                b. 适配如下
                  - MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
                  - AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
         */
        Target target = new Target();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程,它也是一个环绕通知,只不过是最外层的,它要最先执行
        proxyFactory.addAdvisors(list);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        //转成环绕通知
        List<Object> methodInterceptorList = proxyFactory
            .getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
        for (Object o : methodInterceptorList) {
            System.out.println(o);
        }
        
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        // 3. 创建并执行调用链 (环绕通知s + 目标)
        MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
                null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
        );
        methodInvocation.proceed();
    }
}

结果

org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.afterReturning()]; aspect name '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.interceptor.ExposeInvocationInterceptor@ed9d034//把 MethodInvocation 放入当前线程
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@6121c9d6
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@87f383f
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@4eb7f003
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
before2
around...before
before1
target foo
afterReturning
around...after

代理方法执行时会做如下工作

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
    • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
    • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
    • 这体现的是适配器设计模式
  2. 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可
  3. 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用

模拟实现调用链

代理对象调用流程如下(以 JDK 动态代理实现为例)

  • 从 ProxyFactory 获得 Target 和环绕通知链,根据他俩创建 MethodInvocation,简称 mi
  • 首次执行 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知1,执行前增强,再次调用 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知2,执行前增强,调用 mi.proceed() 发现没有环绕通知,调用 mi.invokeJoinPoint() 执行目标方法
  • 目标方法执行结束,将结果返回给环绕通知2,执行环绕通知2 的后增强
  • 环绕通知2继续将结果返回给环绕通知1,执行环绕通知1 的后增强
  • 环绕通知1返回最终的结果
/*
    模拟调用链过程, 是一个简单的递归过程
        1. proceed() 方法调用链中下一个环绕通知
        2. 每个环绕通知内部继续调用 proceed()
        3. 调用到没有更多通知了, 就调用目标方法
 */
public class A18_1 {

    static class Target {
        public void foo() {
            System.out.println("Target.foo()");
        }
    }

    static class Advice1 implements MethodInterceptor {
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("Advice1.before()");
            Object result = invocation.proceed();// 调用下一个通知或目标
            System.out.println("Advice1.after()");
            return result;
        }
    }

    static class Advice2 implements MethodInterceptor {
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("Advice2.before()");
            Object result = invocation.proceed();// 调用下一个通知或目标
            System.out.println("Advice2.after()");
            return result;
        }
    }


    static class MyInvocation implements MethodInvocation {
        //必要的信息:目标对象、目标方法、方法参数、环绕通知
        private Object target;
        private Method method;
        private Object[] args;
        List<MethodInterceptor> methodInterceptorList;
        
        private int count = 1; // 调用次数

        public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
            this.target = target;
            this.method = method;
            this.args = args;
            this.methodInterceptorList = methodInterceptorList;
        }

        @Override
        public Method getMethod() {
            return method;
        }

        @Override
        public Object[] getArguments() {
            return args;
        }

        //需要递归调用,调用的次数就是环绕通知个数 + 1
        @Override
        public Object proceed() throws Throwable { // 调用每一个环绕通知, 调用目标
            if (count > methodInterceptorList.size()) {
                // 调用目标, 返回并结束递归
                return method.invoke(target, args);
            }
            // 逐一调用通知, count + 1
            MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
            //这个递归并不是通过在方法内直接递归的,而是通过在通知内部调用proceed方法来实现递归的
            //Object result = invocation.proceed();// 调用下一个通知或目标
            return methodInterceptor.invoke(this);
        }

        //这个this指的就是目标对象
        @Override
        public Object getThis() {
            return target;
        }
		
        @Override
        public AccessibleObject getStaticPart() {
            return method;
        }
    }

    public static void main(String[] args) throws Throwable {
        Target target = new Target();
        List<MethodInterceptor> list = List.of(
                new Advice1(),
                new Advice2()
        );
        MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
        invocation.proceed();
    }
}

适配器模式

适配器模式(Adapter Pattern):结构型模式之一,将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作

  • 目标角色(Target):该角色定义把其他类转换为何种接口,也就是我们的期望接口。
  • 源角色(Adaptee):你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象。
  • 适配器角色(Adapter):适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:通过继承或是类关联的方式把源角色转换为目标角色。

例:

不同国家的电源插头和插座标准不同。中国插头和英国插座不匹配。

类适配器

适配器通过类来实现,以类来继承和实现接口的方式,来获取被适配类的信息并转换输出重写到适配接口。

中式插座(源角色 Adaptee)

@AllArgsConstructor
@Data
public class ChineseStandard {
    public String getChineseStandard() {
        return "中式插座";
    }
}

英式插座(目标角色 Target)

public interface BritishStandard {
    String getBritishStandard();
}

插座适配器(适配器角色 Adapter)

public class StandardAdapter extends ChineseStandard implements BritishStandard {
    @Override
    public String getBritishStandard() {
        return this.getChineseStandard();
    }
}

笔记本电脑(客户端 Client)

public class Notebook {
    public void charge(BritishStandard britishStandard) {
        if ("中式插座".equals(britishStandard.getBritishStandard())) {
            System.out.println("充电成功!");
        } else {
            System.out.println("充电失败!");
        }
    }
}
对象适配器

通过实例对象(构造器传递)来实现适配器,而不是再用继承,其余基本同类适配器。

修改插座适配器即可

@AllArgsConstructor
public class StandardAdapter implements BritishStandard {
    private ChineseStandard chineseStandard;

    @Override
    public String getBritishStandard() {
        return chineseStandard.getChineseStandard();
    }
}

测试类

public class AdapterTest {
    public static void main(String[] args) {
        // 充电成功!
        new Notebook().charge(new StandardAdapter(new ChineseStandard()));
    }
}

如果我们的源目标接口中还有一些其他我们不需要的方法,我们并不想去实现它,我们就可以将适配器作为一个抽象类,当我们实现适配器抽象类的时候只要重写我们需要的方法即可。这时候我们就用到了接口适配器。

接口适配器

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。

英式插座(目标角色 Target)

public interface BritishStandard {
    String getBritishStandard();

    String getTypeC();

    String getUSB();
}

插座适配器(适配器角色 Adapter)

@AllArgsConstructor
public abstract class StandardAdapter extends ChineseStandard implements BritishStandard {

    @Override
    public String getBritishStandard() {
        return null;
    }

    @Override
    public String getTypeC() {
        return null;
    }

    @Override
    public String getUSB() {
        return null;
    }
}

测试类

public class AdapterTest {
    public static void main(String[] args) {
        StandardAdapter standardAdapter= new StandardAdapter() {
            @Override
            public String getBritishStandard() {
                return new ChineseStandard().getChineseStandard();
            }
        };
        // 充电成功!
        new Notebook().charge(standardAdapter);
    }
}

动态通知调用

区别就是看通知方法调用的时候是否需要提供参数

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
  2. 所谓动态通知,体现在上面方法的 DynamicInterceptionAdvice 部分,这些通知调用时因为要为通知方法绑定参数,还需再次利用切点表达式
  3. 动态通知调用复杂程度高,性能较低
public class A19 {

    @Aspect
    static class MyAspect {
        @Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象
        public void before2(int x) {
            System.out.printf("before2(%d)%n", x);
        }
    }

    static class Target {
        public void foo(int x) {
            System.out.printf("target foo(%d)%n", x);
        }
    }

    @Configuration
    static class MyConfig {
        //作用:将高级切面转换为低级切面,创建代理对象
        @Bean
        AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean
        public MyAspect myAspect() {
            return new MyAspect();
        }
    }

    public static void main(String[] args) throws Throwable {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(MyConfig.class);
        context.refresh();

        //拿到ProxyCreater对象
        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        //获取所有有资格的Advisor
        List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");
		//目标对象
        Target target = new Target();
        //创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);//目标对象
        factory.addAdvisors(list);//切面
        Object proxy = factory.getProxy(); // 获取代理
		//转成通知,里面不仅有环绕通知还有其他类型(InterceptorAndDynamicMethodMatcher@37d4349f)
        //因为动态通知需要切点,所以这个就是把环绕通知和切点绑定到一起的新类型
        List<Object> interceptorList = factory
            .getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
        for (Object o : interceptorList) {
            showDetail(o);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
        //它的构造方法是受保护的,所以我们创建它的匿名子类来调用其中的方法
        ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(
                proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList
        ) {};

        invocation.proceed();
    }

    public static void showDetail(Object o) {
        try {
            Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
            if (clazz.isInstance(o)) {
                Field methodMatcher = clazz.getDeclaredField("methodMatcher");
                methodMatcher.setAccessible(true);
                Field methodInterceptor = clazz.getDeclaredField("interceptor");
                methodInterceptor.setAccessible(true);
                System.out.println("环绕通知和切点:" + o);
                System.out.println("\t切点为:" + methodMatcher.get(o));
                System.out.println("\t通知为:" + methodInterceptor.get(o));
            } else {
                System.out.println("普通环绕通知:" + o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果

普通环绕通知:org.springframework.aop.interceptor.ExposeInvocationInterceptor@7c711375
普通环绕通知:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@57cf54e1
环绕通知和切点:org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher@37d4349f
	切点为:AspectJExpressionPointcut: (int x) execution(* foo(..)) && args(x)
	通知为:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@434a63ab
>>>>>>>>>>>>>>>>>>>>>>>>>>
before1
before2(100)
target foo(100)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值