Spring注解-1-SpringBoot是如何处理注解的

本文基于Spring 5.2.7

这是个很大的话题,但是是个非常实在的话题,注解天天用,处处用,请问你知道他是怎么起作用的吗?
如果你使用了注解,那么一定有代码在什么地方检索这个注解,并为这个注解的语义写了对应的逻辑。但是注解本身是没有任何行为的,他只是一个标记而已。所以注解是惰性的,看到注解时并没有行为,你需要找到注解对应的行为才能理解注解实际的作用。
但是要找到一个注解对应的行为很难,因为很多注解都是些三方库提供的,要想找到每个注解对应的行为有如大海捞针,能搞清楚这个知识点很难。

一、SpringBoot启动

SpringBoot要解析哪些注解在代码中已经写死了,所有注解的解析都是按照既定的顺序在执行,如@Configuration,@Import,这些都会在启动过程中解析,所以一切的起点是启动类上,但是,我们会看到启动类上往往是一个汇总的注解,如@EnableTransactionManagement,然后这个类上面会引入其他注解,那Spring是怎么解析到他引入的注解的呢?

原理就是Spring在扫描注解是一种递归的方式,比如在处理@Import注解时,就会递归处理:
org.springframework.context.annotation.ConfigurationClassParser#collectImports(...)

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
		throws IOException {

	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

这是Spring在启动解析配置时,递归解析注解,这里的sourceClass就是启动类,然后递归检查启动类上的@Import注解,所以才能检查到注解上的注解。

一般而言,spring会扫描所有的类,而且会扫描这些类上面的注解,Spring自身提供了很多注解,你写了Spring提供的注解,他就会扫描到,进而执行响应的动作。

你可以在启动类上写很多注解,必须要写的注解是org.springframework.boot.autoconfigure.SpringBootApplication
我们已经写了main()方法,SpringBoot已经自主启动运行,此时他会回过头来扫描启动类上的注解并解析,其实SpringBootApplication这个注解本身并没有语义,他是一个复合注解,用于引入其他的注解,这就要求Spring在扫描注解时能够递归扫描,实际上Spring就是这么做的。在判断一个bean上是否有某个注解时,Spring都是递归解析注解的。或者说所有的复合注解都是没有语义的,有语义的是复合注解上的单个注解。​​​​​​​

@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)
	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)
	String[] excludeName() default {};

	/**
	 * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
	 * for a type-safe alternative to String-based package names.
	 * <p>
	 * <strong>Note:</strong> this setting is an alias for
	 * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity}
	 * scanning or Spring Data {@link Repository} scanning. For those you should add
	 * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and
	 * {@code @Enable...Repositories} annotations.
	 * @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.
	 * <p>
	 * <strong>Note:</strong> this setting is an alias for
	 * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity}
	 * scanning or Spring Data {@link Repository} scanning. For those you should add
	 * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and
	 * {@code @Enable...Repositories} annotations.
	 * @return base packages to scan
	 * @since 1.3.0
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	/**
	 * Specify whether {@link Bean @Bean} methods should get proxied in order to enforce
	 * bean lifecycle behavior, e.g. to return shared singleton bean instances even in
	 * case of direct {@code @Bean} method calls in user code. This feature requires
	 * method interception, implemented through a runtime-generated CGLIB subclass which
	 * comes with limitations such as the configuration class and its methods not being
	 * allowed to declare {@code final}.
	 * <p>
	 * The default is {@code true}, allowing for 'inter-bean references' within the
	 * configuration class as well as for external calls to this configuration's
	 * {@code @Bean} methods, e.g. from another configuration class. If this is not needed
	 * since each of this particular configuration's {@code @Bean} methods is
	 * self-contained and designed as a plain factory method for container use, switch
	 * this flag to {@code false} in order to avoid CGLIB subclass processing.
	 * <p>
	 * Turning off bean method interception effectively processes {@code @Bean} methods
	 * individually like when declared on non-{@code @Configuration} classes, a.k.a.
	 * "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore behaviorally
	 * equivalent to removing the {@code @Configuration} stereotype.
	 * @since 2.2
	 * @return whether to proxy {@code @Bean} methods
	 */
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

SpringBoot的run()方法里已经执行了很多事情,他里面就会扫描所有bean,并处理相关注解。启动类上面的注解此时也会被处理。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值