第五讲:BeanFactory后处理器
目录
一、BeanFactory后处理器-ConfigurationClassPostProcessor(进行解析@Bean @Configuration @ComponetScan等)
二、MapperScannerConfigurer 解析@Mapper注解
三、工厂后处理器模拟实现-组件扫描 @Component注解
4.筛选出这些类中是否加入@Component注解及其派生类的注解
学习目标
1.BeanFactory后处理器的作用:为BeanFactory提供扩展功能
2.常见的BeanFactory后处理器
准备工作
主类
package com.itheima.a5;
import com.itheima.a5.mapper.Mapper1;
import com.itheima.a5.mapper.Mapper2;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import java.io.IOException;
/*
BeanFactory 后处理器的作用
*/
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
//注册了一个config的bean
context.registerBean("config", Config.class);
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
package com.itheima.a5;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.itheima.a5.component")
public class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean //mybatis的sql服务器工厂类
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
//初始化方法
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
package com.itheima.a5.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class Bean1 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean1() {
log.debug("我被 Spring 管理啦");
}
}
package com.itheima.a5.component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class Bean2 {
private static final Logger log = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
log.debug("我被 Spring 管理啦");
}
}
这样执行主方法
得到结果是 config
主要原因是我们没有添加BeanFactory的后处理器 无法识别@Configuration @ComponentScan @Bean @Componet等注解
一、BeanFactory后处理器-ConfigurationClassPostProcessor(进行解析@Bean @Configuration @ComponetScan等)
二、MapperScannerConfigurer 解析@Mapper注解
功能相当于mybatis的一个@MapperScan注解
加入两个类
package com.itheima.a5.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface Mapper1 {
}
package com.itheima.a5.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface Mapper2 {
}
三、工厂后处理器模拟实现-组件扫描 @Component注解
1.先通过注解的工具类找到@Component注解中的包名
2.通过这个容器的通配符类扫描到该包下的所有类
这里bean2 bean3是有@Component注解的 bean4是没有的
3.查找这些类中是否加入@Component注解
如果判断是Conponent的派生注解 @Service @Controller等使用
4.筛选出这些类中是否加入@Component注解及其派生类的注解
然后通过或者bean的名字和bean的信息 将其加载到beanFactory中
实现了没有使用beanFactory的后处理器也扫描到了bean2和bean3
四、工厂后处理器模拟实现-组件扫描 @Bean注解
package com.itheima.a5;
import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.a5.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.itheima.a5.component")
public class Config {
private Bean2 bean2() {
return new Bean2();
}
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean //mybatis的sql服务器工厂类
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
//初始化方法
@Bean(initMethod = "init")
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
package com.itheima.a5;
import com.itheima.a5.mapper.Mapper1;
import com.itheima.a5.mapper.Mapper2;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
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.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Set;
/*
BeanFactory 后处理器的作用
*/
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
//注册了一个config的bean
context.registerBean("config", Config.class);
CachingMetadataReaderFactory factory=new CachingMetadataReaderFactory();
//读取Config类的元信息
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a5/Config.class"));
//读取被@Bean修饰的方法信息
Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata metadata : annotatedMethods) {
System.out.println(metadata);
}
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
}
}
结果为
package com.itheima.a5;
import com.itheima.a5.mapper.Mapper1;
import com.itheima.a5.mapper.Mapper2;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanNameGenerator;
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.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Set;
/*
BeanFactory 后处理器的作用
*/
public class A05 {
private static final Logger log = LoggerFactory.getLogger(A05.class);
public static void main(String[] args) throws IOException {
// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
//注册了一个config的bean
context.registerBean("config", Config.class);
CachingMetadataReaderFactory factory=new CachingMetadataReaderFactory();
//读取Config类的元信息
MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/itheima/a5/Config.class"));
//读取被@Bean修饰的方法信息
Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata metadata : annotatedMethods) {
System.out.println(metadata);
BeanDefinitionBuilder builder =BeanDefinitionBuilder.genericBeanDefinition();
//将这些@Bean修饰的方法 加入到congfig类中
builder.setFactoryMethodOnBean(metadata.getMethodName(),"config");
//自动装配
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
context.getDefaultListableBeanFactory().registerBeanDefinition(metadata.getMethodName(),beanDefinition);
}
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
// ⬇️销毁容器
context.close();
/*
学到了什么
a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义
*/
}
}
得到结果我们发现是没有 初始化的 我么要进行设置才行
五、工厂后处理器模拟实现-组件扫描 @Mapper注解
最基本的原理是
// @Bean
// public MapperFactoryBean <Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
// MapperFactoryBean< Mapper1> factory=new MapperFactoryBean<>(Mapper1.class);
// factory.setSqlSessionFactory(sqlSessionFactory);
// return factory;
// }
但是这种的缺点是 要一个一个的写,因此我们写一个扫描包下所以的接口
package com.itheima.a5;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import java.io.IOException;
public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
//通过路径 得到资源数组
Resource[] resources = resolver.getResources("classpath:com/itheima/a5/mapper/**/*.class");
//创建注解声明名字的生产者的对象
AnnotationBeanNameGenerator beanNameGenerator=new AnnotationBeanNameGenerator();
//获取元信息的
CachingMetadataReaderFactory metadataReaderFactory=new CachingMetadataReaderFactory();
for (Resource resource : resources) {
//获取资源中的元信息
MetadataReader reader = metadataReaderFactory.getMetadataReader(resource);
//资源中的类的元信息
ClassMetadata classMetadata = reader.getClassMetadata();
//判断是否是接口
if (classMetadata.isInterface()) {
//获得bean的表示 即蓝图
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.
genericBeanDefinition(MapperFactoryBean.class).
addConstructorArgValue(classMetadata.getClassName())
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE).getBeanDefinition();
//这里不能用上面的beanDefiition来获取名称 因为上面描述的bean 产生的MapperFactoryBean的名称
//因此这里新创建了一个beanDefinition1 ,利用的是接口名称的bean
AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
String s = beanNameGenerator.generateBeanName(beanDefinition1, beanFactory);
beanFactory.registerBeanDefinition(s,beanDefinition);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
最后得到了mapper1和mapper2
总结
学到了什么
a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义