1.理解Enable模块驱动
从Spring 3.1开始,Spring Framework开始支持“@Enable模块驱动”,所谓“模块”是指具备相同领域的功能组件集合,组合所形成的一个独立单元,比如Web MVC模块,AspectJ代理模块、Caching(缓存)模块等。
在Spring框架中,有着许多模块化的Annotation,这些注解均已@Enable为前缀。
框架实现 | @Enable模块注解 | 激活模块 |
---|---|---|
Spring Framework | @EnableWebMvc | Web MVC模块 |
@EnableTransactionManagement | 事物管理模块 | |
@EnableCaching | Caching模块 | |
@EnableMbeanExport | JMX模块 | |
@EnableAsync | 异步处理模块 | |
@EnableWebFlux | WebFlux模块 | |
@EnableAspectJautoProxy | AspectJ代理模块 | |
Spring Boot | @EnableAutoConfiguration | 自动装配模块 |
@EnableManagementContext | Actuator管理模块 | |
@EnableConfigurationProperties | 配置属性绑定模块 | |
… | … | … |
“Enable模块驱动”的意义能够简化配置步骤,实现“按需装配”,同时屏蔽组件集合装配的细节。Srping实现Enable模块驱动的方法大致分为两类,分别“注解驱动”和“接口编程”,无论哪种方式,均依赖于@Import注解。@Import注解用于导入一个和多个ConfigurationClass,将其注册为SpringBean。
2.基于“注解驱动”实现@Enable模块
对于已“注解驱动”实现的方式,我们可以先参考Spring中已有的实现,录入@EnableWebMvc。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
很明显,EnableWebMvc注解Impot了DelegatingWebMvcConfiguration类,
而DelegatingWebMvcConfiguration是一个@Configuration类。
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport{
...
}
通过模仿EnableWebMvc,我们可以自己写一个简单的@Enable“注解驱动”的实现。先写一个@Configuration类
@Configuration
public class HelloWorldConfiguration {
@Bean
public String helloWorld() {
return "Hello World";
}
}
定义对应的@Enable注解@EnableHelloWorld
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}
最后标注@EnableHelloWorld到对应引导类上,并运行观察结果
@EnableHelloWorld
public class EnableHelloWorldBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(EnableHelloWorldBootstrap.class);
context.refresh();
String helloWorld = context.getBean("helloWorld", String.class);
System.out.println(helloWorld);
}
}
//输出:Hello World
3.基于“接口编程”实现@Enable模块
相对于“注解驱动”,“接口编程”的实现方式更为复杂。同样的我们先参考Spring现有实现@EnableCaching。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching{
...
}
看上去和之前的@EnableWebMvc没啥区别,我们再看看Import的CachingConfigurationSelector.class类
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
...
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
...
}
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
...
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes == null) {
throw new IllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
}
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
@Nullable
protected abstract String[] selectImports(AdviceMode adviceMode);
}
我们可以看到,CachingConfigurationSelector以及其父类AdviceModeImportSelector都没有标注注解@Configuration。这个@Import的性质有关。@Imprt注解除了传一个@Configuration类外,同时也支持传ImportSelector或者ImportBeanDefinitionRegistrar的实现类。而AdviceModeImportSelector,正是实现了ImportSelector接口。相较于“注解驱动”,实现接口的方式弹性更大,同时也更为复杂。
- 基于ImportSelector接口的自定义实现
先定义接口,组件以及注解
public interface Server {
void start();
enum ServerType {
HTTP,
FTP
}
}
##################################################################
@Component
public class HttpServer implements Server{
@Override
public void start() {
System.out.println("Http");
}
}
##################################################################
public class FtpServer implements Server {
@Override
public void start() {
System.out.println("FtpServer");
}
}
##################################################################
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Import(ServerImportSelector.class)
public @interface EnableServer {
Server.ServerType type() default Server.ServerType.HTTP;
}
再定义关键的ImportSelector实现类
public class ServerImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String importClassName = "";
Map<String, Object> attribute = importingClassMetadata.getAnnotationAttributes(EnableServer.class.getName());
Server.ServerType type = (Server.ServerType) attribute.get("type");
switch (type) {
case FTP:
importClassName = FtpServer.class.getName();
break;
case HTTP:
importClassName = HttpServer.class.getName();
break;
default:
break;
}
return new String[]{importClassName};
}
}
最后引导类及运行输出
@EnableServer(type = Server.ServerType.FTP)
public class EnableServerBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(EnableServerBootstrap.class);
context.refresh();
Server bean = context.getBean(Server.class);
bean.start();
context.close();
}
}
//输出:FtpServer
- 基于ImportBeanDefinitionRegistrar接口的自定义实现
沿用之前的Server例子,实现ImportBeanDefinitionRegistrar接口
public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ServerImportSelector selector = new ServerImportSelector();
String[] classNames = selector.selectImports(importingClassMetadata);
Stream.of(classNames).map(BeanDefinitionBuilder::genericBeanDefinition)
.map(BeanDefinitionBuilder::getBeanDefinition)
.forEach(definition -> BeanDefinitionReaderUtils.registerWithGeneratedName(definition, registry));
}
}
修改EnableServer,替换@Import
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Import(ServerImportBeanDefinitionRegistrar.class)
public @interface EnableServer {
Server.ServerType type() default Server.ServerType.HTTP;
}
4.@Enable模块驱动原理
在全文的讨论中,我们可以很明显的看到,@Enable模块驱动最重要的就是@Import注解的作用。
从context:annotation-config/标签入手,该标签作用是启用注解。通过Spring Framework “可扩展XML编写”的特性,可知道context:annotation-config/所对应的BeanDefinitionParser实现为AnnotationConfigBeanDefinitionParser。
public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// Obtain bean definitions for all relevant BeanPostProcessors.
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
...
return null;
}
}
这里Spring并未直接进行解析,而是通过AnnotationConfigUtils.registerAnnotationConfigProcessors注册了@Configuration的处理实现,ConfigurationClassPostProcessor。当然,还有其他一些BeanFactoryPostProcessor,不过这里我们重点关注ConfigurationClassPostProcessor
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
...
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...
return beanDefs;
}
在所有bean的配置加载完毕,任何一个bean创建之前调用,Spring会依次调用所有注册到Spring容器中的BeanFactoryPostProcessor类postProcessBeanFactory方法。ConfigurationClassPostProcessor的postProcessBeanFactor中,就包含了对@Import注解的解析。
同样的,处理解析xml标签外,在AnnotationConfigApplicationContext的构造函数中,同样会调用AnnotationConfigUtils.registerAnnotationConfigProcessors注册。
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
...
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
...
}
###############################################################################
public class AnnotatedBeanDefinitionReader {
...
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
...
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
...
}
接着我们重点关注ConfigurationClassPostProcessor#postProcessBeanFactor方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
...
if (!this.registriesPostProcessed.contains(factoryId)) {
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
...
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
...
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
...
// Parse each @Configuration class
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();
...
}
while (!candidates.isEmpty());
...
}
执行期间,最重要的组件莫过于ConfigurationClasParser,它对已注册的Spring BeanDefinition进行注解的解析。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
...
}
this.deferredImportSelectorHandler.process();
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
两个parse的重载方法分别采用CGLIB实现的AnnotationMetadataReadingVisitor和Java反射实现的StandardAnnotationMetadata;
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
...
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
...
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
...
return null;
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
...
finally {
this.importStack.pop();
}
}
}
在processImports中,若@Import的赋值是否为ImportSelector或者ImportBeanDefinitionRegistrar实现,那么会执行ImportSelector和ImportBeanDefinitionRegistrar的处理。同时这里也是一个递归的调用,实现多层次@Import元标注的ConfigurationClass的解析。