目录
2.3为 MyAnnotation 注解添加 id 和 describe 属性:
前言
注解是一种能被添加到java源代码中的元数据,方法、类、参数和包都可以用注解来修饰。注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。
要深入学习注解,我们就必须能定义自己的注解并使用注解,在定义自己的注解之前,我们需要了解java为我们提供的元注解和相关定义注解的语法。
一、自定义注解和元注解
1.元注解
元注解是专门用来注解其他注解的注解,听起来有些绕口,实际上就是专门为自定义注解提供的注解。java.lang.annotation提供了五种元注解:
@Documented – 注解是否将包含在JavaDoc中
@Retention – 什么时候使用该注解
@Target – 注解用于什么地方
@Inherited – 是否允许子类继承该注解
@Repeatable - 是否可重复注解,jdk1.8引入
下面我们看一下每个元注解的作用和相应分参数的使用说明。
1.1@Target
Target类型 | 描述 |
---|---|
ElementType.TYPE | 应用于类、接口(包括注解类型)、枚举 |
ElementType.FIELD | 应用于属性(包括枚举中的常量) |
ElementType.METHOD | 应用于方法 |
ElementType.PARAMETER | 应用于方法的形参 |
ElementType.CONSTRUCTOR | 应用于构造函数 |
ElementType.LOCAL_VARIABLE | 应用于局部变量 |
ElementType.ANNOTATION_TYPE | 应用于注解类型 |
ElementType.PACKAGE | 应用于包 |
ElementType.TYPE_PARAMETER | 应用于类型变量 |
ElementType.TYPE_USE | 应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型) |
1.2@Retention
表明该注解的生命周期
生命周期类型 | 描述 |
---|---|
RetentionPolicy.SOURCE | 编译时被丢弃,不包含在类文件中 |
RetentionPolicy.CLASS | JVM加载时被丢弃,包含在类文件中,默认值 |
RetentionPolicy.RUNTIME | 由JVM 加载,包含在类文件中,在运行时可以被获取到 |
1.3@Document
表明该注解标记的元素可以被Javadoc 或类似的工具文档化
1.4@Inherited
表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解。
2.自定义注解
我们定义一个类的时候是使用的 class 关键字定义的,现在我们想定义一个自己的注解 需要使用 @interface 关键字来定义。除了上面的元注解其他的注解其实都是自定义注解。比如:
@Documented
@Inherited
@Target({ ElementType.FIELD, ElementType.METHOD ,ElementType.TYPE}) //可以在字段、枚举的常量、方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";//注解属性
}
2.1注解属性类型
注解属性 ( 接口方法 ) 返回值类型要求 :
- 基本数据类型 : byte , short , int , long , float , double , char , boolean ;
- 字符串类型 : String ;
- 枚举类型 : enum ;
- 注解类型 ;
- 以上类型的数组形式 ;
注解属性返回值必须是以上的类型 , 不能设置其它类型返回值 , 否则会报错 ;
2.2注解属性赋值
注解中定义了属性 , 在使用注解时 , 需要给注解属性赋值;定义注解属性时 , 可以使用default关键字指定属性默认值 , 下面代码中 , 制定注解属性 intValue 值类型为 int 整型 , 默认值 88;
int intValue() default 88
如果 注解属性 指定了默认值 , 在使用注解时 , 可以选择 不为该属性赋值 ( 此时使用默认属性值 ) , 也可以进行赋值 ( 指定一个新的属性值 ) ;
如果 注解属性 没有指定默认值 , 则使用注解时 , 必须为其指定一个默认值 , 否则编译时报错 ;
数组类型的注解属性赋值时 , 使用大括号进行赋值 , 大括号内是数组元素 , 如果只有一个属性 , 可以省略大括号
2.3为 MyAnnotation 注解添加 id 和 describe 属性:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface MyAnnotation {
int id();
String describe();
}
也可以为 id 和 describe 属性添加默认值,当 id 或 describe 属性不指定具体的值的时候就会使用默认值:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface MyAnnotation {
int id() default 0;
String describe() default "";
}
二、自定义注解简单使用
1.1使用1
1.定义自定义注解
import java.lang.annotation.*;
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface MyAnnotation {
int id() default 0;
String describe() default "";
}
2.定义pojo类
import com.alibaba.fastjson.annotation.JSONField;
import com.itheima.Annotation.MyAnnotation;
public class Student {
@JSONField(ordinal = 0)
@MyAnnotation
public String name;
@MyAnnotation(id = 1, describe = "分数")
public Integer score;
@MyAnnotation(describe = "getName方法")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
}
3.使用反射获取类成员变量上的所有注解
import com.itheima.Annotation.MyAnnotation;
import com.itheima.Pojo.Student;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class TestAnnotation {
public static void main(String[] args) {
Class<?> studentClass = Student.class;
Field[] fields = studentClass.getDeclaredFields();//获取所有的类成员变量字段
for (Field field : fields) {
String fieldName = field.getName(); //获取该类成员变量的名字
System.out.println("成员变量名是:" + fieldName);
Annotation[] annotations = field.getAnnotations(); //获取该类成员变量上所有声明周期是运行时的注解
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
String annotationName = annotationType.getSimpleName();//注解的简短名称
System.out.println(" 使用的注解是:" + annotationName);
//判断该注解是不是 MyAnnotation 注解,是的话打印其 id 和 describe 属性
if (annotationType.equals(MyAnnotation.class)) {
MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
int id = myAnnotation.id();
String describe = myAnnotation.describe();
System.out.println(" MyAnnotation注解中的id是:" + id);
System.out.println(" MyAnnotation注解中的describe是:" + describe);
}
}
System.out.println("--------------------");
}
}
}
4.输出结果
成员变量名是:name
使用的注解是:JSONField
使用的注解是:MyAnnotation
MyAnnotation注解中的id是:0
MyAnnotation注解中的describe是:
--------------------
成员变量名是:score
使用的注解是:MyAnnotation
MyAnnotation注解中的id是:1
MyAnnotation注解中的describe是:分数
--------------------
5.反射获取成员变量上的指定注解
import com.itheima.Annotation.MyAnnotation;
import com.itheima.Pojo.Student;
import java.lang.reflect.Field;
public class TestAnnotation {
public static void main(String[] args) {
Class<?> studentClass = Student.class;
//反射遍历所有成员变量
Field[] fields = studentClass.getDeclaredFields();
for (Field field : fields) {
//如果其成员变量上有 MyAnnotation 注解,就打印成员变量名和注解里的内容
if (field.isAnnotationPresent(MyAnnotation.class)) {
String fieldName = field.getName();
//获取变量字段上的 MyAnnotation 注解
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
int id = annotation.id();
String describe = annotation.describe();
System.out.println("成员变量是:" + fieldName);
System.out.println("MyAnnotation注解中的id是:" + id);
System.out.println("MyAnnotation注解中的describe是:" + describe);
}
System.out.println();
}
}
}
输出:
成员变量是:name
MyAnnotation注解中的id是:0
MyAnnotation注解中的describe是:
成员变量是:score
MyAnnotation注解中的id是:1
MyAnnotation注解中的describe是:分数
6.获取方法上的指定注解
import com.itheima.Annotation.MyAnnotation;
import com.itheima.Pojo.Student;
import java.lang.reflect.Method;
public class TestAnnotation {
public static void main(String[] args) {
Class<?> studentClass = Student.class;
/* 反射遍历所有方法 */
Method[] methods = studentClass.getDeclaredMethods();
for (Method method : methods) {
//如果其方法上有 MyAnnotation 注解,就打印方法名和注解里的内容
if (method.isAnnotationPresent(MyAnnotation.class)) {
String methodName = method.getName();
//获取方法上的 MyAnnotation 注解
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
int id = annotation.id();
String describe = annotation.describe();
System.out.println("方法名是:" + methodName);
System.out.println("MyAnnotation注解中的id是:" + id);
System.out.println("MyAnnotation注解中的describe是:" + describe);
}
}
}
}
输出:方法名是:getName
MyAnnotation注解中的id是:0
MyAnnotation注解中的describe是:getName方法
1.2使用2
1.自定义注解initsex
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface InitSex {
enum SEX_TYPE {MAN, WOMAN}
SEX_TYPE sex() default SEX_TYPE.MAN;
}
2.自定义注解ValidateAge
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {
/**
* 最小值
*/
int min() default 18;
/**
* 最大值
*/
int max() default 99;
/**
* 默认值
*/
int value() default 20;
}
3.定义pojo
import com.itheima.Annotation.InitSex;
import com.itheima.Annotation.ValidateAge;
public class User {
private String username;
@ValidateAge(min = 20, max = 35, value = 22)
private int age;
@InitSex(sex = InitSex.SEX_TYPE.MAN)
private String sex;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
4.测试
import com.itheima.Annotation.InitSex;
import com.itheima.Annotation.ValidateAge;
import com.itheima.Pojo.User;
import java.lang.reflect.Field;
public class TestInitParam {
public static void main(String[] args) throws IllegalAccessException {
User user = new User();
initUser(user);
// 年龄为0,校验为通过情况
boolean checkResult = checkUser(user);
printResult(checkResult);
// 重新设置年龄,校验通过情况
user.setAge(22);
checkResult = checkUser(user);
printResult(checkResult);
}
static void initUser(User user) throws IllegalAccessException {
// 获取User类中所有的属性(getFields无法获得private属性)
Field[] fields = User.class.getDeclaredFields();
// 遍历所有属性
for (Field field : fields) {
// 如果属性上有此注解,则进行赋值操作
if (field.isAnnotationPresent(InitSex.class)) {
InitSex init = field.getAnnotation(InitSex.class);
field.setAccessible(true);
// 设置属性的性别值
field.set(user, init.sex().toString());
System.out.println("完成属性值的修改,修改值为:" + init.sex().toString());
}
}
}
static boolean checkUser(User user) throws IllegalAccessException {
// 获取User类中所有的属性(getFields无法获得private属性)
Field[] fields = User.class.getDeclaredFields();
boolean result = true;
// 遍历所有属性
for (Field field : fields) {
// 如果属性上有此注解,则进行赋值操作
if (field.isAnnotationPresent(ValidateAge.class)) {
ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
field.setAccessible(true);
int age = (Integer)field.get(user);
if (age < validateAge.min() || age > validateAge.max()) {
result = false;
System.out.println("年龄值不符合条件");
}
}
}
return result;
}
static void printResult(boolean checkResult) {
if (checkResult) {
System.out.println("校验通过···");
} else {
System.out.println("校验未通过···");
}
}
}
5.输出
完成属性值的修改,修改值为:MAN
年龄值不符合条件
校验未通过···
校验通过···
三、自定义注解结合aop使用
1.pojo
import com.itheima.Annotation.InitSex;
import com.itheima.Annotation.ValidateAge;
public class User {
private String username;
@ValidateAge(min = 20, max = 35, value = 22)
private int age;
@InitSex(sex = InitSex.SEX_TYPE.MAN)
private String sex;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
2.自定义注解
2.1 自定义注解InitSex
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface InitSex {
enum SEX_TYPE {MAN, WOMAN}
SEX_TYPE sex() default SEX_TYPE.MAN;
}
2.2 自定义注解ValidateAge
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {
/**
* 最小值
*/
int min() default 18;
/**
* 最大值
*/
int max() default 99;
/**
* 默认值
*/
int value() default 20;
}
2.3 自定义注解Log
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
3. 切面
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Aspect
@Component
public class LogAspect {
@Around("@annotation(com.itheima.Annotation.Log)")
public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
//操作时间
Date operateTime = new Date();
//操作类名
String className = joinPoint.getTarget().getClass().getName();
//操作方法名
String methodName = joinPoint.getSignature().getName();
//操作方法参数
Object[] args = joinPoint.getArgs();
String methodParams = Arrays.toString(args);
long begin = System.currentTimeMillis();
//调用原始目标方法运行
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
//方法返回值
String returnValue = JSONObject.toJSONString(result);
//操作耗时
Long costTime = end - begin;
//记录操作日志
System.out.println("操作时间:"+operateTime+" 操作类名:"+className+" 操作方法名:"+methodName+
" 操作方法参数:"+methodParams+" 方法返回值:"+returnValue+" 操作耗时"+costTime);
return result;
}
}
4.测试切面和自定义注解的结合使用(切点是被自定义注解Log注解的方法)
import com.itheima.Annotation.InitSex;
import com.itheima.Annotation.Log;
import com.itheima.Annotation.ValidateAge;
import com.itheima.Pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Field;
@RestController
@RequestMapping("/test")
public class AopDemo {
@Log
@RequestMapping("/show3")
@ResponseBody
public void testAop(String ss) throws IllegalAccessException{
User user = new User();
initUser(user);
// 年龄为0,校验为通过情况
boolean checkResult = checkUser(user);
printResult(checkResult);
// 重新设置年龄,校验通过情况
user.setAge(22);
checkResult = checkUser(user);
printResult(checkResult);
}
public void initUser(User user) throws IllegalAccessException {
// 获取User类中所有的属性(getFields无法获得private属性)
Field[] fields = User.class.getDeclaredFields();
// 遍历所有属性
for (Field field : fields) {
// 如果属性上有此注解,则进行赋值操作
if (field.isAnnotationPresent(InitSex.class)) {
InitSex init = field.getAnnotation(InitSex.class);
field.setAccessible(true);
// 设置属性的性别值
field.set(user, init.sex().toString());
System.out.println("完成属性值的修改,修改值为:" + init.sex().toString());
}
}
}
public boolean checkUser(User user) throws IllegalAccessException {
// 获取User类中所有的属性(getFields无法获得private属性)
Field[] fields = User.class.getDeclaredFields();
boolean result = true;
// 遍历所有属性
for (Field field : fields) {
// 如果属性上有此注解,则进行赋值操作
if (field.isAnnotationPresent(ValidateAge.class)) {
ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
field.setAccessible(true);
int age = (Integer)field.get(user);
if (age < validateAge.min() || age > validateAge.max()) {
result = false;
System.out.println("年龄值不符合条件");
}
}
}
return result;
}
public void printResult(boolean checkResult) {
if (checkResult) {
System.out.println("校验通过···");
} else {
System.out.println("校验未通过···");
}
}
}
5.输出
完成属性值的修改,修改值为:MAN
年龄值不符合条件
校验未通过···
校验通过···
操作时间:Thu Jul 18 16:26:39 CST 2024 操作类名:com.itheima.service.impl.AopDemo 操作方法名:testAop 操作方法参数:[test] 方法返回值:null 操作耗时58