文章目录
一句话:bean工厂后置处理器就是对bean实例化前的beanDefinition的注册和修改
1、BeanFactoryPostProcessor的执行时机和作用
准备一个bean对象
@Component
@Slf4j
public class LifeCycleBean {
public LifeCycleBean() {
log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>构造");
}
@PostConstruct
public void init() {
log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>初始化");
}
@PreDestroy
public void destroy() {
log.debug("bean生命周期>>>>>>>>>>>>>>>>>>>销毁");
}
}
bean后置处理器,bean初始化前后执行
@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("初始化后置处理器>>>>>>> 初始化之前执行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean"))
log.debug("初始化后置处理器>>>>>>> 初始化之后执行");
return bean;
}
}
启动类:
@SpringBootApplication
public class Test {
public static void main(String[] args) throws IOException {
//创建容器
ConfigurableApplicationContext context = SpringApplication.run(Test.class, args);
//关闭容器
context.close();
}
}
1.1、BeanFactoryPostProcessor-bean工厂定义后置处理器
- bean工厂后置处理器核心接口
- 执行时机: 在bean调用构造函数,init方法,bean后处理器执行之前执行
- 方法作用: 主要用来对bean定义做一些改变
自定义bean工厂后置处理器:
@Component
@Slf4j
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for(String name:beanFactory.getBeanDefinitionNames()) {
if("lifeCycleBean".equals(name)) {
log.debug("BeanFactoryPostProcessor-->bean工厂后置处理器>>>>>>> bean定义之后,实例化之前执行");
}
}
}
}
1.2、BeanDefinitionRegistryPostProcessor-bean工厂定义注册后置处理器
- BeanFactoryPostProcessor工厂后置处理器子接口
- 执行时机: 在bean调用构造函数,init方法,bean后处理器执行,BeanFactoryPostProcessor-bean工厂后置处理器之前执行
- 方法作用: 用来注册更多的bean到spring容器
自定义bean工厂后置处理器:
@Component
@Slf4j
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for(String name:beanFactory.getBeanDefinitionNames()) {
if("lifeCycleBean".equals(name)) {
log.debug("BeanDefinitionRegistryPostProcessor-->bean工厂后置处理器>>>>>>> bean定义之后,实例化之前执行,对bean做一些改变");
}
}
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
for(String name:beanDefinitionRegistry.getBeanDefinitionNames()) {
if("lifeCycleBean".equals(name)) {
log.debug("BeanDefinitionRegistryPostProcessor-->bean工厂后置处理器>>>>>>> bean定义之后,实例化之前执行,注册更多的bean到容器");
}
}
}
}
生命周期bean添加bean工厂后置处理器后输出结果:
[INFO ] 22:09:09.210 [main] o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
[INFO ] 22:09:09.234 [main] o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
[INFO ] 22:09:09.236 [main] o.a.catalina.core.StandardService - Starting service [Tomcat]
[INFO ] 22:09:09.236 [main] o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.53]
[INFO ] 22:09:09.413 [main] o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
[INFO ] 22:09:09.413 [main] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 3704 ms
[DEBUG] 22:09:09.557 [main] c.xc.a05.beanfactory.LifeCycleBean - bean生命周期>>>>>>>>>>>>>>>>>>>构造
[DEBUG] 22:09:09.559 [main] c.x.a.b.MyBeanPostProcessor - 初始化后置处理器>>>>>>> 初始化之前执行
[DEBUG] 22:09:09.560 [main] c.xc.a05.beanfactory.LifeCycleBean - bean生命周期>>>>>>>>>>>>>>>>>>>初始化
[DEBUG] 22:09:09.560 [main] c.x.a.b.MyBeanPostProcessor - 初始化后置处理器>>>>>>> 初始化之后执行
[INFO ] 22:09:09.995 [main] o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page: class path resource [static/index.html]
[INFO ] 22:09:10.249 [main] c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource
[INFO ] 22:09:10.584 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} inited
[INFO ] 22:09:11.129 [main] o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
[INFO ] 22:09:11.184 [main] o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[INFO ] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
[TRACE] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet - Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@4f7be6c8
[TRACE] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet - Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@647b9364
[TRACE] 22:09:11.184 [main] o.s.web.servlet.DispatcherServlet - Detected org.springframework.web.servlet.theme.FixedThemeResolver@b6bccb4
[TRACE] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet - Detected DefaultRequestToViewNameTranslator
[TRACE] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet - Detected SessionFlashMapManager
[DEBUG] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
[INFO ] 22:09:11.188 [main] o.s.web.servlet.DispatcherServlet - Completed initialization in 4 ms
[INFO ] 22:09:11.192 [main] o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
[INFO ] 22:09:11.220 [main] com.xc.a05.beanfactory.Test - Started Test in 6.616 seconds (JVM running for 8.194)
[INFO ] 22:09:12.418 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} closing ...
[INFO ] 22:09:12.420 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} closed
[DEBUG] 22:09:12.422 [main] c.xc.a05.beanfactory.LifeCycleBean - bean生命周期>>>>>>>>>>>>>>>>>>>销毁
Disconnected from the target VM, address: '127.0.0.1:58089', transport: 'socket'
Process finished with exit code 0
2、常用Bean工厂后置处理器实现
配置类:
@Configuration
@ComponentScan("com.xc.a05.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("root");
return dataSource;
}
}
bean:
@Slf4j
public class Bean1 {
public Bean1() {
log.debug("我被 Spring 管理啦");
}
}
com.xc.a05.component包下的bean:
@Component
@Slf4j
public class Bean2 {
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
@Controller
@Slf4j
public class Bean3 {
public Bean3() {
log.debug("我被 Spring 管理啦");
}
}
@Slf4j
public class Bean4 {
public Bean4() {
log.debug("我被 Spring 管理啦");
}
}
启动类:干净的容器则为任务后置处理器,什么都没有
public class A05 {
public static void main(String[] args) throws IOException {
// GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
// 初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
输出结果:Config配置类@ComponentScan和@Bean都没有被扫描
config
Process finished with exit code 0
2.1、ConfigurationClassPostProcessor-bean工厂后置处理器实现类
添加此bean工厂后置处理器:
public class A05 {
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.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// 销毁容器
context.close();
}
}
输出结果:
[DEBUG] 23:05:41.352 [main] com.xc.a05.component.Bean2 - 我被 Spring 管理啦
[DEBUG] 23:05:41.362 [main] com.xc.a05.component.Bean3 - 我被 Spring 管理啦
[DEBUG] 23:05:41.378 [main] com.xc.a05.Bean1 - 我被 Spring 管理啦
[INFO ] 23:05:41.584 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} inited
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
bean2
bean3
bean1
dataSource
[INFO ] 23:05:41.637 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} closing ...
[INFO ] 23:05:41.638 [main] c.a.druid.pool.DruidDataSource - {dataSource-1} closed
Process finished with exit code 0
解析@ComponentScan @Bean @Import @ImportResource注解添加bean定义
2.1.1、手写代码-解析@ComponentScan
- 这是一个手写模拟解析@ComponentScan的bean工厂后置处理器
- 因为是注册添加bean定义所有在postProcessBeanDefinitionRegistry,修改bean定义则为postProcessBeanFactory
- 只需要将此自定义后置处理器与ConfigurationClassPostProcessor一样加载到bean容器即可
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//获取配置类的@ComponentScan注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
if (componentScan != null) {
//遍历@ComponentScan注解下多个包路径进行操作
for (String p : componentScan.basePackages()) {
System.out.println("包路径:"+p);
// 将包路径转换为类路径
// com.itheima.a05.component -> classpath*:com/itheima/a05/component/**/*.class
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
System.out.println("类路径:"+path);
//读取类class元数据信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
// 通过类路径获取class文件
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
for (Resource resource : resources) {
System.out.println("class资源文件:"+resource);
// 获取类class详细信息
MetadataReader reader = factory.getMetadataReader(resource);
System.out.println("类名:" + reader.getClassMetadata().getClassName());
AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
// 如果加了@Component或其派生注解
if (annotationMetadata.hasAnnotation(Component.class.getName())
|| annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
// 创建bean定义
BeanDefinition bd = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName())
.getBeanDefinition();
// 通过bean定义创建bean名称
String name = generator.generateBeanName(bd, beanFactory);
// 将bean添加到bean容器
beanFactory.registerBeanDefinition(name, bd);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.1.2、手写代码-解析@Bean
- 这是一个手写模拟解析@Bean的bean工厂后置处理器
- 因为是注册添加bean定义所有在postProcessBeanDefinitionRegistry,修改bean定义则为postProcessBeanFactory
- 只需要将此自定义后置处理器与ConfigurationClassPostProcessor一样加载到bean容器即可
public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
try {
//读取类class元数据信息
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//获取class类资源
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/xc/a05/Config.class"));
//1、获取class类注解信息 2、获取被@Bean注解标注的信息
Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata method : methods) {
System.out.println("被@Bean标注方法:"+method);
//创建bean定义
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//获取工厂对象和工厂方法
builder.setFactoryMethodOnBean(method.getMethodName(), "config");
//解析@Bean上面的初始化方法
String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
if (initMethod.length() > 0) {
builder.setInitMethodName(initMethod);
}
BeanDefinition bd = builder.getBeanDefinition();
beanFactory.registerBeanDefinition(method.getMethodName(), bd);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2、MapperScannerConfigurer-bean工厂后置处理器实现类
扫描解析@Mapper注解接口,原理与解析@ComponentScan类似
总结:解析@ComponentScan, @Bean, @Mapper 等注解为bean容器注册添加bean定义==
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/148604.html