Spring 高级 工厂后处理器模拟实现-@Bean
一、初步扫描到所有被@Bean注解标注的方法的信息
package com.mangoubiubiu.show.a05; import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.JmxEnabled; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.stereotype.Component; import java.awt.*; import java.io.IOException; import java.util.Set; @Slf4j public class A05Application { private MetadataReader reader; public static void main(String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); //将config注册到容器里面 context.registerBean("config",Config.class); // context.registerBean(ComponentScanPostProcessor.class); // context.registerBean(MapperScannerConfigurer.class,beanDefinition -> { // beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper"); // }); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); //不走类加载 效率比反射高 //先读取Config.class类的信息 MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class")); //拿到所有被@Bean注解标注的方法的信息 Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata method:annotatedMethods) { System.out.println(method); } //初始化容器 context.refresh(); for (String name: context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
package com.mangoubiubiu.show.a05; import com.alibaba.druid.pool.DruidDataSource; import com.mangoubiubiu.show.a05.component.Bean2; 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.mangoubiubiu.show.a05.component") public class Config { @Bean public Bean1 bean1(){ return new Bean1(); } public Bean2 bean2(){ return new Bean2(); } @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } @Bean(initMethod = "init") public DruidDataSource druidDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/zeal_user"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } }
成功拿到config被@Bean 标注的方法信息。
二、进阶拿到BeanDefinition将相应的bean 注入到ioc容器里面
package com.mangoubiubiu.show.a05; import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.JmxEnabled; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.stereotype.Component; import java.awt.*; import java.io.IOException; import java.util.Set; @Slf4j public class A05Application { private MetadataReader reader; public static void main(String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); //将config注册到容器里面 context.registerBean("config",Config.class); // context.registerBean(ComponentScanPostProcessor.class); // context.registerBean(MapperScannerConfigurer.class,beanDefinition -> { // beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper"); // }); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); //不走类加载 效率比反射高 //先读取Config.class类的信息 MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class")); //拿到所有被@Bean注解标注的方法的信息 Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata method:annotatedMethods) { System.out.println(method); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); //第一个参数 method.getMethodName() 获取方法名,哪个方法作为工厂方法 第二个参数 工厂对象 只需要指定工厂对象在容器里面的bean 名称 builder.setFactoryMethodOnBean(method.getMethodName(),"config"); //拿到BeanDefinition AbstractBeanDefinition bd = builder.getBeanDefinition(); context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(),bd); } //初始化容器 context.refresh(); for (String name: context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
进阶发现sqlSessionFactoryBean 多了一个DataSource 参数,这个参数默认要指定一个自动装配,不指定自动装配,没有办法解析工厂方法的参数
三、进阶创建BeanDefinition指定一个装配模式。
对于构造方法的参数,对于工厂方法的参数 如果要用自动装配的话,选择的装配模式是AUTOWIRE_CONSTRUCTOR
package com.mangoubiubiu.show.a05; import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.JmxEnabled; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.stereotype.Component; import java.awt.*; import java.io.IOException; import java.util.Set; @Slf4j public class A05Application { private MetadataReader reader; public static void main(String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); //将config注册到容器里面 context.registerBean("config",Config.class); // context.registerBean(ComponentScanPostProcessor.class); // context.registerBean(MapperScannerConfigurer.class,beanDefinition -> { // beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper"); // }); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); //不走类加载 效率比反射高 //先读取Config.class类的信息 MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class")); //拿到所有被@Bean注解标注的方法的信息 Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata method:annotatedMethods) { System.out.println(method); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); //第一个参数 method.getMethodName() 获取方法名,哪个方法作为工厂方法 第二个参数 工厂对象 只需要指定工厂对象在容器里面的bean 名称 builder.setFactoryMethodOnBean(method.getMethodName(),"config"); //拿到BeanDefinition //对于构造方法的参数,对于工厂方法的参数 如果要用自动装配的话,选择的装配模式是AUTOWIRE_CONSTRUCTOR builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); AbstractBeanDefinition bd = builder.getBeanDefinition(); context.getDefaultListableBeanFactory().registerBeanDefinition(method.getMethodName(),bd); } //初始化容器 context.refresh(); for (String name: context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
运行发现sqlSessionFactoryBean 被成功注入到IOC容器里面
但是initMethod 没有被解析
四、解析Bean相关属性 ,抽取成AtBeanPostProcessor
package com.mangoubiubiu.show.a05; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.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 BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { try { CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); //不走类加载 效率比反射高 //先读取Config.class类的信息 MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/mangoubiubiu/show/a05/Config.class")); //拿到所有被@Bean注解标注的方法的信息 Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata method:annotatedMethods) { System.out.println(method); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); //其他方法没有配置initMethod 获取到的是空字符串 String initMethod = method.getAllAnnotationAttributes(Bean.class.getName()).get("initMethod").get(0).toString(); if(initMethod.length()>0){ //设置初始化方法 builder.setInitMethodName(initMethod); } //第一个参数 method.getMethodName() 获取方法名,哪个方法作为工厂方法 第二个参数 工厂对象 只需要指定工厂对象在容器里面的bean 名称 builder.setFactoryMethodOnBean(method.getMethodName(),"config"); //拿到BeanDefinition //对于构造方法的参数,对于工厂方法的参数 如果要用自动装配的话,选择的装配模式是AUTOWIRE_CONSTRUCTOR builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR); AbstractBeanDefinition bd = builder.getBeanDefinition(); if (configurableListableBeanFactory instanceof DefaultListableBeanFactory) { DefaultListableBeanFactory beanFactory = ((DefaultListableBeanFactory) configurableListableBeanFactory); beanFactory.registerBeanDefinition(method.getMethodName(),bd); } } } catch (IOException e) { e.printStackTrace(); } } }
package com.mangoubiubiu.show.a05; import com.mangoubiubiu.show.a05.component.ComponentScanPostProcessor; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.JmxEnabled; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.stereotype.Component; import java.awt.*; import java.io.IOException; import java.util.List; import java.util.Set; @Slf4j public class A05Application { private MetadataReader reader; public static void main(String[] args) throws IOException { GenericApplicationContext context = new GenericApplicationContext(); //将config注册到容器里面 context.registerBean("config",Config.class); // context.registerBean(ComponentScanPostProcessor.class); // context.registerBean(MapperScannerConfigurer.class,beanDefinition -> { // beanDefinition.getPropertyValues().add("basePackage","com.mangoubiubiu.show.a05.mapper"); // }); context.registerBean(AtBeanPostProcessor.class); //初始化容器 context.refresh(); for (String name: context.getBeanDefinitionNames()) { System.out.println(name); } context.close(); } }
五、总结
1 读取Config.class的源数据信息
2 读取被@Bean标注的方法信息
3 针对每一个方法生成 BeanDefinition
4 指定bean 的名称 指定工厂方法是谁 是哪个Bean的方法
5 对@Bean的属性做解析
6 将Bean 注入到IOC容器中