1.@ComponentScan实现原理
1.上下文创建
new AnnotationConfigApplicationContext(MainConfig.class)
具体过程有参考spring上下文参考
2.register(componentClasses);//就是配置类的注册
3.refresh();
具体过程有参考ioc容器的刷新
ComponentScan的解析在invokeBeanFactoryPostProcessors(beanFactory);这一步
有一个后置处理器beanfactory的后置处理器
ConfigurationClassPostProcessor
遍历所有的后置处理器优先执行实现了优先级接口(PriorityOrdered或者Order的)的
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
//第一个就是BeanDefinitionRegistryPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
//执行后置处理器的
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
//获取所有的bean定义遍历
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//获取@Configuration的属性值,获得bean定义候选者
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
//候选者排序
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
//是不是单例,设置名称生成器
//设置环境
//开始解析
//先判断这个注解是不是@Component注解的
//配置文件@PropertySources处理
//ComponentScans.class, ComponentScan.class处理
// Parse each @Configuration class
//注意关键到了,后面解析上面扫描的basePackages开始配置并扫描
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
//后面就不看了
}
- 上面关键的步骤是解析候选者 parser.parse(candidates);
- 先判断这个注解是不是@Component注解的
- basePackages开始配置并扫描,解析源码
org.springframework.context.annotation.ComponentScanAnnotationParser#parse - 最后一步是 returnscanner.doScan(StringUtils.toStringArray(basePackages));
2.自定义
1.MyMapperScan注解
这里面用了@Import语法
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MapperScan.class)
public @interface MyMapperScan {
String[] basePackages() default {};
Class<?>[] classes() default {};
}
MapperScans实现了ImportBeanDefinitionRegistrar接口,也是beanfactory的后置处理器
/**
* @author authorZhao
* @date 2019/12/13
*/
public class MapperScan implements ImportBeanDefinitionRegistrar, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(MapperScan.class);
/**
* 扫描的包路径
*/
private String[] basePackage;
/**
* 需要扫描的类
*/
private Class[] classes;
private ApplicationContext applicationContext;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取注解的值
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName());
this.basePackage = (String[])annotationAttributes.get("basePackages");
this.classes = (Class[])annotationAttributes.get("classes");
//importingClassMetadata.getMetaAnnotationTypes()
//名字生成器采用spring默认的,不设置也行
//重点到了,自己的scan,扫描包
MySqlMapperScanner mapperScanner = new MySqlMapperScanner(registry);
//mapperScanner.setBeanNameGenerator(AnnotationBeanNameGenerator.INSTANCE);
mapperScanner.setResourceLoader(this.applicationContext);
//mapperScanner.isCandidateComponent()
//this.scan(StringUtils.tokenizeToStringArray((String) this.basePackage, (String) ",; \t\n"));
logger.info("开始扫描包:{}",basePackage);
mapperScanner.doScan(this.basePackage);
logger.info("开始扫描类:{}",classes);
if(classes!=null&&classes.length>0){
//对于claess属性就直接手工注册
for (Class clzz:classes){
if(!clzz.isInterface())continue;
if(!registry.containsBeanDefinition(clzz.getSimpleName())){
//if(
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clzz);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
definition.getConstructorArgumentValues().addGenericArgumentValue(clzz);
definition.setBeanClass(MyFactoryBean.class);
//这里采用的是byType方式注入,类似的还有byName等
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
registry.registerBeanDefinition(clzz.getSimpleName(), definition);
logger.info("{}定义信息注册完毕",clzz.getSimpleName());
}
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
3.MySqlMapperScanner 直接继承ClassPathBeanDefinitionScanner ,利用spring的扫描方法实现扫描
/**
* @author authorZhao
* @date 2019/12/13
*/
public class MySqlMapperScanner extends ClassPathBeanDefinitionScanner {
//这个构造器必须的
public MySqlMapperScanner(BeanDefinitionRegistry registry) {
super(registry);
}
//扫描
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
//直接调用父类的扫描方法,bean的定义已经被注册
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
for (BeanDefinitionHolder holder : beanDefinitions) {
//循环设置属性,为后面实例化做准备
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
//spring源码就是采用这种方式给工厂类注入属性
//serviceInterface方法名,第二个参数是bean定义
//definition.getPropertyValues().add("serviceInterface", (Object) definition.getBeanClassName());
//我的工厂采用的是构造注入,并且只有一个参数
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
constructorArgumentValues.addIndexedArgumentValue(0,definition.getBeanClassName());
definition.getConstructorArgumentValues().addArgumentValues(constructorArgumentValues);
//设置自己的工厂类
definition.setBeanClass(MyFactoryBean.class);
}
return beanDefinitions;
}
protected void registerDefaultFilters() {
this.addIncludeFilter((metadataReader,metadataReaderFactory)->{
if(metadataReader.getClassMetadata().isInterface())return true;
return false;
});
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}
MyFactoryBean里面生成代理类的方法见上文(上一篇文章)
**
* 为Mapper生成代理对象
* @author authorZhao
* @param <T>
*/
public class MyFactoryBean<T> implements FactoryBean<T> {
private Logger logger = LoggerFactory.getLogger(MyFactoryBean.class);
private Class<T> interfaceClass;
public MyFactoryBean(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
public MyFactoryBean() {
}
@Override
public T getObject() throws Exception {
logger.info("正在为{}生成代理对象",interfaceClass.getName());
T t = (T)MapperProxy.getObject(interfaceClass);
if(t==null){
logger.error("{}代理对象,生成失败",interfaceClass.getName());
}
logger.info("{}代理对象,生成成功",interfaceClass.getName());
return t;
}
@Override
public Class<T> getObjectType() {
return interfaceClass;
}
@Override
public boolean isSingleton() {
return true;
}
/**
* 如果采用方法注入属性,必须准备无参构造,不然报错
* @param serviceInterface
*/
public void setServiceInterface(Class<T> serviceInterface) {
this.interfaceClass = serviceInterface;
}
}
3.测试
准备了四个接口,四个Mapper,名字不一样
启动类
@SpringBootApplication
@MyMapperScan(basePackages = {"com.git.sql.mapper","com.git.sql.mapper2"},classes = MyMapper4.class)
public class SqlApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SqlApplication.class, args);
Arrays.stream(run.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println("启动完毕");
}
//输出结果包含
//myMapper
//myMapper2
//myMapper3
//MyMapper4
}
结束
- 本文是上一篇文章的补充自己封装jdbc与spring整合
上一篇写死只为某一个特定的类生成代理,现在通过追踪spring源码的角度为某一写接口生成代理 - 其实在spring的scan里面还是有很多学文的,如何根据路径扫描,扫描本项目和其他jjar包,这里暂时没深入研究
- 下一篇将补充cglib的代理,并且将代理类生成为文件,并且使用工具反编译,这里有坑
- 本文为作者原创,转载请申明出去
参考:spring源码
源代码地址dubbo练习