package com.jd.util.interceptor.mybatis.scan;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyResourceConfigurer;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public class RouterScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
private String basePackage;
private Class<? extends Annotation> annotationClass;
private ApplicationContext applicationContext;
private String beanName;
private boolean processPropertyPlaceHolders;
private BeanNameGenerator nameGenerator;
public RouterScannerConfigurer() {
}
/**
*
* @param basePackage 扫描的包
*/
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
/**
*
* @param annotationClass 注解的类
*/
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
/**
<!-- 自动扫描 mapper 接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="${mapper.scan.package.name}" />
<property name="processPropertyPlaceHolders" value="true"/>
<property name="sqlSessionTemplateBeanName" value="projectSqlSessionTemplate" />
</bean>
中的 <property name="processPropertyPlaceHolders" value="true"/>
* @param processPropertyPlaceHolders
*/
public void setProcessPropertyPlaceHolders(boolean processPropertyPlaceHolders) {
this.processPropertyPlaceHolders = processPropertyPlaceHolders;
}
/**
*
* @param applicationContext 重写的ApplicationContextAware接口中的方法
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
*
* @param name 重写的BeanNameAware接口的中的方法
*/
@Override
public void setBeanName(String name) {
this.beanName = name;
}
public BeanNameGenerator getNameGenerator() {
return this.nameGenerator;
}
public void setNameGenerator(BeanNameGenerator nameGenerator) {
this.nameGenerator = nameGenerator;
}
/**
* 重写InitializingBean接口的方法
* @throws Exception
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.basePackage, "Property 'basePackage' is required");
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
/**
* 重写的 BeanDefinitionRegistryPostProcessor的方法
* @param registry
* @throws BeansException
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
//实现了ClassPathBeanDefinitionScanner并对registerFilters方法与doScan方法进行了重写。
ClassPathRouterScanner scanner = new ClassPathRouterScanner(registry);
scanner.setAnnotationClass(this.annotationClass);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
//registerFilters方法主要是根据annotationClass属性来指定只扫描指定包下面的类带有某个注解的接口或者是为某个接口的子接口的接口。
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
/**
<property name="basePackage" value="${basePackage}"/>
如果用${basePackage}表达式的话:
加入<property name="processPropertyPlaceHolders" value="true">
此函数的说明给我嗯进行了说明:BeanDefinitionRegistries会在应用启动的时候调用,并且会早于BeanFactoryPostProcessors的调用,
这就意味着PropertiesResourceConfigurers还没有被加载所有对于属性文件的引用将会失效,为避免此种情况发生,
此方法手动地找出定义的PropertyResourceConfigurers并进行调用以以保证对于属性的引用可以正常工作。
(1)找到所有已经注册的PropertyResourceConfigurer类型的bean。
(2)模拟Spring的环境来用处理器。这里通过使用呢new DefaultlistableBeanFactory()来模拟Spring中的环境(完成处理器的调用后便失效),
将映射的bean,也就是MapperScannerConfigurer类型bean注册到环境中来进行后处理器的调用。
*/
/**
* 有个坑 要求mybatis-spring在1.2.5以下版本的mybatis-spring一下配置无法启动 用的类不对 和 mybatis 版本号
* 经分析1.2.5以下的mybatis-spring的MapperScannerConfigurer中判断是否为GenericApplicationContext的实例
* 改为了:if (!prcs.isEmpty() && this.applicationContext instanceof ConfigurableApplicationContext)
* 启动时不为GenericApplicationContext的实例时属性无法加载
* 1.3.1以后版本已经修改
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
*/
private void processPropertyPlaceHolders() {
//从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
//Spring自定义占位符替换(PropertyPlaceholderConfigurer)
/**
Spring提供了的一种叫做BeanFactoryPostProcessor的容器扩展机制。它允许我们在容器实例化对象之前,
对容器中的BeanDefinition中的信息做一定的修改(比如对某些字段的值进行修改,这就是占位符替换的根本)。
*/
Map<String, PropertyResourceConfigurer> prcs = this.applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
if (!prcs.isEmpty() && this.applicationContext instanceof ConfigurableApplicationContext) {
//beanName = org.mybatis.spring.mapper.MapperScannerConfigurer
BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext)this.applicationContext).getBeanFactory().getBeanDefinition(this.beanName);
//DefaultListableBeanFactory(bean工厂):它有一个ConcurrentHashMap成员变量,以beanName为键,BeanDefinition为值保存注册的bean。
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//registerBeanDefinition干的事情就是把bean与name对应起来,并且把bean的实例与name都存储起来(将其注册到Bean工厂中ConcurrentHashMap对象中,以后再用)。
factory.registerBeanDefinition(this.beanName, mapperScannerBean);
Iterator i$ = prcs.values().iterator();
while(i$.hasNext()) {
//PropertyResourceConfigurer实现了BeanFactoryPostProcessor接口。
PropertyResourceConfigurer prc = (PropertyResourceConfigurer)i$.next();
/**
* Spring提供了的一种叫做BeanFactoryPostProcessor的容器扩展机制。它允许我们在容器实例化对象之前,
* 对容器中的BeanDefinition中的信息做一定的修改(比如对某些字段的值进行修改,这就是占位符替换的根本)。
*/
/**
* DefaultListableBeanFactory implements ConfigurableListableBeanFactory:
DefaultListableBeanFactory实现接口 ConfigurableListableBeanFactory、BeanDefinitionRegistry(bean定义的注册接口), 并继承AbstractAutowireCapableBeanFactory,实现全部类管理的功能。
DefaultListableBeanFactory其实要实现的功能就是以list集合的方式操作bean,为什么要拆成这么多的类和接口呢。这里面可能基于几点考虑。
1 功能的不同维度,分不同的接口,方便以后的维护和其他人的阅读。如:AutowireCapableBeanFactory、ListableBeanFactory、HierarchicalBeanFactory等
2 不同接口的实现,分布在不同的之类里,方便以后不同接口多种实现的扩展
3 从整个类图的分布,可以看出spring在这块是面向接口编程,后面类的实现,他们认为只是接口功能实现的一种,随时可以拓展成多种实现
*/
prc.postProcessBeanFactory(factory);
}
PropertyValues values = mapperScannerBean.getPropertyValues();
this.basePackage = this.updatePropertyValue("basePackage", values);
}
}
private String updatePropertyValue(String propertyName, PropertyValues values) {
PropertyValue property = values.getPropertyValue(propertyName);
if (property == null) {
return null;
} else {
Object value = property.getValue();
if (value == null) {
return null;
} else if (value instanceof String) {
return value.toString();
} else {
return value instanceof TypedStringValue ? ((TypedStringValue)value).getValue() : null;
}
}
}
}
package com.jd.util.interceptor.mybatis.scan;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
/**
ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider
ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner的基类,其本身主要作用是包扫描.
ClassPathBeanDefinitionScanner在其基础上做了注册功能,所以ClassPathBeanDefinitionScanner需要传入一个BeanDefinitionRegistry对象.
而ClassPathScanningCandidateComponentProvider扫描的对象是并不需要注册到BeanDefinitionRegistry中去的.
*/
public class ClassPathRouterScanner extends ClassPathBeanDefinitionScanner {
private Class<? extends Annotation> annotationClass;
public ClassPathRouterScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
public void registerFilters() {
boolean acceptAllInterfaces = true;
//对于annotationClass属性的处理
/**
* 如果annotationClass不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,
* 而封装此属性的过滤器就是AnnotationTypeFilter。AnnotationTypeFilter保证在扫描对应Java文件时只接受标记有注解为annotationClass的接口。
*/
if (this.annotationClass != null) {
this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
//对于markerInterface属性的处理
/**
* 如果markerInterface不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,
* 而封装此属性的过滤器就是实现AssignableTypeFilter接口的局部类。
* 表示扫描过程中只有实现markerInterface接口的接口才会被接受。
*/
if (acceptAllInterfaces) {
this.addIncludeFilter(new TypeFilter() {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
}
//不扫描package-info.java文件
this.addExcludeFilter(new TypeFilter() {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
}
});
/**
对于命名为package-info的Java文件,默认不作为逻辑实现接口,将其排除掉,使用TypeFilter接口的局部类实现match方法。
从上面的函数我们看出,控制扫描文件Spring通过不同的过滤器完成,这些定义的过滤器记录在了includeFilters和excludeFilters属性中。
*/
}
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//先根据父类ClassPathBeanDefinitionScanner得到basePackages下的所有接口描述持有类 BeanDefinitionHolder的集合
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
Iterator i$ = beanDefinitions.iterator();
while(i$.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)i$.next();
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
/**
* 对于每一个GenericBeanDefinition设置对应的类型为MapperFactoryBean
* 如果是正常单个配置mapperInterface的这种:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
org.mybatis.spring.sample.mapper.UserMapper是一个接口,我们创建一个MapperFactoryBean实例,
然后注入这个接口和sqlSessionFactory(mybatis中提供的SqlSessionFactory接口,
MapperFactoryBean会使用SqlSessionFactory创建SqlSession)这两个属性。
之后想使用这个UserMapper接口的话,直接通过spring注入这个bean,然后就可以直接使用了,spring内部会创建一个这个接口的动态代理。
当发现要使用多个MapperFactoryBean的时候,一个一个定义肯定非常麻烦,于是mybatis-spring提供了MapperScannerConfigurer这个类,
它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean。
通过自动扫描mapper批量的方式:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="${mapper.scan.package.name}" />
<property name="processPropertyPlaceHolders" value="true"/>
<property name="sqlSessionTemplateBeanName" value="projectSqlSessionTemplate" />
</bean>
批量扫描的原理就是 转换成一个一个的MapperFactoryBean
*/
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
//开始构造MapperFactoryBean类型的bean.
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
//设置Bean的真实类型MapperFactoryBean
definition.setBeanClass(MapperFactoryBean.class);
boolean explicitFactoryUsed = false;
if (!explicitFactoryUsed) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
//spring里面可以设置BeanDefinition自动注入类型
definition.setAutowireMode(2);
}
}
}
return beanDefinitions;
}
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (super.checkCandidate(beanName, beanDefinition)) {
return true;
} else {
this.logger.warn("Skipping MapperFactoryBean with name '" + beanName + "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!");
return false;
}
}
}
MapperScannerConfigurer的大致原理分析(去除了一部分属性)
猜你喜欢
转载自blog.csdn.net/luzhensmart/article/details/81124383
今日推荐
周排行