目录
一.ConfigurationClassPostProcessor后处理器
(1)创建两个mapper,在com.jjh.mapper包下:
一.ConfigurationClassPostProcessor后处理器
先创建一个配置类
@Configuration
public class Config {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://lcoalhost:3306?mybatis?useSSL=false&serverTimeZone=Asia/Shanghai");
dataSource.setDriverClassName("com.jdbc.cj.mysql.Driver");
dataSource.setUsername("jjh123");
dataSource.setPassword("123456");
return dataSource;
}
}
创建GenericApplicationContext容器
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.refresh();
System.out.println(context.getBean("config"));
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
运行程序:
18:18:31.329 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@1990a65e
18:18:31.348 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
com.jjh.configure.Config@4cc451f2
config
我们发现只有config本身这个类被定义了,我们需要把他里面@Bean注解的bean都定义
添加ConfigurationClassPostProcessor
//设置ConfigurationClassPostProcessor
context.registerBean(ConfigurationClassPostProcessor.class);//@Configuration @Bean @Import @ImportResource
再次运行:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
sqlSessionFactoryBean
dataSource
进程已结束,退出代码0
我们看到这三个bean也都定义了
二.MapperScannerConfigurer
下面我们定义两个mapper接口
@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
然后为了能够定义这个被@Mapper注解的接口
添加:MapperScannerConfigurer
//设置@Mapper注解解析
context.registerBean(MapperScannerConfigurer.class,beanDefinition -> {
beanDefinition.getPropertyValues().add("basePackage","com.jjh.mapper");
});
我们需要给basePackage属性赋值
然后运行:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.mybatis.spring.mapper.MapperScannerConfigurer
sqlSessionFactoryBean
dataSource
mapper1
mapper2
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
进程已结束,退出代码0
我们可以看到mapper1和mapper2都被定义了,除此之外还加入了其他一些beanFactory后处理器
三.@ComponetScan组件扫描
1.原理
我们想要获取@ComponetScan(basePackages="")中的包名下的文件,然后判断是否被加上了@Componet注解,以此来创建bean对象
2.验证
首先,我们给Config类加上@ComponetScan注解
@Configuration
@ComponentScan("com.jjh.component")
public class Config {
}
这里指定了包名为com.jjh.component,下面我们来测试
//获取哪个类上有注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
//获取componentScan的basePackages属性
assert componentScan != null;
String[] packages = componentScan.basePackages();
//创建注解读取工厂
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
for (String p : packages) {
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
//获取这个路径下的文件
for (Resource resource : context.getResources(path)) {
//返回这个类的一些信息
MetadataReader reader = factory.getMetadataReader(resource);
System.out.println("类名:"+reader.getClassMetadata().getClassName());
System.out.println("是否有@Component:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
System.out.println("是否有@Component的派生:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
}
}
注意:这里最终需要把com.jjh.component变换为classpath*:com/jjh/component/**/*.class,
/**值的是这个包底下的所有子包,以.class结尾的文件
我们看一下哪些类注解了@Component
@Component
public class Bean1 {
}
public class Bean2 {
}
@Component
@ConfigurationProperties(prefix = "java")
public class Bean3 {
}
@Controller
public class Bean4 {
}
我们看到Bean2没有注解,然后Bean4注解的是@Controller,这个我们都知道,它的效果和@Component是一样的
下面我们来运行一下:
类名:com.jjh.component.Bean1
是否有@Component:true
是否有@Component的派生:false
类名:com.jjh.component.Bean2
是否有@Component:false
是否有@Component的派生:false
类名:com.jjh.component.Bean3
是否有@Component:true
是否有@Component的派生:false
类名:com.jjh.component.Bean4
是否有@Component:false
是否有@Component的派生:true
可以看到,成功的知道了哪些类含有@Component注解以及@Controller
下面,我们只需要把带注解的类注册到beanFactory中就可以了:
if(reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
||reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
//如果加了注解,我们需要给它注册到beanFactory中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
//生成bean名称
String name = generator.generateBeanName(beanDefinition,beanFactory);
//注册bean定义
beanFactory.registerBeanDefinition(name,beanDefinition);
注意:我们这里需要一个bean名称生成器,在之前定义一个:
// 创建Bean名称生成器
BeanNameGenerator generator = new AnnotationBeanNameGenerator();
现在,让我们获取这些bean名称,看一下是否定义成功:
context.refresh();
//获取bean名称
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
运行:
config
bean1
bean3
bean4
可以看到,成功的把加了@Component和@Controller注解的bean定义成功了!
3.抽取方法
这样看起来很复杂,我们把这个流程抽取为一个类方法,然后把这个类注册到beanFactory中去,它就会自动执行
这里要实现BeanFactoryPostProcessor接口中的postProcessBeanFactory()方法,它会在context.
refresh()时调用,来解析@ComponentScan
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
//获取哪个类上有注解
ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
//获取componentScan的basePackages属性
assert componentScan != null;
String[] packages = componentScan.basePackages();
//创建注解读取工厂
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
// 创建Bean名称生成器
BeanNameGenerator generator = new AnnotationBeanNameGenerator();
for (String p : packages) {
String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
//获取这个路径下的文件
for (Resource resource : new PathMatchingResourcePatternResolver().getResources(path)) {
//返回这个类的一些信息
MetadataReader reader = factory.getMetadataReader(resource);
// System.out.println("类名:"+reader.getClassMetadata().getClassName());
// System.out.println("是否有@Component:"+reader.getAnnotationMetadata().hasAnnotation(Component.class.getName()));
// System.out.println("是否有@Component的派生:"+reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()));
if(reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
||reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())){
//如果加了注解,我们需要给它注册到beanFactory中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(reader.getClassMetadata().getClassName()).getBeanDefinition();
//生成bean名称
String name = generator.generateBeanName(beanDefinition, (BeanDefinitionRegistry) beanFactory);
//注册bean定义
((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(name,beanDefinition);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
把它注册到context中:
//添加我的beanFactory后处理器
context.registerBean(MyBeanFactoryPostProcessor.class);
运行结果:
config
com.jjh.configure.MyBeanFactoryPostProcessor
bean1
bean3
bean4
可以看到同理
三.@Bean实现
1.原理
同理,查找看哪个类上有@Configuration注解,然后获取这个类中带@Bean注解的方法,通过工厂方法,生产bean,然后把bean定义注册到beanFactory中即可
2.验证
创建配置类Config
@Configuration
@ComponentScan("com.jjh.component")
public class Config {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://lcoalhost:3306?mybatis?useSSL=false&serverTimeZone=Asia/Shanghai");
dataSource.setDriverClassName("com.jdbc.cj.mysql.Driver");
dataSource.setUsername("jjh123");
dataSource.setPassword("123456");
return dataSource;
}
}
然后写以下代码:
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//读取Config配置类
MetadataReader metadataReader = factory.getMetadataReader(new ClassPathResource("com/jjh/configure/Config.class"));
//获取@Bean注解的方法
Set<MethodMetadata> beanMethods = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata beanMethod : beanMethods) {
//获取到被@Bean注解的方法,接下来调用工厂方法生产bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(beanMethod.getMethodName(),"config");
//设置自动装配模式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
AbstractBeanDefinition definition = builder.getBeanDefinition();
context.getDefaultListableBeanFactory()
.registerBeanDefinition(beanMethod.getMethodName(),definition);
}
就是判断是否方法上有@Bean,如果有,就构造即可,
注意:仔细看Config类中SqlSessionFactoryBean中需要DataSource参数,这里我们需要设置自动装配模式
//设置自动装配模式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
然后我们来运行一下代码:
context.refresh();
//获取bean名称
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
config
sqlSessionFactoryBean
dataSource
可以看到成功的定义了@Bean注解的bean
3.抽取方法
同样,我们也可以抽取为一个类方法,然后注册这个类
public class AtBeanBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//读取Config配置类
MetadataReader metadataReader = null;
try {
metadataReader = factory.getMetadataReader(new ClassPathResource("com/jjh/configure/Config.class"));
} catch (IOException e) {
throw new RuntimeException(e);
}
//获取@Bean注解的方法
Set<MethodMetadata> beanMethods = metadataReader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata beanMethod : beanMethods) {
//获取到被@Bean注解的方法,接下来调用工厂方法生产bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.setFactoryMethodOnBean(beanMethod.getMethodName(),"config");
//设置自动装配模式
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
AbstractBeanDefinition definition = builder.getBeanDefinition();
//我们需要转型一下
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition(beanMethod.getMethodName(),definition);
}
}
}
注册这个类
//添加@Bean 后处理器
context.registerBean(AtBeanBeanFactoryPostProcessor.class);
运行,查看结果:
config
com.jjh.configure.AtBeanBeanFactoryPostProcessor
sqlSessionFactoryBean
dataSource
可以看到同样的效果
四.@Mapper实现
1.原理
我们知道这个注解是mybatis提供的用来创建dao层方法的,那么我们spring如何实例化他呢
因为他是一个接口,所以spirng实际上实现的是MapperFactoryBean,然后利用他的方法来创建mapper对象
2.验证
(1)创建两个mapper,在com.jjh.mapper包下:
@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}
(2)在配置类中来实现:
@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
MapperFactoryBean<Mapper1> factoryBean = new MapperFactoryBean<>(Mapper1.class);
factoryBean.setSqlSessionFactory(sqlSessionFactory);
return factoryBean;
}
我们配置这个bean,然后利用我们上一步的@Bean的实现,直接运行即可:
查看结果:
config
com.jjh.configure.AtBeanBeanFactoryPostProcessor
sqlSessionFactoryBean
dataSource
mapper1
我们发现mapper1已经成功实现了!
3.抽取方法
下面我们直接抽取方法来实现:
package com.jjh.configure;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
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;
/**
* @author JJH
*/
public class MapperBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
//创建bean名称生成器
BeanNameGenerator generator = new AnnotationBeanNameGenerator();
//获取类路径下的mapper文件
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources ;
try {
resources = resolver.getResources("classpath:com/jjh/mapper/**/*.class");
for (Resource resource : resources) {
//读取文件的原信息
MetadataReader reader = factory.getMetadataReader(resource);
//获取文件的类信息
ClassMetadata classMetadata = reader.getClassMetadata();
//判断该文件是否是接口
if (classMetadata.isInterface()) {
//是接口,然后判断是否上面有@Mapper
if (reader.getAnnotationMetadata().hasAnnotation(Mapper.class.getName())) {
//有@Mapper注解,就需要构造方法了
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(MapperFactoryBean.class);
//对它的set方法设置自动装配
builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//对它的构造方法设置要传递的类型
builder.addConstructorArgValue(classMetadata.getClassName());
//构建beanDefinition
AbstractBeanDefinition bf1 = builder.getBeanDefinition();
//为了保持mapper的名称,我们需要在定义一个BeanDefinitionBuilder
BeanDefinition bf2 =BeanDefinitionBuilder
.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
//我们根据每个mapper生成bean名称
String name = generator.generateBeanName(bf2, beanFactory);
//我们将beanDefinition注入beanFactory
beanFactory.registerBeanDefinition(name,bf1);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
注册这个后处理类:
//添加@Mapper 后处理器
context.registerBean(MapperBeanFactoryPostProcessor.class);
运行代码,查看结果:
config
com.jjh.configure.AtBeanBeanFactoryPostProcessor
com.jjh.configure.MapperBeanFactoryPostProcessor
mapper1
mapper2
sqlSessionFactoryBean
dataSource
我们发现成功的将mapper1和mapper2定义到beanFactory中了!