美文网首页Spring
Spring组合注解与@Enable*注解

Spring组合注解与@Enable*注解

作者: elijah777 | 来源:发表于2019-06-10 11:15 被阅读0次

Spring组合注解与@Enable*注解

这篇文章主要谈论Spring中的注解,在近端发型的新版本的都是以注解为主,代替了xml配置,这也简化了开发的代码量。

以及开启某项功能的支持用的@Enable*等,是如何实现的。

组合注解

如果一个类或者方法前使用多个注解,也会有些繁琐,Spring设计原则中就是要消除代码

元注解就是可以注解到别的注解上的注解,被注解的注解称为组合注解,Spring很多注解可以作为元注解,而且有很多的组合注解,如@Configuration就是一个组合@Component注解,

代码示例

首先写的小例子字演示一下,通过常用的组合Configuration和ComponentScan元注解为@MyConfiguration

自定义注解


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
​
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
/**
 * 组合Configuration和ComponentScan元注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@ComponentScan
public @interface MyConfiguration {
 // 覆盖value参数
 String[] value() default {};
}

配置类

/**
 * 使用新的注释代替原有 Configuration 和ComponentScan
 */
@MyConfiguration("con.ch3.annotation")
public class DemoConfig {
}

服务类

@Service
public class DemoService {
​
 public void outputResult() {
 System.out.println("通过组合注释配置获得Bean");
 }
}

方法调用入口

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
​
/**
 * @description:
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-06-03 22:17
 */
public class AnnotationMain {
 public static void main(String[] args) {
 AnnotationConfigApplicationContext context =
 new AnnotationConfigApplicationContext(DemoConfig.class);
 DemoService service = context.getBean(DemoService.class);
 service.outputResult();
 context.close();
 }
}

打印结果

onnected to the target VM, address: '127.0.0.1:58117', transport: 'socket'
六月 10, 2019 10:36:25 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@64616ca2: startup date [Mon Jun 10 10:36:25 CST 2019]; root of context hierarchy
六月 10, 2019 10:36:26 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@64616ca2: startup date [Mon Jun 10 10:36:25 CST 2019]; root of context hierarchy
通过组合注释配置获得Bean
Disconnected from the target VM, address: '127.0.0.1:58117', transport: 'socket'
​
Process finished with exit code 0

这样便可以用自定义的注解代替类或者方法比较繁杂的注解,一般公司的研发框架回大量使用这样的组合注解

@Enable*

接下来再介绍另外一种开启某方法的注解,@EnableAsync ,@EnableScheduling等等,分为三类不同的方法,开启特定的功能

第一类直接导入配置类
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.SchedulingConfiguration;
​
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}
package org.springframework.scheduling.annotation;
​
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.scheduling.config.TaskManagementConfigUtils;
​
/**
 * {@code @Configuration} class that registers a {@link
 * ScheduledAnnotationBeanPostProcessor} bean capable of processing Spring's @{@link
 * Scheduled} annotation.
 *
 * <p>This configuration class is automatically imported when using the @{@link
 * EnableScheduling} annotation.  See {@code @EnableScheduling} Javadoc for complete usage
 * details.
 *
 * @author Chris Beams
 * @since 3.1
 * @see EnableScheduling
 * @see ScheduledAnnotationBeanPostProcessor
 */
@Configuration
public class SchedulingConfiguration {
​
 @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
 return new ScheduledAnnotationBeanPostProcessor();
 }
​
}

该注释首先导入了 SchedulingConfiguration,开启了 Configuration,注册了 ScheduledAnnotationBeanPostProcessor Bean

第二类依据条件选择配置类
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.AsyncConfigurationSelector;
​
/**
 *      第二类:依据条件选择配置类
 *          AsyncConfigurationSelector 通过条件来选择需要导入的配置类
 *          继承 AdviceModeImportSelector  又实现了ImportSelector接口
 *           接口重写selectImports方法 进行事先条件判断
 *            PROXY 或者 ASPECTJ 选择不同的配置类
 *
 * @author ShenShuaihu
 * @version 1.0
 * @date  23:26
 */
​
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
 Class<? extends Annotation> annotation() default Annotation.class;
​
 boolean proxyTargetClass() default false;
​
 AdviceMode mode() default AdviceMode.PROXY;
​
 int order() default 2147483647;
}

AsyncConfigurationSelector类

package org.springframework.scheduling.annotation;
​
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
​
/**
 * Selects which implementation of {@link AbstractAsyncConfiguration} should be used based
 * on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class.
 *
 * @author Chris Beams
 * @since 3.1
 * @see EnableAsync
 * @see ProxyAsyncConfiguration
 */
public class AsyncConfigurationSelector extends AdviceModeImportSelector<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
 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;
 }
 }
​
}

ImportSelector 接口

package org.springframework.context.annotation;
​
import org.springframework.core.type.AnnotationMetadata;
​
public interface ImportSelector {
​
 /**
 * Select and return the names of which class(es) should be imported based on
 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
 */
 String[] selectImports(AnnotationMetadata importingClassMetadata);
​
}

接口实现的方法

@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
 Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
 AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
 Assert.notNull(attributes, String.format(
 "@%s is not present on importing class '%s' as expected",
 annoType.getSimpleName(), importingClassMetadata.getClassName()));
​
 AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
 String[] imports = selectImports(adviceMode);
 Assert.notNull(imports, String.format("Unknown AdviceMode: '%s'", adviceMode));
 return imports;
}
​
//  selectImports 方法
​
@Override
 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;
 }
 }

AsyncConfigurationSelector 通过条件来选择需要导入的配置类, 继承 AdviceModeImportSelector 又实现了ImportSelector接口,接口重写selectImports方法 进行事先条件判断 PROXY 或者 ASPECTJ 选择不同的配置类

第三类动态注册Bean
import org.springframework.context.annotation.Import;
​
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
​
 boolean proxyTargetClass() default false;
​
}

AspectJAutoProxyRegistrar类

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
​
/**
 * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
 * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry}
 * as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation.
 *
 * @author Chris Beams
 * @since 3.1
 * @see EnableAspectJAutoProxy
 */
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
​
 /**
 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
 * {@code @Configuration} class.
 */
 @Override
 public void registerBeanDefinitions(
 AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
​
 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
​
 AnnotationAttributes enableAJAutoProxy =
 AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
 if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
 }
 }
​
}

registerOrEscalateApcAsRequired方法

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
 Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
 if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
 BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
 if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
 int requiredPriority = findPriorityForClass(cls);
 if (currentPriority < requiredPriority) {
 apcDefinition.setBeanClassName(cls.getName());
 }
 }
​
 return null;
 } else {
 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
 beanDefinition.setSource(source);
 beanDefinition.getPropertyValues().add("order", -2147483648);
 beanDefinition.setRole(2);
 registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
 return beanDefinition;
 }
}

AspectJAutoProxyRegistrar 事先实现了ImportBeanDefinitionRegistrar 接口

作用是在运行时自动添加Bean到已有的配置类,通过重写 registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2)

AnnotationMetadata 获取当前配置类上的注解,BeanDefinitionRegistry 用来注册Bean

更多参考:

Springboot @Enable*注解的工作原理 https://www.jianshu.com/p/1241c1079dd6

相关文章

网友评论

    本文标题:Spring组合注解与@Enable*注解

    本文链接:https://www.haomeiwen.com/subject/ctgaxctx.html