在上一篇文章中,我们提到了,RPC框架所需要的java基础,第一点就是java的动态代理机制,动态代理机制的基础是反射,无论是在实际编程或者是面试时,都是java知识的重中之重。
java反射:
定义:
在运行状态中,对于任意一个类,都能够知道这一个类的所有属性和方法,对于任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息以及动态调用类方法的功能称为java的反射机制。
作用:
1、动态的创建类的实例,将类绑定到现有对象中,或从现有对象中获取类型。 2、应用程序需要在运行时从某个特定的程序集中载入一个特定的类。
个人理解的反射机制就是,某些类在程序运行的一开始并没有加载,但是随着程序的运行,我们发现这些类也需要用到,此时就可以通过反射机制,来获取到类的属性和方法。
代理模式:
定义:
委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。代理模式可以提供非常好的访问控制,应用比较广泛。
而其中的代理模式中的动态代理不仅在rpc远程访问中有重要的应用,同样在Spring AOP和其他应用中也起到了很重要的作用。
代理模式的通用类图:
Subject: 抽象主题角色:可以是抽象类,也可以是接口。抽象主题是一个普通的业务类型,无特殊要求。
RealSubject: 具体主题角色:也叫做被委托角色或被代理角色,是业务逻辑的具体执行者。
Proxy: 代理主题角色:也叫做委托类或代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后处理工作。
按照代理创建的时期来进行分类,可以分为动态代理和静态代理。
静态代理:
一个代理类只能实现一种抽象主题角色,在程序运行之前,代理类.class文件就已经被创建,代理类和委托类的关系在运行前就确定。
动态代理:
一个代理类通过反射机制,可以实现多种不类型的抽象主题角色。动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。以下为动态代理概括图:
实现代码块如下:
抽象角色类实现(动态代理的抽象决策类只能使用接口):
public interface Subject {
/**
* 接口方法,抽象主题类
*/
public void request();
}
具体决策类实现:
public class ConcreteSubject implements Subject{
/**
* 具体业务实现逻辑
*/
@Override
public void request() {
//业务处理逻辑
System.out.println("逻辑执行");
}
}
动态创建代理对象的类(代理类,使用反射机制):
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyHandler implements InvocationHandler {
/**
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
/**
*目标对象
*/
private Object target;
/**
*/
public Object newProxyInstance(Object target){
this.target = target;
Object result = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
return result;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//TODO原对象方法调用添加的预处理逻辑
Object ret = null;
try{
//调用目标方法
ret = method.invoke(target, args);
}catch (Exception e){
//log("调用{}.{}发生异常", target.getClass().getName(), method.getName(), e);
throw e;
}
return ret;
}
}
客户端类:
import lombok.extern.slf4j.Slf4j;
import java.util.logging.Logger;
public class Client {
public static void main(String[] args){
System.out.println("开始");
ProxyHandler handler = new ProxyHandler();
Subject subject = (Subject) handler.newProxyInstance(new ConcreteSubject());
subject.request();
System.out.println("结束");
}
}
运行结果: