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
Spring Framework注解编程模型是Spring Boot注解驱动及自动装配的重要前提
- 元注解(Meta-Annotations)
- Spring模式注解(Stereotype Annotations)
- Spring组合注解(Composed Annotations)
- Spring注解属性别名和覆盖(Attribute Aliase & Overrides)
元注解
元注解是指一个能声明在其他注解上的注解,例如@Documented可作为任何注解的元注解
Spring模式注解
通俗来说Spring模式注解即@Component“派生”注解,由于Java语言规范,注解不允许继承、没有派生子类的能力。因此Spring Framework都过元标注方式实现注解之间的派生
@Component作为由Spring容器托管的通用模式组件,任何被@Componet标注的组件均为组件扫描的候选对象,凡是被@Component元标注的注解,例如@Service,也被视作组件扫描的对象
理解@Component“派生性”
在Spring Framwork 2.5中,@Repository从仅仅起到标记类的作用,到实现了@Component的作用
添加了@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>
- 运行结果
自定义的注解也实现了@Component注解的Bean注入
@Commponent“派生性”原理
Schema教程 https://www.w3school.com.cn/schema/schema_schema.asp
框架内建了context:Component-scan元素。XML Schema允许自定义元素前缀,命令空间是预先预定的,无法直接调整其内容。
XML Schema命名空间需要与其处理类建立映射关系,且配置在相对于classpath的规约资源/MRETA-INF/spring.handlers文件中
Spring应用上下文启动时调用如下方法,注册命名空间下所有local元素的Bean定义解析器
之后一层层找下去//TODO
多层次@component“派生性”
使用@SpringBootApplication代替@Service、@Conponent注解,可以顺利访问
证明@SpringBootApplication也是@Conponent多层次派生注解
该特性从Spring Framework3.0开始被添加
多层次@Component原理
细节不阐述,AnnotationAttributesReadingVisitor类会递归查找元注解,且从Spring Framework4.0开始被添加
而之前最多只查找2层
Spring组合注解
注解“元标注”一个或多个其他注解,将这些关联的注解行为组合成单个自定义注解。
最典型例如@SpringBootApplication是3个注解的组合注解,表明其是Spring模式注解、激活Spring Boot自动装配、指定@Compoent范围,减少开发人员理解和记忆的成本
在抽象层面,无论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可用于统一注解属性方法之间相互别名,能够构建同注解属性间的别名
Comments | 0 条评论