君临-行者无界

导航

Spring EnableXX注解原理及应用

Spring提供了一系列以Enable开头的注解,这些注解本质上是激活Spring的某些管理功能。例如@EnableWebMvc注解引入了MVC框架在Spring应用中需要用到的所有bean,@EnableAsync注解可以使Bean在spring应用中支持异步功能,@EnableTransactionManagement开启事务支持。打开这些注解的源码不难发现这些@EnableXX注解的定义都包含一个@Import注解,通过导入一些配置类来完成特定的功能。

@Import注解导入配置方式的三种类型

第一类 配置类

例如,@EnableScheduling中直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}

我们可以仿照着写一个类似的demo,将自定义的注解加在配置类上即可加载bean到spring容器。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ConfigurationDemo.class)
public @interface EnableConfigDemo {

    String value() default "";
}
@Configuration
public class ConfigurationDemo {

    @Bean
    public TestBean01 getTestBean01(){
        return  new TestBean01();
    }

}
public class TestBean01 {

    public void sayHello(){
        System.out.println("hello ,I am TestBean01");
    }
}

第二类 ImportSelector的实现类

@Import导入ImportSelector的实现类时,Spring会把selectImport方法的返回值对应的Bean注入到Spring容器(其核心原理是spring的BeanFactoryPostProcessor),例如@EnableAsync、@EnableTransactionManagement

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}

自己动手写一个demo

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SelectorDemo.class)
public @interface EnableSelectorDemo {

    String dataSourcetype() default "druid";
}
public class SelectorDemo implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableSelectorDemo.class.getName(), false);
        AnnotationAttributes annotationAttributes1 = AnnotationAttributes.fromMap(annotationAttributes);
        String dataSourcetype = annotationAttributes1.getString("dataSourcetype");
        if("druid".equals(dataSourcetype)){
            return new String[]{"com.example.beans.DruidDataSource"};
        }else{
            return new String[]{"com.example.beans.HpDataSource"};
        }

    }
}

第三类 动态注册Bean

@Import导入ImportBeanDefinitionRegistrar的实现类,通过重写方法registerBeanDefinitions()注入bean(其核心原理也是spring的BeanFactoryPostProcessor,是spring的重要扩展接口),例如@EnableAspectJAutoProxy

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

手写一个demo

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(RegistrarDemo.class)
public @interface EnableRegistrar {
    String[] scanPackage() default "";
}
//模仿spring整合mybaits
public class RegistrarDemo implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableRegistrar.class.getName(), false));
        String[] scanPackages = annotationAttributes.getStringArray("scanPackage");
        List<String> classNames = new ArrayList<String>();
        for (String scanPackage : scanPackages) {
            classNames.addAll(scanBasePackage(scanPackage));
        }
        try {
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
                if(aClass.isAnnotationPresent(MapperDemo.class)){
                    BeanDefinitionBuilder bdb1 = BeanDefinitionBuilder.rootBeanDefinition(aClass);
                    BeanDefinition beanDefinition1 = bdb1.getBeanDefinition();
                    registry.registerBeanDefinition(aClass.getName(), beanDefinition1);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }


    private List<String> scanBasePackage(String basePackName){
        List<String> classNames = new ArrayList<String>();
        String path = basePackName.replace(".","/");
        System.out.println(path);
        URL url = this.getClass().getClassLoader().getResource(path);
        System.out.println(url);
        File dir = new File(url.getFile());
        File[] files = dir.listFiles();
        for (File file: files) {
            if(file.isDirectory()){
                scanBasePackage(basePackName +"."+file.getName());
            }else if(file.isFile()){
                classNames.add(basePackName +"." + file.getName().replace(".class",""));
                System.out.println("扫描到的类有" + basePackName +"." + file.getName().replace(".class",""));
            }
        }
        return classNames;
    }

}

springboot中的自动装配机制

springboot中的自动装配机制是基于@EnableAutoConfiguration这个注解里完成的,这个@EnableAutoConfiguration注解可以显式地调用,否则它会在@SpringBootApplication注解中隐式地被调用,@EnableAutoConfiguration注解中使用了AutoConfigurationImportSelector作为ImportSelector。核心源码如下:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
//读取META-INF/spring.factories下的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

这也是我们封装自己的springboot-starter是需要在META-INF/spring.factories配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx的原因

posted on 2020-11-13 19:00  请叫我西毒  阅读(781)  评论(0编辑  收藏  举报