目录

说明

一、引用值的注解

1. @Value

2. @ConfigurationProperties

3. @AliasFor

二、使用功能的注解

1. @Slf4j

2. @EnableScheduling

3. @Scheduled

4. @EnableAutoConfiguration

5. @RestController

6. @PostMapping、@GetMapping、@DeleteMapping、@PutMapping

7. @EnableConfigurationProperties

三、引入类的注解

1. @Import

2. @ServletComponentScan

3. @MapperScan

四、处理逻辑的注解

1. @Conditional

五、修饰的注解

1. @Retention

六、便捷封装的注解

1. @Repeatable

七、其他

1. @SpringBootConfiguration


说明

本文按自己理解,将注解进行归类。仅代表个人观点,非大众观点。自行食用。

  • 引用值:引用别处的值,用在其他地方。
  • 使用功能:开启某项功能,比如日志、任务调度等。
  • 引入类:引入某些类。
  • 处理逻辑:需要系统去判断的。
  • 修饰:作修饰说明的。
  • 便捷封装

一、引用值的注解

1. @Value

(1)应用场景

注解在字段。

读取配置文件中单个字段的值。

(2)代码举例

spring:application:name: SpringBootDemo

上边yml配置文件,下边控制器方法获取打印

    @Value("${spring.application.name}")private String applicationName;/*** 登录* <p>** @param employeeDomain 员工实体类* @return org.springframework.web.servlet.ModelAndView* @author ZRH* @date 2020-08-18* @version 1.0.0*/@PostMapping(value = "/login")public RestResultDTO<Object> login(@RequestBody EmployeeDomain employeeDomain, HttpSession session) {log.info(applicationName);return handleMessage(employeeService.checkAccount(employeeDomain, session), session.getAttribute(CommonConstant.LOGIN_EMPLOYEE));}

2. @ConfigurationProperties

(1)应用场景

注解在指定类,引用类用@Autowired。

读取配置文件中多个字段的值,映射到指定类(用prefix指定),记得setter注入,得写个setter方法。或者像我用@Data。

(2)代码举例

dog:name: 旺财host: "胖\t虎"friend: '二\t狗'
/*** 将配置文件中配置的每一个属性的值,映射到这个组件中* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;* prefix = "dog":配置文件中哪个下面的所有属性进行一一映射** 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能;* @author ZRH* @version 1.0.0* @date 2020/9/1*/@Data
@Component
@ConfigurationProperties(prefix = "dog")
public class DogDomain {private String name;private String host;private String friend;}
/*** 测试** @author ZRH* @version 1.0.0* @date 2020/8/31*/
@RestController
@RequestMapping
public class MyController {@Autowiredprivate DogDomain dogDomain;@GetMapping("/index")public ModelAndView goIndex(ModelAndView modelAndView) {System.out.println(dogDomain);modelAndView.setViewName("index");return modelAndView;}
}

3. @AliasFor

(1)应用场景

注解在注解属性上

作用:

  • 定义一个注解的属性,可以被另一个注解接收到
  • 相同注解内两属性通用,简略

用法:

  1. 定义一个注解中的两个属性互为别名
  2. 也可以不同注解的属性声明别名

(2)代码举例

像@ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};// ...
}

互为别名,value和basePackages作用就一样了。这样的话,如果我们只需要指定basePackages,就可以使用value属性代替(还可以省略value):@ComponentScan("com.winrh")。而多个属性时,可以定义@ComponentScan(basePackages = "com.winrh", lazyInit = true),这样条理更加清晰。


像@SpringBootApplication

public @interface SpringBootApplication {@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};@AliasFor(annotation = ComponentScan.class,attribute = "basePackages")String[] scanBasePackages() default {};@AliasFor(annotation = ComponentScan.class,attribute = "basePackageClasses")Class<?>[] scanBasePackageClasses() default {};@AliasFor(annotation = ComponentScan.class,attribute = "nameGenerator")Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;
}

就用了@ComponentScan、@EnableAutoConfiguration、@Configuration的属性。再通过AnnotatedElementUtils.getMergedAnnotation方法,就可以将别名的值赋给另一个的注解的属性。这样,即使我定义的是SpringBootApplication的属性,但是@ComponentScan、@EnableAutoConfiguration、@Configuration都收到了


二、使用功能的注解

1. @Slf4j

(1)应用场景

注解在类上。

使用日志,这里日志抽象层用slf4j,具体的日志看你配置。配合lombok,可以省去自己创建Logger对象,直接调用log对象即可。

(2)代码举例

/*** 登录控制器类** @author ZRH* @version 1.0.0* @date 2020/8/18*/
@Slf4j
@RestController
public class LoginController extends BaseController {@AutowiredEmployeeService employeeService;@Value("${spring.application.name}")private String applicationName;/*** 登录* <p>** @param employeeDomain 员工实体类* @return org.springframework.web.servlet.ModelAndView* @author ZRH* @date 2020-08-18* @version 1.0.0*/@PostMapping(value = "/login")public RestResultDTO<Object> login(@RequestBody EmployeeDomain employeeDomain, HttpSession session) {log.info(applicationName);return handleMessage(employeeService.checkAccount(employeeDomain, session), session.getAttribute(CommonConstant.LOGIN_EMPLOYEE));}
}

2. @EnableScheduling

(1)应用场景

注解在入口类上。

意为启用任务调度、定时任务。启用后,再在自己定义的定时任务类上注解@Component,并在其方法上注解@Scheduled,到时候生成上下文之前会扫描所有@Scheduled注解(详情见自动配置章节)。

(2)代码举例

@SpringBootApplication
@EnableScheduling
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

上边为入口类,下边为自定义的定时任务类

/*** 定时备份* @author ZRH* @version 1.0.0* @date 2020/9/4*/
@Slf4j
@Component
public class DumpTask {@Scheduled(cron = "*/10 * * * * ?")public void cron(){log.info("每隔10秒执行一次");}@Scheduled(fixedDelay = 4000)public void fixedDelay(){log.info("循环调用fixedDelay,延迟为4s");}
}

3. @Scheduled

见@EnableScheduling

4. @EnableAutoConfiguration

(1)应用场景

这在Spring Boot入口类的@SpringBootApplication注解中见到。目的是开启Spring Boot自动扫描配置类的功能,让其尝试根据你添加的jar依赖自动配置你的Spring应用

例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连接beans,那么其将自动配置一个内存型(in-memory)数据库。

(2)代码举例

看看注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

AutoConfigurationImportSelector是DeferredImportSelector实现类

进入AutoConfigurationImportSelector#selectImports方法(可结合下文的@Import理解,导入的流程可参考我的第二章):

    public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}

根据annotationMetadata进行模式的选择,是不导入,还是导入。导入则跳到AutoConfigurationImportSelector#getAutoConfigurationEntry方法:

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}}

里面有个AutoConfigurationImportSelector#getCandidateConfigurations

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());// ...return configurations;}

很明显,调用了SpringFactoriesLoader#loadFactoryNames去你项目的META-INF/spring.properties找配置文件,再去Spring Boot的jar包里找 @EnableAutoConfiguration对应的ConfigurationClass配置文件(详见我写的第二章)。

找到之后,每一个自动配置类结合对应的xxxProperties.java读取配置文件进行自动配置功能。如下面这些:

咱找一个,AOP的:

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Advice.class)static class AspectJAutoProxyingConfiguration {@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",matchIfMissing = false)static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class CglibAutoProxyConfiguration {}}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.aspectj.weaver.Advice")@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class ClassProxyingConfiguration {ClassProxyingConfiguration(BeanFactory beanFactory) {if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}}}}

看到这个@ConditionalOnProperty了吗,prefix+name就是你需要启动aop,就在全局配置文件写上的属性,比如这里的spring.aop.auto=true,你设置了true,那它就解析这个配置类。

继续找个WebMvcConfiguration

建议看我的第十五章自定义starter,就容易理解了。


自定义spring.properties里就可以写:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xxx.MyConfiguration

然后进行配置类去重:

configurations = this.removeDuplicates(configurations);

再排除spring.autoconfigure.exclude配置的ConfigurationClass:

Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);

最后过滤掉一些配置类:

configurations = this.getConfigurationClassFilter().filter(configurations);

使用spring.factories中配置的AutoConfigurationImportFilter的实现类(OnBeanCondition,OnClassCondition,OnWebApplicationCondition)过滤部分ConfigurationClass,这里处理@ConditionalOnBean,@ConditionalOnClass,@ConditionalOnMissingClass等注解。

OnClassCondition可以判断当前Java环境中存在或者不存在某一个classSpringBoot 自动配置功能可以实现当我们引入某个框架jar后,自动配置完成该框架的配置正是通过该条件判断类实现

5. @RestController

(1)应用场景

注解在类上

Spring4之后新加入的注解,用于标注控制层组件,返回JSON视图再也不用老是在方法注解@ResponseBody,方便REST开发。

/*** 文件上传控制器* @author ZRH* @version 1.0.0* @date 2020/9/3*/
@Slf4j
@RestController
public class UploadController extends BaseController{@Autowiredprivate UploadService uploadService;@PostMapping("/upload")public RestResultDTO<Long> upload(HttpSession session, MultipartFile multipartFile){return handleMessage(uploadService.uploadFile(session.getServletContext().getRealPath("/") + "upload/", multipartFile));}
}

(2)代码举例

瞄一下@RestController,自带@ResponseBody,妙啊

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {@AliasFor(annotation = Controller.class)String value() default "";
}

6. @PostMapping、@GetMapping、@DeleteMapping、@PutMapping

(1)应用场景

REST风格开发,代替繁琐的设置method属性

(2)代码举例

/*** 文件上传控制器* @author ZRH* @version 1.0.0* @date 2020/9/3*/
@Slf4j
@RestController
public class UploadController extends BaseController{@Autowiredprivate UploadService uploadService;@PostMapping("/upload")public RestResultDTO<Long> upload(HttpSession session, MultipartFile multipartFile){return handleMessage(uploadService.uploadFile(session.getServletContext().getRealPath("/") + "upload/", multipartFile));}
}

瞄一下@PostMapping

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = {RequestMethod.POST}
)
public @interface PostMapping {@AliasFor(annotation = RequestMapping.class)String name() default "";@AliasFor(annotation = RequestMapping.class)String[] value() default {};@AliasFor(annotation = RequestMapping.class)String[] path() default {};@AliasFor(annotation = RequestMapping.class)String[] params() default {};@AliasFor(annotation = RequestMapping.class)String[] headers() default {};@AliasFor(annotation = RequestMapping.class)String[] consumes() default {};@AliasFor(annotation = RequestMapping.class)String[] produces() default {};
}

已经写好method属性了

7. @EnableConfigurationProperties

(1)应用场景

在自定义starter时,会编写自动配置类,这时就会用@EnableConfigurationProperties来启动参数里的配置类(例子可看第十五章)。

(2)代码举例

比如我的自动配置类:

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(HelloService.class)
@EnableConfigurationProperties(HelloProperties.class)
public class HelloWorldAutoConfiguration {@Autowiredprivate HelloProperties helloProperties;@Bean@ConditionalOnMissingBean(HelloService.class)@ConditionalOnProperty(prefix = "helloworld", value = "enabled", havingValue = "true")HelloService helloService() {return new HelloService(helloProperties);}
}

看下注解内容:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {/*** The bean name of the configuration properties validator.* @since 2.2.0*/String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";/*** Convenient way to quickly register* {@link ConfigurationProperties @ConfigurationProperties} annotated beans with* Spring. Standard Spring Beans will also be scanned regardless of this value.* @return {@code @ConfigurationProperties} annotated beans to register*/Class<?>[] value() default {};}

@Import(EnableConfigurationPropertiesRegistrar.class),这就是在初始化上下文时,ConfigurationClassParser解析配置类,会解析@Import。最后loadBeanDefinitions注册beanDefinition。


三、引入类的注解

对象的创建必须通过构造方法创建,分无参和有参构造器。基于spring体系的创建Bean注解有:@Configuration里的@Bean、@Component、@Controller、@Service、@Repository等。

如果想自定义一些初始设置比较复杂的bean时,可以在类上用@Configuration注解,然后类内部在返回具体bean的方法上使用@Bean注解。那么,要让容器找到这个配置类,并让容器进行管理,方法有这么几种:

① 在@ComponentScan指定的配置类所在包

② 用@Import来导入一个或多个类(会被spring容器管理),或者配置类(配置类里的@Bean标记的类也会被spring容器管理)

  • 直接指定要引入的配置类,如:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}
  • 或者引入实现了ImportSelector接口的类

如果并不确定引入哪个配置类,需要根据@Import注解所标识的类或者另一个注解(通常是注解)里的定义信息选择配置类的话,用这种方式。例如@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

是通过AutoConfigurationImportSelector类,根据注解@EnableTransactionManagement所指定的AnnotationMetadata来选择使用哪个配置类的。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
  • 或者是指定实现了ImportBeanDefinitionRegistrar接口的类

如果是第三方包,而且又不是确定的类,并且这些类并不是spring专用,因而不想用spring的注解进行侵入式标识时,那么,如何找到这些类放到spring的容器呢?这就用到了注解@Import引入ImportBeanDefinitionRegistrar子类的方式,最典型的应用就是mybatis,使用工具自动生成了一批mapper和entity。而如何把这些普通的类放入容器,就是通过注解。典型代表@MapperScan

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {String[] value() default {};String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;Class<? extends Annotation> annotationClass() default Annotation.class;Class<?> markerInterface() default Class.class;String sqlSessionTemplateRef() default "";String sqlSessionFactoryRef() default "";Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;String lazyInitialization() default "";
}

这个注解用@Import引入了MapperScannerRegistrar类,这个类里会取得注解@MapperScan作设置的package,然后扫描这个package下所有的类,并放入容器中。

1. @Import

用来导入一个或多个类(会被spring容器管理),或者配置类(配置类里的@Bean标记的类也会被spring容器管理)。详见上方介绍。

2. @ServletComponentScan

(1)应用场景

注解在入口类上,并指定servlet所在包即可扫描。

(2)代码举例

例如,我写了个过滤器:

然后指定包:

@SpringBootApplication
@MapperScan("com.winrh.mapper")
@ServletComponentScan("com.winrh.filter")
@EnableScheduling
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

看看@ServletComonentScan

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ServletComponentScanRegistrar.class})
public @interface ServletComponentScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};Class<?>[] basePackageClasses() default {};
}

原来是用@Import引入了ServletComponetScanRegistrar,可参考上文提及的@Import的三种方式。

3. @MapperScan

在上文@Import的三种方式中提及了。扫描MyBatis的接口及映射文件

使用场景:

@SpringBootApplication
@MapperScan("com.winrh.mapper")
@ServletComponentScan("com.winrh.filter")
@EnableScheduling
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

四、处理逻辑的注解

1. @Conditional

(1)应用场景

可注解在配置类的方法上,也可注解在配置类上。

当判断正确,则选用该方法(注解类上则是选用该类的所有方法)

(2)代码举例

先定义两个实现了Condition接口的条件类:

/*** 测试@Conditional* @author ZRH* @version 1.0.0* @date 2020/9/8*/
public class NeverFalse implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {return true;}
}
/*** 测试@Conditional* @author ZRH* @version 1.0.0* @date 2020/9/8*/
public class NeverTrue implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {return false;}
}

接着定义一个配置类:

/*** 测试@Conditional* @author ZRH* @version 1.0.0* @date 2020/9/8*/
@Configuration
public class TestConfig {@Conditional(NeverTrue.class)@Beanpublic CategoryDomain getDrinkCategory() {return new CategoryDomain(1, "酒水");}@Conditional(NeverFalse.class)@Beanpublic CategoryDomain getVegetableCategory() {return new CategoryDomain(2, "鲜蔬");}
}

这样,鲜蔬通过,酒水不通过。测试下:

@SpringBootTest
class DemoApplicationTests {@Testvoid contextLoads() {ApplicationContext ctx = new AnnotationConfigApplicationContext(TestConfig.class);CategoryDomain categoryDomain = ctx.getBean(CategoryDomain.class);System.out.println(categoryDomain);}}

结果一致。

那么原理是什么呢

我们可以从refreshContext方法进入, 一直到ConfigurationClassPostProcessor#processConfigBeanDefinitions再到ConfigurationClassParser#processConfigurationClass,对配置类进行解析,此时会先检查该配置类是否有@Conditional注解,有就跳过:

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}return;}this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);do {sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);} while(sourceClass != null);this.configurationClasses.put(configClass, configClass);}}

就在ConditionEvaluator#shouldSkip

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {if (metadata != null && metadata.isAnnotated(Conditional.class.getName())) {if (phase == null) {return metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata)metadata) ? this.shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION) : this.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);} else {List<Condition> conditions = new ArrayList();Iterator var4 = this.getConditionClasses(metadata).iterator();while(var4.hasNext()) {String[] conditionClasses = (String[])var4.next();String[] var6 = conditionClasses;int var7 = conditionClasses.length;for(int var8 = 0; var8 < var7; ++var8) {String conditionClass = var6[var8];Condition condition = this.getCondition(conditionClass, this.context.getClassLoader());conditions.add(condition);}}AnnotationAwareOrderComparator.sort(conditions);var4 = conditions.iterator();Condition condition;ConfigurationPhase requiredPhase;do {do {if (!var4.hasNext()) {return false;}condition = (Condition)var4.next();requiredPhase = null;if (condition instanceof ConfigurationCondition) {requiredPhase = ((ConfigurationCondition)condition).getConfigurationPhase();}} while(requiredPhase != null && requiredPhase != phase);} while(condition.matches(this.context, metadata));return true;}} else {return false;}}

五、修饰的注解

1. @Retention

(1)应用场景

常看到注解里有@Retention,故来说说。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {/*** Returns the retention policy.* @return the retention policy*/RetentionPolicy value();
}

有个属性,为RetentionPolicy类型,看下源码:

public enum RetentionPolicy {// 在编译的时候,注解被编译器抛弃SOURCE,// 默认。注解被编译器保存在class文件,但JVM加载class文件时注解不保留CLASS,// 注解保存在class文件,且JVM加载class文件时仍保留RUNTIME
}

(2)代码举例

  • 像lombok的@Data是SOURCE级别
package lombok;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.SOURCE)
public @interface Data {String staticConstructor() default "";
}
  • 像Spring的@Autowired是RUNTIME级别
package org.springframework.beans.factory.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;@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {boolean required() default true;
}

六、便捷封装的注解

1. @Repeatable

(1)应用场景

注解在注解上

JDK8新增注解。将多个注解替换为一个数组注解

(2)代码举例

可以看看@ComponentScan注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {// ...
}

再看ComponentScans.class

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {ComponentScan[] value();
}

ComponentScans中value属性是一个ComponentScan数组,这里@Repeatable表示当配置了多个@ComponentScan时,@ComponentScan可以被@ComponentScans代替JDK8中支持重复的注解


七、其他

1. @SpringBootConfiguration

(1)应用场景

应用在@SpringBootApplication里,本身又包含@Configuration注解,表明可作配置类,可以写@Bean来玩。

(2)代码举例

@SpringBootApplication
@MapperScan("com.winrh.mapper")
@ServletComponentScan("com.winrh.filter")
@EnableScheduling
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

入口类注解了@SpringBootApplication,而后者又包含@SpringBootConfiguration

@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 {// ...
}

再看

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}

有个@Configuration注解,以及与Configuration的proxyBeanMethods互为别名的proxyBeanMethods属性。

这样一来,入口类就可以作配置类,可以这么玩:

@SpringBootApplication
@MapperScan("com.winrh.mapper")
@ServletComponentScan("com.winrh.filter")
@EnableScheduling
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@Beanpublic CategoryDomain getCategory(){return new CategoryDomain(3, "酒水");}}

控制器类:

    @AutowiredCategoryDomain categoryDomain;/*** 登录* <p>** @param employeeDomain 员工实体类* @return org.springframework.web.servlet.ModelAndView* @author ZRH* @date 2020-08-18* @version 1.0.0*/@PostMapping(value = "/login")public RestResultDTO<Object> login(@RequestBody EmployeeDomain employeeDomain, HttpSession session) {log.info(categoryDomain.toString());return handleMessage(employeeService.checkAccount(employeeDomain, session), session.getAttribute(CommonConstant.LOGIN_EMPLOYEE));}

效果就有了


待更新...

【SpringBoot】十四、常见注解(场景及源码)相关推荐

  1. 《视觉SLAM十四讲》第二版源码slambook2编译调试

    slambook2-master/ch2 编译正常,log如下: slambook2-master/ch2# mkdir build && cd build && cm ...

  2. 【DVWA系列】十四、JavaScript 攻击(源码分析漏洞利用)

    文章目录 DVWA CSP Bypass 绕过浏览器的安全策略 一.Low 级别 二.Medium 级别 三.High 级别 四.Impossible 级别 DVWA CSP Bypass 绕过浏览器 ...

  3. 深入理解Spark 2.1 Core (十四):securityManager 类源码分析

    securityManager主要用于权限设置,比如在使用yarn作为资源调度框架时,用于生成secret key进行登录.该类默认只用一个实例,所以的app使用同一个实例,下面是该类的所有源代码: ...

  4. Spring注解开发【源码分析】

    一.注册组件 @ComponentScan 扫描指定包下的组件. String[] value():扫描基础包范围 Filter[] includeFilters():扫描并获取符合过滤规则的组件 F ...

  5. Java计算机毕业设计四六级在线考试系统源码+系统+数据库+lw文档

    Java计算机毕业设计四六级在线考试系统源码+系统+数据库+lw文档 Java计算机毕业设计四六级在线考试系统源码+系统+数据库+lw文档 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 ...

  6. springboot+vue+mysql外卖点餐管理系统源码(包安装+讲解)

     博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流.专注于Java技术领域和毕业设计✌ 项目名称 springboot+vue+mysql外卖点餐管理系统源码(包安装+讲解) 视 ...

  7. 基于 SpringBoot + Vue 的物流管理系统(附源码)

    国庆期间哪也没去,在家闲来无事,写了一个基于 SpringBoot + Vue 的物流管理系统,把源码分享给大家,在文章结尾处,自行获取即可~ 一.介绍 基于Java的物流管理系统. 二.软件架构 系 ...

  8. 计算机毕业设计springboot基于Springboot的手机电商网站lmo47源码+系统+程序+lw文档+部署

    计算机毕业设计springboot基于Springboot的手机电商网站lmo47源码+系统+程序+lw文档+部署 计算机毕业设计springboot基于Springboot的手机电商网站lmo47源 ...

  9. @Async注解测试用例附源码(一)

    @Async注解测试用例附源码(一) 问题背景 @Async注解测试用例附源码(一) @Async注解异步线程不生效解决方案(二) @Async测试用例 问题总结 测试用例源码下载 Lyric: 我在 ...

  10. 关于常见的底层驱动源码资料

    Linux有很多常见的底层源码,获取方式,官网,或者github,常见的驱动底层源码都会有

最新文章

  1. Flutter 布局控件完结篇
  2. Apache POI和EasyExcel 第七集:EasyExcel的基本操作,读取和写入Excel,一行足矣
  3. 识别不了socket未知的名称或服务
  4. 《HTML5+CSS3网页设计入门必读》——1.7 使用FTP传输文件
  5. IOS学习笔记之十一(包装类、description、isequal)
  6. 初中生学计算机网络应用怎么样,初中生读计算机网络技术专业怎么样?小编解答...
  7. vue webpack打包入口文件是哪个_Vue 学习笔记(二十五):webpack 相关
  8. linux用call调存储过程,存储过程调用其他模式的存储过程需要注意的地方
  9. Visual Studio Code 1.45 发布
  10. beautifulsoup 搜索第二个标签_自定义网站搜索教程
  11. 《机器人编程实战》一一2.1 为什么需要更多努力
  12. 数据库设计以及PD数据导入数据库
  13. 《乔布斯传.神一样的传奇》读后感
  14. websockets_Websockets在数据工程中鲜为人知的模式
  15. 非负矩阵分解(NMF)
  16. 232串口测试方法介绍
  17. Drf从入门到精通一(API接口、Postman、Restful规范、序列化、快速使用drf、CBV源码分析)
  18. HTML table表格 固定表头 tbody加滚动条
  19. 前端装逼技巧 108 式(一)—— 打工人
  20. 大学里可以考的证书有哪些?

热门文章

  1. pgsql处理文档类型数据_PostgreSQL 基本数据类型及常用SQL 函数操作
  2. JAVA的3DES加密代码转成oc_iOS之DES加密
  3. 矩阵的秩和向量组的秩
  4. 第二讲 单片机C语言之12864液晶显示
  5. created()和activated()的区别
  6. UBOOT移植详细 很全面
  7. 主数据方法论之数据分发与共享
  8. Linux驱动开发-编写PCF8591(ADC)芯片驱动
  9. vcu整车simulink模型
  10. 使用moment计算两个日期的相差天数