案例描述Java反射和注解
反射
Java反射机制是再运行状态中,对于任意一个实体类,都能够指导这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意方法和属性
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
即将类的属性、方法、包、父类、接口等等信息,实现成一个一个的对象
1. Class对象的基本方法
@Data
public class Book {
private String name;
private Integer id;
}
获取Book类的三种方法
@Test
public void testClass() throws ClassNotFoundException {
Class<Book> Class1 =Book.class;
System.out.println(Class1.getClass()); //class java.lang.Class
System.out.println(Class1.getSimpleName());//Book
Class<?> Class2 = Class.forName("com.example.demo.entity.Book");
System.out.println(Class2.getName());//com.example.demo.entity.Book
Book book=new Book();
Class<? extends Book> Class3 = book.getClass();
System.out.println(Class3.getName()); //com.example.demo.entity.Book
}
2.Class对象的基本方法
Class代表类的实体,再运行的Java应用程序中标识类和接口,再这个类中提供了很多有用的方法
方法 | 用途 |
---|---|
asSubClass(Class clazz) | 把传递的类的对象转换成代表子类的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和结构类的对象 |
getInterfaces() | 创建类的实例 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterface() | 获取当前类实现的类或接口 |
3. Field
Field代表类的 成员变量,成员变量(字段)和成员属性是两个概念
比如:当一个Book了中有一个name变量,那我们称为name字段。
但是如果没有getName和setName这两个方法,那么这个类就没有name属性,
反之,如果这个类有getName和setName这两个方法,我们都称他为有name这个属性
我们可以通过getDeclaredField()方法来get&set 私有变量private
@Test
public void testField() throws Exception {
Class<Book> bookClass = Book.class;
Book book = new Book("三体",1);
System.out.println("反射前:"+book);
Field field = bookClass.getDeclaredField("name");
field.setAccessible(true);
Object b = field.get(book);
System.out.println(b);
field.set(book,"神雕侠侣");
System.out.println(book);
}
反射前:Book(name=三体, id=1)
三体
Book(name=神雕侠侣, id=1)
4. Method方法
Method代表一个类中的方法
@Test
public void testMethod() throws Exception{
Class<Book> bookClass = Book.class;
Book book = new Book("天龙八部", 90);
Method getName = bookClass.getDeclaredMethod("getName");
Object Mvalue = getName.invoke(book);
System.out.println(Mvalue); //天龙八部
}
Invoke 方法的用处: SpringAOP切面方法执行的前后进行某些操作,就是使用的invoke方法
动态代理设计模式中,使用的也是Invoke
Method的invoke的应用
创建一个工具类
package com.example.demo.config;
import java.lang.reflect.Method;
import java.util.Arrays;
public final class MethodUtils {
private MethodUtils(){}
/**
* 执行对象的指定方法
* @param obj 要执行方法的对象
* @param methodName 要执行方法名称
* @param params 方法参数
* @return
*/
public static Object invokeMethods(Object obj,String methodName,Object... params) {
Class<?> objClass = obj.getClass();
Object result=null;
Long starttime=System.currentTimeMillis();
try {
System.out.println("方法:"+methodName);
if(params==null||params.length==0) {
Method declaredMethod = objClass.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
result = declaredMethod.invoke(obj);
System.out.println("返回值"+result);
}
else{
int length = params.length;
Class[] classes = new Class[length];
Object[] paramValues =new Object[length];
for(int i=0;i<params.length;i++){
classes[i]=params[i].getClass();
paramValues[i]=params[i];
}
Method method=objClass.getDeclaredMethod(methodName,classes);
System.out.println("传入参数"+ Arrays.toString(params));
method.setAccessible(true);//暴力反射
result = method.invoke(obj, paramValues);
System.out.println("返回值"+result);
}
} catch (Exception e) {
e.printStackTrace();
}
Long endTime=System.currentTimeMillis();
System.out.println("耗时:"+(endTime-starttime));
return result;
}
}
@Test
public void testMethodUtils(){
Book book = new Book("天龙八部", 90);
Object getName = MethodUtils.invokeMethods(book, "getName");
System.out.println(getName); //天龙八部
Object setName = MethodUtils.invokeMethods(book, "setName","斗罗大陆");
System.out.println(book); //Book(name=斗罗大陆, id=90)
}
方法:getName
返回值天龙八部
耗时:0
天龙八部
方法:setName
传入参数[斗罗大陆]
返回值null
耗时:0
Book(name=斗罗大陆, id=90)
5. Constructor(少用)
强制创建对象
在之前的方法中我们提到了newInstace()这个CLass方法
@Test
public void testInstance() throws IllegalAccessException, InstantiationException {
Class<Book> bookClass = Book.class;
Book book = bookClass.newInstance();
System.out.println(book); //Book(name=null, id=null)
}
但newInstace()功能简单,局限性大,只能无参构造
@Test
public void testConstructor() throws Exception{
Class<Book> bookClass = Book.class;
Constructor<Book> declaredConstructor = bookClass.getDeclaredConstructor(String.class, Integer.class);
Book book = declaredConstructor.newInstance("斗罗大陆", 90);
System.out.println(book); //Book(name=斗罗大陆, id=90)
Constructor<Book> declaredConstructor1 = bookClass.getDeclaredConstructor(String.class);
declaredConstructor1.setAccessible(true);
Book book1 = declaredConstructor1.newInstance("仙剑奇侠传");
System.out.println(book1);
}
违背了java是思想
注解
注释:文字描述程序,给程序员看的
注释:
- 说明程序的一个标识
- 给计算机看
- 注解也叫元数据
- 是一种代码级别的说明
- jdk1.5之后引入的一个特性,是一种特殊的接口
- 可以使用在字段、类、方法、包、类、数等上面
注意:
- 注解本身仅起到标识性的作用(没有功能),我们是通过反射去获取到注解,再根据是否有这个注解,注解中的一些属性去判断,执行哪种业务逻辑
(类似接口,没有任何功能,仅是比较规范)
作用分类:
- 编写文档:
- 通过代码里的注解标识去生成API文档(Swagger)
- 代码分析
- 通过注解去对代码进行逻辑上的分析(通过反射区操作业务)
- 编译检查
- 通过注解让编译器去实现基本的编译
JDK中预定的一些注解:
- Override: 检测该注释便是的方法是否继承自父类(接口)
- Deprecated: 标识改内容以过失,可能会移除该功能
- SuppressWarning :压制警告,指定警告级别,这个级别下的警告全部不显示
自定义注解
在实际开发中,可能会出现一些代码及其复杂或者复用性很低的业务逻辑
比如:导出Excel、缓存、将对象返回值转JSON、事物等
格式
元注解
public @interface 注解名称{
属性列表;
}
本质:本质就是一个接口,该接口继承Annotation接口
属性
本质:接口中的抽象方法
属性只能是一下几种数据类型:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
如果定义了属性,再使用的时候需要给属性赋值
如果只有一个属性需要复制,并且这个属性叫value,那么就可以省略属性名
数值赋值时,值用{}包裹,如果数组中只有一个值,那么大括号可以省略
元注解
用于描述注解的注解
-
@Target: 描述该注解作用范围
- ElementType取值
- Type:作用与类
- METHOD:作用与方法
- FIELD:作用与字段
- ElementType取值
-
@Retention:描述注解被保留的阶段
- RetentionPolicy:RUNTIME:当前描述的注解,会保留到class字节码文件中,并被jvm读取到
-
@Documented:描述注解是否被抽取到api文档中
-
@Inherited: 描述注解是否可以被继承
注解案例
编写一个缓存注解,该注解用于缓存指定方法的返回值
- 创建一个注解
package com.example.demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.FIELD})//ElementType.TYPE说明只能作用在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String content(); //当content为value时,使用注解赋值时可以省略
int num() default 0;//有默认值,可以不赋值
}
-
创建一个User类,并加上我们写的注解
package com.example.demo.entity; import com.example.demo.annotation.MyAnnotation; @MyAnnotation(content = "我是内容") public class User { @MyAnnotation(content = "我是丽丝") private String name; private Integer id; private Integer age; @Override public String toString() { return "User{" + "name='" + name + '\'' + ", id=" + id + ", age=" + age + '}'; } public User() {} public User(String name, Integer id, Integer age) { this.name = name; this.id = id; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
-
调用我们新写的注解
3.1 调用类上注解
@Test public void AnnotationTest(){ Class<User> userClass = User.class; MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class); if(annotation!=null){ System.out.println(annotation.content()+":"+annotation.num());//我是内容:0 } }
3.2 调用属性上的注解
@Test public void AnnatationTest2() throws Exception{ Class<User> userClass = User.class; Field[] declaredFields = userClass.getDeclaredFields(); User user=new User(); user.setName("梦琪D路飞"); System.out.println(user); for(Field f:declaredFields){ MyAnnotation annotation = f.getAnnotation(MyAnnotation.class); if(annotation!=null){ String content = annotation.content(); f.setAccessible(true); f.set(user,content); } } System.out.println(user); }
User{name='梦琪D路飞', id=null, age=null}
User{name='我是丽丝', id=null, age=null}
缓存注解
编写一个缓存注解,用于缓存一个返回值
package com.example.demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
String key();
int timeout() default 10;
TimeUnit timeUnit() default TimeUnit.MINUTES;
}
package com.example.demo.utils;
import com.example.demo.annotation.Cache;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CacheUtils {
private static Map<String,Object> cacheMap = new ConcurrentHashMap<>();
private static final String CACHE_SEPARATE = "::";
public static Object invokeMethods(Object obj,String methodName,Object... params) {
Class<?> objClass = obj.getClass();
Object result=null;
Long starttime=System.currentTimeMillis();
try {
if(params==null||params.length==0) {
Method declaredMethod = objClass.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
Cache annotation = declaredMethod.getAnnotation(Cache.class);
if(annotation!=null){
String key = annotation.key();
Object value = cacheMap.get(key);
if(value!=null){
return value;
}
}
result = declaredMethod.invoke(obj);
if(annotation!=null){
String key = annotation.key();
cacheMap.put(key,result);
}
}
else{
int length = params.length;
Class[] classes = new Class[length];
Object[] paramValues =new Object[length];
for(int i=0;i<params.length;i++){
classes[i]=params[i].getClass();
paramValues[i]=params[i];
}
Method method=objClass.getDeclaredMethod(methodName,classes);
method.setAccessible(true);//暴力反射
Cache annotation = method.getAnnotation(Cache.class);
if(annotation!=null){
String key =annotation.key();
Object paramValue = paramValues[0];
String cacheKey=key+CACHE_SEPARATE+paramValue;
Object value = cacheMap.get(cacheKey);
if(value!=null){
return value;
}
}
result = method.invoke(obj, paramValues);
if(annotation!=null){
String key =annotation.key();
Object paramValue = paramValues[0];
String cacheKey=key+CACHE_SEPARATE+paramValue;
cacheMap.put(cacheKey,result);
}
}
} catch (Exception e) {
e.printStackTrace();
}
Long endTime=System.currentTimeMillis();
return result;
}
}
@Test
public void CacheTest() throws Exception{
UserController userController = new UserController();
Object user1 = CacheUtils.invokeMethods(userController, "getUserById", 1);
Object user2 = CacheUtils.invokeMethods(userController, "getUserById", 2);
System.out.println(user1);
System.out.println(user2);
}
getUserById Function
getUserById Function
User{name='James Harden', id=2, age=37}
User{name='Steph Curry', id=3, age=37}