背景
- 大量的XML让开发者厌恶,因此Spring提供了许多注解来完成各种功能。
- 大量的注解让开发者厌恶 ,因此Spring提供了组合注解来完成各种功能。
举例
-
最典型的SpringMVC中我们使用如下注解 @Controller
事实上我们接触到Controller时都会如下说明Controller Service Repository事实上和Component 没有差别 但是Spring后期可能可能条件不同的语义 因此注意使用
/** * Indicates that an annotated class is a "Controller" (e.g. a web controller). * * <p>This annotation serves as a specialization of {@link Component @Component}, * allowing for implementation classes to be autodetected through classpath scanning. * It is typically used in combination with annotated handler methods based on the * {@link org.springframework.web.bind.annotation.RequestMapping} annotation. * * @author Arjen Poutsma * @author Juergen Hoeller * @since 2.5 * @see Component * @see org.springframework.web.bind.annotation.RequestMapping * @see org.springframework.context.annotation.ClassPathBeanDefinitionScanner */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any */ String value() default ""; }
-
我们把修饰其他注解的注解称之为Meta Annotation【元注解】
A meta-annotation is an annotation that is declared on another annotation. An annotation is therefore meta-annotated if it is annotated with another annotation. For example, any annotation that is declared to be documented is meta-annotated with @Documented
from thejava.lang.annotation
package.
-
当我们业务越发复杂的情况下我们会大量的使用注解进行组合 比如
@RestController @RequestMapping("user") public class TbUserController extends AbstractRestController<TbUserVo, TbUserSo, BigInteger> { @Resource private TbUserService tbUserService; }
这个注解还比较少,但是对于SpringBoot 来说可能注解的量会直线上升,比如
@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
-
那么由于如上的问题带来了一个问题 可能一个简单的类中的代码比注解还要少!
-
我们通过组合注解的方式来解决此问题
我们声明如下
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
Spring注解编程模型
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model
对于Spring来说通常定义的注解是要适配原有的功能 比如Controller注解可以声明Bean的名称
但是一旦定义的组合注解的名称相同自然就会存在如下的问题【覆盖】
Spring提供了AliasFor注解来完成别名的转换 比如
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude") Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName") String[] excludeName() default {}; /** * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses} * for a type-safe alternative to String-based package names. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. * <p> * Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
通过如上方式可以指定到元注解的属性【比如SpringBootApplication的scanBasePackageClasses ===》ComponentScan的basePackageClasses】