Spring Enable*高级应用及原理
我相信在很多的Spring boot项目中都大量使用到了以Enable*的注解。
比如EnableAsync、EnableScheduling、EnableAspectJAutoProxy、EnableCaching等,Enable表示开启/允许一项功能。
Enable*工作原理
我们只需要几个很简单的注解就能开启一个复杂的功能,这是多么简易的用法,这是怎么办到的?
首先来看看计划任务@EnableScheduling的源代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
主要核心的配置就是导入了一个配置文件:
@Import(SchedulingConfiguration.class)
1、Configuration
即上面的用法,直接导入Configuration配置类。
2、ImportSelector
根据条件选择导入不同的配置类,参考@EnableAsync
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
/**
* {@inheritDoc}
* @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
*/
@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;
}
}
}
3、ImportBeanDefinitionRegistrar
动态注册Bean,参考@EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
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
该starter提供PersonService,并实现自动配置PersonService
1、创建一个Spring boot 项目
2、引入所需要的jar包
![这里写图片描述](https://img-blog.csdn.net/20180826201830410?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
3、创建需要自动配置的人员信息的实体
![这里写图片描述](https://img-blog.csdn.net/2018082620192753?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
4、创建服务类
![这里写图片描述](https://img-blog.csdn.net/20180826202015913?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
5、编写自动注入类
![这里写图片描述](https://img-blog.csdn.net/20180826202059777?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
6、.注册配置
1.在src/main/resources新建META-INF文件夹
2.在META-INF文件夹下创建spring.factories文件
3.注册配置自动配置信息,内容如下
![这里写图片描述](https://img-blog.csdn.net/20180826202211275?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
7、使用maven 将starter安装到本地
8、创建一个新的spring boot 项目并使用已创建好starter
![这里写图片描述](https://img-blog.csdn.net/2018082620231118?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
9、在新项目的启动类上指定开启需要自动配置的类
![这里写图片描述](https://img-blog.csdn.net/20180826202355613?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
10、在新项目的配置文件中引入需要进行配置的属性
![这里写图片描述](https://img-blog.csdn.net/20180826202504124?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
11、然后创建访问测试的接口
![这里写图片描述](https://img-blog.csdn.net/20180826202539572?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
12、最后启动并进行测试
![这里写图片描述](https://img-blog.csdn.net/20180826202613751?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMjQzNTE1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
在构建starter的过程中,涉及到一些注解
@ConditionalOnBean:当容器中有指定的Bean的条件下
@ConditionalOnClass:当类路径下有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件下