无网站网络营销,万户网站制作,怎么做微信推广和宣传,西安网站建设费用文章目录 容器与 bean1) 容器接口演示1 - BeanFactory 与 ApplicationContext 的区别关键代码参考 收获#x1f4a1;演示2 - 国际化 2) 容器实现演示1 - DefaultListableBeanFactory代码参考 收获#x1f4a1;演示2 - 常见 ApplicationContext 实现代码参考 收获#x1f4a1… 文章目录 容器与 bean1) 容器接口演示1 - BeanFactory 与 ApplicationContext 的区别关键代码参考 收获演示2 - 国际化 2) 容器实现演示1 - DefaultListableBeanFactory代码参考 收获演示2 - 常见 ApplicationContext 实现代码参考 收获 3) Bean 的生命周期演示1 - bean 生命周期代码参考 收获演示2 - 模板方法设计模式关键代码 演示3 - bean 后处理器排序代码参考 收获 4) Bean 后处理器演示1 - 后处理器作用代码参考 收获演示2 - Autowired bean 后处理器运行分析代码参考 收获 5) BeanFactory 后处理器演示1 - BeanFactory 后处理器的作用代码参考 收获演示2 - 模拟解析 ComponentScan代码参考 收获演示3 - 模拟解析 Bean代码参考 收获演示4 - 模拟解析 Mapper 接口代码参考 收获 6) Aware 接口演示 - Aware 接口及 InitializingBean 接口代码参考 收获配置类 Autowired 失效分析 7) 初始化与销毁演示 - 初始化销毁顺序代码参考 收获 8) Scope演示1 - request, session, application 作用域代码参考 收获分析 - singleton 注入其它 scope 失效演示2 - 4种解决方法代码参考 收获 容器与 bean
1) 容器接口 BeanFactory 接口典型功能有 getBean ApplicationContext 接口是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能如 国际化通配符方式获取一组 Resource 资源整合 Environment 环境能通过它获取各种来源的配置信息事件发布与监听实现组件之间的解耦
可以看到我们课上讲的都是 BeanFactory 提供的基本功能ApplicationContext 中的扩展功能都没有用到。
演示1 - BeanFactory 与 ApplicationContext 的区别
关键代码参考
package com.itheima.a01;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Map;/*BeanFactory 与 ApplicationContext 的区别*/
SpringBootApplication
public class A01 {private static final Logger log LoggerFactory.getLogger(A01.class);public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {ConfigurableApplicationContext context SpringApplication.run(A01.class, args);/*1. 到底什么是 BeanFactory- 它是 ApplicationContext 的父接口- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能*/System.out.println(context);/*2. BeanFactory 能干点啥- 表面上只有 getBean- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供*/Field singletonObjects DefaultSingletonBeanRegistry.class.getDeclaredField(singletonObjects);singletonObjects.setAccessible(true);ConfigurableListableBeanFactory beanFactory context.getBeanFactory();MapString, Object map (MapString, Object) singletonObjects.get(beanFactory);map.entrySet().stream().filter(e - e.getKey().startsWith(component)).forEach(e - {System.out.println(e.getKey() e.getValue());});/*3. ApplicationContext 比 BeanFactory 多点啥*/System.out.println(context.getMessage(hi, null, Locale.CHINA));System.out.println(context.getMessage(hi, null, Locale.ENGLISH));System.out.println(context.getMessage(hi, null, Locale.JAPANESE));Resource[] resources context.getResources(classpath*:META-INF/spring.factories);for (Resource resource : resources) {System.out.println(resource);}System.out.println(context.getEnvironment().getProperty(java_home));System.out.println(context.getEnvironment().getProperty(server.port));// context.publishEvent(new UserRegisteredEvent(context));context.getBean(Component1.class).register();/*4. 学到了什么a. BeanFactory 与 ApplicationContext 并不仅仅是简单接口继承的关系, ApplicationContext 组合并扩展了 BeanFactory 的功能b. 又新学一种代码之间解耦途径练习完成用户注册与发送短信之间的解耦, 用事件方式、和 AOP 方式分别实现*/}
}
package com.itheima.a01;import org.springframework.context.ApplicationEvent;public class UserRegisteredEvent extends ApplicationEvent {public UserRegisteredEvent(Object source) {super(source);}
}
package com.itheima.a01;import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;Component
public class Component1 {private static final Logger log LoggerFactory.getLogger(Component1.class);Autowiredprivate ApplicationEventPublisher context;public void register() {log.debug(用户注册);context.publishEvent(new UserRegisteredEvent(this));}}
package com.itheima.a01;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;Component
public class Component2 {private static final Logger log LoggerFactory.getLogger(Component2.class);EventListenerpublic void aaa(UserRegisteredEvent event) {log.debug({}, event);log.debug(发送短信);}
}
收获
通过这个示例结合 debug 查看 ApplicationContext 对象的内部结构学到 到底什么是 BeanFactory 它是 ApplicationContext 的父接口它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory BeanFactory 能干点啥 表面上只有 getBean实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能都由它的实现类提供例子中通过反射查看了它的成员变量 singletonObjects内部包含了所有的单例 bean ApplicationContext 比 BeanFactory 多点啥 ApplicationContext 组合并扩展了 BeanFactory 的功能国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听新学一种代码之间解耦途径事件解耦
建议练习完成用户注册与发送短信之间的解耦用事件方式、和 AOP 方式分别实现 注意 如果 jdk 8, 运行时请添加 --add-opens java.base/java.langALL-UNNAMED这是因为这些版本的 jdk 默认不允许跨 module 反射事件发布还可以异步这个视频中没有展示请自行查阅 EnableAsyncAsync 的用法 演示2 - 国际化
public class TestMessageSource {public static void main(String[] args) {GenericApplicationContext context new GenericApplicationContext();context.registerBean(messageSource, MessageSource.class, () - {ResourceBundleMessageSource ms new ResourceBundleMessageSource();ms.setDefaultEncoding(utf-8);ms.setBasename(messages);return ms;});context.refresh();System.out.println(context.getMessage(hi, null, Locale.ENGLISH));System.out.println(context.getMessage(hi, null, Locale.CHINESE));System.out.println(context.getMessage(hi, null, Locale.JAPANESE));}
}国际化文件均在 src/resources 目录下
messages.properties空
messages_en.properties
hiHellomessages_ja.properties
hiこんにちはmessages_zh.properties
hi你好注意 ApplicationContext 中 MessageSource bean 的名字固定为 messageSource使用 SpringBoot 时国际化文件名固定为 messages空的 messages.properties 也必须存在 2) 容器实现
Spring 的发展历史较为悠久因此很多资料还在讲解它较旧的实现这里出于怀旧的原因把它们都列出来供大家参考
DefaultListableBeanFactory是 BeanFactory 最重要的实现像控制反转和依赖注入功能都是它来实现ClassPathXmlApplicationContext从类路径查找 XML 配置文件创建容器旧FileSystemXmlApplicationContext从磁盘路径查找 XML 配置文件创建容器旧XmlWebApplicationContext传统 SSM 整合时基于 XML 配置文件的容器旧AnnotationConfigWebApplicationContext传统 SSM 整合时基于 java 配置类的容器旧AnnotationConfigApplicationContextSpring boot 中非 web 环境容器新AnnotationConfigServletWebServerApplicationContextSpring boot 中 servlet web 环境容器新AnnotationConfigReactiveWebServerApplicationContextSpring boot 中 reactive web 环境容器新
另外要注意的是后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现但它们是组合了 DefaultListableBeanFactory 的功能并非继承而来
演示1 - DefaultListableBeanFactory
代码参考
package com.itheima.a02;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
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.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;import javax.annotation.Resource;public class TestBeanFactory {public static void main(String[] args) {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();// bean 的定义class, scope, 初始化, 销毁AbstractBeanDefinition beanDefinition BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope(singleton).getBeanDefinition();beanFactory.registerBeanDefinition(config, beanDefinition);// 给 BeanFactory 添加一些常用的后处理器AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);// BeanFactory 后处理器主要功能补充了一些 bean 定义beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor - {beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);});// Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 Autowired Resource ...beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream().sorted(beanFactory.getDependencyComparator()).forEach(beanPostProcessor - {System.out.println( beanPostProcessor);beanFactory.addBeanPostProcessor(beanPostProcessor);});for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}beanFactory.preInstantiateSingletons(); // 准备好所有单例System.out.println( );
// System.out.println(beanFactory.getBean(Bean1.class).getBean2());System.out.println(beanFactory.getBean(Bean1.class).getInter());/*学到了什么:a. beanFactory 不会做的事1. 不会主动调用 BeanFactory 后处理器2. 不会主动添加 Bean 后处理器3. 不会主动初始化单例4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }b. bean 后处理器会有排序的逻辑*/System.out.println(Common: (Ordered.LOWEST_PRECEDENCE - 3));System.out.println(Autowired: (Ordered.LOWEST_PRECEDENCE - 2));}Configurationstatic class Config {Beanpublic Bean1 bean1() {return new Bean1();}Beanpublic Bean2 bean2() {return new Bean2();}Beanpublic Bean3 bean3() {return new Bean3();}Beanpublic Bean4 bean4() {return new Bean4();}}interface Inter {}static class Bean3 implements Inter {}static class Bean4 implements Inter {}static class Bean1 {private static final Logger log LoggerFactory.getLogger(Bean1.class);public Bean1() {log.debug(构造 Bean1());}Autowiredprivate Bean2 bean2;public Bean2 getBean2() {return bean2;}AutowiredResource(name bean4)private Inter bean3;public Inter getInter() {return bean3;}}static class Bean2 {private static final Logger log LoggerFactory.getLogger(Bean2.class);public Bean2() {log.debug(构造 Bean2());}}
}
收获
beanFactory 可以通过 registerBeanDefinition 注册一个 bean definition 对象 我们平时使用的配置类、xml、组件扫描等方式都是生成 bean definition 对象注册到 beanFactory 当中bean definition 描述了这个 bean 的创建蓝图scope 是什么、用构造还是工厂创建、初始化销毁方法是什么等等 beanFactory 需要手动调用 beanFactory 后处理器对它做增强 例如通过解析 Bean、ComponentScan 等注解来补充一些 bean definition beanFactory 需要手动添加 bean 后处理器以便对后续 bean 的创建过程提供增强 例如 AutowiredResource 等注解的解析都是 bean 后处理器完成的bean 后处理的添加顺序会对解析结果有影响见视频中同时加 AutowiredResource 的例子 beanFactory 需要手动调用方法来初始化单例beanFactory 需要额外设置才能解析 ${} 与 #{}
演示2 - 常见 ApplicationContext 实现
代码参考
package com.itheima.a02;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;/*常见 ApplicationContext 实现*/
public class A02 {private static final Logger log LoggerFactory.getLogger(A02.class);public static void main(String[] args) {testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
// testAnnotationConfigApplicationContext();
// testAnnotationConfigServletWebServerApplicationContext();/*DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();System.out.println(读取之前...);for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(读取之后...);XmlBeanDefinitionReader reader new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions(new FileSystemResource(src\\main\\resources\\a02.xml));for (String name : beanFactory.getBeanDefinitionNames()) {System.out.println(name);}*//*学到了什么a. 常见的 ApplicationContext 容器实现b. 内嵌容器、DispatcherServlet 的创建方法、作用*/}// ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建private static void testClassPathXmlApplicationContext() {ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(a02.xml);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(context.getBean(Bean2.class).getBean1());}// ⬇️基于磁盘路径下 xml 格式的配置文件来创建private static void testFileSystemXmlApplicationContext() {FileSystemXmlApplicationContext context new FileSystemXmlApplicationContext(src\\main\\resources\\a02.xml);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(context.getBean(Bean2.class).getBean1());}// ⬇️较为经典的容器, 基于 java 配置类来创建private static void testAnnotationConfigApplicationContext() {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(Config.class);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}System.out.println(context.getBean(Bean2.class).getBean1());}// ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境private static void testAnnotationConfigServletWebServerApplicationContext() {AnnotationConfigServletWebServerApplicationContext context new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}}Configurationstatic class WebConfig {Beanpublic ServletWebServerFactory servletWebServerFactory(){return new TomcatServletWebServerFactory();}Beanpublic DispatcherServlet dispatcherServlet() {return new DispatcherServlet();}Beanpublic DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {return new DispatcherServletRegistrationBean(dispatcherServlet, /);}Bean(/hello)public Controller controller1() {return (request, response) - {response.getWriter().print(hello);return null;};}}Configurationstatic class Config {Beanpublic Bean1 bean1() {return new Bean1();}Beanpublic Bean2 bean2(Bean1 bean1) {Bean2 bean2 new Bean2();bean2.setBean1(bean1);return bean2;}}static class Bean1 {}static class Bean2 {private Bean1 bean1;public void setBean1(Bean1 bean1) {this.bean1 bean1;}public Bean1 getBean1() {return bean1;}}
}
收获
常见的 ApplicationContext 容器实现内嵌容器、DispatcherServlet 的创建方法、作用
3) Bean 的生命周期
一个受 Spring 管理的 bean生命周期主要阶段有
创建根据 bean 的构造方法或者工厂方法来创建 bean 实例对象依赖注入根据 AutowiredValue 或其它一些手段为 bean 的成员变量填充值、建立关系初始化回调各种 Aware 接口调用对象的各种初始化方法销毁在容器关闭时会销毁所有单例对象即调用它们的销毁方法 prototype 对象也能够销毁不过需要容器这边主动调用
一些资料会提到生命周期中还有一类 bean 后处理器BeanPostProcessor会在 bean 的初始化的前后提供一些扩展逻辑。但这种说法是不完整的见下面的演示1
演示1 - bean 生命周期
代码参考
package com.itheima.a03;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;/*bean 的生命周期, 以及 bean 后处理器学到了什么a. Spring bean 生命周期各个阶段b. 模板设计模式, 大流程已经固定好了, 通过接口回调(bean 后处理器)扩展*/
SpringBootApplication
public class A03 {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(A03.class, args);context.close();}
}package com.itheima.a03;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;Component
public class LifeCycleBean {private static final Logger log LoggerFactory.getLogger(LifeCycleBean.class);public LifeCycleBean() {log.debug(构造);}Autowiredpublic void autowire(Value(${JAVA_HOME}) String home) {log.debug(依赖注入: {}, home);}PostConstructpublic void init() {log.debug(初始化);}PreDestroypublic void destroy() {log.debug(销毁);}
}
package com.itheima.a03;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {private static final Logger log LoggerFactory.getLogger(MyBeanPostProcessor.class);Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean))log.debug( 销毁之前执行, 如 PreDestroy);}Overridepublic Object postProcessBeforeInstantiation(Class? beanClass, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean))log.debug( 实例化之前执行, 这里返回的对象会替换掉原本的 bean);return null;}Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean)) {log.debug( 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段);
// return false;}return true;}Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean))log.debug( 依赖注入阶段执行, 如 Autowired、Value、Resource);return pvs;}Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean))log.debug( 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 PostConstruct、ConfigurationProperties);return bean;}Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals(lifeCycleBean))log.debug( 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强);return bean;}
} #mermaid-svg-RoscMQUazmJobqBL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-RoscMQUazmJobqBL .error-icon{fill:#552222;}#mermaid-svg-RoscMQUazmJobqBL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RoscMQUazmJobqBL .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-RoscMQUazmJobqBL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RoscMQUazmJobqBL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RoscMQUazmJobqBL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RoscMQUazmJobqBL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RoscMQUazmJobqBL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RoscMQUazmJobqBL .marker.cross{stroke:#333333;}#mermaid-svg-RoscMQUazmJobqBL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RoscMQUazmJobqBL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RoscMQUazmJobqBL .cluster-label text{fill:#333;}#mermaid-svg-RoscMQUazmJobqBL .cluster-label span{color:#333;}#mermaid-svg-RoscMQUazmJobqBL .label text,#mermaid-svg-RoscMQUazmJobqBL span{fill:#333;color:#333;}#mermaid-svg-RoscMQUazmJobqBL .node rect,#mermaid-svg-RoscMQUazmJobqBL .node circle,#mermaid-svg-RoscMQUazmJobqBL .node ellipse,#mermaid-svg-RoscMQUazmJobqBL .node polygon,#mermaid-svg-RoscMQUazmJobqBL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RoscMQUazmJobqBL .node .label{text-align:center;}#mermaid-svg-RoscMQUazmJobqBL .node.clickable{cursor:pointer;}#mermaid-svg-RoscMQUazmJobqBL .arrowheadPath{fill:#333333;}#mermaid-svg-RoscMQUazmJobqBL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RoscMQUazmJobqBL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RoscMQUazmJobqBL .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-RoscMQUazmJobqBL .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-RoscMQUazmJobqBL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RoscMQUazmJobqBL .cluster text{fill:#333;}#mermaid-svg-RoscMQUazmJobqBL .cluster span{color:#333;}#mermaid-svg-RoscMQUazmJobqBL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RoscMQUazmJobqBL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建 依赖注入 初始化 可用 销毁 创建前后的增强
postProcessBeforeInstantiation 这里返回的对象若不为 null 会替换掉原本的 bean并且仅会走 postProcessAfterInitialization 流程 postProcessAfterInstantiation 这里如果返回 false 会跳过依赖注入阶段
依赖注入前的增强
postProcessProperties 如 Autowired、Value、Resource
初始化前后的增强
postProcessBeforeInitialization 这里返回的对象会替换掉原本的 bean如 PostConstruct、ConfigurationProperties postProcessAfterInitialization 这里返回的对象会替换掉原本的 bean如代理增强
销毁之前的增强
postProcessBeforeDestruction 如 PreDestroy
收获
Spring bean 生命周期各个阶段模板设计模式, 指大流程已经固定好了, 通过接口回调bean 后处理器在一些关键点前后提供扩展
演示2 - 模板方法设计模式
关键代码
public class TestMethodTemplate {public static void main(String[] args) {MyBeanFactory beanFactory new MyBeanFactory();beanFactory.addBeanPostProcessor(bean - System.out.println(解析 Autowired));beanFactory.addBeanPostProcessor(bean - System.out.println(解析 Resource));beanFactory.getBean();}// 模板方法 Template Method Patternstatic class MyBeanFactory {public Object getBean() {Object bean new Object();System.out.println(构造 bean);System.out.println(依赖注入 bean); // Autowired, Resourcefor (BeanPostProcessor processor : processors) {processor.inject(bean);}System.out.println(初始化 bean);return bean;}private ListBeanPostProcessor processors new ArrayList();public void addBeanPostProcessor(BeanPostProcessor processor) {processors.add(processor);}}static interface BeanPostProcessor {public void inject(Object bean); // 对依赖注入阶段的扩展}
}演示3 - bean 后处理器排序
代码参考
package com.itheima.a03;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;import java.util.ArrayList;
import java.util.List;/*bean 后处理的的排序*/
public class TestProcessOrder {public static void main(String[] args) {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);ListBeanPostProcessor list new ArrayList(List.of(new P1(), new P2(), new P3(), new P4(), new P5()));list.sort(beanFactory.getDependencyComparator());list.forEach(processor-{processor.postProcessBeforeInitialization(new Object(), );});/*学到了什么1. 实现了 PriorityOrdered 接口的优先级最高2. 实现了 Ordered 接口与加了 Order 注解的平级, 按数字升序3. 其它的排在最后*/}Order(1)static class P1 implements BeanPostProcessor {private static final Logger log LoggerFactory.getLogger(P1.class);Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug(postProcessBeforeInitialization Order(1));return bean;}}Order(2)static class P2 implements BeanPostProcessor {private static final Logger log LoggerFactory.getLogger(P2.class);Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug(postProcessBeforeInitialization Order(2));return bean;}}static class P3 implements BeanPostProcessor, PriorityOrdered {private static final Logger log LoggerFactory.getLogger(P3.class);Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug(postProcessBeforeInitialization PriorityOrdered);return bean;}Overridepublic int getOrder() {return 100;}}static class P4 implements BeanPostProcessor, Ordered {private static final Logger log LoggerFactory.getLogger(P4.class);Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug(postProcessBeforeInitialization Ordered);return bean;}Overridepublic int getOrder() {return 0;}}static class P5 implements BeanPostProcessor {private static final Logger log LoggerFactory.getLogger(P5.class);Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.debug(postProcessBeforeInitialization);return bean;}}
}
收获
实现了 PriorityOrdered 接口的优先级最高实现了 Ordered 接口与加了 Order 注解的平级, 按数字升序其它的排在最后
4) Bean 后处理器
演示1 - 后处理器作用
代码参考
package com.itheima.a04;import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;/*bean 后处理器的作用*/
public class A04 {public static void main(String[] args) {// ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context new GenericApplicationContext();// ⬇️用原始方法注册三个 beancontext.registerBean(bean1, Bean1.class);context.registerBean(bean2, Bean2.class);context.registerBean(bean3, Bean3.class);context.registerBean(bean4, Bean4.class);context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());//Value解析器context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // Autowired Valuecontext.registerBean(CommonAnnotationBeanPostProcessor.class); // Resource PostConstruct PreDestroyConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());//ConfigurationProperties// ⬇️初始化容器context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例System.out.println(context.getBean(Bean1.class));// ⬇️销毁容器context.close();/*学到了什么a. Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能b. 这些扩展功能由 bean 后处理器来完成*/}
}
package com.itheima.a04;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;public class Bean1 {private static final Logger log LoggerFactory.getLogger(Bean1.class);private Bean2 bean2;Autowiredpublic void setBean2(Bean2 bean2) {log.debug(Autowired 生效: {}, bean2);this.bean2 bean2;}Autowiredprivate Bean3 bean3;Resourcepublic void setBean3(Bean3 bean3) {log.debug(Resource 生效: {}, bean3);this.bean3 bean3;}private String home;Autowiredpublic void setHome(Value(${JAVA_HOME}) String home) {log.debug(Value 生效: {}, home);this.home home;}PostConstructpublic void init() {log.debug(PostConstruct 生效);}PreDestroypublic void destroy() {log.debug(PreDestroy 生效);}Overridepublic String toString() {return Bean1{ bean2 bean2 , bean3 bean3 , home home \ };}
}
package com.itheima.a04;public class Bean2 {
}
package com.itheima.a04;public class Bean3 {
}
package com.itheima.a04;import org.springframework.boot.context.properties.ConfigurationProperties;/*java.homejava.version*/
ConfigurationProperties(prefix java)
public class Bean4 {private String home;private String version;public String getHome() {return home;}public void setHome(String home) {this.home home;}public String getVersion() {return version;}public void setVersion(String version) {this.version version;}Overridepublic String toString() {return Bean4{ home home \ , version version \ };}
}
收获
Autowired 等注解的解析属于 bean 生命周期阶段依赖注入, 初始化的扩展功能这些扩展功能由 bean 后处理器来完成每个后处理器各自增强什么功能 AutowiredAnnotationBeanPostProcessor 解析 Autowired 与 ValueCommonAnnotationBeanPostProcessor 解析 Resource、PostConstruct、PreDestroyConfigurationPropertiesBindingPostProcessor 解析 ConfigurationProperties 另外 ContextAnnotationAutowireCandidateResolver 负责获取 Value 的值解析 Qualifier、泛型、Lazy 等
演示2 - Autowired bean 后处理器运行分析
代码参考
package com.itheima.a04;import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.StandardEnvironment;import java.lang.reflect.Field;
import java.lang.reflect.Method;// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {public static void main(String[] args) throws Throwable {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();beanFactory.registerSingleton(bean2, new Bean2()); // 创建过程,依赖注入,初始化beanFactory.registerSingleton(bean3, new Bean3());beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // ValuebeanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器// 1. 查找哪些属性、方法加了 Autowired, 这称之为 InjectionMetadataAutowiredAnnotationBeanPostProcessor processor new AutowiredAnnotationBeanPostProcessor();processor.setBeanFactory(beanFactory);Bean1 bean1 new Bean1();
// System.out.println(bean1);
// processor.postProcessProperties(null, bean1, bean1); // 执行依赖注入 Autowired Value
// System.out.println(bean1);// Method findAutowiringMetadata AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod(findAutowiringMetadata, String.class, Class.class, PropertyValues.class);
// findAutowiringMetadata.setAccessible(true);
// InjectionMetadata metadata (InjectionMetadata) findAutowiringMetadata.invoke(processor, bean1, Bean1.class, null);// 获取 Bean1 上加了 Value Autowired 的成员变量方法参数信息
// System.out.println(metadata);// 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
// metadata.inject(bean1, bean1, null);
// System.out.println(bean1);// 3. 如何按类型查找值Field bean3 Bean1.class.getDeclaredField(bean3);DependencyDescriptor dd1 new DependencyDescriptor(bean3, false);Object o beanFactory.doResolveDependency(dd1, null, null, null);System.out.println(o);Method setBean2 Bean1.class.getDeclaredMethod(setBean2, Bean2.class);DependencyDescriptor dd2 new DependencyDescriptor(new MethodParameter(setBean2, 0), true);Object o1 beanFactory.doResolveDependency(dd2, null, null, null);System.out.println(o1);Method setHome Bean1.class.getDeclaredMethod(setHome, String.class);DependencyDescriptor dd3 new DependencyDescriptor(new MethodParameter(setHome, 0), true);Object o2 beanFactory.doResolveDependency(dd3, null, null, null);System.out.println(o2);}
}
收获
AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 Value Autowired 的成员变量方法参数的信息表示为 InjectionMetadataInjectionMetadata 可以完成依赖注入InjectionMetadata 内部根据成员变量方法参数封装为 DependencyDescriptor 类型有了 DependencyDescriptor就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找
5) BeanFactory 后处理器
演示1 - BeanFactory 后处理器的作用
代码参考
package com.itheima.a05;import com.itheima.a05.mapper.Mapper1;
import com.itheima.a05.mapper.Mapper2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.GenericApplicationContext;import java.io.IOException;/*BeanFactory 后处理器的作用*/
public class A05 {private static final Logger log LoggerFactory.getLogger(A05.class);public static void main(String[] args) throws IOException {// ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context new GenericApplicationContext();context.registerBean(config, Config.class);
// context.registerBean(ConfigurationClassPostProcessor.class); // ComponentScan Bean Import ImportResource
// context.registerBean(MapperScannerConfigurer.class, bd - { // MapperScanner
// bd.getPropertyValues().add(basePackage, com.itheima.a05.mapper);
// });// context.registerBean(ComponentScanPostProcessor.class); // 解析 ComponentScancontext.registerBean(AtBeanPostProcessor.class); // 解析 Beancontext.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口// ⬇️初始化容器context.refresh();for (String name : context.getBeanDefinitionNames()) {System.out.println(name);}Mapper1 mapper1 context.getBean(Mapper1.class);Mapper2 mapper2 context.getBean(Mapper2.class);// ⬇️销毁容器context.close();/*学到了什么a. ComponentScan, Bean, Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义*/}
}
package com.itheima.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(com.itheima.a05.component)
public class Config {Beanpublic Bean1 bean1() {return new Bean1();}Beanpublic 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/test);dataSource.setUsername(root);dataSource.setPassword(root);return dataSource;}/*Beanpublic MapperFactoryBeanMapper1 mapper1(SqlSessionFactory sqlSessionFactory) {MapperFactoryBeanMapper1 factory new MapperFactoryBean(Mapper1.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}Beanpublic MapperFactoryBeanMapper2 mapper2(SqlSessionFactory sqlSessionFactory) {MapperFactoryBeanMapper2 factory new MapperFactoryBean(Mapper2.class);factory.setSqlSessionFactory(sqlSessionFactory);return factory;}*/
}
package com.itheima.a05;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Bean1 {private static final Logger log LoggerFactory.getLogger(Bean1.class);public Bean1() {log.debug(我被 Spring 管理啦);}
}
ConfigurationClassPostProcessor 可以解析 ComponentScanBeanImportImportResource MapperScannerConfigurer 可以解析 Mapper 接口
收获
ComponentScan, Bean, Mapper 等注解的解析属于核心容器即 BeanFactory的扩展功能这些扩展功能由不同的 BeanFactory 后处理器来完成其实主要就是补充了一些 bean 定义
演示2 - 模拟解析 ComponentScan
代码参考
package com.itheima.a05;import org.springframework.beans.BeansException;
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.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
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.io.IOException;public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {Override // context.refreshpublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {ComponentScan componentScan AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);if (componentScan ! null) {for (String p : componentScan.basePackages()) {System.out.println(p);// com.itheima.a05.component - classpath*:com/itheima/a05/component/**/*.classString path classpath*: p.replace(., /) /**/*.class;System.out.println(path);CachingMetadataReaderFactory factory new CachingMetadataReaderFactory();Resource[] resources new PathMatchingResourcePatternResolver().getResources(path);AnnotationBeanNam eGenerator generator new AnnotationBeanNameGenerator();for (Resource resource : resources) {// System.out.println(resource);MetadataReader reader factory.getMetadataReader(resource);// System.out.println(类名: reader.getClassMetadata().getClassName());AnnotationMetadata annotationMetadata reader.getAnnotationMetadata();// System.out.println(是否加了 Component: annotationMetadata.hasAnnotation(Component.class.getName()));// System.out.println(是否加了 Component 派生: annotationMetadata.hasMetaAnnotation(Component.class.getName()));if (annotationMetadata.hasAnnotation(Component.class.getName())|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {AbstractBeanDefinition bd BeanDefinitionBuilder.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();String name generator.generateBeanName(bd, beanFactory);beanFactory.registerBeanDefinition(name, bd);}}}}} catch (IOException e) {e.printStackTrace();}}
}
收获
Spring 操作元数据的工具类 CachingMetadataReaderFactory通过注解元数据AnnotationMetadata获取直接或间接标注的注解信息通过类元数据ClassMetadata获取类名AnnotationBeanNameGenerator 生成 bean 名解析元数据是基于 ASM 技术
演示3 - 模拟解析 Bean
代码参考
package com.itheima.a05;import org.springframework.beans.BeansException;
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.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.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;import java.io.IOException;
import java.util.Set;public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {CachingMetadataReaderFactory factory new CachingMetadataReaderFactory();MetadataReader reader factory.getMetadataReader(new ClassPathResource(com/itheima/a05/Config.class));SetMethodMetadata methods reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());for (MethodMetadata method : methods) {System.out.println(method);String initMethod method.getAnnotationAttributes(Bean.class.getName()).get(initMethod).toString();BeanDefinitionBuilder builder BeanDefinitionBuilder.genericBeanDefinition();builder.setFactoryMethodOnBean(method.getMethodName(), config);builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);if (initMethod.length() 0) {builder.setInitMethodName(initMethod);}AbstractBeanDefinition bd builder.getBeanDefinition();beanFactory.registerBeanDefinition(method.getMethodName(), bd);}} catch (IOException e) {e.printStackTrace();}}
}
收获
进一步熟悉注解元数据AnnotationMetadata获取方法上注解信息
演示4 - 模拟解析 Mapper 接口
代码参考
package com.itheima.a05;import org.mybatis.spring.mapper.MapperFactoryBean;
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.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;import java.io.IOException;public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {try {PathMatchingResourcePatternResolver resolver new PathMatchingResourcePatternResolver();Resource[] resources resolver.getResources(classpath:com/itheima/a05/mapper/**/*.class);AnnotationBeanNameGenerator generator new AnnotationBeanNameGenerator();CachingMetadataReaderFactory factory new CachingMetadataReaderFactory();for (Resource resource : resources) {MetadataReader reader factory.getMetadataReader(resource);ClassMetadata classMetadata reader.getClassMetadata();if (classMetadata.isInterface()) {AbstractBeanDefinition bd BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).addConstructorArgValue(classMetadata.getClassName()).setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();AbstractBeanDefinition bd2 BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();String name generator.generateBeanName(bd2, beanFactory);beanFactory.registerBeanDefinition(name, bd);}}} catch (IOException e) {e.printStackTrace();}}Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}
收获
Mapper 接口被 Spring 管理的本质实际是被作为 MapperFactoryBean 注册到容器中Spring 的诡异做法根据接口生成的 BeanDefinition 仅为根据接口名生成 bean 名
6) Aware 接口
演示 - Aware 接口及 InitializingBean 接口
代码参考
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;/*Aware 接口及 InitializingBean 接口*/
public class A06 {private static final Logger log LoggerFactory.getLogger(A06.class);public static void main(String[] args) {/*1. Aware 接口用于注入一些与容器相关信息, 例如a. BeanNameAware 注入 bean 的名字b. BeanFactoryAware 注入 BeanFactory 容器c. ApplicationContextAware 注入 ApplicationContext 容器d. EmbeddedValueResolverAware ${}*/GenericApplicationContext context new GenericApplicationContext();
// context.registerBean(myBean, MyBean.class);
// context.registerBean(myConfig1, MyConfig1.class);//1、Autowired失效context.registerBean(myConfig2, MyConfig2.class);//2、Aware未失效context.registerBean(AutowiredAnnotationBeanPostProcessor.class);context.registerBean(CommonAnnotationBeanPostProcessor.class);context.registerBean(ConfigurationClassPostProcessor.class);/*2. 有同学说: b、c、d 的功能用 Autowired 就能实现啊, 为啥还要用 Aware 接口呢简单地说:a. Autowired 的解析需要用到 bean 后处理器, 属于扩展功能b. 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别某些情况下, 扩展功能会失效, 而内置功能不会失效例1: 你会发现用 Aware 注入 ApplicationContext 成功, 而 Autowired 注入 ApplicationContext 失败*//*例2: Java 配置类在添加了 bean 工厂后处理器后,你会发现用传统接口方式的注入和初始化仍然成功, 而 Autowired 和 PostConstruct 的注入和初始化失败*/context.refresh(); // 1. beanFactory 后处理器, 2. 添加 bean 后处理器, 3. 初始化单例context.close();/*学到了什么a. Aware 接口提供了一种【内置】 的注入手段, 可以注入 BeanFactory, ApplicationContextb. InitializingBean 接口提供了一种【内置】的初始化手段c. 内置的注入和初始化不受扩展功能的影响, 总会被执行, 因此 Spring 框架内部的类常用它们*/}
}
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;import javax.annotation.PostConstruct;public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {private static final Logger log LoggerFactory.getLogger(MyBean.class);Overridepublic void setBeanName(String name) {log.debug(当前bean this 名字叫: name);}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.debug(当前bean this 容器是: applicationContext);}Overridepublic void afterPropertiesSet() throws Exception {log.debug(当前bean this 初始化);}Autowiredpublic void aaa(ApplicationContext applicationContext) {log.debug(当前bean this 使用Autowired 容器是: applicationContext);}PostConstructpublic void init() {log.debug(当前bean this 使用PostConstruct 初始化);}
}
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;Configuration
public class MyConfig1 {private static final Logger log LoggerFactory.getLogger(MyConfig1.class);Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.debug(注入 ApplicationContext);}PostConstructpublic void init() {log.debug(初始化);}Bean // beanFactory 后处理器public BeanFactoryPostProcessor processor1() {return beanFactory - {log.debug(执行 processor1);};}}
package com.itheima.a06;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {private static final Logger log LoggerFactory.getLogger(MyConfig2.class);Overridepublic void afterPropertiesSet() throws Exception {log.debug(初始化);}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.debug(注入 ApplicationContext);}Bean // beanFactory 后处理器public BeanFactoryPostProcessor processor2() {return beanFactory - {log.debug(执行 processor2);};}
}
收获
Aware 接口提供了一种【内置】 的注入手段例如 BeanNameAware 注入 bean 的名字BeanFactoryAware 注入 BeanFactory 容器ApplicationContextAware 注入 ApplicationContext 容器EmbeddedValueResolverAware 注入 ${} 解析器 InitializingBean 接口提供了一种【内置】的初始化手段对比 内置的注入和初始化不受扩展功能的影响总会被执行而扩展功能受某些情况影响可能会失效因此 Spring 框架内部的类常用内置注入和初始化
配置类 Autowired 失效分析
Java 配置类不包含 BeanFactoryPostProcessor 的情况 #mermaid-svg-9horOY2ujiw6RcFG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-9horOY2ujiw6RcFG .error-icon{fill:#552222;}#mermaid-svg-9horOY2ujiw6RcFG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9horOY2ujiw6RcFG .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-9horOY2ujiw6RcFG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9horOY2ujiw6RcFG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9horOY2ujiw6RcFG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9horOY2ujiw6RcFG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9horOY2ujiw6RcFG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9horOY2ujiw6RcFG .marker.cross{stroke:#333333;}#mermaid-svg-9horOY2ujiw6RcFG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9horOY2ujiw6RcFG .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9horOY2ujiw6RcFG text.actortspan{fill:black;stroke:none;}#mermaid-svg-9horOY2ujiw6RcFG .actor-line{stroke:grey;}#mermaid-svg-9horOY2ujiw6RcFG .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-9horOY2ujiw6RcFG .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-9horOY2ujiw6RcFG #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-9horOY2ujiw6RcFG .sequenceNumber{fill:white;}#mermaid-svg-9horOY2ujiw6RcFG #sequencenumber{fill:#333;}#mermaid-svg-9horOY2ujiw6RcFG #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-9horOY2ujiw6RcFG .messageText{fill:#333;stroke:#333;}#mermaid-svg-9horOY2ujiw6RcFG .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9horOY2ujiw6RcFG .labelText,#mermaid-svg-9horOY2ujiw6RcFG .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-9horOY2ujiw6RcFG .loopText,#mermaid-svg-9horOY2ujiw6RcFG .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-9horOY2ujiw6RcFG .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-9horOY2ujiw6RcFG .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-9horOY2ujiw6RcFG .noteText,#mermaid-svg-9horOY2ujiw6RcFG .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-9horOY2ujiw6RcFG .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9horOY2ujiw6RcFG .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9horOY2ujiw6RcFG .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-9horOY2ujiw6RcFG .actorPopupMenu{position:absolute;}#mermaid-svg-9horOY2ujiw6RcFG .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-9horOY2ujiw6RcFG .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-9horOY2ujiw6RcFG .actor-man circle,#mermaid-svg-9horOY2ujiw6RcFG line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-9horOY2ujiw6RcFG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor 3. 创建和初始化 3.1 依赖注入扩展(如 Value 和 Autowired) 3.2 初始化扩展(如 PostConstruct) 3.3 执行 Aware 及 InitializingBean 3.4 创建成功 ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 Java 配置类包含 BeanFactoryPostProcessor 的情况因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类而此时的 BeanPostProcessor 还未准备好导致 Autowired 等注解失效 #mermaid-svg-KgvVSmmi6KvMptj1 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 .error-icon{fill:#552222;}#mermaid-svg-KgvVSmmi6KvMptj1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KgvVSmmi6KvMptj1 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-KgvVSmmi6KvMptj1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KgvVSmmi6KvMptj1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KgvVSmmi6KvMptj1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KgvVSmmi6KvMptj1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KgvVSmmi6KvMptj1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KgvVSmmi6KvMptj1 .marker.cross{stroke:#333333;}#mermaid-svg-KgvVSmmi6KvMptj1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KgvVSmmi6KvMptj1 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KgvVSmmi6KvMptj1 text.actortspan{fill:black;stroke:none;}#mermaid-svg-KgvVSmmi6KvMptj1 .actor-line{stroke:grey;}#mermaid-svg-KgvVSmmi6KvMptj1 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 .sequenceNumber{fill:white;}#mermaid-svg-KgvVSmmi6KvMptj1 #sequencenumber{fill:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 .messageText{fill:#333;stroke:#333;}#mermaid-svg-KgvVSmmi6KvMptj1 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KgvVSmmi6KvMptj1 .labelText,#mermaid-svg-KgvVSmmi6KvMptj1 .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-KgvVSmmi6KvMptj1 .loopText,#mermaid-svg-KgvVSmmi6KvMptj1 .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-KgvVSmmi6KvMptj1 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KgvVSmmi6KvMptj1 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-KgvVSmmi6KvMptj1 .noteText,#mermaid-svg-KgvVSmmi6KvMptj1 .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-KgvVSmmi6KvMptj1 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KgvVSmmi6KvMptj1 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KgvVSmmi6KvMptj1 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KgvVSmmi6KvMptj1 .actorPopupMenu{position:absolute;}#mermaid-svg-KgvVSmmi6KvMptj1 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-KgvVSmmi6KvMptj1 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KgvVSmmi6KvMptj1 .actor-man circle,#mermaid-svg-KgvVSmmi6KvMptj1 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-KgvVSmmi6KvMptj1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 3. 创建和初始化 3.1 执行 Aware 及 InitializingBean 3.2 创建成功 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 对应代码
Configuration
public class MyConfig1 {private static final Logger log LoggerFactory.getLogger(MyConfig1.class);Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.debug(注入 ApplicationContext);}PostConstructpublic void init() {log.debug(初始化);}Bean // ⬅️ 注释或添加 beanFactory 后处理器对应上方两种情况public BeanFactoryPostProcessor processor1() {return beanFactory - {log.debug(执行 processor1);};}}注意 解决方法 用内置依赖注入和初始化取代扩展依赖注入和初始化用静态工厂方法代替实例工厂方法避免工厂对象提前被创建 7) 初始化与销毁
演示 - 初始化销毁顺序
代码参考
package com.itheima.a07;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;/*初始化和销毁的执行顺序*/
SpringBootApplication
public class A07_1 {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(A07_1.class, args);context.close();/*学到了什么a. Spring 提供了多种初始化和销毁手段b. Spring 的面试有多么地卷*/}Bean(initMethod init3)public Bean1 bean1() {return new Bean1();}Bean(destroyMethod destroy3)public Bean2 bean2() {return new Bean2();}
}
package com.itheima.a07;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;import javax.annotation.PostConstruct;public class Bean1 implements InitializingBean {private static final Logger log LoggerFactory.getLogger(Bean1.class);PostConstructpublic void init1() {log.debug(初始化1);}Overridepublic void afterPropertiesSet() throws Exception {log.debug(初始化2);}public void init3() {log.debug(初始化3);}
}
package com.itheima.a07;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;import javax.annotation.PreDestroy;public class Bean2 implements DisposableBean {private static final Logger log LoggerFactory.getLogger(Bean2.class);PreDestroypublic void destroy1() {log.debug(销毁1);}Overridepublic void destroy() throws Exception {log.debug(销毁2);}public void destroy3() {log.debug(销毁3);}
}
package com.itheima.a07;import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;public class A07_2 {public static void main(String[] args) {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();beanFactory.registerBeanDefinition(myBean,BeanDefinitionBuilder.genericBeanDefinition(MyBean.class).setDestroyMethodName(destroy).getBeanDefinition());System.out.println(beanFactory.getBean(MyBean.class));beanFactory.destroySingletons(); // 销毁之后, 仍可创建新的单例System.out.println(beanFactory.getBean(MyBean.class));}static class MyBean {public MyBean() {System.out.println(MyBean());}public void destroy() {System.out.println(destroy());}}
}收获
Spring 提供了多种初始化手段除了课堂上讲的 PostConstructBean(initMethod) 之外还可以实现 InitializingBean 接口来进行初始化如果同一个 bean 用了以上手段声明了 3 个初始化方法那么它们的执行顺序是
PostConstruct 标注的初始化方法InitializingBean 接口的初始化方法Bean(initMethod) 指定的初始化方法
与初始化类似Spring 也提供了多种销毁手段执行顺序为
PreDestroy 标注的销毁方法DisposableBean 接口的销毁方法Bean(destroyMethod) 指定的销毁方法
8) Scope
在当前版本的 Spring 和 Spring Boot 程序中支持五种 Scope
singleton容器启动时创建未设置延迟容器关闭时销毁prototype每次使用时创建不会自动销毁需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁request每次请求用到此 bean 时创建请求结束时销毁session每个会话用到此 bean 时创建会话结束时销毁applicationweb 容器用到此 bean 时创建容器停止时销毁
有些文章提到有 globalSession 这一 Scope也是陈旧的说法目前 Spring 中已废弃
但要注意如果在 singleton 注入其它 scope 都会有问题解决方法有
LazyScope(proxyMode ScopedProxyMode.TARGET_CLASS)ObjectFactoryApplicationContext.getBean
演示1 - request, session, application 作用域
代码参考
package com.itheima.a08;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*singleton, prototype, request, session, applicationjdk 9 如果反射调用 jdk 中方法jdk 8 不会有问题演示 request, session, application 作用域打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果如果 jdk 8, 运行时请添加 --add-opens java.base/java.langALL-UNNAMED*/
SpringBootApplication
public class A08 {public static void main(String[] args) {SpringApplication.run(A08.class, args);/*学到了什么a. 有几种 scopeb. 在 singleton 中使用其它几种 scope 的方法c. 其它 scope 的销毁1. 可以将通过 server.servlet.session.timeout10s 观察 session bean 的销毁2. ServletContextScope 销毁机制疑似实现有误*/}
}
package com.itheima.a08;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;RestController
public class MyController {LazyAutowiredprivate BeanForRequest beanForRequest;LazyAutowiredprivate BeanForSession beanForSession;LazyAutowiredprivate BeanForApplication beanForApplication;GetMapping(value /test, produces text/html)public String test(HttpServletRequest request, HttpSession session) {ServletContext sc request.getServletContext();String sb ul li request scope: beanForRequest /li li session scope: beanForSession /li li application scope: beanForApplication /li /ul;return sb;}}
package com.itheima.a08;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;Scope(request)
Component
public class BeanForRequest {private static final Logger log LoggerFactory.getLogger(BeanForRequest.class);PreDestroypublic void destroy() {log.debug(destroy);}}
package com.itheima.a08;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;Scope(session)
Component
public class BeanForSession {private static final Logger log LoggerFactory.getLogger(BeanForSession.class);PreDestroypublic void destroy() {log.debug(destroy);}
}
package com.itheima.a08;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;Scope(application)
Component
public class BeanForApplication {private static final Logger log LoggerFactory.getLogger(BeanForApplication.class);PreDestroypublic void destroy() {log.debug(destroy);}
}打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果如果 jdk 8, 运行时请添加 --add-opens java.base/java.langALL-UNNAMED
收获
有几种 scope在 singleton 中使用其它几种 scope 的方法其它 scope 的销毁时机 可以将通过 server.servlet.session.timeout30s 观察 session bean 的销毁ServletContextScope 销毁机制疑似实现有误
分析 - singleton 注入其它 scope 失效
以单例注入多例为例
有一个单例对象 E
Component
public class E {private static final Logger log LoggerFactory.getLogger(E.class);private F f;public E() {log.info(E());}Autowiredpublic void setF(F f) {this.f f;log.info(setF(F f) {}, f.getClass());}public F getF() {return f;}
}要注入的对象 F 期望是多例
Component
Scope(prototype)
public class F {private static final Logger log LoggerFactory.getLogger(F.class);public F() {log.info(F());}
}测试
E e context.getBean(E.class);
F f1 e.getF();
F f2 e.getF();
System.out.println(f1);
System.out.println(f2);输出
com.itheima.demo.cycle.F6622fc65
com.itheima.demo.cycle.F6622fc65发现它们是同一个对象而不是期望的多例对象
对于单例对象来讲依赖注入仅发生了一次后续再没有用到多例的 F因此 E 用的始终是第一次依赖注入的 F #mermaid-svg-0i3JZaalDJgZkTQ5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .error-icon{fill:#552222;}#mermaid-svg-0i3JZaalDJgZkTQ5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0i3JZaalDJgZkTQ5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .marker.cross{stroke:#333333;}#mermaid-svg-0i3JZaalDJgZkTQ5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0i3JZaalDJgZkTQ5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .cluster-label text{fill:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .cluster-label span{color:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .label text,#mermaid-svg-0i3JZaalDJgZkTQ5 span{fill:#333;color:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .node rect,#mermaid-svg-0i3JZaalDJgZkTQ5 .node circle,#mermaid-svg-0i3JZaalDJgZkTQ5 .node ellipse,#mermaid-svg-0i3JZaalDJgZkTQ5 .node polygon,#mermaid-svg-0i3JZaalDJgZkTQ5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0i3JZaalDJgZkTQ5 .node .label{text-align:center;}#mermaid-svg-0i3JZaalDJgZkTQ5 .node.clickable{cursor:pointer;}#mermaid-svg-0i3JZaalDJgZkTQ5 .arrowheadPath{fill:#333333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0i3JZaalDJgZkTQ5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-0i3JZaalDJgZkTQ5 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-0i3JZaalDJgZkTQ5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0i3JZaalDJgZkTQ5 .cluster text{fill:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 .cluster span{color:#333;}#mermaid-svg-0i3JZaalDJgZkTQ5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0i3JZaalDJgZkTQ5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} e 创建 e set 注入 f f 创建 解决
仍然使用 Lazy 生成代理代理对象虽然还是同一个但当每次使用代理对象的任意方法时由代理创建新的 f 对象 #mermaid-svg-R3xpzmWjYpgrUHp8 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .error-icon{fill:#552222;}#mermaid-svg-R3xpzmWjYpgrUHp8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R3xpzmWjYpgrUHp8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .marker.cross{stroke:#333333;}#mermaid-svg-R3xpzmWjYpgrUHp8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R3xpzmWjYpgrUHp8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .cluster-label text{fill:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .cluster-label span{color:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .label text,#mermaid-svg-R3xpzmWjYpgrUHp8 span{fill:#333;color:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .node rect,#mermaid-svg-R3xpzmWjYpgrUHp8 .node circle,#mermaid-svg-R3xpzmWjYpgrUHp8 .node ellipse,#mermaid-svg-R3xpzmWjYpgrUHp8 .node polygon,#mermaid-svg-R3xpzmWjYpgrUHp8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-R3xpzmWjYpgrUHp8 .node .label{text-align:center;}#mermaid-svg-R3xpzmWjYpgrUHp8 .node.clickable{cursor:pointer;}#mermaid-svg-R3xpzmWjYpgrUHp8 .arrowheadPath{fill:#333333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-R3xpzmWjYpgrUHp8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-R3xpzmWjYpgrUHp8 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-R3xpzmWjYpgrUHp8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-R3xpzmWjYpgrUHp8 .cluster text{fill:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 .cluster span{color:#333;}#mermaid-svg-R3xpzmWjYpgrUHp8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-R3xpzmWjYpgrUHp8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 使用f方法 使用f方法 使用f方法 e 创建 e set 注入 f代理 f 创建 f 创建 f 创建 Component
public class E {AutowiredLazypublic void setF(F f) {this.f f;log.info(setF(F f) {}, f.getClass());}// ...
}注意 Lazy 加在也可以加在成员变量上但加在 set 方法上的目的是可以观察输出加在成员变量上就不行了Autowired 加在 set 方法的目的类似 输出
E: setF(F f) class com.itheima.demo.cycle.F$$EnhancerBySpringCGLIB$$8b54f2bc
F: F()
com.itheima.demo.cycle.F3a6f2de3
F: F()
com.itheima.demo.cycle.F56303b57从输出日志可以看到调用 setF 方法时f 对象的类型是代理类型
演示2 - 4种解决方法
代码参考
package com.itheima.a08.sub;import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;Component
public class E {LazyAutowiredprivate F1 f1;Autowiredprivate F2 f2;Autowiredprivate ObjectFactoryF3 f3;Autowiredprivate ApplicationContext context;public F1 getF1() {return f1;}public F2 getF2() {return f2;}public F3 getF3() {return f3.getObject();}public F4 getF4() {return context.getBean(F4.class);}
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;Scope(prototype)
Component
public class F1 {
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;Scope(value prototype, proxyMode ScopedProxyMode.TARGET_CLASS)
Component
public class F2 {
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;Scope(prototype)
Component
public class F3 {
}
package com.itheima.a08.sub;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;Scope(prototype)
Component
public class F4 {
}
如果 jdk 8, 运行时请添加 --add-opens java.base/java.langALL-UNNAMED
收获
单例注入其它 scope 的四种解决方法 LazyScope(value “prototype”, proxyMode ScopedProxyMode.TARGET_CLASS)ObjectFactoryApplicationContext 解决方法虽然不同但理念上殊途同归: 都是推迟其它 scope bean 的获取