Spring Framework的IOC(控制反转)和DI(依赖注入)目的是为了应用系统不应以Java代码的方式直接控制依赖关系,而是通过容器来加以管理。
因为当时JAVA5的注解特性尚未发布,所以通过XML文件的方式管理Bean之间的依赖关系

注解驱动发展史

注解驱动启蒙时代:Spring Framework 1.x

框架层已经支持@ManagedResource和@Transactional等注解,但被注解的Spring Bean的装配仍然需要使用XML方式

注解驱动过渡时代:Spring Framework 2.x

  • 2.0添加了Bean相关的@Required、数据相关的@Repository及AOP相关的@Aspect等

  • 2.5添加了重要的骨架式注解
    1.依赖注入:@Autowired
    2.依赖查找:@Qualifier
    3.组件声明:@Component、@Service
    4.Spring MVC模块注解:@Controller、@RequestMapping、@ModelAttribute
    5.支持JSR-250规范的@Resource注入、@PostConstruct和@PreDestory生命周期回调注解
    6.允许自定义Spring模式注解

2.x未完全替换XML配置驱动,框架层仍未直接提供驱动注解的Spring应用上下文,仍需要XML配置驱动

注解驱动黄金时代:Spring Framework 3.x

3.x添加大量特性功能全面支持Java5新功能、添加了大量的新注解

  • 3.0替换XML配置方式
    1.@Configuration注解用于替换XML配置
    2.@Bean、@DependsOn、@Lazy、@Primary替代XML属性标签

  • 3.1替换xml元素扫描bean标签
    1.@ComponentScan替换XML的scan标签元素
    2.@Role设置Bean角色
    3.@Profile条件话Bean定义

  • 3.x
    1.在Web方面添加了大量的处理注解@RequestHeader、@CookieValue、@RequestPart
    2.开辟了REST开发路线,@PathVariable、@ResponseBody、@ResponseStatus
    3.整合了Servlet 3.0+规范,实现传统Servlet容器自动装配的能力
    4.@Enable注解,将相同职责的功能组件以模块化的方式装配,极大简化Spring Bean配置

注解驱动完善时代:Spring Framwwork 4.x

  • 4.0
    1.条件化注解@Conditional
    2.将@PropertySource升级为可重复标注注解

  • 4.2
    @AliasFor

  • 4.3
    1.将@ComponentScan提升为@CompontScans
    2.通过@AliasFor引入@GetMapping作为@RequestMapping派生注解
    3.@PostMapping、@RestController
    4.@RestControllerAdvice

  • 4.x
    1.依赖查找注解@LookUp

注解驱动当下时代:Spring Framework5.x

大量使用注解@ComponentScan扫描指定的package,扫描的类越多越耗时,新注解@Indexed为Spring模式注解添加索引,提升启动性能
当Spring启动上下文执行@CompontScan扫描时,@CompontScan不再扫描指定的package,而是读取CandidateComponentsIndex对象

Spring核心注解场景分类

按照Spring Framework版本由低到高,重要性由高到底,排除功能性注解(MVC)

Spring注解模式

  • @Repository:数据仓储模式注解
  • @Component:通用组件注解
  • @Service:服务模式注解
  • @Controller:Web控制器模式注解
  • @Configuration:配置类模式注解

装配注解

  • @ImportResource:替换XML元素
  • @Import:限定@Autowired依赖注入范围
  • @ComponentScan:扫描指定package下的Spring模式注解的类

依赖注入注解

  • @Autowired:Bean依赖注入,支持多种依赖查找方式
  • @Qualifier:细粒度的@Autowired依赖查找
  • @Resource:Java注解的Bean依赖注入,仅支持名称依赖查找方式

Bean定义注解

  • @Bean:替换XML元素
  • @DependsOn:替代XML属性
  • @Lazy:替代XML属性
  • @Primary:替换XML元素
  • @Role:替换XML元素<bean role=..."/>
  • @Lookup:替代XML属性<bean lookup-method"..."/>

Spring条件装配注解

  • @Profile:配置化条件装配
  • @Conditional:编程条件装配

配属属性注解

  • @PropertySource:配置属性抽象PropertySource注解
  • @PropertySources:@PropertySource集合注解

生命周期回调注解

  • @PostConstruct:替换XML元素
  • @PreDestory:替换XML元素

注解属性注解

  • @AliasFor:别名注解属性,复用

性能注解

  • @Indexed:提升Spring注解Bean扫描效率

Spring注解编程模型

https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model
image.png
Spring Framework注解编程模型是Spring Boot注解驱动及自动装配的重要前提

  • 元注解(Meta-Annotations)
  • Spring模式注解(Stereotype Annotations)
  • Spring组合注解(Composed Annotations)
  • Spring注解属性别名和覆盖(Attribute Aliase & Overrides)

元注解

image.png
元注解是指一个能声明在其他注解上的注解,例如@Documented可作为任何注解的元注解

Spring模式注解

image.png
通俗来说Spring模式注解即@Component“派生”注解,由于Java语言规范,注解不允许继承、没有派生子类的能力。因此Spring Framework都过元标注方式实现注解之间的派生

@Component作为由Spring容器托管的通用模式组件,任何被@Componet标注的组件均为组件扫描的候选对象,凡是被@Component元标注的注解,例如@Service,也被视作组件扫描的对象
image.png

理解@Component“派生性”

在Spring Framwork 2.5中,@Repository从仅仅起到标记类的作用,到实现了@Component的作用
image.png
添加了@Component元注解,相应的@Service等都是派生注解

自定义@Component派生注解

  • 模仿@Respository注解自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 测试多层次 @Component派生,请将当前注释
// @Repository // 测试多层次 @Component派生,请将当前反注释,并且将 spring-context 升级到 3.0.0.RELEASE
public @interface StringRepository {

    /**
     * 属性方法必须与 {@link Component#value()} 保持一致
     * @return Bean 的名称
     */
    String value() default "";
}
  • 使用注解依赖注入
@StringRepository("chineseNameRepository")
public class NameRepository {

    /**
     * 查找所有的名字
     *
     * @return non-null List
     */
    public List<String> findAll() {
        return Arrays.asList("张三", "李四", "小马哥");
    }
}
  • 启动类,根据Bean名称去查找Bean
public class DerivedComponentAnnotationBootstrap {

    static {
        // 解决 Spring 2.5.x 不兼容 Java 8 的问题
        // 同时,请注意 Java Security 策略,必须具备 PropertyPermission
        System.setProperty("java.version", "1.7.0");
    }

    public static void main(String[] args) {
        // 构建 XML 配置驱动 Spring 上下文
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        // 设置 XML 配置文件的位置
        context.setConfigLocation("classpath:/META-INF/spring/context.xml");
        // 启动上下文
        context.refresh();
        // 获取名称为 "chineseNameRepository" Bean 对象
        NameRepository nameRepository = (NameRepository) context.getBean("chineseNameRepository");
        // 输出用户名称:[张三, 李四, 小马哥]
        System.out.printf("nameRepository.findAll() = %s \n", nameRepository.findAll()+nameRepository.toString());
   }
}
  • 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 激活注解驱动特性 -->
    <context:annotation-config />

    <!-- 找寻被@Component或者其派生 Annotation 标记的类(Class),将它们注册为 Spring Bean -->
    <context:component-scan base-package="thinking.in.spring.boot.samples.spring25" />

</beans>
  • 运行结果
    image.png

自定义的注解也实现了@Component注解的Bean注入

@Commponent“派生性”原理

Schema教程 https://www.w3school.com.cn/schema/schema_schema.asp
image.png

框架内建了context:Component-scan元素。XML Schema允许自定义元素前缀,命令空间是预先预定的,无法直接调整其内容。
XML Schema命名空间需要与其处理类建立映射关系,且配置在相对于classpath的规约资源/MRETA-INF/spring.handlers文件中
Spring应用上下文启动时调用如下方法,注册命名空间下所有local元素的Bean定义解析器
image.png
之后一层层找下去//TODO

多层次@component“派生性”

image.png

image.png
image.png
使用@SpringBootApplication代替@Service、@Conponent注解,可以顺利访问
证明@SpringBootApplication也是@Conponent多层次派生注解

该特性从Spring Framework3.0开始被添加

多层次@Component原理

细节不阐述,AnnotationAttributesReadingVisitor类会递归查找元注解,且从Spring Framework4.0开始被添加
而之前最多只查找2层

image.png
image.png

Spring组合注解

注解“元标注”一个或多个其他注解,将这些关联的注解行为组合成单个自定义注解。
image.png
最典型例如@SpringBootApplication是3个注解的组合注解,表明其是Spring模式注解、激活Spring Boot自动装配、指定@Compoent范围,减少开发人员理解和记忆的成本

image.png
在抽象层面,无论Spring模式注解(多层次@Component“派生性”)的元信息,还是Spring组合注解的元信息,均由AnnotationMetadata接口API抽象表达,其有2个实现类。

Spring注解属性别名和覆盖

在Java反射编程模型种,注解之间无法继承,也不能实现接口,但Java语言默认将所有注解实现Annotation接口,被表主的对象用API AnnotatedElement表达

Spring注解元信息抽象AnnotationMetadata

基于Java反射API获取元(嵌套)注解及属性信息的实现是很复杂的,需要递归获取元注解

AnnotationMetadata存在两种实现:

  • 基于ASM的AnnotationMetadaReadingVisitor
  • 基于Java反射API的StandarAnnotationMetadata

Spring注解属性抽象

当注解与其元注解属性名有相同的时候,会发生重叠
Spring Framework将注解属性抽象为AnnotationAttributes类,直接扩展了LinkedHashMap,目的在于使用K-V结构,且确保其顺序保持与属性方法声明一致。
由于AnnotationAttributes采用MAp的存储结构,不同层次元注解之间出现同名属性,必然会出现重叠的情况

Spring注解属性覆盖

较低层注解能够覆盖其元注解的同名属性,并且AnnotationAttributes采用注解就近覆盖的设计原则

  • 隐性覆盖:由于元注解的层次高低关系,衍生出Spring注解属性覆盖
  • 显性覆盖:当A @AliasFor B时,属性A显性覆盖了属性B的内容

Spring注解属性别名

@AliasFor可用于统一注解属性方法之间相互别名,能够构建同注解属性间的别名


这个家伙很懒,啥也没有留下😋