何为代理?
生活中,当我们想去做一件事的时候,有时候会自己去做,在不方便的时候,也可能委托给他人去处理。这种事件可以说是被委托人代理我去做我想做的事情。
顾名思义,在程序中,代理机制就是给目标对象提供一个代理对象,由代理对象去控制目标对象的引用,即代目标去执行某些方法。
使用场景:
当我们想要通过某个对象实现某些功能时,可能不一定合适去直接访问,或者说根本无法访问对象。例如:在RMI技术中,客户端需要实现某些接口的方法时,这个的实现类根本不在客户端存在,必须通过远端交予服务器去执行。这时候我们就可以创造代理类对象,在代理类对象方法体内部,完成向服务器发送与远端调用并获得结果的操作。
又或者我们在处理某些业务的时候,常常不是只实现业务本身就可以了,在业务的前置以及后置通常需要添加许多操作(增加日志,拦截等等)。这些都可以算是对核心功能的增强。倘若直接在本类中写好。那么这一步包含的功能太多,耦合性太强。不方便修改。那么,我们是否可以将增强操作单独写一个类里,再写一个目标类的代理类,在代理类里完成对目标类的增强。目标类只关心核心功能的实现。这种思想在SpringAop中也有体现。
静态代理:
接口:
public interface IService {
void dealService();
}
目标类:
public class Service implements IService {
public Service() {
}
@Override
public void dealService() {
System.out.println("目标类功能!");
}
}
增强类:
public class OtherFunction {
public OtherFunction() {
}
public void before() {
System.out.println("前置增强!");
}
public void after() {
System.out.println("后置增强!");
}
}
代理类:
public class ServiceProxy implements IService{
private Service service;
private OtherFunction otherFunction;
public ServiceProxy(Service service, OtherFunction otherFunction) {
this.service = service;
this.otherFunction = otherFunction;
}
@Override
public void dealService() {
otherFunction.before();
service.dealService();
otherFunction.after();
}
}
测试:
public class Test {
public static void main(String[] args) {
Service service = new Service();
OtherFunction otherFunction = new OtherFunction();
ServiceProxy proxy = new ServiceProxy(service, otherFunction);
proxy.dealService();
}
}
测试结果:
分析:
可以看到上面是静态代理的一种简单实现。我们完成了再不修改目标类源代码的同时利用代理类对其功能进行了增强。也避免了对目标类对象的直接调用,而是改为使用代理对象去控制,过程非常简单易懂!
但是静态代理有着不小的弊端: 倘若有大量的目标类需要去代理,那就需要大量的代理类,或者某个目标类的方法过多,代理类需要实现的功能也就越多。代码重用性很差。
Java提供了动态代理的方式,可以很好地解决这个问题。
动态代理:
我们继续沿用上面的目标类Service,接口IService,增强类OtherFunction。获取Jdk代理需要用到Proxy.newProxyInstance这个静态方法。我们来看一下这个方法。
Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
里面需要三个参数:类加载器(自然是由目标类获得),代理类所需要实现的接口列表(由目标类获得),一个调用处理器。
这里我们继续沿用上面的接口、目标类与增强类。
那我们实现一个 InvocationHandler并且将增强类也放进去。
public class ServiceHandler implements InvocationHandler {
private Object target;
private OtherFunction otherFunction;
public ServiceHandler(Object target, OtherFunction otherFunction) {
this.target = target;
this.otherFunction = otherFunction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
otherFunction.before();
Object result = method.invoke(this.target, args);
otherFunction.after();
return result;
}
}
下面是测试代码:
public class TestForJDKProxy {
public static void main(String[] args) {
Service target = new Service();
OtherFunction otherFunction = new OtherFunction();
ServiceHandler h =
new ServiceHandler(target, otherFunction);
IService service = (IService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
h);
service.dealService();
System.out.println(service.getClass().getName());
}
}
输出结果:
可以看到,最后一行代码里面代理对象的类型为$Proxy0。是JDK代理的结果没错。
那么代理类是如何获取的呢
分析:首先我们要有“类加载”的概念。类加载阶段的过程大概分为三步:
(1)通过一个类的全限定名来获取定义此类的二进制字节流(也就是我们常见的.class文件内容)
(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
(3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
注意第一步,我们通常自己编写的类的源文件(.java文件)可以通过编译过程生成.class文件,或者直接使用jar包里的.class文件。这是我们比较熟悉的获取二进制字节流的方式。而事实上获得二进制字节流有很多种,这里不详细说。仅仅提一种——运算时生成,动态代理就是这种获取二进制字节流的典型应用。应用ProxyGenerator.generateProxyClass()来为特定接口生成形式为