Java注解详解
Java 注解详解
本章位置:第二阶段 Java 核心框架
前置知识:反射机制、内省机制、JavaBean
本章内容:Java Annotation 注解
下一篇:Lambda 表达式
学习目标:理解注解是什么、注解本质、内置注解、自定义注解、元注解、注解属性、运行时读取方式,并理解注解在 JUnit、Spring、SpringBoot、MyBatis 等框架中的作用。
一、什么是注解
注解:
Annotation
可以理解为:
给类、方法、字段、参数等程序元素附加额外说明信息,也就是“元数据”。
例如:
@Override
public String toString() {
return "...";
}
这里:
@Override
就是一个注解。
它没有直接改变:
toString 方法的业务逻辑
而是在告诉:
编译器
工具
框架
一些额外信息。
二、注解不是注释
注释:
// 这是一个用户类
主要给:
人看
注解:
@Service
public class UserService {
}
主要可以给:
编译器
JVM
框架
工具
读取和处理。
所以:
注释
偏向人类说明
注解
偏向程序元数据
三、为什么需要注解
假设没有注解,
框架需要知道:
哪个类是 Service
哪个方法是测试方法
哪个字段需要注入
哪个接口映射某个 URL
可能只能:
写 XML
写配置文件
写大量约定代码
注解让这些信息可以直接:
写在代码附近
例如:
@Test
void testAdd() {
}
JUnit 看到:
@Test
就知道:
这是一个测试方法
四、常见注解
你已经见过很多:
@Override
@Deprecated
@SuppressWarnings
@Test
@Service
@Controller
@RestController
@Autowired
@RequestMapping
它们都属于:
注解
五、注解本身会自动执行功能吗
不会。
这是非常重要的一点。
例如:
@MyTest
public void hello() {
}
如果:
没有任何程序读取 @MyTest
那它通常不会自动做任何事情。
所以注解真正生效需要:
编译器处理
框架扫描
反射读取
注解处理器处理
六、注解和反射的关系
例如:
@Service
public class UserService {
}
Spring 大致会:
扫描类
↓
通过反射检查有没有 @Service
↓
如果有
创建对象
↓
加入 IOC 容器
所以:
注解负责“标记”,反射负责“读取和执行”。
七、注解的定义语法
自定义注解使用:
@interface
例如:
public @interface MyAnnotation {
}
注意:
不是 interface
而是:
@interface
八、最简单的自定义注解
public @interface MyTest {
}
使用:
@MyTest
public void test() {
}
九、注解可以放在哪里
取决于:
@Target
可以放在:
类
方法
字段
构造方法
参数
局部变量
包
模块
类型使用位置
十、什么是元注解
元注解:
用来修饰“注解”的注解。
常见:
@Target
@Retention
@Documented
@Inherited
@Repeatable
十一、@Target
用于指定:
注解可以写在哪里
例如:
@Target(
ElementType.METHOD
)
public @interface MyTest {
}
表示:
MyTest 只能写在方法上
十二、ElementType
常见:
TYPE
类、接口、枚举、注解
FIELD
字段
METHOD
方法
PARAMETER
参数
CONSTRUCTOR
构造方法
LOCAL_VARIABLE
局部变量
ANNOTATION_TYPE
注解类型
PACKAGE
包
TYPE_PARAMETER
类型参数
TYPE_USE
类型使用位置
十三、多个 Target
例如:
@Target({
ElementType.TYPE,
ElementType.METHOD
})
public @interface MyAnnotation {
}
表示:
可以放类上
也可以放方法上
十四、@Retention
用于指定:
注解保留到什么时候
常见:
SOURCE
CLASS
RUNTIME
十五、RetentionPolicy.SOURCE
@Retention(
RetentionPolicy.SOURCE
)
表示:
只存在源代码阶段
编译后通常不会保留。
典型:
@Override
主要由编译器使用。
十六、RetentionPolicy.CLASS
@Retention(
RetentionPolicy.CLASS
)
表示:
会进入 .class 文件
但运行时:
不一定能通过反射读取
这是默认策略。
十七、RetentionPolicy.RUNTIME
@Retention(
RetentionPolicy.RUNTIME
)
表示:
运行时仍然保留
可以通过:
反射
读取。
框架注解通常非常常见:
RUNTIME
十八、为什么框架注解常用 RUNTIME
例如:
@Service
Spring 启动时需要:
在程序运行期间
判断类有没有 @Service
所以必须:
运行时保留
十九、@Documented
例如:
@Documented
public @interface MyAnnotation {
}
表示:
生成 Javadoc 时
希望这个注解也出现在文档中
二十、@Inherited
@Inherited
表示:
某些类级别注解
可以被子类继承
注意:
只对类继承语义有特定作用
并不是:
方法、字段、接口上的注解全部自动继承
二十一、@Repeatable
允许:
同一个注解重复使用
例如:
@Role("ADMIN")
@Role("USER")
public class User {
}
需要配合:
@Repeatable
定义容器注解。
二十二、自定义注解完整例子
import java.lang.annotation.*;
@Target(
ElementType.METHOD
)
@Retention(
RetentionPolicy.RUNTIME
)
@Documented
public @interface MyTest {
}
使用:
public class Demo {
@MyTest
public void testAdd() {
System.out.println(
"执行测试"
);
}
}
二十三、注解属性
注解中可以定义:
属性
例如:
public @interface MyTest {
String value();
}
使用:
@MyTest(
value = "加法测试"
)
public void testAdd() {
}
二十四、value 特殊规则
如果注解只有一个:
value
属性,
可以省略:
value =
例如:
@MyTest(
"加法测试"
)
二十五、多个属性
public @interface ApiInfo {
String name();
String description();
int version();
}
使用:
@ApiInfo(
name = "用户接口",
description = "用户管理",
version = 1
)
public class UserController {
}
二十六、默认值 default
public @interface ApiInfo {
String name();
int version()
default 1;
}
使用:
@ApiInfo(
name = "用户接口"
)
version 自动:
1
二十七、注解属性允许哪些类型
注解属性常见允许:
基本类型
String
Class
enum
Annotation
以上类型的一维数组
二十八、String 属性
String value();
二十九、Class 属性
Class<?> type();
使用:
@Handler(
type =
UserService.class
)
三十、enum 属性
定义:
public enum Level {
LOW,
MEDIUM,
HIGH
}
注解:
public @interface Risk {
Level level();
}
使用:
@Risk(
level =
Level.HIGH
)
三十一、数组属性
public @interface Roles {
String[] value();
}
使用:
@Roles({
"ADMIN",
"USER"
})
三十二、单个数组元素可以省略大括号吗
如果只有一个:
@Roles(
"ADMIN"
)
也是可以的。
三十三、内置注解:@Override
@Override
告诉编译器:
这个方法应该重写父类或接口方法
三十四、@Override 的价值
如果父类方法:
public void show() {
}
你误写:
@Override
public void shwo() {
}
编译器会:
直接报错
如果没有 @Override,
你可能误以为:
已经重写
实际却创建了新方法。
三十五、建议重写方法都加 @Override
因为它可以:
提高可读性
帮助编译器检查
减少拼写错误
三十六、@Deprecated
标记:
过时 API
例如:
@Deprecated
public void oldMethod() {
}
调用时 IDE 会:
警告
三十七、@Deprecated 不等于不能用
它通常表示:
不推荐继续使用
未来可能删除
有更好的替代方案
但:
当前可能仍然能运行
三十八、@SuppressWarnings
用于:
抑制编译器警告
例如:
@SuppressWarnings(
"unchecked"
)
三十九、不要滥用 @SuppressWarnings
错误:
一有黄色警告
就全部 suppress
更应该先:
理解警告原因
只有确认合理时再抑制。
四十、@SafeVarargs
用于:
泛型可变参数
相关安全警告。
当前阶段:
了解即可
四十一、@FunctionalInterface
用于标记:
函数式接口
例如:
@FunctionalInterface
public interface Calculator {
int calculate(
int a,
int b
);
}
四十二、函数式接口
函数式接口:
只有一个抽象方法
这样才能:
使用 Lambda
下一篇会详细讲。
四十三、反射读取类注解
假设:
@Retention(
RetentionPolicy.RUNTIME
)
@Target(
ElementType.TYPE
)
public @interface Component {
String value()
default "";
}
使用:
@Component(
"userService"
)
public class UserService {
}
四十四、isAnnotationPresent()
boolean present =
UserService.class
.isAnnotationPresent(
Component.class
);
结果:
true
四十五、getAnnotation()
Component component =
UserService.class
.getAnnotation(
Component.class
);
读取属性:
String value =
component.value();
四十六、getAnnotations()
Annotation[] annotations =
UserService.class
.getAnnotations();
获取:
当前可见注解
四十七、getDeclaredAnnotations()
Annotation[] annotations =
UserService.class
.getDeclaredAnnotations();
主要关注:
当前元素自己声明的注解
四十八、反射读取方法注解
Method method =
UserService.class
.getDeclaredMethod(
"save"
);
boolean present =
method
.isAnnotationPresent(
MyLog.class
);
四十九、读取字段注解
Field field =
User.class
.getDeclaredField(
"username"
);
MyField annotation =
field
.getAnnotation(
MyField.class
);
五十、读取参数注解
方法:
public void login(
@NotEmpty
String username
) {
}
可以通过:
Parameter
反射读取参数注解。
五十一、Parameter
例如:
Method method =
UserService.class
.getMethod(
"login",
String.class
);
Parameter[] parameters =
method
.getParameters();
五十二、参数是否有注解
for (
Parameter parameter :
parameters
) {
boolean present =
parameter
.isAnnotationPresent(
NotEmpty.class
);
}
五十三、综合案例:自定义测试框架
定义:
@Target(
ElementType.METHOD
)
@Retention(
RetentionPolicy.RUNTIME
)
public @interface MyTest {
}
五十四、测试类
public class CalculatorTest {
@MyTest
public void testAdd() {
System.out.println(
"testAdd 执行"
);
}
public void helper() {
}
@MyTest
public void testSubtract() {
System.out.println(
"testSubtract 执行"
);
}
}
五十五、扫描测试方法
Class<
CalculatorTest
> clazz =
CalculatorTest.class;
CalculatorTest testObject =
clazz
.getDeclaredConstructor()
.newInstance();
for (
Method method :
clazz.getDeclaredMethods()
) {
if (
method
.isAnnotationPresent(
MyTest.class
)
) {
method.invoke(
testObject
);
}
}
五十六、这个案例说明了什么
我们已经做出了:
最简版 JUnit 思想
过程:
定义 @MyTest
↓
标记测试方法
↓
反射扫描方法
↓
发现注解
↓
invoke()
JUnit 的真实实现当然复杂很多,
但核心思路很类似。
五十七、综合案例:自定义 IOC 标记
定义:
@Target(
ElementType.TYPE
)
@Retention(
RetentionPolicy.RUNTIME
)
public @interface MyComponent {
String value()
default "";
}
五十八、业务类
@MyComponent(
"userService"
)
public class UserService {
}
五十九、框架扫描思路
如果程序扫描到:
UserService.class
可以:
if (
clazz.isAnnotationPresent(
MyComponent.class
)
) {
Object bean =
clazz
.getDeclaredConstructor()
.newInstance();
}
然后放入:
Map<String, Object>
这就是:
最简 IOC 注解扫描思想
六十、注解 + 反射 + IOC
可以总结:
注解
负责告诉框架
“这个类有什么身份”
反射
负责读取注解并创建对象
IOC 容器
负责保存对象
六十一、综合案例:字段自动注入
定义:
@Target(
ElementType.FIELD
)
@Retention(
RetentionPolicy.RUNTIME
)
public @interface MyAutowired {
}
六十二、使用注解
public class UserService {
@MyAutowired
private UserDao userDao;
}
六十三、反射扫描字段
for (
Field field :
clazz.getDeclaredFields()
) {
if (
field
.isAnnotationPresent(
MyAutowired.class
)
) {
field.setAccessible(
true
);
// 从 IOC 容器中找到对应对象
Object dependency =
container.get(
field
.getType()
);
field.set(
bean,
dependency
);
}
}
六十四、这就是 @Autowired 的基础思想
Spring 真实实现复杂很多,
但核心可以理解:
扫描字段
↓
发现 @Autowired
↓
判断字段类型
↓
从容器查找 Bean
↓
反射注入
六十五、综合案例:方法权限注解
定义:
@Target(
ElementType.METHOD
)
@Retention(
RetentionPolicy.RUNTIME
)
public @interface RequireRole {
String value();
}
六十六、使用权限注解
@RequireRole(
"ADMIN"
)
public void deleteUser() {
}
六十七、框架读取权限
RequireRole annotation =
method.getAnnotation(
RequireRole.class
);
String role =
annotation.value();
然后判断:
当前用户是否有 ADMIN
六十八、这就是声明式编程思想
业务方法:
@RequireRole(
"ADMIN"
)
public void deleteUser() {
}
方法本身不用手动写:
if 用户没有权限
而是:
把规则声明在注解上
由框架统一处理。
这叫:
声明式编程
六十九、Spring 为什么大量使用注解
因为注解可以表达:
组件身份
依赖关系
请求映射
事务
权限
参数校验
配置绑定
例如:
@Service
@Autowired
@GetMapping
@Transactional
@Validated
七十、常见 Spring 注解预览
@Component
@Service
@Repository
@Controller
@RestController
@Autowired
@RequestMapping
@GetMapping
@PostMapping
@Transactional
后面对应章节再详细学习。
七十一、JUnit 注解预览
@Test
@BeforeEach
@AfterEach
@BeforeAll
@AfterAll
@Disabled
本质都是:
给测试框架提供元数据
七十二、MyBatis 注解预览
例如:
@Select(
"SELECT * FROM user"
)
或者:
@Mapper
都是:
注解驱动配置
七十三、注解和 XML 的关系
过去框架大量使用:
XML
例如:
<bean
id="userService"
class="..."
/>
现在很多信息可以写:
@Service
七十四、注解的优点
靠近代码
阅读方便
减少部分 XML
类型信息更直接
开发效率高
七十五、注解的缺点
如果滥用:
类上十几个注解
方法上十几个注解
也可能:
难理解
隐式行为太多
框架魔法感强
所以:
注解不是越多越好
七十六、自定义注解命名
建议:
表达意图
例如:
@RequiresLogin
@AuditLog
@RateLimit
@Sensitive
@Cacheable
不要:
@A
@Test1
@Flag
这种含义模糊的名字。
七十七、注解属性设计原则
尽量:
简单
明确
默认值合理
不要塞太多配置
例如:
@RateLimit(
permits = 10,
seconds = 1
)
很直观。
七十八、运行时注解必须有 RUNTIME
如果你要:
clazz.getAnnotation(...)
读取,
自定义注解必须:
@Retention(
RetentionPolicy.RUNTIME
)
否则运行时可能读不到。
七十九、常见错误:忘记 @Retention
默认:
RetentionPolicy.CLASS
如果你想运行时反射:
必须明确 RUNTIME
八十、常见错误:Target 不匹配
例如:
@Target(
ElementType.METHOD
)
然后写:
@MyTest
public class Demo {
}
会:
编译报错
因为只能用于:
方法
八十一、常见错误:把注解当函数调用
例如:
@MyLog
public void save() {
}
不会自动:
打印日志
必须有框架:
扫描 + 拦截 + 执行逻辑
八十二、常见错误:value 写法不理解
定义:
String value();
使用可以:
@Role(
value = "ADMIN"
)
也可以:
@Role(
"ADMIN"
)
八十三、多个属性不能乱省略
如果:
public @interface Api {
String value();
String method();
}
必须:
@Api(
value = "/users",
method = "GET"
)
不能:
@Api(
"/users",
"GET"
)
注解不支持这种位置参数写法。
八十四、常见错误:数组值写法
定义:
String[] roles();
多个:
@Permission(
roles = {
"ADMIN",
"USER"
}
)
单个:
@Permission(
roles = "ADMIN"
)
都可以。
八十五、常见错误:把 ordinal 当注解配置
如果注解需要状态,
更推荐:
Level.HIGH
而不是:
2
枚举更清晰。
八十六、重复注解
如果需要:
@Role("ADMIN")
@Role("USER")
需要设计:
@Repeatable
八十七、重复注解完整思路
容器:
@Retention(
RetentionPolicy.RUNTIME
)
@Target(
ElementType.TYPE
)
public @interface Roles {
Role[] value();
}
单个:
@Retention(
RetentionPolicy.RUNTIME
)
@Target(
ElementType.TYPE
)
@Repeatable(
Roles.class
)
public @interface Role {
String value();
}
八十八、使用重复注解
@Role(
"ADMIN"
)
@Role(
"USER"
)
public class UserService {
}
八十九、读取重复注解
Role[] roles =
UserService.class
.getAnnotationsByType(
Role.class
);
九十、组合注解思想
Spring 中很多注解可以:
注解上再加注解
形成:
组合注解
例如:
@RestController
本身就可以理解成多个语义组合。
九十一、元注解和组合注解的关系
如果定义:
@Target(
ElementType.TYPE
)
@Retention(
RetentionPolicy.RUNTIME
)
@Component
public @interface MyService {
}
框架如果支持:
元注解扫描
就可能把:
@MyService
也识别成组件。
九十二、注解继承问题
@Inherited 只对:
类
的继承起作用。
并且:
接口上的注解
不会因为实现接口
就自动通过 @Inherited 继承
这一点容易误解。
九十三、方法注解不会自动继承
父类方法:
@MyLog
public void save() {
}
子类重写:
@Override
public void save() {
}
子类新方法对象上:
不一定自动拥有 @MyLog
框架要根据规则自行处理。
九十四、注解代理对象
当你:
Component component =
clazz.getAnnotation(
Component.class
);
拿到的对象本质上:
并不是你手动 new 出来的普通对象
JVM 会提供:
注解接口的运行时实现
你可以直接调用:
component.value();
九十五、注解本质上是一种特殊接口
定义:
public @interface MyTest {
}
编译后本质和:
Annotation 接口体系
有关。
所有注解类型都会:
间接实现 java.lang.annotation.Annotation
九十六、Annotation 接口
java.lang.annotation.Annotation
常见方法:
annotationType()
equals()
hashCode()
toString()
九十七、annotationType()
Annotation annotation =
clazz.getAnnotation(
Component.class
);
Class<? extends Annotation>
type =
annotation
.annotationType();
得到:
Component.class
九十八、运行时扫描的基本流程
框架处理注解常见:
扫描类
↓
获取 Class
↓
检查注解
↓
读取注解属性
↓
根据注解执行框架逻辑
九十九、一个简单路由注解
定义:
@Target(
ElementType.METHOD
)
@Retention(
RetentionPolicy.RUNTIME
)
public @interface Route {
String path();
String method()
default "GET";
}
一百、使用 Route
public class UserController {
@Route(
path = "/users",
method = "GET"
)
public void list() {
}
@Route(
path = "/users",
method = "POST"
)
public void add() {
}
}
一百零一、扫描路由
for (
Method method :
UserController.class
.getDeclaredMethods()
) {
Route route =
method
.getAnnotation(
Route.class
);
if (
route != null
) {
System.out.println(
route.method()
+ " "
+ route.path()
+ " → "
+ method.getName()
);
}
}
一百零二、这和 Spring MVC 有什么关系
Spring MVC:
@GetMapping(
"/users"
)
public List<User>
list() {
}
框架会:
扫描 Controller
↓
读取请求映射注解
↓
建立 URL 与 Method 的映射
↓
请求到来
↓
反射调用对应方法
一百零三、自定义日志注解
@Target(
ElementType.METHOD
)
@Retention(
RetentionPolicy.RUNTIME
)
public @interface AuditLog {
String value();
}
使用:
@AuditLog(
"删除用户"
)
public void deleteUser() {
}
一百零四、日志框架处理思路
方法执行前
↓
读取 @AuditLog
↓
得到“删除用户”
↓
记录操作日志
↓
执行目标方法
后面 AOP 就会非常适合做这种事情。
一百零五、注解和 AOP
很多功能可以写成:
注解
+
AOP
例如:
@Transactional
@Cacheable
@Async
@PreAuthorize
@AuditLog
一百零六、声明式事务
@Transactional
public void transfer() {
}
业务代码不需要手动:
begin
commit
rollback
Spring 根据注解:
自动管理事务
这就是:
注解驱动 + AOP
一百零七、编译期注解处理简单了解
注解不只是:
运行时反射
还可以在编译阶段处理。
例如:
Lombok
MapStruct
部分代码生成框架
会使用:
Annotation Processing
一百零八、Annotation Processor
编译器可以发现:
特定注解
然后执行:
注解处理器
生成:
Java 源码
元数据
辅助代码
一百零九、运行时反射和编译期处理区别
运行时注解
程序启动后读取
典型:Spring
编译期注解处理
编译时处理
典型:Lombok / MapStruct
一百一十、为什么 Lombok 不主要依赖运行时反射
例如:
@Getter
@Setter
Lombok 会在:
编译阶段
修改或生成相关代码。
所以运行后:
Getter / Setter 已经存在
一百一十一、注解的适用场景
适合:
声明组件
声明路由
权限要求
事务
日志
缓存
测试
参数校验
序列化规则
数据库映射
一百一十二、注解不适合承载大量业务数据
不要设计:
@Order(
product1 = "...",
product2 = "...",
...
)
注解更适合:
元数据
配置
规则标记
而不是:
复杂动态业务数据
一百一十三、练习题 1:自定义 @MyTest
要求:
只能作用于方法
运行时保留
使用反射:
执行所有 @MyTest 方法
一百一十四、练习题 2:@Component
定义:
@Component(
"userService"
)
要求:
只能作用于类
运行时可读取
反射输出:
Bean 名称
一百一十五、练习题 3:@Column
定义:
@Column(
"user_name"
)
private String username;
通过反射输出:
字段名 username
数据库列名 user_name
一百一十六、练习题 4:@Required
定义:
@Required
private String username;
反射校验:
字段值不能为 null
一百一十七、练习题 5:@Route
定义:
@Route(
path = "/users",
method = "GET"
)
扫描 Controller:
输出所有路由
一百一十八、练习题 6:@RequireRole
定义:
@RequireRole(
"ADMIN"
)
模拟:
当前角色为 USER
如果无权限:
拒绝执行方法
一百一十九、练习题 7:默认值
定义:
@Retry(
count = 3
)
其中:
count 默认 3
delay 默认 1000
读取注解属性。
一百二十、练习题 8:数组属性
定义:
@Permission(
roles = {
"ADMIN",
"USER"
}
)
遍历 roles。
一百二十一、练习题 9:重复注解
定义:
@Tag(
"Java"
)
@Tag(
"Spring"
)
通过:
getAnnotationsByType()
读取全部标签。
一百二十二、练习题 10:简易 IOC
组合:
@Component
@Autowired
反射
Map 容器
完成:
自动创建对象
自动注入字段
一百二十三、注解必须掌握 API
isAnnotationPresent()
getAnnotation()
getAnnotations()
getDeclaredAnnotations()
getAnnotationsByType()
一百二十四、注解必须掌握元注解
@Target
@Retention
@Documented
@Inherited
@Repeatable
一百二十五、必须掌握 Retention
SOURCE
只保留源码
CLASS
保留到字节码
RUNTIME
运行时可反射读取
一百二十六、必须掌握 Target
至少认识:
TYPE
FIELD
METHOD
PARAMETER
CONSTRUCTOR
一百二十七、必须回答的问题
学完后应该能回答:
1. 什么是注解?
2. 注解和注释有什么区别?
3. 注解本身会自动执行功能吗?
4. @Target 是干什么的?
5. @Retention 是干什么的?
6. SOURCE / CLASS / RUNTIME 有什么区别?
7. 什么是元注解?
8. 如何定义自定义注解?
9. 注解属性可以有哪些类型?
10. value 为什么可以省略属性名?
11. 如何通过反射读取注解?
12. @Inherited 有什么作用?
13. @Repeatable 有什么作用?
14. Spring 为什么大量使用注解?
15. 注解和反射是什么关系?
一百二十八、反射 + 内省 + 注解整体关系
现在可以把三篇内容完整串起来:
反射
│
├─ Class
├─ Constructor
├─ Field
└─ Method
│
▼
提供运行时动态操作能力
内省:
Class
↓
Introspector
↓
BeanInfo
↓
PropertyDescriptor
↓
Getter / Setter
负责:
JavaBean 属性分析
注解:
@Target
@Retention
自定义 Annotation
↓
附加元数据
↓
反射读取
↓
框架执行逻辑
一百二十九、框架底层整体流程
例如 Spring:
扫描 classpath
↓
得到 Class
↓
反射读取 @Component / @Service
↓
反射创建对象
↓
读取 @Autowired
↓
字段或 Setter 注入
↓
Bean 保存 IOC 容器
一百三十、MyBatis 整体思路
读取 XML / 注解
↓
获得 SQL 和映射信息
↓
执行 JDBC
↓
获得 ResultSet
↓
反射 / 属性映射
↓
创建 JavaBean
↓
赋值
一百三十一、JUnit 整体思路
扫描测试类
↓
反射发现 @Test
↓
读取 @BeforeEach
↓
创建测试对象
↓
invoke 测试方法
↓
处理断言结果
一百三十二、本章知识结构
Java Annotation
│
├─ 内置注解
│ ├─ @Override
│ ├─ @Deprecated
│ ├─ @SuppressWarnings
│ └─ @FunctionalInterface
│
├─ 自定义注解
│ ├─ @interface
│ ├─ 属性
│ └─ default
│
├─ 元注解
│ ├─ @Target
│ ├─ @Retention
│ ├─ @Documented
│ ├─ @Inherited
│ └─ @Repeatable
│
├─ 反射读取
│ ├─ 类注解
│ ├─ 方法注解
│ ├─ 字段注解
│ └─ 参数注解
│
└─ 框架应用
├─ JUnit
├─ Spring
├─ Spring MVC
├─ MyBatis
└─ AOP
一百三十三、本章总结
注解最核心的一句话:
注解是给程序元素附加元数据的一种机制。
注解自己通常:
不负责真正执行功能
真正负责功能的是:
编译器
反射
框架
注解处理器
最核心流程:
定义注解
↓
使用注解
↓
扫描程序元素
↓
读取注解
↓
根据注解执行逻辑
现在:
反射
内省
注解
已经完整学完。
这三个知识点共同构成了很多 Java 框架自动化能力的基础:
反射
提供动态操作
内省
提供 Bean 属性识别
注解
提供元数据和规则声明
后面学习:
MyBatis
Servlet
SpringBoot
AOP
事务
权限
时,你会不断看到这三个知识点的影子。
下一篇正式进入:
Lambda 表达式
开始学习 Java 8 以后非常重要的:
函数式编程基础