spring高级篇(一)

1、ApplicationContext与BeanFactory

BeanFactory是ApplicationContext的父级接口:(citl+alt+u查看类关系图)

在springboot的启动类中,我们通过SpringApplication.run方法拿到的是继承了ApplicationContext的ConfigurableApplicationContext接口:

复制代码
 ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringPlusApplication.class, args);

通过.getClass() 方法获取 Spring 应用上下文的类类型,得知在运行时,在本类中ApplicationContext 实现类的具体类型是AnnotationConfigServletWebServerApplicationContext

复制代码
System.out.println(applicationContext.getClass());

IOC,DI,管理SpringBean的生命周期,都是由beanFactory的实现类完成:

复制代码
  //例:通过反射得到存放bean的数组
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        //得到BeanFactory
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();

        //解析beanFactory中有多少单例的bean
        Map<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);

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

注册两个自定义的bean:

复制代码
@Component
public class Component1 {

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

    @Autowired
    private ApplicationEventPublisher publisher;

    public void register(){
        log.debug("用户注册");
        publisher.publishEvent(new UserRegisterEvent(this));
    }

}

@Component
public class Component2 {

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

    @EventListener
    public void sendMessage(UserRegisterEvent event){
        log.debug(event.toString());
        log.info("发送短信");
    }
}

 //例:通过反射得到存放bean的数组
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        //得到BeanFactory
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();

        //解析beanFactory中有多少单例的bean
        Map<String,Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);


        map.entrySet().stream().filter(k->k.getKey().startsWith("component")).forEach(e->{
            System.out.println(e.getKey()+":"+e.getValue());
        });

在上面的案例中,还可以观察到一种现象,即自定义的两个类,Component1和Component2的类名首字母都是大写,而在解析beanFactory时,.startsWith("component") 方法的component却是小写,其原因在于:

Spring中默认情况下,Bean的名称是小写的,Bean的名称是在定义Bean时指定的,如果你没有显式地为Bean指定名称,Spring会根据一定的命名规则自动生成Bean的名称。在自动生成名称时,通常会将类名的首字母小写作为Bean的名称。

同时ApplicationContext也对BeanFactory通过聚合的方式进行了功能的扩展

  • MessageSource:国际化翻译
  • ResourcePatternResolver:根据通配符获取资源
  • EnvironmentCapable:获取配置信息
  • ApplicationEventPublisher:发送事件
1.1、MessageSource

在resource目录下准备四个文件:

messages_en.properties:

复制代码
hi=Hello

messages_ja.properties:

复制代码
hi=こんにちは

messages_zh.properties:

复制代码
hi=你好

System.out.println(applicationContext.getMessage("hi", null, Locale.CHINESE));
System.out.println(applicationContext.getMessage("hi", null, Locale.ENGLISH));
System.out.println(applicationContext.getMessage("hi", null, Locale.JAPANESE));
1.2、ResourcePatternResolver
复制代码
Resource resource = applicationContext.getResource("classpath*:META-INF/spring.factories");
System.out.println(resource);
1.3、 EnvironmentCapable
复制代码
System.out.println(applicationContext.getEnvironment().getProperty("java_home"));   

System.out.println(applicationContext.getEnvironment().getProperty("server.port"));
1.4、 ApplicationEventPublisher

发布事件通知,类似于消息中间件的功能,可以实现解耦:

首先创建一个中间类:

复制代码
public class UserRegisterEvent extends ApplicationEvent {
    public UserRegisterEvent(Object source) {
        super(source);
    }
}

消息生产者,调用publisher.publishEvent():

复制代码
@Component
public class Component1 {

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

    @Autowired
    private ApplicationEventPublisher publisher;

    public void register(){
        log.debug("用户注册");
        publisher.publishEvent(new UserRegisterEvent(this));
    }

}

消息消费者,将UserRegisterEvent作为参数传入,同时方法上需要加上@EventListener:

复制代码
@Component
public class Component2 {

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

    @EventListener
    public void sendMessage(UserRegisterEvent event) {
        log.debug(event.toString());
        log.info("发送短信");
    }
}

主类:

复制代码
Component1 component1 = applicationContext.getBean("component1", Component1.class);
component1.register();

ApplicationEventPublisher的局限性:

不支持分布式事件发布。在典型的 Spring 应用程序中,ApplicationEventPublisher是用于在单个应用程序上下文中发布事件的,而不是用于跨应用程序或分布式系统的事件传播。所以在分布式系统中,推荐使用更加成熟的消息队列、事件总线或者分布式事件处理系统。

1.5、小结:

ApplicationContext和BeanFactory的区别和联系:

区别:

  • ApplicationContext:ApplicationContext是 BeanFactory的子接口之一。它提供了更多的企业级功能,如国际化、事件传播、资源加载等。ApplicationContext在启动时就会加载所有的单例 bean,并在初始化过程中完成依赖注入和各种后处理器的应用。ApplicationContext通常在实际开发中更为常用,因为它提供了更多的特性和功能。
  • BeanFactory :BeanFactory是 Spring 框架的核心接口之一,它提供了 bean 的配置、创建、管理和查找功能。BeanFactory的实现类负责加载 bean 的定义信息,并且在需要时才实例化 bean。与 ApplicationContext相比,BeanFactory更轻量级,因为它只在需要时才初始化 bean,节省了资源。但是它不提供像 ApplicationContext那样的扩展功能。

联系:

  • 接口关系:ApplicationContext是 BeanFactory的子接口,因此 ApplicationContext包含了 BeanFactory的所有功能,并且在此基础上提供了更多的功能。
  • 功能:BeanFactory是 Spring 框架中负责管理 bean 的核心接口,而 ApplicationContext在 BeanFactory的基础上提供了更多的企业级功能和扩展。因此,ApplicationContext 在实际开发中更常用,但如果对资源有较高要求,可以考虑使用轻量级的 BeanFactory。

2、容器实现

列举一些BeanFactory常见的实现:

  • XmlBeanFactory : XmlBeanFactory是 Spring 最基本的容器实现之一,它从 XML 文件中加载 bean 的定义信息,并在需要时实例化和管理 bean。这是 Spring 早期版本中最常用的容器实现之一,但现在已经不推荐使用,因为它在初始化时会加载所有的 bean,可能会造成性能问题。

  • ClassPathXmlApplicationContext: ClassPathXmlApplicationContext是基于 XML 配置文件的应用程序上下文实现。它从类路径中加载 XML 文件,并使用其中的 bean 定义来初始化应用程序上下文。这是使用 Spring 框架时最常见的容器实现之一。

  • FileSystemXmlApplicationContext: FileSystemXmlApplicationContext也是基于 XML 配置文件的应用程序上下文实现,但它从文件系统中加载 XML 文件。与 ClassPathXmlApplicationContext不同,它需要指定 XML 文件的绝对路径或相对路径。

  • AnnotationConfigApplicationContext: AnnotationConfigApplicationContext是基于注解的应用程序上下文实现。它会扫描指定的包路径,查找带有特定注解的类(如@Configuration、@Component等),并根据这些类中的配置来初始化 bean。

  • GenericApplicationContext : GenericApplicationContext是一个泛型的应用程序上下文实现,它可以通过编程方式动态地注册和管理 bean。与基于 XML 或注解的上下文实现不同,GenericApplicationContext允许在运行时动态地添加、修改和删除 bean。(是不带后处理器,较为干净的实现,后续演示后处理器使用该实现完成)

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

2.1、DefaultListableBeanFactory

为了演示功能,首先创建几个静态内部类:

  • Config类的作用是注册自定义bean,类上加入@Configuration 注解。

  • bean3和bean4同时实现了Inter接口。

    /**
    * 注册bean
    */
    @Configuration
    static class config {

    复制代码
          @Bean
          public Bean1 createBean1() {
              return new Bean1();
          }
    
          @Bean
          public Bean2 createBean2() {
              return new Bean2();
          }
    
          @Bean
          public Bean3 createBean3() {
              return new Bean3();
          }
    
          @Bean
          public Bean4 createBean4() {
              return new Bean4();
          }
      }
      
      interface Inter{
    
      }
      
    
      static class Bean1 {
    
          private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    
          @Autowired
          Bean2 bean2;
    
          //那么如果同时加了 @Autowired 和 @Resource 呢?答案是以 @Autowired为准。因为在后处理器中它的位置靠前

    // @Autowired//如果同一个接口有多个实现类,需要注入,必须要使用@Qualifier 或者 和bean的名字同名
    @Resource(name = "createBean4")//如果使用resource并且标注了name,按照name中的bean装配
    private Inter createBean3;

    复制代码
          public Bean1() {
              log.info("Bean1 created");
          }
    
          public Bean2 getBean2() {
              return bean2;
          }
    
          public Inter getInter() {
              return createBean3;
          }
    
    
      }
    
    
      static class Bean2 {
          private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    
          public Bean2() {
              log.info("Bean2 created");
          }
      }
    
      static class Bean3 implements Inter{
    
          private static final Logger log = LoggerFactory.getLogger(Bean3.class);
    
          public Bean3(){
              log.info("Bean3 created");
          }
      }
    
      static class Bean4 implements Inter{
          private static final Logger log = LoggerFactory.getLogger(Bean4.class);
    
          public Bean4(){
              log.info("Bean4 created");
          }
      }

主类:

复制代码
//DefaultListableBeanFactory是beanFactory最重要的实现
 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//定义bean 包括范围,初始化,销毁 这里定义的是config bean
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(config.class).setScope("singleton").getBeanDefinition();
//注册config bean
beanFactory.registerBeanDefinition("config", beanDefinition);

我们还需要给beanFactory添加一些常用的后处理器:(如果不添加后处理器,beanFactory不会主动解析@Configuration ,@Bean 等注解)

复制代码
 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

可以遍历查看后处理器:

复制代码
      for (String name : beanFactory.getBeanDefinitionNames()) {
            //org.springframework.context.annotation.internalConfigurationAnnotationProcessor 解析@Bean
            //org.springframework.context.annotation.internalAutowiredAnnotationProcessor 解析@AutoWired @Value
            //org.springframework.context.annotation.internalCommonAnnotationProcessor 解析@Resource
            //org.springframework.context.event.internalEventListenerProcessor
            //org.springframework.context.event.internalEventListenerFactory
            System.out.println(name);
        }

此时只是bean工厂中有了这些后处理器中的bean,但是要生效还需要:

其中beanFactory.getBeansOfType():根据类型获取多个bean

processor.postProcessBeanFactory():执行bean工厂后处理器

复制代码
Collection<BeanFactoryPostProcessor> values = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values();
for (BeanFactoryPostProcessor processor : values) {
   processor.postProcessBeanFactory(beanFactory);
}

//再次查看beanFactory中的bean
for (String name : beanFactory.getBeanDefinitionNames()) {
   System.out.println(name);
}

此时beanFactory已经有了解析@Configuration ,@Bean 等注解的能力:

因为此时bean1中有一个使用@AutoWired 注入的bean2,此时尝试一下获取bean2:

复制代码
  System.out.println(beanFactory.getBean(Bean1.class).getBean2());

可以发现bean2没有被初始化:

需要添加bean的相关后处理器,解析@AutoWired @Value @Resource 等注解信息:

  • internalConfigurationAnnotationProcessor:解析@Bean

  • internalAutowiredAnnotationProcessor :解析@AutoWired @Value

  • internalCommonAnnotationProcessor: 解析@Resource

    Collection<BeanPostProcessor> postProcessors = beanFactory.getBeansOfType(BeanPostProcessor.class).values();
    for (BeanPostProcessor postProcessor : postProcessors) {
    System.out.println("<<<<<"+postProcessor);
    beanFactory.addBeanPostProcessor(postProcessor);
    }

    for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
    }
    System.out.println(beanFactory.getBean(Bean1.class).getBean2());

至此所有的bean都加载完毕。但是有一个问题:bean3和bean4都实现了Inter接口,我在bean1中对其注入时,如果类型是bean3和bean4的父类,那么使用@AutoWired注解将注入哪一个bean?

如果使用@AutoWired注解,需要配合@Qualifier 注解确定具体注入哪一个实现类。

如果使用@Resource(name = "createBean4")注解,按照name属性的bean进行装配:

属性名虽然叫createBean3,但是实际注入的是@Resource 的name所在的bean:

那如果同时标上了@AutoWired 和@Resource 呢?答案是以 @Autowired为准。因为在后处理器中它的位置靠前:

CommonAnnotationBeanPostProcessor的order排序:(解析@Resource)

AutowiredAnnotationProcessor 的order排序:(解析@AutoWired @Value)

order排序的数字越小,优先级越高。 所以同时标注@AutoWired 和@Resource 时,以@AutoWired 为准。

2.2、常见 ApplicationContext 实现

首先自定义两个bean:

复制代码
static class Bean1{

    
}


static class Bean2{

   private Bean1 bean1;

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

   public Bean1 getBean1() {
       return bean1;
   }

}

使用xml的方式进行bean管理:

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

<!--        定义bean-->
    <bean id="bean1" class="com.itbaima.a01.ApplicaionContextDemo.Bean1"/>

    <bean id="bean2" class="com.itbaima.a01.ApplicaionContextDemo.Bean2">
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>
2.2.1、基于classPath 读取xml文件
java 复制代码
    public static void testClassPathXmlApplicationContext(){
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("test1.xml");
        for (String name : applicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        System.out.println(applicationContext.getBean(Bean2.class).getBean1());
    }
2.2.2、基于磁盘路径读取xml文件
java 复制代码
    public static void testFileSystemXmlApplicationContext(){
        FileSystemXmlApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\Idea_workspace\\2024\\spring-plus\\src\\main\\resources");
        for (String name : applicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(applicationContext.getBean(Bean2.class).getBean1());
    }
2.2.3、基于java配置类实现

会默认加上后处理器:

java 复制代码
  @Configuration
    static class Config{

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

        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }
java 复制代码
    public static void testAnnotationConfigApplicationContext(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        for (String name : applicationContext.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(applicationContext.getBean(Bean2.class).getBean1());
    }
2.2.4、基于java配置类实现 web相关
java 复制代码
 public static void testAnnotationConfigServletWebServerApplicationContext(){
        AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    }
java 复制代码
 @Configuration
    static class WebConfig{

        /**
         * 将内嵌tomcat注册成bean
         * @return
         */
        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public DispatcherServlet dispatcherServlet(){
            return new DispatcherServlet();
        }

        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        @Bean("/hello")
        public Controller controller(){
            return ((httpServletRequest, httpServletResponse) -> {
                httpServletResponse.getWriter().write("hello");
                return null;
            });
        }
    }
2.3、小结
  • 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 需要额外设置才能解析 ${} 与 #{}

3、Bean的生命周期

Spring 中的 bean 生命周期通常包括以下几个阶段:

  1. 实例化(Instantiation):容器根据配置信息(XML、注解等)创建 bean 的实例。这通常涉及到实例化目标 bean 类,并根据配置注入依赖。

  2. 属性设置(Population):容器通过依赖注入(Dependency Injection)将配置的属性值或引用设置到 bean 实例中。这包括基本属性、引用类型、集合类型等。

  3. 初始化(Initialization)

    • Initialization callbacks:在 bean 实例化完成并且所有属性设置完毕后,容器会调用 bean 的InitializingBean接口的afterPropertiesSet()方法,或者调用在配置中声明的初始化方法。
    • BeanPostProcessors:在调用初始化方法之前和之后,容器会调用所有注册的 BeanPostProcessor实现类的postProcessBeforeInitialization() 和postProcessAfterInitialization()方法,允许对 bean 进行自定义的初始化处理。
  4. 使用(In Use):在初始化完成后,bean 可以被容器或其他 bean 使用。

  5. 销毁(Destruction)

    • DisposableBean :如果 bean 实现了 DisposableBean接口,容器在销毁 bean 之前会调用其destroy() 方法。
    • 自定义销毁方法:在配置中可以声明自定义的销毁方法,在容器关闭时调用。
  6. 销毁回调(Destruction callbacks):与初始化类似,容器在销毁 bean 之前会调用所有注册的 BeanPostProcessor实现类的 postProcessBeforeDestruction() 方法,允许对 bean 进行自定义的销毁处理。

java 复制代码
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

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

    @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;
    }
}

4、常见的Bean后处理器

首先创建几个bean:

java 复制代码
public class Bean2 {
}
java 复制代码
public class Bean3 {
}
java 复制代码
public class Bean1 {

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

    Bean2 bean2;

    @Autowired
    public void setBean1(Bean3 bean3) {
        log.debug("setBean3");
        this.bean3 = bean3;
    }

    @Resource
    public void setBean2(Bean2 bean2) {
        log.debug("setBean2");
        this.bean2 = bean2;
    }

    @Autowired
    public void autowiredMethod(@Value("${JAVA_HOME}")String name){
        log.debug("注入:"+name);
    }


    @PostConstruct
    public void initMethod(){
        log.debug("初始化");
    }

    @PreDestroy
    public void destroyMethod(){
        log.debug("销毁");
    }
}
java 复制代码
@ConfigurationProperties(prefix = "java")
public class Bean4 {

    String home;

    String version;


    public Bean4() {
    }

    public Bean4(String home, String version) {
        this.home = home;
        this.version = version;
    }

    /**
     * 获取
     * @return home
     */
    public String getHome() {
        return home;
    }

    /**
     * 设置
     * @param home
     */
    public void setHome(String home) {
        this.home = home;
    }

    /**
     * 获取
     * @return version
     */
    public String getVersion() {
        return version;
    }

    /**
     * 设置
     * @param version
     */
    public void setVersion(String version) {
        this.version = version;
    }

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

在主类中,我们使用ApplicationContext的GenericApplicationContext实现(在容器实现中提到过,GenericApplicationContext是不带后处理器,较为干净的实现)

java 复制代码
  GenericApplicationContext context = new GenericApplicationContext();
java 复制代码
//注册bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
context.registerBean("bean4", Bean4.class);

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

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

此时并没有打印出关于bean中定义的任何信息。

4.1、解析@AutoWired @Value

如果需要解析bean1中的@AutoWired @Value ,则主类中需要增加:

java 复制代码
//这行代码用于设置默认的 Bean 工厂的自动装配候选项解析器(AutowireCandidateResolver)。
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
//AutowiredAnnotationBeanPostProcessor 是 Spring 框架中用于处理 @Autowired 注解的后置处理器,它会在 bean 实例化后,对标记了 @Autowired 注解的字段进行自动注入。
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
4.2、解析@Resource @PostConstruct @PreDestroy
java 复制代码
context.registerBean(CommonAnnotationBeanPostProcessor.class);
4.3、解析@ConfigurationProperties
java 复制代码
//将 ConfigurationPropertiesBindingPostProcessor 注册到 Spring 容器中的默认 Bean 工厂中。
//ConfigurationPropertiesBindingPostProcessor 是 Spring Boot 中用于处理 @ConfigurationProperties 注解的后置处理器。
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());


System.out.println(context.getBean("bean4"));
4.4、@Autowired bean 后处理器运行分析
java 复制代码
 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        
 beanFactory.registerSingleton("bean2", new Bean2());
 beanFactory.registerSingleton("bean3", new Bean3());
        
 //解析@value不出异常
       beanFactory.setAutowireCandidateResolver(newContextAnnotationAutowireCandidateResolver());
 //解析${}需要加上
 beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders);

通过反射,演示一下AutowiredAnnotationBeanPostProcessor后处理器运行的过程:

java 复制代码
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();

//设置后处理器要处理的beanFactory,因为bean2,bean3都在beanFactory中
processor.setBeanFactory(beanFactory);

AutowiredAnnotationBeanPostProcessor执行依赖注入,实际是调用了:

java 复制代码
  processor.postProcessProperties(null, bean1, "bean1");

在该方法内部,再次调用了

因为findAutowiringMetadata() 是私有方法,所以通过反射的方式进行调用处理:

java 复制代码
//通过反射获取findAutowiringMetadata方法
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
findAutowiringMetadata.setAccessible(true);
       
//执行findAutowiringMetadata方法 //参数一:方法所在的对象实例 参数二:方法的参数
//1.收集类中所有加了@AutoWired @Value注解的方法/成员变量
InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor,"bean1",Bean1.class,null);

这样就拿到了findAutowiringMetadata() 方法的返回,利用方法的返回调用inject()方法:

java 复制代码
//2.进行依赖注入
metadata.inject(bean1,"bean1",null);
System.out.println(bean1);

由此可知:postProcessProperties方法内部做了两件事:

1.收集类中所有加了@AutoWired @Value注解的方法/成员变量

2.进行依赖注入

而inject() 方法内部如何根据类型找到值?

java 复制代码
 //1.得到成员变量/方法的信息
 Field bean3 = Bean1.class.getDeclaredField("bean3");
 //2.封装成DependencyDescriptor
 DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(bean3, false);
 //3.调用beanFactory的doResolveDependency
 Object o = beanFactory.doResolveDependency(dependencyDescriptor, null, null, null);
 System.out.println(o);
java 复制代码
//方法注入时,以某个方法的参数为单位去容器中找
Method method = Bean1.class.getDeclaredMethod("setBean1", Bean3.class);
DependencyDescriptor descriptor = new DependencyDescriptor(new MethodParameter(method, 0), false);
Object o1 = beanFactory.doResolveDependency(descriptor, null, null, null);
System.out.println(o1);

//方法的参数值注入,以某个方法的参数为单位去容器中找
Method method1 = Bean1.class.getDeclaredMethod("autowiredMethod", String.class);
DependencyDescriptor dependencyDescriptor1 = new DependencyDescriptor(new MethodParameter(method1, 0), false);
Object o2 = beanFactory.doResolveDependency(dependencyDescriptor1, null, null, null);
System.out.println(o2);

inject内部如何根据类型找到值?

1.得到成员变量/方法的信息

2.封装成DependencyDescriptor

3.调用beanFactory的doResolveDependency

5、BeanFactory 后处理器

如果需要解析@Bean @Mapper 等注解,则需要使用到BeanFactory的后处理器:

java 复制代码
//需要添加对@Bean @ComponentScan @Import 的处理器
context.registerBean(ConfigurationClassPostProcessor.class);

//需要添加对@MapperScan的后处理器
context.registerBean(MapperScannerConfigurer.class, bd -> {
    bd.getPropertyValues().add("basePackage","com.itbaima.a04.mapper");
});
  • ConfigurationClassPostProcessor 可以解析:

    • @ComponentScan

    • @Bean

    • @Import

    • @ImportResource

  • MapperScannerConfigurer 可以解析:

    • @Mapper

下面会模拟源码中@ComponentScan @Bean @Mapper 后处理器的实现,在进行演示之前,首先介绍一下一些会用到的API和类:

  • CachingMetadataReaderFactory:是 Spring 框架中用于加载和缓存类元数据的工厂类,它提供了一种高效的方式来获取类的元数据信息,并且通过缓存机制提高了性能。当需要获取某个类的元数据时,它会通过底层的 MetadataReader实现类来读取类的信息,并将其封装为MetadataReader对象。
  • AnnotationBeanNameGenerator:是 Spring 框架中用于生成 bean 名称的默认策略类之一。当你不显式地为 bean 指定名称时,Spring 会自动使用它来生成 bean 的名称。这样,你就可以通过在类上添加相应的注解来控制 bean 的名称,而不需要额外进行配置。
  • BeanDefinitionBuilder:是 Spring 框架中用于构建 BeanDefinition对象的工具类。在 Spring 中,BeanDefinition是用于描述和定义 bean 的元数据信息的类,包括了 bean 的类名、作用域、构造函数参数、属性值、初始化方法、销毁方法等等。

其中BeanDefinitionBuilder的一些常用方法:

  • static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass): 创建一个通用的 bean 定义构建器,指定 bean 的类名。
  • BeanDefinitionBuilder setScope(String scope): 设置 bean 的作用域。
  • BeanDefinitionBuilder addPropertyValue(String name, Object value): 添加一个属性值到 bean 的属性列表中。
  • BeanDefinitionBuilder addConstructorArgValue(Object value): 添加一个构造函数参数值到 bean 的构造函数参数列表中。
  • BeanDefinitionBuilder setInitMethodName(String methodName): 设置 bean 的初始化方法名。
  • BeanDefinitionBuilder setDestroyMethodName(String methodName): 设置 bean 的销毁方法名。
  • BeanDefinitionBuilder setLazyInit(boolean lazyInit): 设置是否启用延迟初始化。
  • BeanDefinitionBuilder setAutowireMode(int autowireMode): 设置自动装配模式。
5.1、模拟@ComponentScan 的实现

找到类中的@ComponentScan 注解

java 复制代码
 ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
java 复制代码
         if (!ObjectUtils.isEmpty(componentScan)) {
                //得到注解的值
                for (String base : componentScan.basePackages()) {
                    //拼装成路径信息
                    String path = "classpath*:" + base.replace(".","/") + "/**/*.class";
                    //用于读取类的元数据 在运行时加载类的元数据,如类名、注解信息等
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    //用于根据类的注解生成 Bean 的名称
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    //context的扩展
                    //得到路径下的所有资源是ApplicationContext对于BeanFactory的扩展
                    GenericApplicationContext context = new GenericApplicationContext();
                    Resource[] resource = context.getResources(path);
                    for (Resource r : resource) {
                        //从元数据读取工厂中获取一个元数据读取器reader
                        MetadataReader reader = factory.getMetadataReader(r);
    //                    System.out.println("类名"+reader.getClassMetadata().getClassName());
    //                    System.out.println("是否加了Component"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
    //                    System.out.println("是否加了Component 相关"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
                        //从元数据读取器reader中获取注解元数据
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        //如果类上加了Component及相关注解
                        if (annotationMetadata.hasMetaAnnotation(Component.class.getName())
                                || annotationMetadata.hasAnnotation(Component.class.getName())) {

                            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
                            DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
                            //生成一个唯一的bean名称。
                            String name = generator.generateBeanName(beanDefinition, defaultListableBeanFactory);
                            //通过BeanDefinitionBuilder创建的bean定义(即beanDefinition)注册到Spring容器中。
                            // registerBeanDefinition方法将生成的bean名称(name)与对应的bean定义关联起来,使得该bean可以被Spring容器管理和使用。
                            defaultListableBeanFactory.registerBeanDefinition(name, beanDefinition);

                        }
                    }
                }

            }
5.2、模拟解析 @Bean
java 复制代码
//读取@Bean注解
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            //1.读取指定类路径下的信息
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itbaima/a04/Config.class"));
            //2.获取指定类中所有标注了@Bean方法的信息
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            //遍历这些方法
            for (MethodMetadata method : methods) {
                //使用反射获取注解信息
                String initMothod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                //用于创建一个通用的 Bean 定义。
                BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
                //设置工厂方法来创建 Bean。使用config中的方法作为工厂方法创建bean
                definitionBuilder.setFactoryMethodOnBean(method.getMethodName(), "config");
                //处理标注了@Bean的有参方法
                definitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMothod.length()>0){
                    //用于设置 Bean 的初始化方法名。初始化方法是在 Bean 实例化后立即调用的方法。
                    definitionBuilder.setInitMethodName(initMothod);
                }
                //用于获取最终的 Bean 定义对象。
                AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
                DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;
                //将之前创建的 Bean 定义注册到 Spring 的 Bean 工厂中。     参数一:bean的名称 参数二:用于获取最终的 Bean 定义对象。
                defaultListableBeanFactory.registerBeanDefinition(method.getMethodName(), beanDefinition);
            }
5.3、模拟解析@Mapper

关于Mapper工厂,需要在参数中指定SqlSessionFactory,以便Spring 容器将指定的依赖注入到方法的参数中。

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

模拟实现:

与@Bean类似,被Spring bean管理的类型都是工厂,但是在处理bean名称的时候,需要额外进行,保证bean的名称是每个Mapper接口的名称,而不是工厂的名称。

复制代码
 try {
            //获取指定路径下的资源
            PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = patternResolver.getResources("classpath:com/itbaima/a04/mapper/**/*.class");
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            for (Resource resource : resources) {
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                //判断路径下的某个资源是类还是接口
                if (classMetadata.isInterface()) {
                    //生成接口对应的MapperFactoryBean
                    //被spring管理的bean的类型 是 MapperFactoryBean
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())//给构造设置参数值
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)//按照类型装配,对应方法上的参数
                            .getBeanDefinition();
                    //创建bean名称 此处是根据bean定义去生成名称的,我们此时定义的是bean工厂
                    //所以要在定义一个beanDefinition,名字是Mapper1 Mapper2(具体的接口,非工厂)
                    AbstractBeanDefinition beanDefinitionInter = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                    String name = generator.generateBeanName(beanDefinitionInter, registry);
                    registry.registerBeanDefinition(name, beanDefinition);

                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
相关推荐
Java水解2 小时前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
千寻girling2 小时前
一份不可多得的 《 Python 》语言教程
人工智能·后端·python
南风9992 小时前
Claude code安装使用保姆级教程
后端
爱泡脚的鸡腿2 小时前
Node.js 拓展
前端·后端
蚂蚁背大象3 小时前
Rust 所有权系统是为了解决什么问题
后端·rust
子玖4 小时前
go实现通过ip解析城市
后端·go
Java不加班4 小时前
Java 后端定时任务实现方案与工程化指南
后端
心在飞扬5 小时前
RAG 进阶检索学习笔记
后端
Moment5 小时前
想要长期陪伴你的助理?先从部署一个 OpenClaw 开始 😍😍😍
前端·后端·github