什么是Java反射机制
Java反射机制,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;也就是说,Java可以加载一个运行时才得知名称的class,获得其完整结构。
JAVA反射机制提供了什么功能
- 获取类的Class对象
- 获取类的Fields
- 获取类的Method
- 获取类的Constructor
- 新建类的实例
Class<T>的函数newInstance
通过Constructor对象的方法newInstance
举个例子
public class User {
private int id;
private String userName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
public class Test {
public static void main(String[] args) throws Exception {
Class<?> classType = User.class;
//获取属性
Field[] fields = classType.getFields();
for (Field field : fields){
System.out.println(field.getName());
}
Field[] declaredFields = classType.getDeclaredFields();
for (Field declaredField : declaredFields){
System.out.println(declaredField.getName());
}
Field field = classType.getDeclaredField("id");
User user = new User();
user.setId(1);
//暴力访问
field.setAccessible(true);
System.out.println(field.get(user));
Method method1 = classType.getDeclaredMethod("setUserName",String.class);
method1.invoke(user,"kobe");
Method method = classType.getDeclaredMethod("getUserName",null);
System.out.println(method.invoke(user));
}
}
运行结果如下:
id
userName
1
kobe
Java动态代理详解
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问
先来理解一下Java静态代理,声明一个接口UserService
public interface UserService {
public void select();
public void update();
}
public class UserServiceImpl implements UserService {
public void select() {
System.out.println("查询 selectById");
}
public void update() {
System.out.println("更新 update");
}
}
代理类:UserServiceProxy
public class UserServiceProxy implements UserService {
private UserService target; // 被代理的对象
public UserServiceProxy(UserService target) {
this.target = target;
}
public void select() {
before();
target.select(); // 这里才实际调用真实主题角色的方法
after();
}
public void update() {
before();
target.update(); // 这里才实际调用真实主题角色的方法
after();
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
}
}
测试
public class Test {
public static void main(String[] args) {
UserService userServiceImpl = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userServiceImpl);
proxy.select();
proxy.update();
}
}
输出:
log start time [Thu Dec 21 18:13:25 CST 2020]
查询 selectById
log end time [Thu Dec 21 18:13:25 CST 2020]
log start time [Thu Dec 21 18:13:25 CST 2020]
更新 update
log end time [Thu Dec 21 18:13:25 CST 2020]
可以看出,当业务场景复杂时静态代理有明显的缺点,涉及目标对象与代理类同时修改,不易维护
接下来,我们来看下JDK动态代理
主要涉及两个类:java.lang.reflect.Proxy
和 java.lang.reflect.InvocationHandler
新增逻辑处理器 LogHandler 类
public class LogHandler implements InvocationHandler {
Object target; // 被代理的对象,实际的方法执行者
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args); // 调用 target 的 method 方法、利用反射调用类里面的实际方法
after();
return result; // 返回方法的执行结果
}
// 调用invoke方法之前执行
private void before() {
System.out.println(String.format("log start time [%s] ", new Date()));
}
// 调用invoke方法之后执行
private void after() {
System.out.println(String.format("log end time [%s] ", new Date()));
}
}
测试,通过代理类对象调用代理方法
public class Test {
public static void main(String[] args) throws Exception {
// 1. 创建被代理的对象
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 2. 获取对应的 ClassLoader
ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
// 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
Class[] interfaces = userServiceImpl.getClass().getInterfaces();
// 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
// 这里创建的是一个自定义的日志处理器,须传入实际的执行对象 userServiceImpl
InvocationHandler logHandler = new LogHandler(userServiceImpl);
/*
5.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
b.然后根据相应的字节码转换成对应的class,
c.然后调用newInstance()创建代理实例
*/
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
// 调用代理的方法
proxy.select();
System.out.println();
proxy.update();
}
}
输出结果
log start time [Fri Feb 19 17:25:57 CST 2021]
查询 select
log end time [Fri Feb 19 17:25:57 CST 2021]
log start time [Fri Feb 19 17:25:57 CST 2021]
更新 update
log end time [Fri Feb 19 17:25:57 CST 2021]