1、容器接口
1.1、BeanFactory功能
1.1.1、什么是 BeanFactory ?
BeanFactory:
- 是 ApplicationContext 的父接口
- 是 Spring 的核心容器,主要的 ApplicationContext 实现都【组合】了它的功能
从上面类继承图可以看到,ApplicationContext继承了BeanFactory,即ApplicationContext对BeanFactory提供了一些功能上的拓展,这些功能拓展就是继承右侧的那些父接口,比如MessageSource、ResourcePatternResolver、ApplicationEventPublisher、EnvironmentCapable。
其中BeanFactory接口有一些getBean()方法用于获取Bean对象:
public interface BeanFactory {
// ...
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
// ...
}
因此ConfigurableApplicationContext也具备这些方法。编写以下代码,并查看getBean()方法的具体实现:
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) {
// 返回的对象就是Spring容器对象, 选中该类,按住ctrl+alt+u,可以打开类图
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
// 对下面的方法按住ctrl+alt+单击,可以进入其实现类的对应方法
caContext.getBean("aaa");
System.out.println(caContext);
}
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
// ...
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
// 这里是获取了BeanFactory再调用它的getBean()方法,说明它内部的功能拓展是组合了一个BeanFactory作为成员变量,getBean()功能由这个BeanFactory提供
return this.getBeanFactory().getBean(name);
}
// ...
}
1.1.2、BeanFactory能干点啥?
回到BeanFactory接口的定义:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
// 根据Bean名称判断容器中是否包含这个Bean对象
boolean containsBean(String var1);
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
// 根据Bean名称获取它的别名
String[] getAliases(String var1);
}
虽然BeanFactory表面上只有一些getBean()方法,实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能,都由它的实现类提供。比如DefaultListableBeanFactory,下面是它的类结构图:
从上面类图可以看出,DefaultListableBeanFactory是DefaultSingletonBeanRegistry的子类,继承了它的管理单例对象的功能,并且实现了ConfigurableListableBeanFactory接口,在DefaultSingletonBeanRegistry中定义了字段singletonObjects用来存储单例的Bean对象:
为了证明这一点,我们编写一个单例对象并放入Spring容器中:
@Slf4j
@Component
public class Component1 {
}
我们尝试直接从singletonObjects属性中去获取这个Bean,因为singletonObjects属性是私有的,我们可以通过反射来获取:
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
// 返回的对象就是Spring容器对象, 选中该类,按住ctrl+alt+u,可以打开类图
// 这里返回的实现类为 AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
// 获取caContext的BeanFactory,这里返回的实现类为 DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = caContext.getBeanFactory();
// 反射获取 beanFactory 的 singletonObject 字段,它是一个Map<String, Object>
Field fSingletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
fSingletonObjects.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) fSingletonObjects.get(beanFactory);
// 查询 singletonObjects 集合,找到我们定义的 Bean
map.forEach((k, v) -> {
if (v instanceof Component1) {
System.out.println(k + "=" + v);
}
});
}
}
结果:
component1=com.clp.components.Component1@272a179c
1.2、ApplicationContext功能
我们查看ApplicationContext类图:
BeanFactory 和 ApplicationContext 并不仅仅是简单接口继承的关系,ApplicationContext 组合并扩展了 BeanFactory 的功能。
它分别继承了MessageSource、ResourcePatternResolver、ApplicationEventPublisher、EnvironmentCapable接口来扩展功能,接下来分别看一下这几个父接口都有什么样的功能。
1.2.1、父接口 - MessageSource
其中MessageSource接口定义如下:
public interface MessageSource {
// 根据 code 返回不同语言版本的翻译后结果
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
这个接口的功能是根据键找到不同语言版本的翻译后结果,翻译信息需要我们自己定义在配置文件中,SpringBoot默认配置在名称如 messages.properties、messages_en.properties、messages_ja.properties 、message_zh.properties 等等的配置文件中,其中 messages.properties 存储通用的翻译结果,message_en.properties 为存储英文的翻译结果,message_zh.properties存储中文的翻译结果等等。
比如我们要编写键为“code_hello”的英文翻译结果,可以在message_en.properties中编写如下内容:
code_hello=hello
而要编写该键的中文翻译结果,可以在message_cn.properties中编写如下内容:
code_hello="你好"
我们尝试通过ApplicationContext获取翻译结果:
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
// 这里返回的实现类为 AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
ApplicationContext aContext = caContext;
String msgHello = caContext.getMessage("code_hello", null, Locale.CHINA); // 会从messages_zh.properties读取
System.out.println(msgHello);
}
}
结果:
"你好"
1.2.2、父接口 - ResourcePatternResolver & EnvironmentCapable
ResourcePatternResolver 用于加载和解析资源文件。
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, IOException {
// 这里返回的实现类为 AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
ApplicationContext aContext = caContext;
/**
* ResourcePatternResolver 可以获取类路径的资源文件:
*/
Resource[] resources1 = aContext.getResources("classpath:application.properties");
for (Resource resource : resources1) {
System.out.println(resource);
}
/**
* classpath 和 classpath* 区别:
* classpath:只会到你的class路径中查找找文件。
* classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。
* 注意: 用classpath*:需要遍历所有的classpath,所以加载速度是很慢的;
* 因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*。
*/
Resource[] resources2 = aContext.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources2) {
System.out.println(resource);
}
}
}
结果:
class path resource [application.properties]
URL [file:/G:/Study/Java/java-study/spring-source-study/target/classes/META-INF/spring.factories]
URL [jar:file:/G:/Tools/apache-maven-3.6.1/mvn_repository/org/springframework/boot/spring-boot/2.6.13/spring-boot-2.6.13.jar!/META-INF/spring.factories]
URL [jar:file:/G:/Tools/apache-maven-3.6.1/mvn_repository/org/springframework/boot/spring-boot-autoconfigure/2.6.13/spring-boot-autoconfigure-2.6.13.jar!/META-INF/spring.factories]
URL [jar:file:/G:/Tools/apache-maven-3.6.1/mvn_repository/org/springframework/spring-beans/5.3.23/spring-beans-5.3.23.jar!/META-INF/spring.factories]
1.2.3、父接口 - EnvironmentCapable
EnvironmentCapable 可以获取环境变量、配置信息。
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, IOException {
// 这里返回的实现类为 AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
ApplicationContext aContext = caContext;
/**
* environment 用于获取配置信息,包括环境变量、application.properties中的配置属性
*/
ConfigurableEnvironment environment = aContext.getEnvironment();
System.out.println(environment.getProperty("java_home"));
System.out.println(environment.getProperty("server.port"));
}
}
结果:
D:\jdks\jdk1.8
8080
1.2.4、父接口 - ApplicationEventPublisher
用于推送事件。将事件发布者和接收者进行解耦。
代码演示1:
@Slf4j
@Component
public class Component2 {
/**
* 添加 @EventListener 表示该方法会监听事件
* @param event 监听的事件类型
*/
@EventListener
public void aaa(UserRegisterEvent event) {
log.info("{}", event);
}
}
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, IOException {
// 这里返回的实现类为 AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
ApplicationContext aContext = caContext;
// 发布事件,所有监听这个事件的对象都会被通知
aContext.publishEvent(new UserRegisterEvent(aContext));
}
}
结果:
2023-04-10 10:10:47.477 INFO 24060 --- [ main] com.clp.components.Component2 : com.clp.UserRegisterEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@367ffa75, started on Mon Apr 10 10:10:46 CST 2023]
代码演示2:
/**
* 模拟事件发布者
*/
@Slf4j
@Component
public class Component1 {
/**
* 手动注入 应用事件发布器 对象
*/
@Autowired
private ApplicationEventPublisher context;
/**
* 模拟用户进行注册,并发布用户注册事件
*/
public void register() {
log.info("用户注册");
context.publishEvent(new UserRegisterEvent(this));
}
}
/**
* 模拟事件监听者
*/
@Slf4j
@Component
public class Component2 {
/**
* 添加 @EventListener 表示该方法会监听事件
* @param event 监听的事件类型
*/
@EventListener
public void aaa(UserRegisterEvent event) {
log.info("{}", event);
log.info("发送短信...");
}
}
/**
* SpringBoot 启动类
*/
@SpringBootApplication
public class SpringSourceStudyApplication {
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, IOException {
// 这里返回的实现类为 AnnotationConfigServletWebServerApplicationContext
ConfigurableApplicationContext caContext = SpringApplication.run(SpringSourceStudyApplication.class, args);
ApplicationContext aContext = caContext;
// 方式2:
aContext.getBean(Component1.class).register();
}
}
结果:
2023-04-10 10:20:22.329 INFO 7088 --- [ main] com.clp.components.Component1 : 用户注册
2023-04-10 10:20:22.330 INFO 7088 --- [ main] com.clp.components.Component2 : com.clp.UserRegisterEvent[source=com.clp.components.Component1@4d847d32]
2023-04-10 10:20:22.330 INFO 7088 --- [ main] com.clp.components.Component2 : 发送短信...
2、容器实现
2.1、BeanFactory 实现
先看以下代码演示:
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 1、添加Bean 的定义(Bean定义包含:class、scope、初始化方法、销毁方法等信息)
// 1.1、创建 Bean定义(通过Builder)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
// 1.2、将 Bean定义 注册到 Bean工厂
beanFactory.registerBeanDefinition("config", beanDefinition);
// 循环查找 Bean工厂 中所有 Bean定义
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
@Slf4j
static class Bean1 {
public Bean1() {
log.info("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
@Slf4j
static class Bean2 {
public Bean2() {
log.info("构造 Bean2()");
}
}
}
结果:
config
这里控制台只打印了 config,并没有打印 @Bean 注解的 Bean1 和 Bean2,说明 BeanFactory 并没有解析 Config 类的 @Configuration 注解,也即缺少解析 @Bean 的能力,功能不完整。
下面代码使用了Bean工厂处理器对Bean工厂的功能进行了补充:
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 1、添加Bean 的定义(Bean定义包含:class、scope、初始化方法、销毁方法等信息)
// 1.1、创建 Bean定义(通过Builder)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
// 1.2、将 Bean定义 注册到 Bean工厂
beanFactory.registerBeanDefinition("config", beanDefinition);
// 2、使用 后处理器 扩展 BeanFactory 的功能,下面的代码给 BeanFactory 补充了 Bean1 和 Bean2 的定义
// 使用工具类,该方法的作用是给 BeanFactory 添加一些常用的(内置)后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 执行 Bean工厂后处理器(后处理器执行后, Config 的 @Bean 会生效)
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
// 循环查找 Bean工厂 中所有 Bean定义
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
// 检查 Bean1 是否成功注入了 Bean2,发现并没有成功注入
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
@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.info("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
@Slf4j
static class Bean2 {
public Bean2() {
log.info("构造 Bean2()");
}
}
}
结果:
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
10:54:52.759 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
10:54:52.761 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
10:54:52.775 [main] INFO com.clp.TestBeanFactory$Bean1 - 构造 Bean1()
null
上面代码我们发现使用了 Bean工厂处理器 的postProcessBeanFactory() 之后 Config 类的 @Bean 配置生效了,但是 Bean1 并没有成功注入 Bean2。
下面代码使用了 Bean处理器 对 Bean 的注解进行了处理:
public class TestBeanFactory {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 1、添加Bean 的定义(Bean定义包含:class、scope、初始化方法、销毁方法等信息)
// 1.1、创建 Bean定义(通过Builder)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
// 1.2、将 Bean定义 注册到 Bean工厂
beanFactory.registerBeanDefinition("config", beanDefinition);
// 2、使用 后处理器 扩展 BeanFactory 的功能,下面的代码给 BeanFactory 补充了 Bean1 和 Bean2 的定义
// 使用工具类,该方法的作用是给 BeanFactory 添加一些常用的(内置)后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 执行 Bean工厂后处理器(后处理器执行后, Config 的 @Bean 会生效)
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
// 3、使用 Bean后处理器,针对 Bean 的生命周期各个阶段提供扩展,例如对 @Autowired、@Resource ... 的解析
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor -> {
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
// 循环查找 Bean工厂 中所有 Bean定义
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
// 检查 Bean1 是否成功注入了 Bean2,这次成功注入了
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
@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.info("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
@Slf4j
static class Bean2 {
public Bean2() {
log.info("构造 Bean2()");
}
}
}
结果:
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
11:02:22.912 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
11:02:22.914 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
11:02:22.932 [main] INFO com.clp.TestBeanFactory$Bean1 - 构造 Bean1()
11:02:22.949 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
11:02:22.949 [main] INFO com.clp.TestBeanFactory$Bean2 - 构造 Bean2()
com.clp.TestBeanFactory$Bean2@7bb58ca3
上面的代码我们发现,BeanFactory 仅仅是保存了 BeanDefinition,并且只有在调用 getBean() 方法后才对 Bean 进行了实例化。我们可以直接调用下面的方法(在 ConfigurableListableBeanFactory 接口中定义)来手动提前创建所有的单例对象:
// 提前创建所有单例对象
beanFactory.preInstantiateSingletons();
总结:
- BeanFactory不会做的事:① 不会主动调用 BeanFactory 后处理器;② 不会主动添加 Bean 后处理器;③ 不会主动初始化单例;④ 不会解析 BeanFactory,还不会解析 ${} 与 #{}。
- Bean后处理器会有排序的逻辑。
- @Autowired实现逻辑:先匹配类型,如果有多个Bean匹配上,那么再检查变量名称,如果名称有匹配上的Bean会使用这个Bean。
2.2、ApplicationContext 实现
@Slf4j
public class TestApplicationContext {
@Slf4j
static class Bean1 {
}
@Slf4j
static class Bean2 {
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
public static void main(String[] args) {
// testClassPathXmlApplicationContext();
// testFileSystemXmlApplicationContext();
// testAnnotationConfigApplicationContext();
testAnnotationConfigServletWebServerApplicationContext();
// xmlprinciple();
}
// 1、较为经典的容器,基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
/** 打印结果:
* bean1
* bean2
* com.clp.TestApplicationContext$Bean1@6a1aab78
*/
}
// 2、基于磁盘路径下的 xml 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext() {
// 绝对路径写法。如要使用相对路径写法,则从模块路径开始,即 src\main\resources\spring-config01.xml
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
"G:\\Study\\Java\\java-study\\spring-source-study\\src\\main\\resources\\spring-config01.xml"
);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
/** 打印结果:
* bean1
* bean2
* com.clp.TestApplicationContext$Bean1@6a1aab78
*/
}
// 基于 xml 文件的 ApplicationContext 实现原理:
private static void xmlprinciple() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前...");
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("读取之后...");
// 使用 XmlBeanDefinitionReader 来进行解析
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 此处为类路径资源,如果是系统路径资源则是 new FileSystemResource("G:\\Study\\Java\\java-study\\spring-source-study\\src\\main\\resources\\spring-config01.xml")
reader.loadBeanDefinitions(new ClassPathResource("spring-config01.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
/**
* 打印结果:
* 读取之前...
* 读取之后...
* 18:33:35.565 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [spring-config01.xml]
* bean1
* bean2
*/
}
@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;
}
}
// 3、较为经典的容器,基于 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());
/**
* 打印结果:
* 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
* testApplicationContext.Config
* bean1
* bean2
* com.clp.TestApplicationContext$Bean1@670002
*/
}
@Configuration
static class WebConfig {
/**
* 产生 Tomcat Web容器(Web服务器) 的工厂
* @return
*/
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
/**
* 前控制器 DispatcherServlet
* @return
*/
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
/**
* 注册 DispatcherServlet 到 Tomcat 容器(Web服务器)
* @return
*/
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/"); // 表示所有请求都经过 DispatcherServlet
}
/**
* 创建一个控制器,@Bean内的参数("/hello")表示请求路径,匹配到此路径时会执行该注解下的方法
* @return
*/
@Bean("/hello")
public Controller controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}
// 4、较为经典的容器,基于 Java 配置类来创建,用于web环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
/**
* 结果:当浏览器输入 127.0.0.1:8080/hello后,接收到 "hello" 返回信息
*/
}
}
3、Bean的生命周期
3.1、代码演示
以下是Bean生命周期演示的一个例子:
@Slf4j
@Component
public class LifeCircleBean {
// 以下方法的调用时机顺序:从上到下
public LifeCircleBean() {
log.info("构造 LifeCircleBean()");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home) {
log.info("依赖注入:{}", home);
}
@PostConstruct
public void init() {
log.info("初始化");
}
@PreDestroy
public void destroy() {
log.info("销毁");
}
}
@SpringBootApplication
public class Test03Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test03Application.class, args);
context.close();
}
}
结果:
...
2023-04-10 19:00:46.292 INFO 21796 --- [ main] com.clp.test03.LifeCircleBean : 构造 LifeCircleBean()
2023-04-10 19:00:46.295 INFO 21796 --- [ main] com.clp.test03.LifeCircleBean : 依赖注入:D:\jdks\jdk1.8
2023-04-10 19:00:46.296 INFO 21796 --- [ main] com.clp.test03.LifeCircleBean : 初始化
2023-04-10 19:00:46.438 INFO 21796 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2023-04-10 19:00:46.511 INFO 21796 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-04-10 19:00:46.519 INFO 21796 --- [ main] com.clp.test03.Test03Application : Started Test03Application in 1.58 seconds (JVM running for 2.037)
2023-04-10 19:00:46.551 INFO 21796 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-04-10 19:00:46.556 INFO 21796 --- [ main] com.clp.test03.LifeCircleBean : 销毁
Process finished with exit code 0
第二个例子:
@Slf4j
@Component
public class LifeCircleBean {
// 以下方法的调用时机顺序:从上到下
public LifeCircleBean() {
log.info("构造 LifeCircleBean()");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home) {
log.info("依赖注入:{}", home);
}
@PostConstruct
public void init() {
log.info("初始化");
}
@PreDestroy
public void destroy() {
log.info("销毁");
}
}
@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCircleBean")) {
log.info("<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean");
}
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCircleBean")) {
log.info("<<<<<<<< 实例化之后执行,这里如果返回 false 会跳过依赖注入阶段");
// return false;
}
return true; // 返回true会继续依赖注入阶段,返回false则会跳过该阶段,一般不会改为false
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCircleBean")) {
log.info("<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource");
}
return pvs;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCircleBean")) {
log.info("<<<<<<<< 初始化之前执行,这里返回的对象会替换掉原本的 Bean,如 @PostConstruct、@ConfigurationProperties");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCircleBean")) {
log.info("<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 Bean,如 代理增强");
}
return bean;
}
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCircleBean")) {
log.info("<<<<<<<< 销毁之前执行,如 @PreDestroy");
}
}
}
@SpringBootApplication
public class Test03Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test03Application.class, args);
context.close();
}
}
结果:
...
2023-04-10 19:29:52.201 INFO 4528 --- [ main] com.clp.test03.MyBeanPostProcessor : <<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean
2023-04-10 19:29:52.203 INFO 4528 --- [ main] com.clp.test03.LifeCircleBean : 构造 LifeCircleBean()
2023-04-10 19:29:52.205 INFO 4528 --- [ main] com.clp.test03.MyBeanPostProcessor : <<<<<<<< 实例化之后执行,这里如果返回 false 会跳过依赖注入阶段
2023-04-10 19:29:52.205 INFO 4528 --- [ main] com.clp.test03.MyBeanPostProcessor : <<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource
2023-04-10 19:29:52.207 INFO 4528 --- [ main] com.clp.test03.LifeCircleBean : 依赖注入:D:\jdks\jdk1.8
2023-04-10 19:29:52.209 INFO 4528 --- [ main] com.clp.test03.MyBeanPostProcessor : <<<<<<<< 初始化之前执行,这里返回的对象会替换掉原本的 Bean,如 @PostConstruct、@ConfigurationProperties
2023-04-10 19:29:52.209 INFO 4528 --- [ main] com.clp.test03.LifeCircleBean : 初始化
2023-04-10 19:29:52.209 INFO 4528 --- [ main] com.clp.test03.MyBeanPostProcessor : <<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 Bean,如 代理增强
2023-04-10 19:29:52.373 INFO 4528 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2023-04-10 19:29:52.450 INFO 4528 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-04-10 19:29:52.458 INFO 4528 --- [ main] com.clp.test03.Test03Application : Started Test03Application in 1.582 seconds (JVM running for 2.075)
2023-04-10 19:29:52.487 INFO 4528 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-04-10 19:29:52.494 INFO 4528 --- [ main] com.clp.test03.MyBeanPostProcessor : <<<<<<<< 销毁之前执行,如 @PreDestroy
2023-04-10 19:29:52.494 INFO 4528 --- [ main] com.clp.test03.LifeCircleBean : 销毁
Process finished with exit code 0
3.2、设计模式——模板方法
代码演示自定义Bean工厂以及自定义后处理器:
public class TestMethodTemplate {
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.addBeanPostProcessor(obj -> System.out.println("解析 @Autowired ..."));
beanFactory.addBeanPostProcessor(obj -> System.out.println("解析 @Resource ..."));
Object bean = beanFactory.getBean();
}
// 模板方法 Template Method Pattern
static class MyBeanFactory {
private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor processor) {
beanPostProcessors.add(processor);
}
public Object getBean() {
Object bean = new Object();
System.out.println("构造 " + bean);
System.out.println("依赖注入 " + bean); // 如要实现 @Autowired、@Resource 注解功能要修改代码
// ... 后处理器对依赖注入阶段作拓展
for (BeanPostProcessor processor : beanPostProcessors) {
processor.inject(bean);
}
System.out.println("初始化 " + bean);
return bean;
}
}
// 自定义后处理器
static interface BeanPostProcessor {
public void inject(Object obj); // 对依赖注入阶段的功能做一些扩展
}
}
结果:
构造 java.lang.Object@79fc0f2f
依赖注入 java.lang.Object@79fc0f2f
解析 @Autowired ...
解析 @Resource ...
初始化 java.lang.Object@79fc0f2f
4、Bean后处理器
4.1、常用的Bean后处理器 & 代码演示
Bean后处理器的作用:为Bean的生命周期的各个阶段提供扩展。
首先创建如下3个Bean:
@Slf4j
public class Bean1 {
private Bean2 bean2;
private Bean3 bean3;
private String home;
@Autowired
public void setBean2(Bean2 bean2) {
log.info("@Autowired 生效:{}", bean2);
this.bean2 = bean2;
}
@Resource
public void setBean3(Bean3 bean3) {
log.info("@Resource 生效:{}", bean3);
this.bean3 = bean3;
}
@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 生效");
}
@Override
public String toString() {
return "Bean1{" +
"bean2=" + bean2 +
", bean3=" + bean3 +
", home='" + home + '\'' +
'}';
}
}
public class Bean2 {
}
public class Bean3 {
}
在主方法中编写如下代码:
public class Test04Application {
public static void main(String[] args) {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
// 用原始方法注册3个bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
// 销毁容器
context.close();
}
}
结果:
19:56:42.453 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@75a1cd57
19:56:42.510 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
19:56:42.541 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
19:56:42.541 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
19:56:42.604 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@75a1cd57, started on Mon Apr 10 19:56:42 CST 2023
我们发现 GenericApplicationContext 因为没有添加对应的注解后处理器,因此Bean1的所有注解都没有生效。我们尝试在初始化容器之前往 context 中添加一些后处理器的 Bean:
public class Test04Application {
public static void main(String[] args) {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
// 用原始方法注册3个bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
// 添加后处理器bean
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 解决@Autowired注解的方法参数有@Value情况
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired 注解后处理器
context.registerBean(CommonAnnotationBeanPostProcessor.class); // 不仅会解析 @Resource,还会解析 @PostConstruct 和 @PreDestroy
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
// 销毁容器
context.close();
}
}
结果:
20:04:58.619 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@6f2b958e
20:04:58.655 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor'
20:04:58.678 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.CommonAnnotationBeanPostProcessor'
20:04:58.688 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
20:04:58.804 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
20:04:58.805 [main] INFO com.clp.test04.Bean1 - @Resource 生效:com.clp.test04.Bean3@e874448
20:04:58.814 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
20:04:58.831 [main] INFO com.clp.test04.Bean1 - @Value 生效:D:\jdks\jdk1.8
20:04:58.837 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
20:04:58.838 [main] INFO com.clp.test04.Bean1 - @Autowired 生效:com.clp.test04.Bean2@1fc2b765
20:04:58.838 [main] INFO com.clp.test04.Bean1 - @PostConstruct 生效
20:04:58.853 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@6f2b958e, started on Mon Apr 10 20:04:58 CST 2023
20:04:58.854 [main] INFO com.clp.test04.Bean1 - @PreDestroy 生效
Process finished with exit code 0
发现添加了注解后处理器后,在Bean1中定义的注解都生效了。下面演示 @ConfigurationProperties 的bean后处理器:
/**
* 下面这两个是java自带的环境变量,不用自己配置即可使用
* java.home
* java.version
*/
@ConfigurationProperties(prefix = "java") // 表示在 java.* 中查找匹配同名字段的属性并赋值
public class Bean4 {
private String home;
private String version;
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return "Bean4{" +
"home='" + home + '\'' +
", version='" + version + '\'' +
'}';
}
}
public class Test04Application {
public static void main(String[] args) {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
// 用原始方法注册3个bean
context.registerBean("bean4", Bean4.class);
// 添加后处理器bean
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory()); // 解析 @ConfigurationProperties
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
System.out.println(context.getBean(Bean4.class));
// 销毁容器
context.close();
}
}
结果:
...
Bean4{home='D:\jdks\jdk1.8\jre', version='1.8.0_301'}
...
4.2、@Autowired bean后处理器执行分析
代码演示1:
public class TestAutoWired {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 这里是直接往bean工厂中添加成品的bean
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 支持解析 @Value
// 1、查找哪些属性、方法添加了 @Autowired,这称之为 InjectionMetaData
AutowiredAnnotationBeanPostProcessor 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);
}
}
结果:
Bean1{bean2=null, bean3=null, home='null'}
11:21:53.821 [main] INFO com.clp.test04.Bean1 - @Value 生效:${JAVA_HOME}
11:21:53.831 [main] INFO com.clp.test04.Bean1 - @Autowired 生效:com.clp.test04.Bean2@4cf777e8
Bean1{bean2=com.clp.test04.Bean2@4cf777e8, bean3=null, home='${JAVA_HOME}'}
代码演示2:
@Slf4j
public class Bean1 {
private Bean2 bean2;
@Autowired
private Bean3 bean3;
private String home;
@Autowired
public void setBean2(Bean2 bean2) {
log.info("@Autowired 生效:{}", bean2);
this.bean2 = bean2;
}
@Resource
public void setBean3(Bean3 bean3) {
log.info("@Resource 生效:{}", bean3);
this.bean3 = bean3;
}
@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 生效");
}
@Override
public String toString() {
return "Bean1{" +
"bean2=" + bean2 +
", bean3=" + bean3 +
", home='" + home + '\'' +
'}';
}
}
public class Bean2 {
}
public class Bean3 {
}
public class TestAutoWired {
public static void main(String[] args) throws Throwable {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("bean2", new Bean2()); // 这里是直接往bean工厂中添加成品的bean
beanFactory.registerSingleton("bean3", new Bean3());
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // 支持解析 @Value
beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器
// 查找哪些属性、方法添加了 @Autowired,这称之为 InjectionMetaData
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(beanFactory); // 设置bean工厂,供processor使用
Bean1 bean1 = new Bean1();
// 后处理器处理 @Autowired @Value
// processor.postProcessProperties(null, bean1, "bean1");
// 1.1、下面是模拟 processor.postProcessProperties(null, bean1, "bean1") 的内部执行过程:
// 调用 processor 的 私有方法 : private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> beanClass, PropertyValues pvs)
// 该方法的功能:找到 BeanFactory 中哪些 Bean 中的哪些方法被 @Autowired 修饰,收集到 InjectionMetadata 中
Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod(
"findAutowiringMetadata", String.class, Class.class, PropertyValues.class
);
findAutowiringMetadata.setAccessible(true);
// 获取 Bean1 上加了 @Autowired 信息的成员变量、方法参数信息
InjectionMetadata injectionMetadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);
System.out.println(injectionMetadata); // org.springframework.beans.factory.annotation.InjectionMetadata@4883b407
// 1.2、调用 injectionMetadata.inject() 方法来进行依赖注入,注入时按类型查找值
// injectionMetadata.inject(bean1, "bean1", null);
// 下面是模拟 injectionMetadata.inject(bean1, "bean1", null); 进行依赖注入的过程
// 1.2.1、-------- 字段上加 @Autowired 的注入
// 如何按类型查找值
Field bean3Field = Bean1.class.getDeclaredField("bean3");
DependencyDescriptor dd1 = new DependencyDescriptor(bean3Field, false);
Object obj = beanFactory.doResolveDependency(dd1, null, null, null);
System.out.println(obj); // com.clp.test04.Bean3@31a5c39e
// 找到之后,通过 bean3Field.set() 进行赋值
System.out.println(bean1); // Bean1{bean2=null, bean3=null, home='null'}
bean3Field.setAccessible(true); //
bean3Field.set(bean1, obj);
System.out.println(bean1); // Bean1{bean2=null, bean3=com.clp.test04.Bean3@31a5c39e, home='null'}
// 1.2.2、-------- 方法上加 @Autowired 的注入
Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), true); // 表示 setBean2 方法的第 0 个参数
Object obj2 = beanFactory.doResolveDependency(dd2, null, null, null);
System.out.println(obj2); // com.clp.test04.Bean2@490ab905
System.out.println(bean1); // Bean1{bean2=null, bean3=com.clp.test04.Bean3@31a5c39e, home='null'}
setBean2.setAccessible(true);
setBean2.invoke(bean1, obj2);
System.out.println(bean1); // Bean1{bean2=com.clp.test04.Bean2@72d818d1, bean3=com.clp.test04.Bean3@490ab905, home='null'}
// 1.2.3、-------- 方法上加 @Autowired ,并且方法参数加 @Value 的注入
Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
Object obj3 = beanFactory.doResolveDependency(dd3, null, null, null);
System.out.println(obj3); // D:\jdks\jdk1.8
}
}
结果:
org.springframework.beans.factory.annotation.InjectionMetadata@1794d431
com.clp.test04.Bean3@490ab905
Bean1{bean2=null, bean3=null, home='null'}
Bean1{bean2=null, bean3=com.clp.test04.Bean3@490ab905, home='null'}
com.clp.test04.Bean2@72d818d1
Bean1{bean2=null, bean3=com.clp.test04.Bean3@490ab905, home='null'}
13:49:46.853 [main] INFO com.clp.test04.Bean1 - @Autowired 生效:com.clp.test04.Bean2@72d818d1
Bean1{bean2=com.clp.test04.Bean2@72d818d1, bean3=com.clp.test04.Bean3@490ab905, home='null'}
13:49:46.859 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
D:\jdks\jdk1.8
5、BeanFactory后处理器
5.1、代码演示
BeanFactory后处理器的作用:为BeanFactory提供扩展。
先创建如下代码:
// 在test05.component 包下:
@Component
public class Bean2 {
}
// 在test05包下:
public class Bean1 {
}
/**
* 一共5个bean:
* component包中的 Bean2 是一个Bean
* Config 自己是一个 Bean
* Config 类下的 3 个 @Bean
*/
@Configuration
@ComponentScan("com.clp.test05.component")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
预期一共创建了5个Bean,我们编写下面代码查看运行结果:
@Slf4j
public class Test05Application {
public static void main(String[] args) {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
结果:
config
发现只打印了config这个bean,也就是说@Bean、@ComponentScan的注解并没有生效。这时我们需要引入BeanFactory后处理器来帮我们对这些注解进行解析。
代码演示:
@Slf4j
public class Test05Application {
public static void main(String[] args) {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class); // 添加配置类后处理器,处理@ComponentScan、@Bean、@Import、@ImportResource
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
结果:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
bean2
bean1
dataSource
sqlSessionFactoryBean
添加两个Mapper接口:
// test05.mapper 包下
@Mapper
public interface Mapper1 {
}
public interface Mapper2 {
}
代码演示3:
@Slf4j
public class Test05Application {
public static void main(String[] args) {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class); // 添加配置类后处理器,处理@ComponentScan、@Bean、@Import、@ImportResource
context.registerBean(MapperScannerConfigurer.class, beanDefinition -> {
// 添加 Mapper 的扫描的类所在的包路径
beanDefinition.getPropertyValues().add("basePackage", "com.clp.test05.mapper");
}); // 相当于 @MapperScanner 的作用
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
结果:
...
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.mybatis.spring.mapper.MapperScannerConfigurer
bean2
bean1
dataSource
sqlSessionFactoryBean
mapper1
mapper2
...
5.2、自定义BeanFactory后处理器实现
实现@CompoentScan、@Component注解的Bean工厂后处理器代码演示:
// test05.component 包下
@Component
public class Bean3 {
}
// test05.component 包下
@Component
public class Bean2 {
}
// test05 包下
public class Bean1 {
}
// test05 包下
@Configuration
@ComponentScan("com.clp.test05.component")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
// test05 包下
/**
* 解析我们编写的 Config类的 @ComponetScan 的 BeanFactory后处理器实现
*/
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
/**
* 在 context 调用 refresh() 时会回调这个方法
* @param configurableListableBeanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
// 利用 AnnotationUtils 工具类判断 Config.class 类是否有 @ComponentScan 注解,如果有就返回它,没有返回null
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
// 获取注解上的 basePackages() 信息并遍历
for (String basePackage : componentScan.basePackages()) {
System.out.println(basePackage); // com.clp.test05.component
// 将 com.clp.test05.component 转换为 classpath*:com/clp/test05/component/**/*.class
String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
System.out.println(path); // classpath*:com/clp/test05/component/**/*.class
// // 获取 path下 对应的所有资源(使用ApplicationContext的ResourcePatternResolver接口功能)
// Resource[] resources = context.getResources(path);
// 这里无法获取 ApplicationContext,使用下面的方法获取资源
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
// 创建 MetadataReader 工厂,用来产生 MetadataReader
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// BeanNameGenerator 用来生成 Bean 的 name
BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
System.out.println(resource);
// file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean2.class]
// file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean3.class]
// file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean4.class]
// 工厂产生 resource 资源的对应 MetadataReader
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// 查看对应的信息
System.out.println("类名:" + classMetadata.getClassName());
System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
System.out.println("是否加了 @Component的派生注解(如@Controller):" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
// 构建 Bean 定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
// 判断 父BeanFactory 是否是 子BeanFactory
if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory beanFactory = ((DefaultListableBeanFactory) configurableListableBeanFactory);
// 生成 Bean 的名称
String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
// 将 Bean 定义注册到 Bean 工厂
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// test05 包下
@Slf4j
public class Test05Application {
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ComponentScanPostProcessor.class); // 注册我们自己定义的BeanFactory后处理器
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
结果:
com.clp.test05.component
classpath*:com/clp/test05/component/**/*.class
file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean2.class]
类名:com.clp.test05.component.Bean2
是否加了 @Component:true
是否加了 @Component的派生注解(如@Controller):false
file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean3.class]
类名:com.clp.test05.component.Bean3
是否加了 @Component:false
是否加了 @Component的派生注解(如@Controller):true
file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean4.class]
类名:com.clp.test05.component.Bean4
是否加了 @Component:false
是否加了 @Component的派生注解(如@Controller):false
15:10:56.918 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
15:10:56.919 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean2'
15:10:56.919 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
config
com.clp.test05.ComponentScanPostProcessor
bean2
bean3
15:10:56.955 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@156643d4, started on Tue Apr 11 15:10:56 CST 2023
Process finished with exit code 0
在上面的基础上,实现@Bean的Bean工厂后处理器代码演示:
/**
* 解析 @ComponetScan 的 BeanFactory后处理器实现
*/
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
/**
* 在 context 调用 refresh() 时会回调这个方法
* @param configurableListableBeanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
// 利用 AnnotationUtils 工具类判断 Config.class 类是否有 @ComponentScan 注解,如果有就返回它,没有返回null
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
// 获取注解上的 basePackages() 信息并遍历
for (String basePackage : componentScan.basePackages()) {
System.out.println(basePackage); // com.clp.test05.component
// 将 com.clp.test05.component 转换为 classpath*:com/clp/test05/component/**/*.class
String path = "classpath*:" + basePackage.replace(".", "/") + "/**/*.class";
System.out.println(path); // classpath*:com/clp/test05/component/**/*.class
// // 获取 path下 对应的所有资源(使用ApplicationContext的ResourcePatternResolver接口功能)
// Resource[] resources = context.getResources(path);
// 这里无法获取 ApplicationContext,使用下面的方法获取资源
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
// 创建 MetadataReader 工厂,用来产生 MetadataReader
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// BeanNameGenerator 用来生成 Bean 的 name
BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
for (Resource resource : resources) {
System.out.println(resource);
// file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean2.class]
// file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean3.class]
// file [G:\Study\Java\java-study\spring-source-study\target\classes\com\clp\test05\component\Bean4.class]
// 工厂产生 resource 资源的对应 MetadataReader
MetadataReader reader = factory.getMetadataReader(resource);
ClassMetadata classMetadata = reader.getClassMetadata();
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
// 查看对应的信息
System.out.println("类名:" + classMetadata.getClassName());
System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
System.out.println("是否加了 @Component的派生注解(如@Controller):" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
// 构建 Bean 定义
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
// 判断 父BeanFactory 是否是 子BeanFactory
if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory beanFactory = ((DefaultListableBeanFactory) configurableListableBeanFactory);
// 生成 Bean 的名称
String beanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
// 将 Bean 定义注册到 Bean 工厂
beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Slf4j
public class Test05Application {
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(AtBeanPostProcessor.class); // 添加 BeanFactory 后处理器
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
结果:
com.clp.test05.Config.bean1()
com.clp.test05.Config.bean2()
com.clp.test05.Config.dataSource()
com.clp.test05.Config.sqlSessionFactoryBean(javax.sql.DataSource)
...
config
bean1
bean2
dataSource
sqlSessionFactoryBean
...
Spring将MyBatis的Mapper实现类对象加入到容器中的内部如下:
@Configuration
@ComponentScan("com.clp.test05.component")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
/**
* Spring 内部实现注入 ByBatis Mapper 对象的方式(即不通过扫描 @Mapper 注解方式):
*/
// @Bean
// public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
// MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);
// factoryBean.setSqlSessionFactory(sqlSessionFactory);
// return factoryBean;
// }
//
// @Bean
// public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
// MapperFactoryBean<Mapper2> factoryBean = new MapperFactoryBean<>(Mapper2.class);
// factoryBean.setSqlSessionFactory(sqlSessionFactory);
// return factoryBean;
// }
}
我们自定义BeanFactory后处理来简化这一过程:
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:com/clp/test05/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 beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
.addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) // 设置自动装配模式
.getBeanDefinition();
// 将 BeanDefinition 注册到 Bean工厂
// 这里新生成了一个BeanDefinition,仅仅是为了生成一个不同的名字
AbstractBeanDefinition tempBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String beanName = generator.generateBeanName(tempBeanDefinition, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
@Slf4j
public class Test05Application {
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个 【干净】 的容器(没有额外的内置bean以及后处理器)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(AtBeanPostProcessor.class); // 添加 BeanFactory 后处理器处理 @Bean 注解
context.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口
// 初始化容器
context.refresh(); // 执行beanFactory后处理器,添加bean后处理器,初始化所有单例
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name + " - " + context.getBean(name));
}
// 销毁容器
context.close();
}
}
结果:
com.clp.test05.Config.bean1()
com.clp.test05.Config.bean2()
com.clp.test05.Config.dataSource()
com.clp.test05.Config.sqlSessionFactoryBean(javax.sql.DataSource)
...
config - com.clp.test05.Config@65fb9ffc
com.clp.test05.AtBeanPostProcessor - com.clp.test05.AtBeanPostProcessor@3e694b3f
com.clp.test05.MapperPostProcessor - com.clp.test05.MapperPostProcessor@1bb5a082
mapper1 - org.apache.ibatis.binding.MapperProxy@639c2c1d
mapper2 - org.apache.ibatis.binding.MapperProxy@443118b0
bean1 - com.clp.test05.Bean1@765d7657
bean2 - com.clp.test05.component.Bean2@74235045
dataSource - {
CreateTime:"2023-04-11 16:26:05",
ActiveCount:0,
PoolingCount:0,
CreateCount:0,
DestroyCount:0,
CloseCount:0,
ConnectCount:0,
Connections:[
]
}
sqlSessionFactoryBean - org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@618b19ad
6、Aware接口 & InitializingBean接口
Aware 和 InitializingBean 接口:
- Aware接口提供了一种【内置】的注入手段,可以注入BeanFactory、ApplicationContext。
- InitializingBean接口提供了一种【内置】的初始化手段。
- 内置的注入和初始化不受扩展功能的影响,总会被执行,因此Spring框架的内部的类常用它们。
Aware 接口用于注入一些与容器相关的信息,例如:
- BeanNameAware 注入 Bean 的名字
- BeanFactoryAware 注入 BeanFactory 容器
- ApplicationContextAware 注入 ApplicationContext 容器
- EmbeddedValueResolverAware ${}
为什么不用@Autowired实现后面3个功能?
- @Autowired的解析需要用到Bean后处理器,属于扩展功能;
- 而Aware接口属于内置功能,不加任何扩展,Spring就能识别。
某些情况下,扩展功能会失效,而内置功能不会失效。
例1:你会发现用 Aware 注入 ApplicationContext 成功,而 @AutoWired 注入 ApplicationContext 失败(我自己尝试了一下,却发现是可以注入的...)。
代码演示:
/**
* 实现的接口执行顺序是从左到右
*/
@Slf4j
public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
@Override
public void setBeanName(String name) {
log.info("当前的Bean " + this + " 名字叫:" + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("当前的Bean " + this + " 基于实现 ApplicationContextAware 获取的容器是:" + applicationContext);
}
@Autowired
public void aaa(ApplicationContext applicationContext) {
log.info("当前的Bean " + this + " 基于 @Autowired 注入的容器是:" + applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("当前的Bean " + this + " 基于实现 InitializingBean 初始化");
}
@PostConstruct
public void init() {
log.info("当前的Bean " + this + " 基于 @PostConstruct 初始化");
}
}
@Slf4j
public class Test06Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("myBean", MyBean.class);
// 这里不加任何Bean后处理器扩展,导致 @Autowired、@PostConstruct 失效,但是基于实现接口依然生效
// 初始化容器
context.refresh();
// 关闭
context.close();
}
}
结果:
16:59:00.464 [main] INFO com.clp.test06.MyBean - 当前的Bean com.clp.test06.MyBean@57fffcd7 名字叫:myBean
16:59:00.467 [main] INFO com.clp.test06.MyBean - 当前的Bean com.clp.test06.MyBean@57fffcd7 基于实现 ApplicationContextAware 获取的容器是:org.springframework.context.support.GenericApplicationContext@156643d4, started on Tue Apr 11 16:59:00 CST 2023
16:59:00.467 [main] INFO com.clp.test06.MyBean - 当前的Bean com.clp.test06.MyBean@57fffcd7 基于实现 InitializingBean 初始化
例2:Java 配置类在添加了 Bean 工厂后处理器后,你会发现用传统接口方式的注入和初始化依然生效,但是@Autowired 和 @PostConstruct 的注入和初始化失效。
代码演示1 - 失效情况:
@Slf4j
@Configuration
public class MyConfig1 {
@Autowired
public void setApplicationContext(ApplicationContext context) {
log.info("注入 ApplicationContext");
}
@PostConstruct
public void init() {
log.info("初始化");
}
/**
* 添加 BeanFactory 后处理器
*/
@Bean
public BeanFactoryPostProcessor processor1() {
return beanFactory -> log.info("执行 processor1");
}
}
@Slf4j
public class Test06Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// context.registerBean("myBean", MyBean.class);
// 这里不加任何Bean后处理器扩展,导致 @Autowired、@PostConstruct 失效,但是基于实现接口依然生效
context.registerBean("myConfig1", MyConfig1.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.registerBean(ConfigurationClassPostProcessor.class);
// 初始化容器
context.refresh();
/**
* context.refresh()内部的执行顺序:
* 1、BeanFactory后处理器 补充 BeanDefinition
* 2、注册 Bean 后处理器
* 3、初始化单例
* 4、依赖注入扩展(如 @Value、@Autowired) 以及 初始化扩展(如 @PostConstruct),会回调2中的Bean后处理器
* 5、执行 Aware 和 InitializingBean
* 6、创建成功
*/
// Java 配置类包含 BeanFactoryPostProcessor的情况,因此要创建其中的BeanFactoryPostProcessor必须提前创建Java配置类,
// 而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效
// 关闭
context.close();
}
}
结果:
...
17:24:07.347 [main] INFO com.clp.test06.MyConfig1 - 执行 processor1
...
代码演示2 - 问题解决:
@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");
}
/**
* 添加 BeanFactory 后处理器
*/
@Bean
public BeanFactoryPostProcessor processor2() {
return beanFactory -> log.info("执行 processor2");
}
}
@Slf4j
public class Test06Application {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// context.registerBean("myBean", MyBean.class);
// 这里不加任何Bean后处理器扩展,导致 @Autowired、@PostConstruct 失效,但是基于实现接口依然生效
context.registerBean("myConfig2", MyConfig2.class);
context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
context.registerBean(CommonAnnotationBeanPostProcessor.class);
context.registerBean(ConfigurationClassPostProcessor.class);
// 初始化容器
context.refresh();
// 关闭
context.close();
}
}
结果:
...
17:25:08.833 [main] INFO com.clp.test06.MyConfig2 - 注入 ApplicationContext
17:25:08.834 [main] INFO com.clp.test06.MyConfig2 - 初始化
...
17:25:08.850 [main] INFO com.clp.test06.MyConfig2 - 执行 processor2
7、初始化与销毁
代码演示:
/**
* 初始化方法执行顺序:从上到下
*/
@Slf4j
public class Bean1 implements InitializingBean, BeanFactoryAware, ApplicationContextAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("注入 BeanFactory");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("注入 ApplicationContext");
}
@PostConstruct
public void init1() {
log.info("初始化1");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("初始化2");
}
private void init3() {
log.info("初始化3");
}
}
@Slf4j
public class Bean2 implements DisposableBean {
@PreDestroy
public void destroy1() {
log.info("销毁1");
}
@Override
public void destroy() throws Exception {
log.info("销毁2");
}
private void destroy3() {
log.info("销毁3");
}
}
@SpringBootApplication
public class Test07Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test07Application.class, args);
context.close();
}
@Bean(initMethod = "init3")
public Bean1 bean1() {
return new Bean1();
}
@Bean(destroyMethod = "destroy3")
public Bean2 bean2() {
return new Bean2();
}
}
结果:
2023-04-11 17:38:09.087 INFO 41328 --- [ main] com.clp.test07.Bean1 : 注入 BeanFactory
2023-04-11 17:38:09.088 INFO 41328 --- [ main] com.clp.test07.Bean1 : 注入 ApplicationContext
2023-04-11 17:38:09.089 INFO 41880 --- [ main] com.clp.test07.Bean1 : 初始化1
2023-04-11 17:38:09.090 INFO 41880 --- [ main] com.clp.test07.Bean1 : 初始化2
2023-04-11 17:38:09.090 INFO 41880 --- [ main] com.clp.test07.Bean1 : 初始化3
...
2023-04-11 17:38:09.366 INFO 41880 --- [ main] com.clp.test07.Bean2 : 销毁1
2023-04-11 17:38:09.367 INFO 41880 --- [ main] com.clp.test07.Bean2 : 销毁2
2023-04-11 17:38:09.367 INFO 41880 --- [ main] com.clp.test07.Bean2 : 销毁3
8、Scope
8.1、代码演示
代码演示:
@Slf4j
@Scope("request")
@Component
public class BeanForRequest {
@PreDestroy
public void destroy() {
log.info("destroy");
}
}
@Slf4j
@Scope("session")
@Component
public class BeanForSession {
@PreDestroy
public void destroy() {
log.info("destroy");
}
}
@Slf4j
@Scope("application")
@Component
public class BeanForApplication {
@PreDestroy
public void destroy() {
log.info("destroy");
}
}
@RestController
public class MyController {
@Lazy
@Autowired
private BeanForRequest beanForRequest;
@Lazy
@Autowired
private BeanForSession beanForSession;
@Lazy
@Autowired
private BeanForApplication beanForApplication;
@GetMapping(value = "/test", produces = "text/html")
public String test(HttpServletRequest request, HttpSession session) {
ServletContext sc = request.getServletContext();
String str =
"<ul>" +
"<li>" + "request scope: " + beanForRequest + "</li>" +
"<li>" + "session scope: " + beanForSession + "</li>" +
"<li>" + "application scope: " + beanForApplication + "</li>" +
"</ul>";
return str;
}
}
/**
* singleton、prototype、request、session、application
* jdk >= 9 ,如果反射调用 jdk 中方法,会报 IllegalAccessException
* 解决:运行时添加参数:--add-opens java.base/java.lang=ALL-UNNAMED
* jdk <= 8 ,不会有这个问题
*/
@SpringBootApplication
public class Test08Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test08Application.class);
}
}
在火狐浏览器访问:localhost:8080/test
request scope: com.clp.test08.BeanForRequest@5fc14f91
session scope: com.clp.test08.BeanForSession@75e2f23a
application scope: com.clp.test08.BeanForApplication@736c9214
每次刷新页面,request scope 一行的 BeanForRequest 的地址都会发生改变,session行 和 application行 不会
在谷歌浏览器访问:localhost:8080/test
request scope: com.clp.test08.BeanForRequest@41bb6af1
session scope: com.clp.test08.BeanForSession@8660ca
application scope: com.clp.test08.BeanForApplication@736c9214
每次刷新页面,request scope 一行的 BeanForRequest 的地址都会发生改变,session行 和 application行 不会
根据演示结果可以发现,同一个浏览器不断刷新页面,request 域的 BeanForRequest 对象都不一样,session域 和 application域 的对象都不变;不同浏览器的 request
域 和 session域 不一样,但 application 域是一样的。
在 application.properties 中设置session超时时间:
# 设置session的失效时间
server.servlet.session.timeout=10s
日志打印如下:
...
2023-04-12 11:03:27.679 INFO 35420 --- [alina-utility-2] com.clp.test08.BeanForSession : destroy
...
说明存放在Session域的BeanForSession对象被销毁了。
8.2、Scope 失效问题
代码演示:
@Scope("prototype")
@Component
public class F1 {
}
/**
* 默认为单例
*/
@Component
public class E {
@Autowired
private F1 f1;
public F1 getF1() {
return f1;
}
}
@Slf4j
@ComponentScan("com.clp.test08")
public class Test08Application2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test08Application2.class);
E e = context.getBean(E.class);
log.info("{}", e.getF1());
log.info("{}", e.getF1());
log.info("{}", e.getF1());
context.close();
}
}
结果:
...
11:13:18.869 [main] INFO com.clp.test08.Test08Application2 - com.clp.test08.F1@339bf286
11:13:18.870 [main] INFO com.clp.test08.Test08Application2 - com.clp.test08.F1@339bf286
11:13:18.870 [main] INFO com.clp.test08.Test08Application2 - com.clp.test08.F1@339bf286
...
我们期望e每次获取到的F1都是不一样的,但是打印的结果都是同一个F1,也就是F1的@Scope失效了。
原因:对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的F1,因此E用的始终是第一次依赖注入的F1。
解决:
- 仍然使用@Lazy生成代理;
- 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的F1对象。
代码演示 - 解决:
@Scope("prototype")
@Component
public class F1 {
}
/**
* 解决方式2:在@Scope添加proxyMode属性
*/
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
@Scope("prototype")
@Component
public class F3 {
}
@Scope("prototype")
@Component
public class F4 {
}
/**
* 默认为单例
*/
@Component
public class E {
/**
* 解决方式 1:添加 @Lazy 注解
*/
@Lazy
@Autowired
private F1 f1;
@Autowired
private F2 f2;
/**
* 解决方式3:注入 F3 的对象工厂,在获取 F3 使通过 f3Factory 获取 F3 对象返回
*/
@Autowired
private ObjectFactory<F3> f3Factory;
/**
* 解决方式3:注入 applicationContext 容器,通过 getBean() 方法获取多例
*/
@Autowired
private ApplicationContext applicationContext;
public F1 getF1() {
return f1;
}
public F2 getF2() {
return f2;
}
public F3 getF3() {
return f3Factory.getObject();
}
public F4 getF4() {
return applicationContext.getBean(F4.class);
}
}
@Slf4j
@ComponentScan("com.clp.test08")
public class Test08Application2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test08Application2.class);
E e = context.getBean(E.class);
// 解决方式1(@Lazy):E 始终使用同一个 F1代理类,但每次调用 getF1() 方法 代理类代理内部都会有一个不同的 F1 对象
log.info("{}, {}", e.getF1().getClass(), e.getF1());
log.info("{}, {}", e.getF1().getClass(), e.getF1());
log.info("{}, {}", e.getF1().getClass(), e.getF1());
log.info("\n");
// 解决方式2:@Scope添加proxyMode属性
log.info("{}, {}", e.getF2().getClass(), e.getF2());
log.info("{}, {}", e.getF2().getClass(), e.getF2());
log.info("{}, {}", e.getF2().getClass(), e.getF2());
log.info("\n");
// 解决方式3:注入 F3 的对象工厂,在获取 F3 使通过 f3Factory 获取 F3 对象返回
log.info("{}, {}", e.getF3().getClass(), e.getF3());
log.info("{}, {}", e.getF3().getClass(), e.getF3());
log.info("{}, {}", e.getF3().getClass(), e.getF3());
log.info("\n");
// 解决方式4:注入 applicationContext 容器,通过 getBean() 方法获取多例
log.info("{}, {}", e.getF4().getClass(), e.getF4());
log.info("{}, {}", e.getF4().getClass(), e.getF4());
log.info("{}, {}", e.getF4().getClass(), e.getF4());
log.info("\n");
context.close();
}
}
结果:
...
11:39:23.792 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F1$$EnhancerBySpringCGLIB$$27c62a72, com.clp.test08.F1@1bc715b8
11:39:23.796 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F1$$EnhancerBySpringCGLIB$$27c62a72, com.clp.test08.F1@1c55f277
11:39:23.796 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F1$$EnhancerBySpringCGLIB$$27c62a72, com.clp.test08.F1@3e8f7922
11:39:23.796 [main] INFO com.clp.test08.Test08Application2 -
11:39:23.796 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F2$$EnhancerBySpringCGLIB$$761f5b9, com.clp.test08.F2@58e6d4b8
11:39:23.797 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F2$$EnhancerBySpringCGLIB$$761f5b9, com.clp.test08.F2@1de5f0ef
11:39:23.797 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F2$$EnhancerBySpringCGLIB$$761f5b9, com.clp.test08.F2@376a312c
11:39:23.797 [main] INFO com.clp.test08.Test08Application2 -
11:39:23.798 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F3, com.clp.test08.F3@5ef0d29e
11:39:23.798 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F3, com.clp.test08.F3@3ce3db41
11:39:23.798 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F3, com.clp.test08.F3@e260766
11:39:23.798 [main] INFO com.clp.test08.Test08Application2 -
11:39:23.800 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F4, com.clp.test08.F4@34a97744
11:39:23.800 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F4, com.clp.test08.F4@4275c20c
11:39:23.800 [main] INFO com.clp.test08.Test08Application2 - class com.clp.test08.F4, com.clp.test08.F4@7c56e013
11:39:23.800 [main] INFO com.clp.test08.Test08Application2 -
...
9、AOP
9.1、AOP实现之 ajc 编译器
使用ajc编译器实现代理增强。不常用。
@Slf4j
@Aspect
public class MyAspect {
@Before("execution(* com.clp.test09.service.MyService.*())")
public void before() {
log.info("before()");
}
}
@Slf4j
@Service
public class MyService {
public final void foo() {
log.info("foo()");
bar();
}
public void bar() {
log.info("bar()");
}
}
@Slf4j
@SpringBootApplication
public class Test09Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test09Application.class, args);
MyService service = context.getBean(MyService.class);
log.info("service class: {}", service.getClass());
service.foo();
context.close();
}
}
9.2、AOP 实现之 agent 类加载
在类加载阶段进行增强。
注意几点:
1、版本选择了 Java 8,因为目前的aspectj-maven-plugin 1.14.0 最高只支持 Java 16
2、运行时需要在 VM options 里加入 javaagent:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 C:/Users/manyh/.m2/repository 改为你自己的 maven 仓库起始地址
代码同9.1。
9.3、AOP 实现之 proxy —— jdk 动态代理
9.3.1、JDK 动态代理实现
/**
* jdk 动态代理:只能针对接口进行代理,cglib没有这个限制
* 注意的几点:
* 1、Target 和 Proxy 是兄弟关系(都实现了Foo接口)
* 2、Target 可以是 final 的
*/
public class JdkProxyTest {
interface Foo {
void foo();
}
static class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] args) {
// 准备目标对象
Target target = new Target();
ClassLoader loader = JdkProxyTest.class.getClassLoader(); // 用来加载在运行期间动态生成的字节码
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
// 反射调用
return method.invoke(target, args); // 让代理也返回方法执行结果
}
});
proxy.foo();
}
}
结果:
before
target foo
9.3.2、jdk 动态代理实现原理
/**
* jdk 动态代理实现原理(模拟实现)
*/
public class JdkProxyPrinciple {
interface Foo {
void foo();
int bar();
}
static class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo()");
}
@Override
public int bar() {
System.out.println("target bar()");
return 100;
}
}
// 模拟代理类实现
static class $Proxy0 extends Proxy implements Foo {
private static Method foo;
private static Method bar;
static {
try {
foo = Foo.class.getMethod("foo");
bar = Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
public $Proxy0(InvocationHandler h) {
super(h);
}
/**
* 这里异常抛出分为2类:运行时异常,直接抛出;受检异常,转换成运行时异常再抛出
*/
@Override
public void foo() {
try {
h.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public int bar() {
try {
Object result = h.invoke(this, bar, new Object[0]);
return ((int) result);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
// 1、实现功能的增强
System.out.println("before...");
// 2、调用目标
return method.invoke(new Target(), args);
}
});
proxy.foo();
proxy.bar();
}
}
结果:
before...
target foo()
before...
target bar()
9.3、AOP 实现之 proxy —— cglib 动态代理
9.3.1、cglib 动态代理实现
/**
* cglib 动态代理
* 注意:
* 1、Target 是父类型, proxy 是子类型
* 2、Target 不能为 final
* 3、Target 的方法不能为 final (private方法默认为final),如果为final,不会报错,但是该方法不会被代理
*/
public class CglibProxyTest {
static class Target {
public void foo() {
System.out.println("target foo()");
}
public final void finalFoo() {
System.out.println("target finalFoo()");
}
}
public static void main(String[] args) {
// 准备目标对象
Target target = new Target();
Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
/**
*
* @param o 代理对象
* @param method 方法
* @param objects 方法参数
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before ...");
// Object result = method.invoke(target, objects); // 用方法反射调用目标对象
// Object result = methodProxy.invoke(target, args); // MethodProxy 可以避免反射调用目标,内部没有用反射,需要目标才能调用(Spring使用的方式)
Object result = methodProxy.invokeSuper(o, args);// MethodProxy 可以避免反射调用目标,内部没有用反射,不需要目标,需要代理对象自身才能调用
System.out.println("after ...");
return result;
}
});
proxy.foo();
System.out.println("-------------");
proxy.finalFoo();
}
}
结果:
before ...
target foo()
after ...
-------------
target finalFoo()
9.3.2、cglib 动态代理实现原理
/**
* cglib 动态代理实现原理(模拟)
*/
public class CglibProxyPrinciple {
static class Target {
public void save() {
System.out.println("save()");
}
public void save(int i) {
System.out.println("save(int)");
}
public void save(long l) {
System.out.println("save(long)");
}
}
static class Proxy extends Target {
private static Method save0;
private static Method save1;
private static Method save2;
private static MethodProxy save0Proxy;
private static MethodProxy save1Proxy;
private static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getMethod("save");
save0 = Target.class.getMethod("save", int.class);
save0 = Target.class.getMethod("save", long.class);
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
private MethodInterceptor interceptor;
public void setMethodInterceptor(MethodInterceptor interceptor) {
this.interceptor = interceptor;
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long l) {
super.save(l);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@Override
public void save() {
try {
interceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
interceptor.intercept(this, save0, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long l) {
try {
interceptor.intercept(this, save0, new Object[]{l}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
public static void main(String[] args) {
Target target = new Target();
Proxy proxy = new Proxy();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
// return method.invoke(target, args);
// return methodProxy.invoke(target, args); // 内部没有反射,需要结合目标对象
return methodProxy.invokeSuper(proxy, args); // 内部没有反射,不需要目标对象,仅需要代理对象自身
}
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}
结果:
before
save()
before
save(int)
before
save(long)
9.4、jdk 和 cglib 的统一
P46
P17
标签:Spring,高级,class,Bean,context,com,public,clp From: https://www.cnblogs.com/aoe1231/p/17297007.html