点击查看【bilibili】 参考视频: 黑马程序员Spring视频教程,全面深度讲解spring5底层原理
红色字体类名称 蓝色字体超链接 黄色字体类型 绿色字体属性名称
1.容器接口 1 2 3 4 5 6 7 8 @SpringBootApplication public class AdvancedSpringApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(AdvancedSpringApplication.class, args); } }
在idea里查看ConfigurableApplicationContext的类图: ConfigurableApplicationContext 接口继承了 ApplicationContext 接口,而 ApplicationContext 接口又间接地继承了 BeanFactory 接口,除此之外还继承了其他很多接口,相当于对 BeanFactory 进行了拓展。
什么是 BeanFactory BeanFactory 是SpringIoC容器 ,提供一系列Bean的接口。 基于它也有众多子接口:
它是 ApplicationContext 的父接口
它才是 Spring 的核心容器,主要的 ApplicationContext 实现 组合 了它的功能,也就是说,BeanFactory 是 ApplicationContext 中的一个成员变量。
常用的 context.getBean("xxx") 方法,其实是调用了 BeanFactory 的 getBean() 方法。
BeanFactory 能做什么? 通过这些方法定义可知,BeanFactory 表面上只有 getBean() 方法,但实际上 Spring 中的控制反转、基本的依赖注入、乃至 Bean 的生命周期中的各个功能都是由它的实现类提供。
DefaultListableBeanFactory 实现了 BeanFactory 接口,它能管理 Spring 中所有的 Bean,当然也包含 Spring 容器中的那些单例对象。
DefaultListableBeanFactory 还继承了 DefaultSingletonBeanRegistry 类,这个类就是用来管理 Spring 容器中的单例对象。
在 IDEA 提供的类图中选中 DefaultSingletonBeanRegistry,然后按下 F4 进入这个类。它有一个 Map 类型的成员变量 singletonObjects:
1 private final Map<String, Object> singletonObjects = new ConcurrentHashMap (256 );
Map的key就是Bean的名称,value是此bean,即单例对象。
而我们要对单例bean操作的话, Spring提供SmartInitializingSingleton#afterSingletonsInstantiated()方法可以在单例Bean初始化后被调用处理。
现有如下两个Bean:
1 2 3 4 5 6 7 @Component public class Component1 {} @Component public class Component2 {}
查看singletonObjects里是否存在这两个bean的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @SpringBootApplication public class AdvancedSpringApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(AdvancedSpringApplication.class, args); Field singletonObjects = null ; try { singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects" ); singletonObjects.setAccessible(Boolean.TRUE); Map<String, Object> map = (Map<String, Object>) singletonObjects.get(run.getBeanFactory()); map.entrySet().stream().filter(e -> e.getKey().startsWith("component" )) .forEach(e -> System.out.println(e.getKey() + " " + e.getValue())); } catch (Exception e) { throw new RuntimeException (e); } } }
1 2 component daidawang.advancedSpring.Component@1ee29c84 component1 daidawang.advancedSpring.Component1@7c8326a4
ApplicationContext 的功能 回顾ConfigurableApplicationContext类的类图: ApplicationContext 除了继承 BeanFactory 外,还继承了:
MessageSource:使其具备处理国际化资源的能力
ResourcePatternResolver:使其具备使用通配符进行资源匹配的能力
EnvironmentCapable:使其具备读取 Spring 环境信息、配置文件信息的能力
ApplicationEventPublisher:使其具备发布事件的能力
MessageSource 的使用 主要用于提供国际化支持。 在 SpringBoot 项目的 resources 目录下创建 messages.properties、messages_en.properties、messages_zh_CN.properties、messages_zh_TW.properties 四个国际化文件,除 messages.properties 外,其余三个文件内容如下:
注意检查 File > Settings > Editor > File Encodings 将确保 Project Encoding 、 Default Encoding 、 Properties files 设置为 UTF-8。 测试 MessageSource 接口中 getMessage() 方法的使用:
1 2 3 4 5 6 7 8 9 10 11 @SneakyThrows @SuppressWarnings("unchecked") public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); System.out.println(context.getMessage("thanks" , null , Locale.ENGLISH)); System.out.println(context.getMessage("thanks" , null , Locale.SIMPLIFIED_CHINESE)); System.out.println(context.getMessage("thanks" , null , Locale.TRADITIONAL_CHINESE)); context.close(); }
ResourcePatternResolver 的使用 主要用于解析带有通配符的资源路径,通常用于查找符合特定模式的所有资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SneakyThrows @SuppressWarnings("unchecked") public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); Resource[] resources = context.getResources("classpath:application.properties" ); Assert.isTrue(resources.length > 0 , "加载类路径下的 application.properties 文件失败" ); resources = context.getResources("classpath*:META-INF/spring.factories" ); Assert.isTrue(resources.length > 0 , "加载类路径下的 META-INF/spring.factories 文件失败" ); context.close(); }
EnvironmentCapable 的使用 用于表示访问spring应用上下文中的 Environment 对象组件。 Environment对象提供访问外部配置的能力,例如系统属性、环境变量或配置文件读取属性值。 在application.yml 里添加如下配置:
1 2 3 4 5 6 properties: name: "daidawang" age: 20 person: gender: "man"
java_home 是从环境变量中读取,properties.name 则是从 application.yml 配置文件中读取。
1 2 3 4 5 6 7 8 9 10 @SneakyThrows @SuppressWarnings("unchecked") public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); System.out.println(context.getEnvironment().getProperty("java_home" )); System.out.println(context.getEnvironment().getProperty("properties.name" )); context.close(); }
1 2 D:\software\work\jdk\jdk8\jdk daidawang
ApplicationEventPublisher 的使用 点击进入 [48事件 - 监听器](AQWSSpring四十九讲杂项 (yuque.com) “超链接title”)
点击进入 [48事件 - 发布器](AQWSSpring四十九讲杂项 (yuque.com) “超链接title”)
ApplicationEventPublisher调用publishEvent() 方法传入ApplicationEvent 实例参数 来完成发布事件。 并通过@EventListener 注解来监听事件。 定义事件类UserRegisteredEvent:
1 2 3 4 5 6 public class UserRegisteredEvent extends ApplicationEvent { private static final long serialVersionUID = 6319117283222183184L ; public UserRegisteredEvent (Object source) { super (source); } }
将Component1 作为发送事件的 Bean:
1 2 3 4 5 6 7 8 9 10 @Slf4j @Component public class Component1 { @Autowired private ApplicationEventPublisher context; public void register () { log.info("用户注册" ); context.publishEvent(new UserRegisteredEvent (this )); } }
将Compinedt2作为事件监听器:
1 2 3 4 5 6 7 8 9 @Slf4j @Component public class Component2 { @EventListener public void aaa (UserRegisteredEvent event) { log.info("{}" , event); log.info("发送短信" ); } }
在 main() 方法中使用 Component1 发送事件:
1 2 3 4 5 6 7 8 9 @SneakyThrows @SuppressWarnings("unchecked") public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args); context.getBean(Component1.class).register(); context.close(); }
1 2 3 2024 -08-14T14:28 :57.394 +08:00 INFO 21560 --- [advanced-spring] [ main] d.advancedSpring.bean.Component1 : 用户注册2024 -08-14T14:28 :57.394 +08:00 INFO 21560 --- [advanced-spring] [ main] d.advancedSpring.bean.Component2 : daidawang.advancedSpring.bean.UserRegisteredEvent[source=daidawang.advancedSpring.bean.Component1@7fab4be7]2024 -08-14T14:28 :57.395 +08:00 INFO 21560 --- [advanced-spring] [ main] d.advancedSpring.bean.Component2 : 发送短信
2.容器实现 BeanFactory的实现 需要使用到BeanFactory的实现类DefaultListableBeanFactory。 有了Bean工厂还需要定义Bean,之后把定义的Bean注册到工厂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @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()" ); } }
自定义beanDefinition并注册到BeanFactory bean工厂手动注册beanDefinition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class A02Application { public static void main (String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DdwangBeanFactory.Config.class) .setScope("singleton" ) .getBeanDefinition(); beanFactory.registerBeanDefinition("config" , beanDefinition); System.out.println("--------------------手动注册config开始打印-------------------------" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); System.out.println("--------------------手动注册config打印结束-------------------------" ); } }
1 2 3 --------------------手动注册config开始打印------------------------- config --------------------手动注册config打印结束-------------------------
说明现在Bean工厂 有且只有一个 名为config的Bean。 现在明明有注解 @Configuration 和 @Bean 为什么Bean工厂里没有呢? 是因为现在的 DefaultListableBeanFactory 缺少了解析注解的能力。
添加注解解析器 1 2 3 4 5 6 7 8 9 public static void main (String[] args) { AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); System.out.println("---------------------注册注解配置处理器开始打印------------------------" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); System.out.println("---------------------注册注解配置处理器打印结束------------------------" ); }
1 2 3 4 5 6 7 8 9 10 11 --------------------手动注册config开始打印------------------------- config --------------------手动注册config打印结束------------------------- ---------------------注册注解配置处理器开始打印------------------------ 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 ---------------------注册注解配置处理器打印结束------------------------
根据打印出的信息,可以看到有一个名为 org.springframework.context.annotation.internalConfigurationAnnotationProcessor 的 Bean,根据其所含的 ConfigurationAnnotationProcessor 字样,可以知道这个 Bean 就是用来处理 @Configuration 和 @Bean 注解的,将配置类中定义的 Bean 信息补充到 BeanFactory 中。
那为什么在 Bean 工厂中依旧没有 bean1 和 bean2 呢?
现在仅仅是将处理器添加到了 Bean 工厂,还没有使用处理器。
使用注解解析器 使用处理器很简单,先获取到处理器,然后再使用即可。像 internalConfigurationAnnotationProcessor 这样的 Bean,都有一个共同的类型,名为 BeanFactoryPostProcessor,因此可以: 添加并使用BeanFactoryPostProcessor后置处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void main (String[] args) { beanFactory.getBeansOfType(BeanFactoryPostProcessor.class) .values() .forEach(p -> p.postProcessBeanFactory(beanFactory)); System.out.println("---------------------添加并使用BeanFactory 后置处理器开始打印------------------------" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); System.out.println("---------------------添加并使用BeanFactory 后置处理器打印结束------------------------" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 --------------------手动注册config开始打印------------------------- config --------------------手动注册config打印结束------------------------- ---------------------注册注解配置处理器开始打印------------------------ 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 ---------------------注册注解配置处理器打印结束------------------------ ---------------------添加并使用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 bean3 bean4 ---------------------添加并使用BeanFactory 后置处理器打印结束------------------------
添加依赖注入功能 现在bean1 和 bean2 已经被添加到Bean工厂, 修改 bean1使之依赖于 bean2,查看是否成功注入到 bean1中: bean1依赖于 bean2
1 2 3 4 5 6 7 8 9 10 11 12 13 @Slf4j static class Bean1 { public Bean1 () { log.debug("构造 Bean1()" ); } @Autowired private Bean2 bean2; public Bean2 getBean2 () { return bean2; } }
1 2 3 System.out.println("----------------------Bean后置处理器开始打印-----------------------" ); System.out.println(beanFactory.getBean(DdwangBeanFactory.Bean1.class).getBean2()); System.out.println("----------------------Bean后置处理器打印结束-----------------------" );
1 2 3 4 ----------------------Bean后置处理器开始打印----------------------- 16:54:10.857 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1() null ----------------------Bean后置处理器打印结束-----------------------
发现目前的bean工厂无法 注入 bean2 到bean1。 internalAutowiredAnnotationProcessor 后置处理器用于解析 @Autowired internalCommonAnnotationProcessor 后置处理器用于解析 @Resource 它们都有一个共同的类型 BeanPostProcessor,因此可以: 添加并使用Bean后置处理器:BeanPostProcessor
1 2 3 4 5 6 7 8 9 10 11 public static void main (String[] args) { beanFactory.getBeansOfType(BeanPostProcessor.class) .values() .forEach(beanFactory::addBeanPostProcessor); System.out.println("----------------------Bean后置处理器开始打印-----------------------" ); System.out.println(beanFactory.getBean(DdwangBeanFactory.Bean1.class).getBean2()); System.out.println("----------------------Bean后置处理器打印结束-----------------------" ); }
1 2 3 4 5 ----------------------Bean后置处理器开始打印----------------------- 16:56:35.432 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1() 16:56:35.445 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2 -- 构造 Bean2() daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2@587e5365 ----------------------Bean后置处理器打印结束-----------------------
建立 BeanPostProcessor 和 BeanFactory 的关系后,bean2 被成功注入到 bean1 中了。
按需加载bean改为预加载 当需要使用 Bean 时,Bean 才会被创建,即按需加载。
那有没有什么办法预先就初始化好单例对象呢?
添加 beanFactory.preInstantiateSingletons();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class A02Application { public static void main (String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DdwangBeanFactory.Config.class) .setScope("singleton" ) .getBeanDefinition(); beanFactory.registerBeanDefinition("config" , beanDefinition); System.out.println("--------------------手动注册config开始打印-------------------------" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); System.out.println("--------------------手动注册config打印结束-------------------------" ); AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); System.out.println("---------------------注册注解配置处理器开始打印------------------------" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); System.out.println("---------------------注册注解配置处理器打印结束------------------------" ); beanFactory.getBeansOfType(BeanFactoryPostProcessor.class) .values() .forEach(p -> p.postProcessBeanFactory(beanFactory)); System.out.println("---------------------添加并使用BeanFactory 后置处理器开始打印------------------------" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); System.out.println("---------------------添加并使用BeanFactory 后置处理器打印结束------------------------" ); beanFactory.getBeansOfType(BeanPostProcessor.class) .values() .forEach(beanFactory::addBeanPostProcessor); beanFactory.preInstantiateSingletons(); System.out.println("----------------------Bean后置处理器开始打印,开启预先初始化单例-----------------------" ); System.out.println(beanFactory.getBean(DdwangBeanFactory.Bean1.class).getBean2()); System.out.println("----------------------Bean后置处理器打印结束-----------------------" ); } }
添加beanFactory.preInstantiateSingletons();后控制台打印:
1 2 3 4 5 6 7 8 ---------------------添加并使用BeanFactory 后置处理器打印结束------------------------ 17:03:25.096 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1() 17:03:25.109 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2 -- 构造 Bean2() 17:03:25.110 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean3 -- 构造 Bean3() 17:03:25.111 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean4 -- 构造 Bean4() ----------------------Bean后置处理器开始打印,开启预先初始化单例----------------------- daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2@4961f6af ----------------------Bean后置处理器打印结束-----------------------
发现在输出———-Bean后置处理器开始打印,开启预先初始化单例——– 之前bean就初始化了,
如何注释掉40行 beanFactory.preInstantiateSingletons();,
发现在输出———-Bean后置处理器开始打印,开启预先初始化单例——– 之后bean被调用才初始化。
1 2 3 4 5 6 ---------------------添加并使用BeanFactory 后置处理器打印结束------------------------ ----------------------Bean后置处理器开始打印,开启预先初始化单例----------------------- 17:04:48.876 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean1 -- 构造 Bean1() 17:04:48.891 [main] INFO daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2 -- 构造 Bean2() daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean2@587e5365 ----------------------Bean后置处理器打印结束-----------------------
总结BeanFactory的实现 通过上面代码测试得到,BeanFactory 不会 :
不会主动调用 BeanFactory 后置处理器;
不会主动添加 Bean 后置处理器;
不会主动初始化单例对象;
解析 ${} 和 #{} (未写代码)详见 4.7 ${}内嵌值后置处理器
后置处理器的排序 @Autowired根据类型注入,如果同种类型的 Bean 有多个且没有指定变量名则报错。
@Resource 根据名称注入。
如果使用 @Autowired 的变量名称注入bean3, 又使用 @Resource(name = “bean4”) 注入bean4 呢?
新增如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 @Configuration static class Config { @Bean public Bean3 bean3 () { return new Bean3 (); } @Bean public Bean4 bean4 () { return new Bean4 (); } } interface Inter {} @Slf4j static class Bean3 implements Inter { public Bean3 () { log.info("构造 Bean3()" ); } } @Slf4j static class Bean4 implements Inter { public Bean4 () { log.info("构造 Bean4()" ); } } @Slf4j static class Bean1 { public Bean1 () { log.info("构造 Bean1()" ); } @Autowired private Bean2 bean2; public Bean2 getBean2 () { return bean2; } @Autowired @Resource(name = "bean4") private Inter bean3; public Inter getInter () { return bean3; } }
1 2 3 4 public static void main (String[] args) { System.out.println(beanFactory.getBean(Bean1.class).getInter()); }
1 daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean4@707194ba
根据打印的结果可知,@Autowired 先生效了,这是因为 internalAutowiredAnnotationProcessor 排在 internalCommonAnnotationProcessor 之前。可以查看bean处理器的先后关系:
1 2 3 4 5 6 7 8 public static void main (String[] args) { beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(i -> { System.out.println(">>>> " + i); beanFactory.addBeanPostProcessor(i); }); }
1 2 >>>> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@385c9627 >>>> org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@139982de
通过流处理来改变它们的顺序:
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) { beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream() .sorted(Objects.requireNonNull(beanFactory.getDependencyComparator())) .forEach(i -> { System.out.println(">>>> " + i); beanFactory.addBeanPostProcessor(i); }); System.out.println(beanFactory.getBean(Bean1.class).getInter()); }
1 2 3 >>>> org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@139982de >>>> org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@682b2fa daidawang.advancedSpring.bean.a02.DdwangBeanFactory$Bean4@445b295b
为什么使用 beanFactory.getDependencyComparator() 后就改变了 BeanPostProcessor 的先后顺序呢?
在调用的 AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); 方法源码中有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void registerAnnotationConfigProcessors (BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null ); } public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors ( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null ) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver ()); } } }
进入AutowiredAnnotationBeanPostProcessor 源码:其oreder为 Integer.MAX_VALUE-2
进入CommonAnnotationBeanPostProcessor 源码:其oreder为 Integer.MAX_VALUE-3
值越小,优先级越大,就排在更前面。
所以CommonAnnotationBeanPostProcessor 的优先级高,@Resource 就先生效
ApplicationContext的实现 基础准备
1 2 3 4 5 6 7 8 9 static class Bean1 {} static class Bean2 { @Getter @Setter private Bean1 bean1; }
ClassPathXmlApplicationContext 读取类路径下的xml文件并初始化和管理bean.
基于 classpath 下的 xml 格式的配置文件来创建。
创建一个名为为b01的xml,并配置bean2
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <bean id ="bean1" class ="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean1" /> <bean id ="bean2" class ="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean2" > <property name ="bean1" ref ="bean1" /> </bean > </beans >
1 2 3 4 5 private static void testClassPathXmlApplicationContext () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("b01.xml" ); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); System.out.println(context.getBean(Bean2.class).getBean1()); }
1 2 3 bean1 bean2 daidawang.advancedSpring.bean.a02.DdwangApplicationContext$Bean1@503d687a
FileSystemXmlApplicationContext 从磁盘路径下读取xml格式的配置文件
1 2 3 4 5 private static void testFileSystemXmlApplicationContext () { FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext ("D:\\workspace\\advanced-spring\\src\\main\\resources\\b01.xml" ); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); System.out.println(context.getBean(Bean2.class).getBean1()); }
1 2 3 bean1 bean2 daidawang.advancedSpring.bean.a02.DdwangApplicationContext$Bean1@503d687a
XmlBeanDefinitionReader ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 都依赖于从 XML 文件中读取 Bean 的信息,而这都利用了 XmlBeanDefinitionReader 进行读取。
1 2 3 4 5 6 7 8 9 10 private static void testXmlBeanDefinitionReader () { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); System.out.println("读取前" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (beanFactory); reader.loadBeanDefinitions(new FileSystemResource ("src\\main\\resources\\b01.xml" )); System.out.println("读取后" ); Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println); }
1 2 3 4 读取之前... 读取之后... bean1 bean2
AnnotationConfigApplicationContext 基于Java配置类创建bean
先创建一个配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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; } }
1 2 3 4 5 private static void testAnnotationConfigApplicationContext () { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (Config.class); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); System.out.println(context.getBean(Bean2.class).getBean1()); }
1 2 3 4 5 6 7 8 9 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 ddwangApplicationContext.Config bean1 bean2 daidawang.advancedSpring.bean.a02.DdwangApplicationContext$Bean1@14555e0a
与基于XML创建的ApplicationContext 相比,AnnotationConfigApplicationContext 使得容器多了一些后置处理器相关的Bean。
如果要在先前的两种方式中也添加上这些 后置处理器Bean,可以在 XML 进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <bean id="bean1" class="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean1" /> <bean id="bean2" class="daidawang.advancedSpring.bean.a02.DdwangApplicationContext.Bean2" > <property name="bean1" ref="bean1" /> </bean> <!-- 添加后置处理器 --> <context:annotation-config /> </beans>
AnnotationConfigServletWebServerApplicationContext 基于Java配置类创建bean,内嵌WebServer用于Web环境的容器。
先定义配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Configuration static class WebConfig { @Bean public ServletWebServerFactory servletWebServerFactory () { return new TomcatServletWebServerFactory (); } @Bean public DispatcherServlet dispatcherServlet () { return new DispatcherServlet (); } @Bean public DispatcherServletRegistrationBean dispatcherServletRegistrationBean (DispatcherServlet dispatcherServlet) { return new DispatcherServletRegistrationBean (dispatcherServlet, "/" ); } @Bean("/hello") public Controller controller () { return (request, response) -> { response.getWriter().println("hello" ); return null ; }; } }
1 2 3 4 private static void testAnnotationConfigServletWebServerApplicationContext () { AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext (WebConfig.class); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); }
运行代码,在浏览器中访问 http://localhost:8080/hello 路径则会显示出 hello 字样:
内嵌的Tomcat是如何工作的(通过AnnotationConfigServletWebServerApplicationContex自动启动内嵌的 Tomcat 服务器,并将所有配置的 Bean 注册到上下文中)
web程序的入口都是通过 “/“ 的通配符,先由DispatcherServlet处理后分发请求到控制器
一个最基础的web程序需要 Tomcat,DispatcherServlet,和将DispatcherServlet注册到Tomcat上
BeanDefinition BeanDefinition 它封装了 Bean 基本信息,Spring 根据 BeanDefinition 就能创建出符合要求的 Bean。
读取 BeanDefinition 可以通过下列两种类完成:
BeanDefinitionReader
ClassPathBeanDefinitionScanner
BeanDefinitionReader 该接口中对 loadBeanDefinitions() 方法进行了多种重载,支持传入一个或多个 Resource 对象、资源位置来加载 BeanDefinition 。
它有一系列相关实现,比如:
XmlBeanDefinitionReader :通过读取 XML 文件来加载;
PropertiesBeanDefinitionReader :通过读取 properties 文件来加载,此类已经被 @Deprecated 注解标记;
除此之外,还有一个 AnnotatedBeanDefinitionReader ,尽管它并不是 BeanDefinition 的子类,但它们俩长得很像,根据其类注释可知:它能够通过编程的方式对 Bean 进行注册,是 ClassPathBeanDefinitionScanner 的替代方案,能读取通过注解定义的 Bean。
ClassPathBeanDefinitionScanner 通过扫描指定包路径下的 @Component 及其派生注解来注册 Bean,是 @ComponentScan 注解的底层实现。
BeanDefinitionRegistry AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 中都有一个 BeanDefinitionRegistry 类型的成员变量,
它是一个接口,提供了 BeanDefinition 的增加、删除和查找功能。
注册与获取Bean 将 Bean 添加到 Spring 容器里的大致流程,
利用 <font style="color:#DF2A3F;">BeanDefinitionReader</font> 读取配置文件或注解信息,为每一个 Bean 生成一个 <font style="color:#DF2A3F;">BeanDefinition</font>
将 <font style="color:#DF2A3F;">BeanDefinition</font> 注册到 <font style="color:#DF2A3F;">BeanDefinitionRegistry</font> 中
当创建 Bean 对象时,直接从 <font style="color:#DF2A3F;">BeanDefinitionRegistry</font> 中获取 <font style="color:#DF2A3F;">BeanDefinition</font> ,来生成 Bean。
如果生成的 Bean 是单例 的,Spring 会将它们保存到 <font style="color:#DF2A3F;">SingletonBeanRegistry</font> 中,即三级缓存的第一级缓存,后续需要时从这里面寻找,避免重复创建。
那么向 Spring 容器中添加单例 Bean 时,可以跳过注册 BeanDefinition ,直接向 SingletonBeanRegistry 中添加创建完成的 Bean。既然添加的是创建完成的 Bean,所以 这个单例 Bean 不会经过 Spring 的生命周期 。
SingletonBeanRegistry 是一个接口,它有一个子接口名为 ConfigurableListableBeanFactory ,而这恰好是 BeanFactoryPostProcessor 接口中抽象方法的参数:
1 2 3 4 @FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException; }
使用 BeanFactoryPostProcesser,注册创建完成的 Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Slf4j public class TestBeanFactoryPostProcessor { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (); context.registerBean("bean2" , Bean2.class); context.registerBean(MyBeanFactoryPostProcessor.class); context.refresh(); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); System.out.println(">>>>>>>>>>>>>>>>>>" ); System.out.println(context.getBean(Bean1.class)); } static class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { Bean1 bean1 = new Bean1 (); bean1.setName("mofan" ); beanFactory.registerSingleton("bean1" , bean1); } } @Getter @ToString static class Bean1 { @Setter private String name; private Bean2 bean2; @Autowired private void setBean2 (Bean2 bean2) { log.debug("依赖注入 bean2" ); this .bean2 = bean2; } @PostConstruct public void init () { log.debug("初始化..." ); } } static class Bean2 { } }
1 2 3 4 5 6 7 8 9 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 bean2 testBeanFactoryPostProcessor.MyBeanFactoryPostProcessor >>>>>>>>>>>>>>>>>> TestBeanFactoryPostProcessor.Bean1(name=mofan, bean2=null)
BeanDefinition 的名称数组中不包含 bean1,也没有输出任何与经过 Spring 生命周期相关的日志信息,容器中 bean1 里注入的 bean2 也是 null。这表明通过这种方式注册的 Bean 不会注册 BeanDefinition ,也不会经过 Spring 生命周期。
根据前面的补充,现在可以AnnotatedBeanDefinitionReader 这样注册并获取 Bean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class DefaultListableBeanFactoryTest { static class MyBean { } public static void main (String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader (beanFactory); reader.registerBean(MyBean.class); MyBean bean = beanFactory.getBean(MyBean.class); System.out.println(bean); } }
3. Bean生命周期
通过 Bean 后置处理器 的方式说明 Bean 生命周期:
@Autowired 依赖注入
@PostConstruct 初始化
@PreDestroy 销毁
通过 Bean 内置处理器 的方式说明 Bean 生命周期
InitializingBean 初始化
DisposableBean 销毁
配置文件中指定初始化和销毁方法 :
标签。
<destroy-method=”destroyMethod”>标签。
使用@Bean注解指定初始化和销毁方法:
@Bean注解的initMethod属性来指定初始化
@Bean注解的destroyMethod属性来指定销毁方法。
自定义一个 SpringBoot 的主启动类:
1 2 3 4 5 6 7 8 @SpringBootApplication public class A03Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args); context.close(); } }
在启动类所在路径下再定义一个类,使其能够被自动装配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Slf4j @Component public class LifeCycleBean { public LifeCycleBean () { log.info("构造" ); } @Autowired public void autowire (@Value("${JAVA_HOME}") String home) { log.info("依赖注入: {}" , home); } @PostConstruct public void init () { log.info("初始化" ); } @PreDestroy public void destroy () { log.info("销毁" ); } }
运行主启动类,查看控制台的日志信息(只列举主要信息):
1 2 3 4 d.advancedSpring.bean.a03.LifeCycleBean : 构造 d.advancedSpring.bean.a03.LifeCycleBean : 依赖注入: D:\software\work\jdk\jdk8\jdk d.advancedSpring.bean.a03.LifeCycleBean : 初始化 d.advancedSpring.bean.a03.LifeCycleBean : 销毁
Spring 还提供了一些对 Bean 生命周期的各个阶段进行拓展的 BeanPostProcessor ,比如 InstantiationAwareBeanPostProcessor 和 DestructionAwareBeanPostProcessor 。
实现这两个接口,并使用 @Component 注解标记实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @Slf4j @Component public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor , DestructionAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) throws BeansException { if ("lifeCycleBean" .equals(beanName)) { log.info("<<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean" ); } return null ; } @Override public boolean postProcessAfterInstantiation (Object bean, String beanName) throws BeansException { if ("lifeCycleBean" .equals(beanName)) { log.info("<<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点" ); } return true ; } @Override public PropertyValues postProcessProperties (PropertyValues pvs, Object bean, String beanName) throws BeansException { if ("lifeCycleBean" .equals(beanName)) { log.info("<<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource" ); } return pvs; } @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { if ("lifeCycleBean" .equals(beanName)) { log.info("<<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties" ); } return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { if ("lifeCycleBean" .equals(beanName)) { log.info("<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强" ); } return bean; } @Override public void postProcessBeforeDestruction (Object o, String beanName) throws BeansException { if ("lifeCycleBean" .equals(beanName)) { log.info("<<<<<<<<<< 销毁执行之前,如 @PreDestroy" ); } } }
再运行主启动类,查看控制台的日志信息(只列举主要信息):
1 2 3 4 5 6 7 8 9 10 11 12 d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean d.advancedSpring.bean.a03.LifeCycleBean : 构造 d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点 d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource d.advancedSpring.bean.a03.LifeCycleBean : 依赖注入: D:\software\work\jdk\jdk8\jdk d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties d.advancedSpring.bean.a03.LifeCycleBean : 初始化 d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强 o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' d.a.bean.a03.A03Application : Started A03Application in 1.474 seconds (process running for 2.081) d.a.bean.a03.MyBeanPostProcessor : <<<<<<<<<< 销毁执行之前,如 @PreDestroy d.advancedSpring.bean.a03.LifeCycleBean : 销毁
什么是实例化 什么是初始化?
模板方法设计模式 为什么实现了 BeanPostProcessor 接口后就能够在 Bean 生命周期的各个阶段进行拓展呢?
这就是模板方法设计模式。
模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。
使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
现有如下代码,模拟 BeanFactory 构造 Bean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class TestMethodTemplate { public static void main (String[] args) { MyBeanFactory myBeanFactory = new MyBeanFactory (); myBeanFactory.getBean(); } static class MyBeanFactory { private static ArrayList<BeanPostProcessor> processors = new ArrayList <>(); public Object getBean () { Object bean = new Object (); System.out.println("构造 " + bean); System.out.println("依赖注入 " + bean); System.out.println("初始化 " + bean); return bean; } }
1 2 3 构造 java.lang.Object@49097b5d 依赖注入 java.lang.Object@49097b5d 初始化 java.lang.Object@49097b5d
现在需要在依赖注入后新增 解析功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public class TestMethodTemplate { public static void main (String[] args) { MyBeanFactory myBeanFactory = new MyBeanFactory (); myBeanFactory.addProcessor(bean -> System.out.println("解析 @Autowired" )); myBeanFactory.addProcessor(bean -> System.out.println("解析 @Resource" )); myBeanFactory.getBean(); } static class MyBeanFactory { private static ArrayList<BeanPostProcessor> processors = new ArrayList <>(); public Object getBean () { Object bean = new Object (); System.out.println("构造 " + bean); System.out.println("依赖注入 " + bean); for (BeanPostProcessor processor : processors) { processor.inject(bean); } System.out.println("初始化 " + bean); return bean; } public void addProcessor (BeanPostProcessor beanPostProcessor) { processors.add(beanPostProcessor); } } static interface BeanPostProcessor { void inject (Object bean) ; } }
1 2 3 4 构造 java.lang.Object@5fd0d5ae 依赖注入 java.lang.Object@5fd0d5ae 解析 @Autowired 初始化 java.lang.Object@5fd0d5a
4. Bean后置处理器 编写如下三个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Slf4j @ToString public class Bean1 { private Bean2 bean2; @Autowired public void setBean2 (Bean2 bean2) { log.info("@Autowired 生效: {}" , bean2); this .bean2 = bean2; } private Bean3 bean3; @Resource public void setBean3 (Bean3 bean3) { log.info("@Resource 生效: {}" , bean3); this .bean3 = bean3; } private String home; @Autowired public void setHome (@Value("${JAVA_HOME}") String home) { log.info("@Value 生效: {}" , home); this .home = home; } @PostConstruct public void init () { log.info("@PostConstruct 生效" ); } @PreDestroy public void destroy () { log.info("@PreDestroy 生效" ); } }
1 2 3 4 5 public class Bean2 {} public class Bean3 {}
Bean2 和 Bean3 很简单,而在 Bean1 中使用了多个注解以实现 Bean 注入和值注入。
GenericApplicationContext容器 GenericApplicationContext 是一个干净的容器,而且GenericApplicationContext 有refresh() 方法初始化容器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class A04Application { public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("bean1" , Bean1.class); context.registerBean("bean2" , Bean2.class); context.registerBean("bean3" , Bean3.class); context.refresh(); context.close(); } }
运行上述方法后,控制台中只打印了与 Spring 相关的日志信息,也就是说 Bean1 中使用的注解并没有生效。
@Autowired@Value后置处理器 向 GenericApplicationContext 添加一些与 Bean 后置处理器相关的 Bean,使得 Bean1 中使用的注解能够生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) { context.registerBean("bean3" , Bean3.class); context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver ()); context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.refresh(); }
1 2 INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@4386f16 INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: D:\software\work\jdk\jdk8\jdk
@Resource@PostConstruct@PreDestroy处理器 @Autowired 和 @Value 注解成功生效,但 @Resource 、@PostConstruct 和 @PreDestroy 依旧没有生效,因此还需要添加解析它们的 Bean 后置处理器。
1 2 3 4 5 6 7 8 public static void main (String[] args) { context.registerBean(CommonAnnotationBeanPostProcessor.class); }
1 2 3 4 5 INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Resource 生效: daidawang.advancedSpring.bean.a04.Bean3@1b1473ab INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@3e92efc3 INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: D:\software\work\jdk\jdk8\jdk INFO daidawang.advancedSpring.bean.a04.Bean1 -- @PostConstruct 生效: D:\software\work\jdk\jdk8\jdk INFO daidawang.advancedSpring.bean.a04.Bean1 -- @PreDestroy 生效: D:\software\work\jdk\jdk8\jdk
看到上面控制台打印的朋友还能回答为什么 @Resource 比 @Autowired 先执行吗? 😏
@ConfigurationProperties后置处理器 编写带有@ConfigurationProperties 的Bean4,获取环境变量中 java.home 和 java.version 的信息。
1 2 3 4 5 6 @Data @ConfigurationProperties(prefix = "java") public class Bean4 { private String home; private String version; }
对先前的 main() 方法进行补充:
1 2 3 4 5 6 7 8 9 10 11 public static void main (String[] args) { context.registerBean("bean4" , Bean4.class); System.out.println(context.getBean(Bean4.class)); }
1 Bean4(home=null, version=null)
说明 Bean4 成功添加到容器中,但值注入失败了,显然也是因为缺少解析 @ConfigurationProperties 注解的后置处理器。
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { context.registerBean("bean4" , Bean4.class); ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory()); System.out.println(context.getBean(Bean4.class)); }
1 Bean4(home=D:\software\work\jdk\jdk17, version=17.0.9)
后置处理器执行时机 在Bean的生命周期 里 @Autowired@Value的处理器 或者 @Resource@PostConstruct@PreDestroy处理器 @ConfigurationProperties的处理器 是什么时候执行的呢?
视频P17 的最后 016-第四讲-常见bean后处理器3_哔哩哔哩_bilibili 老师简单的提了一下但是没有写代码证明。
那么我们用已经学习到的知识去验证一下把!
如何不看下面的代码你通过 前面16讲的课程也可以独立写出来吗? 自己写写证明这些后置处理器的执行时机再来和我对一下把(我的也是自己写的不一定对)
将bean1加入容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Slf4j @ToString @Component public class Bean1 { private Bean2 bean2; }
创建 MyBeanPostProcessor 并实现 InstantiationAwareBeanPostProcessor , DestructionAwareBeanPostProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Slf4j @Component class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor , DestructionAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) throws BeansException { if ("bean1" .equals(beanName)) { log.info("<<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean" ); } return null ; } @Override public boolean postProcessAfterInstantiation (Object bean, String beanName) throws BeansException { if ("bean1" .equals(beanName)) { log.info("<<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点" ); } return true ; } @Override public PropertyValues postProcessProperties (PropertyValues pvs, Object bean, String beanName) throws BeansException { if ("bean1" .equals(beanName)) { log.info("<<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource" ); } return pvs; } @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { if ("bean1" .equals(beanName)) { log.info("<<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties" ); } return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { if ("bean1" .equals(beanName)) { log.info("<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强" ); } return bean; } @Override public void postProcessBeforeDestruction (Object o, String beanName) throws BeansException { if ("bean1" .equals(beanName)) { log.info("<<<<<<<<<< 销毁执行之前,如 @PreDestroy" ); } }
A04Application 上添加@SpringBootApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication public class A04Application { ConfigurableApplicationContext context1 = SpringApplication.run(A04Application.class, args); context1.close(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 2024-08-16T14:24:15.290+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean 2024-08-16T14:24:15.292+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点 2024-08-16T14:24:15.293+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource 2024-08-16T14:24:15.293+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @Resource 生效: daidawang.advancedSpring.bean.a04.Bean3@3a082ff4 2024-08-16T14:24:15.296+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@237f7970 2024-08-16T14:24:15.297+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @Value 生效: D:\software\work\jdk\jdk8\jdk 2024-08-16T14:24:15.298+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties 2024-08-16T14:24:15.298+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @PostConstruct 生效: D:\software\work\jdk\jdk8\jdk 2024-08-16T14:24:15.298+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强 2024-08-16T14:24:15.822+08:00 INFO 3052 --- [advanced-spring] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' 2024-08-16T14:24:15.830+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.A04Application : Started A04Application in 2.076 seconds (process running for 3.592) 2024-08-16T14:24:15.935+08:00 INFO 3052 --- [advanced-spring] [ main] d.a.bean.a04.MyBeanPostProcessor : <<<<<<<<<< 销毁执行之前,如 @PreDestroy 2024-08-16T14:24:15.935+08:00 INFO 3052 --- [advanced-spring] [ main] d.advancedSpring.bean.a04.Bean1 : @PreDestroy 生效: D:\software\work\jdk\jdk8\jdk
AutowiredAnnotationBeanPostProcessor 通过上面的代码,知道 AutowiredAnnotationBeanPost Processor 是处理解析 @Autowired @Value,
那么他的如何运行的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class AutowiredAnnotationBeanPostProcessor 运行原理 { @SneakyThrows public static void main (String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); beanFactory.registerSingleton("bean2" ,new Bean2 ()); beanFactory.registerSingleton("bean3" ,new Bean3 ()); beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver ()); AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor (); postProcessor.setBeanFactory(beanFactory); Bean1 bean1 = new Bean1 (); System.out.println(bean1); postProcessor.postProcessProperties(null ,bean1,"bean1" ); System.out.println(bean1); } }
1 2 3 4 Bean1(bean2=null, bean3=null, home=null) 15:41:49.646 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@3c0a50da 15:41:49.659 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: ${JAVA_HOME} Bean1(bean2=daidawang.advancedSpring.bean.a04.Bean2@3c0a50da, bean3=null, home=${JAVA_HOME})
发现调用 AutowiredAnnotationBeanPostProcessor#postProcessProperties() 方法时,
Bean1 中的 bean2、成功且正常的被注入,
进入源码,我们发现 findAutowiringMetadata() ,用于查找指定 bean对象哪些地方使用了 @Autowired、@Value 等与注入相关的注解,并将这些信息封装在 InjectionMetadata 对象中,之后调用其 inject() 方法利用反射完成注入。
查找带有@Autowired的方法 findAutowiringMetadata() 方法是一个私有方法,尝试利用反射进行调用并进行断点查看 InjectionMetadata 对象中的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SneakyThrows public static void main (String[] args) { AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor (); postProcessor.setBeanFactory(beanFactory); Bean1 bean1 = new Bean1 (); Method method = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata" , String.class, Class.class, PropertyValues.class); method.setAccessible(true ); InjectionMetadata metadata = (InjectionMetadata) method.invoke(postProcessor, "bean1" , Bean1.class, null ); System.out.println(bean1); }
InjectionMetadata 中有一个名为 injectedElements 的集合类型成员变量,根据上图所示,injectedElements 存储了被相关注解标记的成员变量、方法的信息,因为 Bean1 中的 bean3 成员变量、setBean2() 和 setHome() 方法恰好被 @Autowired 注解标记。
反射依赖注入 然后按照源码一样,调用 InjectionMetadata#inject() 方法进行依赖注入:
1 2 3 4 5 6 7 8 9 10 11 @SneakyThrows public static void main (String[] args) { InjectionMetadata metadata = (InjectionMetadata) method.invoke(postProcessor, "bean1" , Bean1.class, null ); metadata.inject(bean1, "bean1" , null ); System.out.println(bean1); }
1 2 3 4 5 Bean1(bean2=null, bean3=null, home=null) 15:52:37.841 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@5a4aa2f2 15:52:37.853 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: ${JAVA_HOME} Bean1(bean2=daidawang.advancedSpring.bean.a04.Bean2@5a4aa2f2, bean3=null, home=${JAVA_HOME}) org.springframework.beans.factory.annotation.InjectionMetadata@4de5031f
InjectionMetadata#inject() 是如何 根据不同的类型 都注入的呢
修改
Bean1,给private Bean3 bean3;上添加@Autowired 注解
1 2 + @Autowired private Bean3 bean3;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @SneakyThrows public static void main (String[] args) { Field bean3 = Bean1.class.getDeclaredField("bean3" ); DependencyDescriptor dd1 = new DependencyDescriptor (bean3, false ); Object o1 = beanFactory.doResolveDependency(dd1, null , null , null ); System.out.println(o1); Method setBean2 = Bean1.class.getDeclaredMethod("setBean2" , Bean2.class); DependencyDescriptor dd2 = new DependencyDescriptor (new MethodParameter (setBean2, 0 ), false ); Object o2 = beanFactory.doResolveDependency(dd2, null , null , null ); System.out.println(o2); Method setHome = Bean1.class.getDeclaredMethod("setHome" , String.class); DependencyDescriptor dd3 = new DependencyDescriptor (new MethodParameter (setHome, 0 ), true ); Object o3 = beanFactory.doResolveDependency(dd3, null , null , null ); System.out.println(o3); }
${}内嵌值后置处理器 上面的控制台打印,我们发现 home=${JAVA_HOME}),
还缺少${} 的后置处理器
1 2 3 4 5 6 7 8 @SneakyThrows public static void main (String[] args) { beanFactory.addEmbeddedValueResolver(new StandardEnvironment ()::resolvePlaceholders); }
1 2 3 4 5 Bean1(bean2=null, bean3=null, home=null) Bean1(bean2=null, bean3=null, home=null) 16:11:57.283 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Autowired 生效: daidawang.advancedSpring.bean.a04.Bean2@5ea434c8 16:11:57.306 [main] INFO daidawang.advancedSpring.bean.a04.Bean1 -- @Value 生效: D:\software\work\jdk\jdk8\jdk Bean1(bean2=daidawang.advancedSpring.bean.a04.Bean2@5ea434c8, bean3=null, home=D:\software\work\jdk\jdk8\jdk)
添加${} 的后置处理器后 home正常解析。
5. BeanFactory后置处理器 BeanFactory后置处理器为BeanFactory提供扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package daidawang.advancedSpring.bean.a05;import com.alibaba.druid.pool.DruidDataSource;import org.mybatis.spring.SqlSessionFactoryBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;@Configuration @ComponentScan() 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/advanced_spring" ); dataSource.setName("root" ); dataSource.setPassword("123456" ); return dataSource; } }
1 2 3 4 5 6 7 8 9 10 package daidawang.advancedSpring.bean.a05;import lombok.extern.slf4j.Slf4j;@Slf4j public class Bean1 { public Bean1 () { log.info("我被 Spring 管理啦" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 package daidawang.advancedSpring.bean.a05.component;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;@Slf4j @Component public class Bean2 { public Bean2 () { log.info("我被 Spring 管理啦" ); } }
继续使用 GenericApplicationContext 作为容器,向容器中注册 config:
1 2 3 4 5 6 7 8 9 10 11 12 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("config" , Config.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); }
并没有打印出除 config 以外的 Bean 信息,也就是说 Config 类中的 @ComponentScan 和 @Bean 注解都没有生效。
根据经验,显然是因为缺少某个后置处理器,引出下文:
ConfigurationClassPostProcessor ConfigurationClassPostProcessor 后置处理器: @ComponentScan @Bean @Import @ImportResource
1 2 3 4 5 6 7 8 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("config" , Config.class); context.registerBean(ConfigurationClassPostProcessor.class); }
1 2 3 4 5 6 7 8 9 10 11 14:28:46.521 [main] INFO daidawang.advancedSpring.bean.a05.component.Bean2 -- 我被 Spring 管理啦 14:28:46.525 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦 14:28:46.660 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1,root} inited config org.springframework.context.annotation.ConfigurationClassPostProcessor bean2 bean1 sqlSessionFactoryBean dataSource 14:28:46.716 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1} closing ... 14:28:46.717 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-1} closed
在使用 MyBatis 时,经常会使用到 @Mapper 注解,而这个注解的解析也需要使用到特定的 BeanFactory 后置处理器。
以下两个接口被 @Mapper 注解标记
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package daidawang.advancedSpring.bean.a05.mapper;import org.apache.ibatis.annotations.Mapper;@Mapper public interface Mapper1 {} package daidawang.advancedSpring.bean.a05.mapper;import org.apache.ibatis.annotations.Mapper;@Mapper public interface Mapper2 {}
然后添加解析 @Mapper 注解的后置处理器 MapperScannerConfigurer :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package daidawang.advancedSpring.bean.a05;import lombok.SneakyThrows;import org.mybatis.spring.mapper.MapperScannerConfigurer;import org.springframework.context.annotation.ConfigurationClassPostProcessor;import org.springframework.context.support.GenericApplicationContext;public class A05Application { @SneakyThrows public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("config" ,Config.class); context.registerBean(ConfigurationClassPostProcessor.class); context.registerBean(MapperScannerConfigurer.class ,bd->{bd.getPropertyValues().add("basePackage" ,"daidawang.advancedSpring.bean.a05.mapper" );}); context.refresh(); for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println(beanDefinitionName); } context.close(); } }
添加了MapperScannerConfigurer 后置处理器后,mapper1 和mapper2 被注册到容器里
模拟实现 模拟实现@ComponentScan 组件扫描 模拟 beanFactory 后置处理器 ConfigurationClassPostProcessor ,是如何通过@ComponentScan 注解 将带有 @Component 及其派生注解 的类 注册到容器。
在bean2目录下创建bean3,bean4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package daidawang.advancedSpring.bean.a05.component;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;@Slf4j @Controller public class Bean3 { public Bean3 () { log.info("我被 Spring 管理啦" ); } } package daidawang.advancedSpring.bean.a05.component;import lombok.extern.slf4j.Slf4j;@Slf4j public class Bean4 { public Bean4 () { log.info("我被 Spring 管理啦" ); } }
编写 ComponentScanPostProcessor 用于实现 @Component 注解的解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package daidawang.advancedSpring.bean.a05;import lombok.SneakyThrows;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.DefaultListableBeanFactory;import org.springframework.context.annotation.AnnotationBeanNameGenerator;import org.springframework.context.annotation.ComponentScan;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.core.io.Resource;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.classreading.CachingMetadataReaderFactory;import org.springframework.core.type.classreading.MetadataReader;import org.springframework.stereotype.Component;import java.awt.*;import java.io.IOException;public class ComponentScanPostProcessor implements BeanFactoryPostProcessor { @Override @SneakyThrows public void postProcessBeanFactory (ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory (); AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator (); if (componentScan != null ) { for (String packageName : componentScan.basePackages()) { System.out.println(packageName); String path = "classpath*:" + packageName.replace("." , "/" ) + "/*.class" ; for (Resource resource : new PathMatchingResourcePatternResolver ().getResources(path)) { MetadataReader reader = factory.getMetadataReader(resource); AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata(); if (annotationMetadata.hasAnnotation(Component.class.getName()) || annotationMetadata.hasMetaAnnotation(Component.class.getName())) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition( reader.getClassMetadata().getClassName()) .getBeanDefinition(); if (configurableListableBeanFactory instanceof DefaultListableBeanFactory beanFactory) { String beanName = generator.generateBeanName(beanDefinition, beanFactory); beanFactory.registerBeanDefinition(beanName, beanDefinition); } } } } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("config" , Config.class); context.registerBean(ComponentScanPostProcessor.class); context.refresh(); for (String name : context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); }
1 2 3 4 5 6 7 daidawang.advancedSpring.bean.a05.component 16:48:18.613 [main] INFO daidawang.advancedSpring.bean.a05.component.Bean2 -- 我被 Spring 管理啦 16:48:18.615 [main] INFO daidawang.advancedSpring.bean.a05.component.Bean3 -- 我被 Spring 管理啦 config daidawang.advancedSpring.bean.a05.ComponentScanPostProcessor bean2 bean3
总结
实现 BeanFactoryPostProcessor ,并重写其方法
获取 @ComponentScan 的值,并将值转为类路径
根据类路径获取路径下所有的Resource 对象
根据Resource 对象获取MetadataReader 对象进而获取AnnotationMetadata 对象
AnnotationMetadata 判断 是否带有 @Component 或 其派生注解 的类
通过MetadataReader 获取带有@Component 或 其派生注解 的类 的类路径 创建 BeanDefinition 对象
通过 AnnotationBeanNameGenerator 创建 BeanName
DefaultListableBeanFactory 注册 BeanDefinition 到容器
模拟实现注入@Bean Config 类中再增加一个方法作为干扰项:
1 2 3 4 5 6 7 8 9 10 11 @Configuration @ComponentScan("daidawang.advancedSpring.bean.a05.component") public class Config { public Bean2 bean2 () { return new Bean2 (); } }
自行编写一个 BeanFactoryPostProcessor 的实现类用于解析 @Bean 注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package daidawang.advancedSpring.bean.a05;import lombok.SneakyThrows;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;import org.springframework.context.annotation.Bean;import org.springframework.core.io.ClassPathResource;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.MethodMetadata;import org.springframework.core.type.classreading.CachingMetadataReaderFactory;import org.springframework.core.type.classreading.MetadataReader;import org.springframework.util.StringUtils;public class DDWBeanPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override @SneakyThrows public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory (); MetadataReader reader = factory.getMetadataReader(new ClassPathResource ("daidawang/advancedSpring/bean/a05/Config.class" )); AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata(); for (MethodMetadata annotatedMethod : annotationMetadata.getAnnotatedMethods(Bean.class.getName())) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); builder.setFactoryMethodOnBean(annotatedMethod.getMethodName(), "config" ); builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); String initMethod = (String) annotatedMethod.getAnnotationAttributes(Bean.class.getName()).get("init" ); if (StringUtils.hasLength(initMethod)) { builder.setInitMethodName(initMethod); } AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); registry.registerBeanDefinition(annotatedMethod.getMethodName(), beanDefinition); } } }
在构造 BeanDefinition 时调用了 setAutowireMode() 方法设置注入模式,这是因为在自定义的 Config 类中有一特殊的被 @Bean 标记的方法:
1 2 3 4 5 6 @Bean public SqlSessionFactoryBean sqlSessionFactoryBean (DataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean (); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; }
接收一个 DataSource 类型的参数,需要将容器中这个类型的 Bean 进行注入,设置的 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR 注入模式则能完成这个功能。
1 2 3 4 5 6 7 18:10:51.353 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦 config daidawang.advancedSpring.bean.a05.DDWBeanPostProcessor bean1 sqlSessionFactoryBean dataSource 18:10:51.453 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-0} closing ...
模拟实现注入@Mapper @Mapper 注解写在接口上, 而根据前面内容得知,@Mapper 被解析后在 Spring 容器中也存在与被标记的接口相关的 Bean。
难道 Spring 能管理接口?
那肯定是不行的,Spring 只能管理对象这是毋庸置疑的。那这些接口是怎么变成对象被 Spring 管理的呢?
这依赖于 MapperFactoryBean 将接口转换为对象。
在 Config 添加注册 mapper1MapperFactoryBean 和 mapper2MapperFactoryBean 的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Bean public MapperFactoryBean<Mapper1> mapper1MapperFactoryBean (SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean <>(Mapper1.class); mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory); return mapperFactoryBean;} @Bean public MapperFactoryBean<Mapper2> mapper2MapperFactoryBean (SqlSessionFactory sqlSessionFactory) {MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean <>(Mapper2.class); mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory); return mapperFactoryBean;}
再运行 main() 方法可以看到容器中存在名为 mapper1MapperFactoryBean 和 mapper2MapperFactoryBean 的 Bean。
这种方式虽然可以完成 Mapper 接口的注册,但每次只能单个注册,不能批量注册。
移除 Config 类中的 mapper1() 和 mapper2() 方法,自行编写 BeanDefinitionRegistryPostProcessor 接口的实现类完成 @Mapper 注解的解析:
按照 模拟实现组件扫描 ComponentSacnPostProcessor 的方式,我先在 A05Application 类上添加了 @MapperScan(“daidawang.advancedSpring.bean.a05.mapper”)
然后开始编写MapperPostProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override @SneakyThrows public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory (); AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator (); MapperScan annotation = AnnotationUtils.findAnnotation(A05Application.class, MapperScan.class); if (annotation != null ){ String[] value = annotation.value(); String packageName = value[0 ]; String path = "classpath*:" + packageName.replace("." , "/" ) + "/*.class" ; for (Resource resource : new PathMatchingResourcePatternResolver ().getResources(path)) { MetadataReader reader = factory.getMetadataReader(resource); AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata(); if (annotationMetadata.hasAnnotation(Mapper.class.getName()) && annotationMetadata.isInterface()) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition(); String beanName = generator.generateBeanName(beanDefinition, registry); registry.registerBeanDefinition(beanName, beanDefinition); } } } } @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinitionRegistryPostProcessor.super .postProcessBeanFactory(beanFactory); } }
1 2 3 4 5 6 public static void main (String[] args) { context.registerBean(MapperPostProcessor.class); context.refresh(); for (String name : contBean
1 Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [daidawang.advancedSpring.bean.a05.mapper.Mapper1]: Specified class is an interface
抛出 Mapper1 是一个接口的异常,正如前文提到的 Spring 无法管理接口,上文是通过创建MapperFactoryBean MapperFactoryBean 来实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Bean public MapperFactoryBean<Mapper1> mapper1MapperFactoryBean (SqlSessionFactory sqlSessionFactory) { MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean <>(Mapper1.class); mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory); return mapperFactoryBean; } @Bean public MapperFactoryBean<Mapper2> mapper2MapperFactoryBean (SqlSessionFactory sqlSessionFactory) { MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean <>(Mapper2.class); mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory); return mapperFactoryBean; }
所以当创建 BeanDefinitionean 对象的时候,也需要创建 MapperFactoryBean 类型的BeanDefinitionean .
而 BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()) 这样就创建了 Mappper 类型的 BeanDefinitionean ,
为了创建MapperFactoryBean 型的BeanDefinitionean 做出如下修改
1 2 3 4 5 6 7 8 9 10 11 12 13 public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(MapperFactoryBean.class) .getBeanDefinition(); String beanName = generator.generateBeanName(beanDefinition, registry); registry.registerBeanDefinition(beanName, beanDefinition); } } } }
1 Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mapperFactoryBean': Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
好了已经创建了 MapperFactoryBean 类性的 BeanDefinitionean 对象,
但是由于没有给定参数SqlSessionFactory 而抛出异常 。
添加.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(MapperFactoryBean.class) .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) .getBeanDefinition(); String beanName = generator.generateBeanName(beanDefinition, registry); registry.registerBeanDefinition(beanName, beanDefinition); } } } }
1 Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mapperFactoryBean': Property 'mapperInterface' is required
由于MapperFactoryBean 的构造是 Mapper 类的类路径
MapperFactoryBean mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);
所以添加 .addConstructorArgValue(annotationMetadata.getClassName())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(MapperFactoryBean.class) .addConstructorArgValue(annotationMetadata.getClassName()) .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) .getBeanDefinition(); String beanName = generator.generateBeanName(beanDefinition, registry); registry.registerBeanDefinition(beanName, beanDefinition); } } } }
1 2 3 4 5 6 7 8 9 08:03:41.154 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦 config daidawang.advancedSpring.bean.a05.DDWBeanPostProcessor daidawang.advancedSpring.bean.a05.MapperPostProcessor bean1 sqlSessionFactoryBean dataSource mapperFactoryBean 08:03:41.342 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-0} closing ...
观察控制台发现 只打印了 mapperFactoryBean ,没有打印 Mapper1 与 Mapper2
这是因为 通过AnnotationBeanNameGenerator 创建 beanName 的时候,
默认是 beanDefinition 的名字,而 beanDefinition 已经被改为了 MapperFactoryBean.class
在注册 Mapper1 时往容器添加了 mapperFactoryBean , 在注册 Mapper2 时 又添加了相同名字的 mapperFactoryBean , 导致 Mapper1 Mapper2 在容器里共用了同一个也名字
我们可以借用源码的思路用二级缓存来解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package daidawang.advancedSpring.bean.a05;import lombok.SneakyThrows;import org.apache.ibatis.annotations.Mapper;import org.mybatis.spring.annotation.MapperScan;import org.mybatis.spring.mapper.MapperFactoryBean;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.*;import org.springframework.context.annotation.AnnotationBeanNameGenerator;import org.springframework.context.annotation.ComponentScan;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.core.io.Resource;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.ClassMetadata;import org.springframework.core.type.classreading.CachingMetadataReaderFactory;import org.springframework.core.type.classreading.MetadataReader;import java.lang.invoke.VarHandle;public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override @SneakyThrows public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) throws BeansException { CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory (); AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator (); MapperScan annotation = AnnotationUtils.findAnnotation(A05Application.class, MapperScan.class); if (annotation != null ){ String[] value = annotation.value(); String packageName = value[0 ]; String path = "classpath*:" + packageName.replace("." , "/" ) + "/*.class" ; for (Resource resource : new PathMatchingResourcePatternResolver ().getResources(path)) { MetadataReader reader = factory.getMetadataReader(resource); ClassMetadata classMetadata = reader.getClassMetadata(); AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata(); if (annotationMetadata.hasAnnotation(Mapper.class.getName()) && annotationMetadata.isInterface()) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(MapperFactoryBean.class) .addConstructorArgValue(annotationMetadata.getClassName()) .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) .getBeanDefinition(); AbstractBeanDefinition definition = BeanDefinitionBuilder .genericBeanDefinition(annotationMetadata.getClassName()) .getBeanDefinition(); String beanName = generator.generateBeanName(definition, registry); registry.registerBeanDefinition(beanName, beanDefinition); } } } } @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinitionRegistryPostProcessor.super .postProcessBeanFactory(beanFactory); } }
1 2 3 4 5 6 7 8 9 10 08:19:25.224 [main] INFO daidawang.advancedSpring.bean.a05.Bean1 -- 我被 Spring 管理啦 config daidawang.advancedSpring.bean.a05.DDWBeanPostProcessor daidawang.advancedSpring.bean.a05.MapperPostProcessor bean1 sqlSessionFactoryBean dataSource mapper1 mapper2 08:19:25.393 [main] INFO com.alibaba.druid.pool.DruidDataSource -- {dataSource-0} closing ...
成功自定义 Mapper 后置处理器, 当然我这里是通过 @MapperScan 注解来完成的,也可以和老师一样手动拼写类路径。
46 . @Value 注入底层 46.1 解析@Value 在第四讲黑马四十九讲容器与Bean ** 3.2@Autowired@Value后置处理器 **中给工厂设置了 <font style="background-color:rgba(255, 255, 255, 0);">ContextAnnotationAutowireCandidateResolver</font>类,来深入了解一下吧。
1 2 3 4 5 6 public class Bean1 { @Value("${JAVA_HOME}") private String home; @Value("18") private int age; }
这里的 JAVA_HOME 由系统环境变量填充,18 为整型。
解析 @Value需要两步:
获取 @Value注解中的值
解析属性值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @SuppressWarnings("all") @Configuration public class A46 { @SneakyThrows public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (A46.class); DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver (); beanFactory.setAutowireCandidateResolver(resolver); Field home = Bean1.class.getDeclaredField("home" ); text1(home, resolver, context); } private static void text1 (Field field, ContextAnnotationAutowireCandidateResolver resolver, AnnotationConfigApplicationContext context) { DependencyDescriptor dependencyDescriptor = new DependencyDescriptor (field, Boolean.FALSE); String value = resolver.getSuggestedValue(dependencyDescriptor).toString(); System.out.println(value); value= context.getEnvironment().resolvePlaceholders(value); System.out.println(value); } }
1 2 ${JAVA_HOME} D:\software\work\jdk\jdk8\jdk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static void text2 (Field field, ContextAnnotationAutowireCandidateResolver resolver, AnnotationConfigApplicationContext context) { DependencyDescriptor dependencyDescriptor = new DependencyDescriptor (field, Boolean.FALSE); String value = resolver.getSuggestedValue(dependencyDescriptor).toString(); System.out.println("@Value 的 value 属性值: " + value); value= context.getEnvironment().resolvePlaceholders(value); System.out.println("解析得到的值: " + value); System.out.println("解析得到的值的类型: " + value.getClass()); Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, dependencyDescriptor.getDependencyType()); System.out.println("转换后的类型: " + age.getClass()); }
1 2 3 4 @Value 的 value 属性值: 18 解析得到的值: 18 解析得到的值的类型: class java .lang.String 转换后的类型: class java .lang.Integer
46.2 EL表达式的解析 @Value 里面还可以写 EL表达式: @Value("#{@bean3}"),表示根据名称找到 bean3 的 bean 同 @Autowired
EL 表达式全称:SpringEL ,简称:SpEL, 用于 #{} 之间
新建如下几个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Bean2 { @Value("#{@bean3}") private Bean3 bean3; } @Component("bean3") public class Bean3 {} static class Bean4 { @Value("#{'hello, ' + '${JAVA_HOME}'}") private String value; }
如果继续使用 test2() 方法解析 Bean2 中的 @Value 注解
1 2 3 4 public static void main (String[] args) { text2(Bean2.class.getDeclaredField("bean3" ), resolver, context); }
会出现:
1 2 3 4 @Value 的 value 属性值: #{@bean3} 解析得到的值: #{@bean3} 解析得到的值的类型: class java.lang.String Failed to convert value of type 'java.lang.String' to required type 'dai.dawang.sundy.a46Value注解的原理.A46$Bean3'; Cannot convert value of type 'java.lang.String' to required type 'dai.dawang.sundy.a46Value注解的原理.A46$Bean3': no matching editors or conversion strategy found
是最后一步类型转换出错, 无法将 String转换为 A46$Bean3。
为了解析成功,需要在转换前解析 EL 表达式 #{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static void main (String[] args) { test3(context, resolver, Bean2.class.getDeclaredField("bean3" )); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>" ); text3(Bean4.class.getDeclaredField("value" ), resolver, context); } private static void text3 (Field field, ContextAnnotationAutowireCandidateResolver resolver, AnnotationConfigApplicationContext context) { DependencyDescriptor dependencyDescriptor = new DependencyDescriptor (field, Boolean.FALSE); String value = resolver.getSuggestedValue(dependencyDescriptor).toString(); System.out.println("@Value 的 value 属性值: " + value); value= context.getEnvironment().resolvePlaceholders(value); System.out.println("解析得到的值: " + value); System.out.println("解析得到的值的类型: " + value.getClass()); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); Object bean3 = beanFactory.getBeanExpressionResolver().evaluate(value, new BeanExpressionContext (beanFactory, null )); Object result = beanFactory.getTypeConverter().convertIfNecessary(bean3,dependencyDescriptor.getDependencyType() ); System.out.println("转换后的类型: " + result.getClass()); }
1 2 3 4 5 6 7 8 9 @Value 的 value 属性值: #{@bean3} 解析得到的值: #{@bean3} 解析得到的值的类型: class java.lang.String 转换后的类型: class dai.dawang.sundy.a46Value注解的原理.A46$Bean3 >>>>>>>>>>>>>>>>>>>>>>>>> @Value 的 value 属性值: #{'hello, ' + '${JAVA_HOME}'} 解析得到的值: #{'hello, ' + 'D:\software\work\jdk\jdk8\jdk'} 解析得到的值的类型: class java.lang.String 转换后的类型: class java.lang.String
6. Aware 接口 允许 Bean 在初始化过程 中获取 Spring 容器中的某些组件或信息.
Aware 接口 Aware 接口用于注入一些与容器相关的信息,比如:
BeanNameAware 注入 Bean 的名字
BeanFactoryAware 注入 BeanFactory 容器
ApplicationContextAware 注入 ApplicationContext 容器
EmbeddedValueResolverAware 解析 ${}
Ini tializingBean :初始化方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j public class MyBean implements BeanNameAware , ApplicationContextAware { @Override public void setBeanName (String name) { log.info("当前 Bean: " + this + "名字叫: " + name); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { log.info("当前 Bean: " + this + "容器是: " + applicationContext); } }
1 2 3 4 5 6 7 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("myBean" , MyBean.class); context.refresh(); context.close(); }
1 2 当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 名字叫:daidawang.advancedSpring.bean.a06.MyBean 当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 容器对象是:org.springframework.context.support.GenericApplicationContext@6615435c, started on Sun Aug 18 17:31:44 CST 2024
InitialzingBean 1 2 3 4 5 6 7 8 9 @Slf4j public class MyBean implements BeanNameAware , ApplicationContextAware, InitializingBean { @Override public void afterPropertiesSet () throws Exception { log.info("当前 Bean: " + this + " 初始化" ); } }
1 2 3 当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 名字叫:daidawang.advancedSpring.bean.a06.MyBean 当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 容器对象是:org.springframework.context.support.GenericApplicationContext@6615435c, started on Sun Aug 18 17:31:44 CST 2024 当前bean daidawang.advancedSpring.bean.a06.MyBean@481a15ff 初始化
当同时实现 Aware 接口和 InitializingBean 接口时,会先执行 Aware 接口。
BeanFactoryAware 、ApplicationContextAware 和 EmbeddedValueResolverAware 三个接口的功能可以使用 @Autowired 注解实现,InitializingBean 接口的功能也可以使用 @PostConstruct 注解实现,为什么还要使用接口呢?
@Autowired 和 @PostConstruct 注解的解析需要使用 Bean 后置处理器,属于拓展功能,而这些接口属于内置功能,不加任何拓展 Spring 就能识别。在某些情况下,拓展功能会失效,而内容功能不会失效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Slf4j public class MyBean implements BeanNameAware , ApplicationContextAware, InitializingBean { @Autowired public void setApplicationContextWithAutowired (ApplicationContext applicationContext) { log.info("当前 Bean: " + this + " 使用 @Autowired 注解,容器是: " + applicationContext); } @PostConstruct public void init () { log.info("当前 Bean: " + this + " 使用 @PostConstruct 注解初始化" ); } }
再运行 main() 方法会发现使用的注解没有被成功解析,原因很简单,GenericApplicationContext 是一个干净的容器,其内部没有用于解析这些注解的后置处理器。如果想要这些注解生效,则需要像前文一样添加必要的后置处理器:
1 2 context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.registerBean(CommonAnnotationBeanPostProcessor.class);
@Autowired 的失效情况 在某些情况下,尽管容器中存在必要的后置处理器,但 @Autowired 和 @PostConstruct 注解也会失效。
1 2 3 4 5 6 7 8 9 10 11 12 13 @Slf4j @Configuration public class MyConfig1 { @Autowired public void setApplicationContext (ApplicationContext applicationContext) { log.error("注入 ApplicationContext" ); } @PostConstruct public void init () { log.error("初始化" ); } }
1 2 3 4 5 6 7 8 9 10 11 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("myConfig1" , MyConfig1.class); context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.registerBean(CommonAnnotationBeanPostProcessor.class); context.registerBean(ConfigurationClassPostProcessor.class); context.refresh(); context.close(); }
1 2 18 :16 :39.141 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 注入 ApplicationContext18 :16 :43.666 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 初始化
@Autowired 和 @PostConstruct 注解成功被解析。
如果再对 Config1 进行一点小小的修改呢?
1 2 3 4 5 6 7 8 9 10 @Slf4j @Configuration public class MyConfig1 { @Bean public BeanFactoryPostProcessor processor1 () { return processor -> log.info("执行 processor1" ); } }
在 Config1 中添加了一个被 @Bean 注解标记的 processor1() 方法,用于向容器中添加 BeanFactoryPostProcessor 。
如果再运行 main() 方法:
1 18 :16 :43.693 [main] INFO daidawang.advancedSpring.bean.a06.Config2 -- 执行 processor1
processor1() 方法成功生效,但 @Autowired 和 @PostConstruct 注解的解析失败了。
对于 context.refresh(); 方法来说,它主要按照以下顺序干了三件事:
执行 BeanFactory 后置处理器;
添加 Bean 后置处理器;
创建和初始化单例对象。
比如当 Java 配置类不包括 BeanFactoryPostProcessor 时:
BeanFactoryPostProcessor 会在 Java 配置类初始化之前执行。
当 Java 配置类中定义了BeanFactoryPostProcessor 时,如果要创建配置类中的 BeanFactoryPostProcessor 就必须 提前 创建和初始化 Java 配置类。
在创建和初始化 Java 配置类时,由于 BeanPostProcessor 还未准备好,无法解析配置类中的 @Autowired 等注解,导致 @Autowired 等注解失效:
要解决这个问题也很简单,使用相关接口的功能实现注入和初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Slf4j @Configuration public class MyConfig2 implements InitializingBean , ApplicationContextAware { @Override public void afterPropertiesSet () throws Exception { log.info("初始化" ); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { log.info("注入 ApplicationContext" ); } @Bean public BeanFactoryPostProcessor processor2 () { return processor -> log.info("执行 processor2" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { GenericApplicationContext context = new GenericApplicationContext (); context.registerBean("myConfig2" , MyConfig2.class); context.registerBean(AutowiredAnnotationBeanPostProcessor.class); context.registerBean(CommonAnnotationBeanPostProcessor.class); context.registerBean(ConfigurationClassPostProcessor.class); context.refresh(); context.close(); }
1 2 3 18 :16 :39.141 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 注入 ApplicationContext18 :16 :43.666 [main] ERROR daidawang.advancedSpring.bean.a06.Config2 -- 初始化18 :16 :43.693 [main] INFO daidawang.advancedSpring.bean.a06.Config2 -- 执行 processor2
总结 Aware
Aware 接口提供了一种 内置 的注入手段,可以注入 BeanFactory、ApplicationContext;
InitializingBean 接口提供了一种 内置 的初始化手段;
内置的注入和初始化不受拓展功能的影响,总会被执行,因此 Spring 框架内部的类总是使用这些接口。
7. 初始化与销毁 初始化 spring 支持 3 种初始化方式
@PostConstruct ,后置处理器,执行顺序第一
实现InitializingBean ,内置处理器,执行顺序第二
@Bean(initMethod = “initMethod”),BeanDefinition 指定的,执行顺序第三
创建 Bean1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Slf4j public class Bean1 implements InitializingBean { @Override public void afterPropertiesSet () throws Exception { System.out.println("InitializingBean初始化" ); } @PostConstruct public void init () { System.out.println("PostConstruct初始化" ); } public void initMethod () { System.out.println("initMethod初始化" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @SpringBootApplication public class A07Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args); context.close(); } @Bean(initMethod = "initMethod") public Bean1 bean1 () { return new Bean1 (); } }
1 2 3 PostConstruct初始化 InitializingBean初始化 initMethod初始化
销毁 spring 支持 3 种销毁方式
@ PreDestroy ,后置处理器,执行顺序第一
实现 DisposableBean ,内置处理器,执行顺序第二
@Bean(destroyMethod = “destroyMethod”),BeanDefinition 指定的,执行顺序第三
创建 Bean2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Bean2 implements DisposableBean { @Override public void destroy () throws Exception { System.out.println("destroy销毁" ); } @PreDestroy public void deprecated () { System.out.println("@PreDestroy销毁" ); } public void destroyMethod () { System.out.println("destroyMethod销毁" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SpringBootApplication public class A07Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(A07Application.class, args); context.close(); } @Bean(initMethod = "initMethod") public Bean1 bean1 () { return new Bean1 (); } @Bean(destroyMethod = "destroyMethod") public Bean2 Bean2 () { return new Bean2 (); } }
1 2 3 @PreDestroy销毁 destroy销毁 destroyMethod销毁
Aware 与内外置处理器顺序 添加 Aware 接口到 Bean1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package daidawang.advancedSpring.bean.a07初始化与销毁;import jakarta.annotation.PostConstruct;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.BeanFactoryAware;import org.springframework.beans.factory.BeanNameAware;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.config.EmbeddedValueResolver;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.EmbeddedValueResolverAware;import org.springframework.util.StringValueResolver;@Slf4j public class Bean1 implements InitializingBean , BeanNameAware, ApplicationContextAware, BeanFactoryAware, EmbeddedValueResolverAware { @PostConstruct public void init () { System.out.println("PostConstruct初始化" ); } @Override public void afterPropertiesSet () throws Exception { System.out.println("InitializingBean初始化" ); } public void initMethod () { System.out.println("initMethod初始化" ); } @Override public void setBeanName (String name) { System.out.println("BeanNameAware执行" ); } @Override public void setApplicationContext (ApplicationContext applicationContext) throws BeansException { System.out.println("ApplicationContextAware执行" ); } @Override public void setBeanFactory (BeanFactory beanFactory) throws BeansException { System.out.println("BeanFactoryAware执行" ); } @Override public void setEmbeddedValueResolver (StringValueResolver resolver) { System.out.println("EmbeddedValueResolverAware执行" ); } }
1 2 3 4 5 6 7 BeanNameAware执行 BeanFactoryAware执行 EmbeddedValueResolverAware执行 ApplicationContextAware执行 PostConstruct初始化 InitializingBean初始化 initMethod初始化
这些初始化方式的执行顺序:
BeanNameAware:这是最早执行的 Aware 接口之一,因为它在 Bean 的实例化之后立即调用。
ApplicationContextAware:在BeanNameAware之后执行,在Bean的实例化之后,但在依赖注入之前。
BeanFactoryAware:同样在依赖注入之前执行。
EmbeddedValueResolverAware:在依赖注入之前执行,用于解析 ${} 表达式中的占位符。
依赖注入:Spring 容器会进行依赖注入,即注入 @Autowired 标注的字段或方法。
@PostConstruct:在依赖注入之后执行。
InitializingBean.afterPropertiesSet():在依赖注入完成之后执行,通常在 @PostConstruct 之后。
@Bean(initMethod = “initMethod”):在 afterPropertiesSet() 方法之后执行
8. Scope 一个 bean 没有指定@Scope注解,默认是单例的。
Scope 类型
singleton 单例,ApplicationContest 创建而创建,ApplicationContest 销毁而销毁
prototype 多例, 使用时创建,手动销毁
request 存在于 request 域中,生命周期同request 域
sesion 会话域,会话开始 bean 创建,会话结束 bean 销毁
application 应用程序域,应用程序开始 bean 创建,Spring 没有实现 销毁,WebServerContext
验证request 、sesion、application 做出如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @RestController public class MyController { @Lazy @Autowired private BeanForApplication beanForApplication; @Lazy @Autowired private BeanForRequest beanForRequest; @Lazy @Autowired private BeanForSession beanForSession; @GetMapping("/test") public String test (HttpServletRequest request, HttpSession session) { session.setMaxInactiveInterval(10 ); return "<ul>" + "<li>request scope: " + beanForRequest + "</li>" + "<li>session scope: " + beanForSession + "</li>" + "<li>application scope: " + beanForApplication + "</li>" + "</ul>" ; } }
如果不添加@Lazy 注解会抛出注释的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Slf4j @Component @Scope("application") public class BeanForApplication { @PreDestroy public void destroy () { log.info("销毁BeanForApplication" ); } } @Slf4j @Component @Scope("request") public class BeanForRequest { @PreDestroy public void destroy () { log.info("销毁BeanForRequest" ); } } @Slf4j @Component @Scope("session") public class BeanForSession { @PreDestroy public void destroy () { log.info("销毁BeanForSession" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @SpringBootApplication public class A08Application { public static void main (String[] args) { SpringApplication.run(A08Application.class, args); } @Bean(initMethod = "init") public DruidDataSource dataSource () { DruidDataSource dataSource = new DruidDataSource (); dataSource.setUrl("jdbc:mysql://localhost:3306/advanced_spring" ); dataSource.setName("root" ); dataSource.setPassword("123456" ); return dataSource; } }
启动后访问
http://localhost:8080/test
每次刷新页面,重新请求 request scope 都会改变。同时控制台打印
1 INFO 3212 --- [nio-8080-exec-2] d.a.bean.a08Scope.BeanForRequest : 销毁BeanForRequest
同一个浏览器无论发送多少次请求,都属于一个会话( session 域),所以 BeanForSession 不会改变。
同一个 web 程序也不会改变.。
在打开一个新浏览器访问
会发现 会话( session 域)有所改变
而 web 程序仍然不会改变:
在 Singleton 中使用其他类型的注意事项 一个单例的 bean 注入其他 scope 类型的 bean 会出现问题,
例如在单例 bean 里引用 多例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Component public class E { @Getter @Autowired private F1 f1; } @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class F1 { @PreDestroy public void destroy () { System.out.println("销毁F1" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 @Slf4j @ComponentScan("daidawang.advancedSpring.bean.a09Scope") public class A09Application { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (A09Application.class); context.registerBean(ConfigurationClassPostProcessor.class); E e = context.getBean(E.class); log.info("{}" , e.getF1()); log.info("{}" , e.getF1()); log.info("{}" , e.getF1()); context.close(); } }
多次调用获取多例 F
1 2 3 A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@58ea606c A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@58ea606c A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@58ea606c
scope 失效解决 却发现 F 是同一个,明明在 F 类上指定了多例@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
这是因为对于单例对象来讲,依赖注入仅发生一次,后续再没有用到多例 F,E 用的始终是第一次依赖注入 F
下面是解决方法,理念上殊途同归:都是延迟其他 scope bean 的获取,而不是在单例 bean 里直接获取其他 scope bean 对象
@Lazy 注解解决失效 用代理去获取多例
解决
通过 懒加载 @Lazy ,代理对象虽然还是同一个,但是每次使用代理对象的任意方法时
由代理创建新 F 对象
在 E 类里添加@Lazy
1 2 3 4 5 6 7 8 9 10 11 12 @Component public class E { @Autowired @Lazy public void setF (F f) { this .f = f; log.info("setF(F f) {}" , f.getClass()); } }
1 2 3 A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@543295b0 A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@74e28667 A09Application -- daidawang.advancedSpring.bean.a09Scope.F1@663c9e7a
指定 proxyMode 类型解决 用代理去获取多例
创建 F2 并指定 proxyMode 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS) public class F2 { @PreDestroy public void destroy () { System.out.println("销毁F1" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Slf4j @ComponentScan("daidawang.advancedSpring.bean.a09Scope") public class A09Application { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (A09Application.class); context.registerBean(ConfigurationClassPostProcessor.class); E e = context.getBean(E.class); log.info("{}" , e.getF2()); log.info("{}" , e.getF2()); log.info("{}" , e.getF2()); context.close(); } }
1 2 3 A09Application -- daidawang.advancedSpring.bean.a09Scope.F2@c05fddc A09Application -- daidawang.advancedSpring.bean.a09Scope.F2@5a5a729f A09Application -- daidawang.advancedSpring.bean.a09Scope.F2@4b520ea8
ObjectFactory 获取多例对象 用工厂去获取多例
创建 F3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class F3 { @PreDestroy public void destroy () { System.out.println("销毁F3" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Component public class E { @Lazy @Getter @Autowired private F1 f1; @Autowired @Getter private F2 f2; @Autowired private ObjectFactory<F3> objectFactory; public F3 getF3 () { return objectFactory.getObject(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Slf4j @ComponentScan("daidawang.advancedSpring.bean.a09Scope") public class A09Application { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (A09Application.class); context.registerBean(ConfigurationClassPostProcessor.class); E e = context.getBean(E.class); log.info("{}" , e.getF3()); log.info("{}" , e.getF3()); log.info("{}" , e.getF3()); context.close(); } }
1 2 3 A09Application -- daidawang.advancedSpring.bean.a09Scope.F3@4201c465 A09Application -- daidawang.advancedSpring.bean.a09Scope.F3@2de56eb2 A09Application -- daidawang.advancedSpring.bean.a09Scope.F3@5745ca0e
ApplicationContext 用容器去获取多例
创建 F4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package daidawang.advancedSpring.bean.a09Scope;import jakarta.annotation.PreDestroy;import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class F4 { @PreDestroy public void destroy () { System.out.println("销毁F4" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Component public class E { @Autowired private ApplicationContext applicationContext; public F3 getF3 () { return objectFactory.getObject(); } public F4 getF4 () { return applicationContext.getBean(F4.class); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Slf4j @ComponentScan("daidawang.advancedSpring.bean.a09Scope") public class A09Application { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (A09Application.class); context.registerBean(ConfigurationClassPostProcessor.class); E e = context.getBean(E.class); log.info("{}" , e.getF4()); log.info("{}" , e.getF4()); log.info("{}" , e.getF4()); context.close(); } }
1 2 3 A09Application -- daidawang.advancedSpring.bean.a09Scope.F4@43f02ef2 A09Application -- daidawang.advancedSpring.bean.a09Scope.F4@338fc1d8 A09Application -- daidawang.advancedSpring.bean.a09Scope.F4@4722ef0c
9. 补充 比反射快的方式: 读取资源元数据创建一个CachingMetadataReaderFactory实例
context.refresh(); 方法来说,它主要按照以下顺序干了三件事:
执行 BeanFactory 后置处理器;
添加 Bean 后置处理器;
创建和初始化单例对象。