网站优化塔山双喜,中学网站建设方案计划,中企动力z云邮企业邮箱,对网站建设的调研报告java高级——注解和反射 前情提要一、注解1. 什么是注解2. 内置注解3. 元注解:exclamation::exclamation::exclamation:3.1 Target#xff08;限定注解可以应用的目标元素类型#xff0c;比如只能出现在class上#xff09;3.2 Retention#xff08;指定注解的保留策略… java高级——注解和反射 前情提要一、注解1. 什么是注解2. 内置注解3. 元注解:exclamation::exclamation::exclamation:3.1 Target限定注解可以应用的目标元素类型比如只能出现在class上3.2 Retention指定注解的保留策略即注解在什么级别可用一般都是RUNTIME3.3 Documented指示注解应该被包含在Javadoc中3.4 Inherited可以被子类继承3. 5. Repeatable (Java 8允许在同一元素上多次使用相同的注解) 4. 自定义注解 二、反射1. 反射的基本概念2. 反射的核心类3. 获取Class对象的三种方式4. 反射的基本操作4.1 创建对象4.2 获取和操作字段4.3 调用方法 5. 反射的应用场景6. 反射的优缺点7. 反射的真实场景开发 总结 前情提要 上一篇文章我们仔细的研究了高阶函数、stream流困扰了多年的问题解决了怎么能自定义一个函数式接口甚至都不知道那叫做函数式接口。 java高级——高阶函数、stream流 一、注解
1. 什么是注解 注解Annotation是从Java5.0开始引入的技术。 Annotation的作用 不是程序本身可以对程序做出解释类似于注释 可以被其它程序比如编译器读取 Annotation的格式 “注释名”还可以添加一些参数例如SuppressWarningsvalue“unchecked” Annotation可以使用在哪些地方 普遍使用在class、method、interface、field。
2. 内置注解 内置注解一般是Java中自带的且比较常见的注解。
Override修辞方法表示重写超类中的另一个方法声明在继承和实现类中非常常见。 Deprecated修辞方法表示该方法已经不推荐使用了相当于过时。 SuppressWarnings消除警告信息比如说一些写法过时或者说不当编译器会进行提示可以用这个注解干掉但是❌不推荐使用这样会让程序可能出现未知的bug。 上面三个是Java中内置注解比较常见的当然在spring家族中我们肯定在GetMapping、PostMapping等也很熟悉这一篇文章就让你学会怎么在Java中自定义注解。
3. 元注解❗️❗️❗️ 元注解就是修饰注解的注解说白了就是给自定义注解所提供的。
3.1 Target限定注解可以应用的目标元素类型比如只能出现在class上
Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Target {ElementType[] value();
}ElementType取值 TYPE类、接口、枚举 FIELD字段 METHOD方法 PARAMETER参数 CONSTRUCTOR构造器 LOCAL_VARIABLE局部变量 ANNOTATION_TYPE注解类型 PACKAGE包 TYPE_PARAMETER类型参数(Java 8) TYPE_USE类型使用(Java 8)
✨示例
Target({ElementType.METHOD, ElementType.TYPE})
public interface MyAnnotation {}3.2 Retention指定注解的保留策略即注解在什么级别可用一般都是RUNTIME
Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Retention {RetentionPolicy value();
}RetentionPolicy取值 SOURCE仅源码级别编译时丢弃 CLASS编译时保留但JVM不加载(默认) RUNTIME运行时保留可通过反射读取
✨示例
Retention(RetentionPolicy.RUNTIME)
public interface MyAnnotation {}3.3 Documented指示注解应该被包含在Javadoc中
Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Documented {
}✨示例
Documented
public interface MyAnnotation {}3.4 Inherited可以被子类继承
Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Inherited {
}特点 仅对类注解有效 接口上的注解不会被实现类继承 方法注解不会被重写方法继承
3. 5. Repeatable (Java 8允许在同一元素上多次使用相同的注解)
Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Repeatable {Class? extends Annotation value();
}✨示例
// 1. 定义容器注解
public interface Schedules {Schedule[] value();
}// 2. 使用Repeatable
Repeatable(Schedules.class)
public interface Schedule {String time();
}// 3. 使用方式
Schedule(time morning)
Schedule(time evening)
public void doSomething() {}4. 自定义注解
import java.lang.annotation.*;Target(ElementType.METHOD) // 可用于方法
Retention(RetentionPolicy.RUNTIME) // 运行时保留
Documented // 包含在Javadoc中
public interface MethodLogger {// 日志级别默认为INFOLogLevel level() default LogLevel.INFO;// 是否记录方法参数boolean logParameters() default true;// 是否记录返回值boolean logResult() default false;// 超时警告阈值(毫秒)long timeoutThreshold() default -1;
}enum LogLevel {DEBUG, INFO, WARN, ERROR
}public class UserService {MethodLogger(level LogLevel.DEBUG, logResult true)public User getUserById(Long id) {// 方法实现}MethodLogger(timeoutThreshold 500)public void updateUser(User user) {// 方法实现}
}上面就是一个自定义注解的例子定义注解还是相对简单的重点还是怎么去使用注解要使用注解就需要了解反射机制先不要着急让我们来了解一下Java中的反射机制。
二、反射 反射(Reflection)是Java语言的一个强大特性它允许程序在运行时动态地获取类的信息并操作类或对象的属性、方法和构造器。反射机制是Java被视为动态语言的关键特性之一。 用大白话来说反射允许我们在程序运行的过程中读取某个类的详细信息同时也可以调用这个类的方法或者访问其中的属性。因为在一般的编程中我们都是指定某个具体的类来操作其中的方法和属性所属的位置是调用者层面✨反射则是站在开发者层面通过开发者编写的代码或者提供的某个信息找到对应的类并进行操作举个例子。 springmvc中如果一个controller类出现了两个相同路径的注解比如GetMapping“aaa”,这时候编译器是会报错的因为不允许两个相同的请求路径那底层是怎么实现的呢虽然没有使用反射机制但实现的原理和过程和反射类似。 注解的检查是在启动的时候就完成的具体实现的类在RequestMappingHandlerMapping中它是扫描的所有的controller和RequestMapping最后注册RequestMapping的时候进行冲突检测。这里使用扫描的手段获取了想要得到的对象而在反射中我们根据对应的类名创建出想要的对象达到相同的目的。
1. 反射的基本概念 获取任意一个类的Class对象 获取类的所有成员变量、方法、构造器等信息 创建对象、调用方法、访问或修改字段值 动态代理
2. 反射的核心类 Class类代表类的实体 Field类代表类的成员变量 Method类代表类的方法 Constructor类代表类的构造方法 这里面最常用的还是Field类一般我们都是用于操作对象上面的属性如果熟悉EasyExcel框架的就知道导出是可以通过Excel注解实现的底层就是访问的Field类来获取对应的信息。而方法则一般用于框架开发和一些特殊的机制需要调用类上面的方法。
3. 获取Class对象的三种方式
// 1. 通过类名.class获取
Class? clazz1 String.class;// 2. 通过对象.getClass()获取
String str Hello;
Class? clazz2 str.getClass();// 3. 通过Class.forName()获取(最常用)
Class? clazz3 Class.forName(java.lang.String);4. 反射的基本操作
4.1 创建对象
// 获取Class对象
Class? clazz Class.forName(com.example.Person);// 使用无参构造器创建对象
Object obj1 clazz.newInstance(); // 已过时Java9
Object obj2 clazz.getDeclaredConstructor().newInstance();// 使用有参构造器创建对象
Constructor? constructor clazz.getConstructor(String.class, int.class);
Object obj3 constructor.newInstance(张三, 25);注意哈在后期的Java版本中逐渐的淘汰了newInstance这个方法最好使用第二种。❗️
4.2 获取和操作字段
// 获取Class对象
Class? clazz Class.forName(com.example.Person);
Object obj clazz.getDeclaredConstructor().newInstance();// 获取公共字段
Field publicField clazz.getField(publicFieldName);// 获取所有字段(包括私有)
Field[] fields clazz.getDeclaredFields();
Field privateField clazz.getDeclaredField(privateFieldName);// 访问字段值
privateField.setAccessible(true); // 对私有字段需要设置可访问
Object value privateField.get(obj);// 修改字段值
privateField.set(obj, newValue);这是我们最常用的一种写法获取到对象的属性可以读取对应的值介绍完基本的概念之后会将一个真实开发的例子公式的计算。
4.3 调用方法
// 获取Class对象
Class? clazz Class.forName(com.example.Person);
Object obj clazz.getDeclaredConstructor().newInstance();// 获取公共方法
Method publicMethod clazz.getMethod(methodName, parameterTypes);// 获取所有方法(包括私有)
Method[] methods clazz.getDeclaredMethods();
Method privateMethod clazz.getDeclaredMethod(privateMethodName, parameterTypes);// 调用方法
privateMethod.setAccessible(true); // 对私有方法需要设置可访问
Object result privateMethod.invoke(obj, args);5. 反射的应用场景
框架开发如Spring的IoC容器、Hibernate的ORM实现
动态代理AOP编程的基础
注解处理运行时通过反射读取注解信息
工具类开发如BeanUtils、JSON序列化等
IDE功能如代码提示、自动补全
6. 反射的优缺点
优点 提高程序的灵活性和扩展性 可以在运行时动态获取类信息 可以实现动态创建对象和调用方法 缺点 性能开销反射操作比直接代码调用慢 安全限制反射需要运行时权限 内部暴露可以访问私有成员破坏了封装性 代码复杂度反射代码可读性较差调试困难
7. 反射的真实场景开发 之前的公司中有一个需求是要定制工资的计算公式根据公式计算薪资结果那么计算公式定义的时候最重要的一个概念是动态参数和动态的函数也就是后期可能会因为业务需求增加参数和函数那总不能改代码吧。所以我们利用了注解尽量让代码的改动变小大致的实现思路如下 代码中有一个专门的package是calculateDomain这里面定义了所有的参数类如果对象中的某个属性需要定义为计算参数在属性上添加注解TableColumnMapping即可。
/*** 标注可以映射参数的字段*/
Target(ElementType.FIELD)
Retention(RetentionPolicy.RUNTIME)
Documented
public interface TableColumnMapping {String name() default ;
}// 使用示例TableColumnMapping(name 岗位工资标准(jobSalaryStandard))private String jobSalaryStandard;添加完注解之后我们继续定义一个常量记录类名和一些其它的配置信息如下 public static final ListMapString, String FACTOR_MAPPING_TABLE_LIST Arrays.asList(new HashMap() {{put(id, SalaryInfo);put(name, 薪酬信息(salary_info));put(class, com.tpehr.calculate.domain.SalaryInfo);}}) 之后定义方法获取对应的注解信息然后返回给前台。
private static ListMapString, String getColumnsByClassName(String className) throws ClassNotFoundException {ListMapString, String resList new ArrayList();// 解析出所有标记注解的字段Class clazz Class.forName(className);Field[] fields clazz.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(TableColumnMapping.class)) {MapString, String map new HashMap();TableColumnMapping tableColumnMapping field.getAnnotation(TableColumnMapping.class);map.put(id, field.getName());map.put(name, tableColumnMapping.name());resList.add(map);}}return resList;}核心的代码如上因为返回给前台的信息就是一个大的工资类型比如社保社保下面还会有很多详细的计算项所以加了一层配置常量后期只需要维护对应的常量和Java对象即可。
总结 关于自定义注解和反射到这里就结束了我们目前只需要掌握反射的基本使用方式就可以了已经能适用于大多数场景。