什么是代理
代理(Proxy)是一种设计模式,它提供了访问目标对象的另一种方式。通过代理对象访问目标对象可以带来诸多好处,比如在不修改目标对象的基础上,增加额外的功能操作,从而扩展了目标对象的功能。这种方法体现了一个编程中重要的思想:避免随意修改他人已经编写的代码或方法,而是通过代理的方式来对其进行扩展和增强。
代理模式的实现可以分为两类,动态代理和静态代理。其中动态代理有可以分为jdk动态代理和cglib动态代理两种。
静态代理
在使用静态代理时,需要定义一个接口或父类,被代理对象和代理对象一起实现或继承相同的接口或父类。在程序启动之前,代理类的.class文件已经存在。代理类可以是程序员直接创建的.java文件,也可以是通过某些工具生成的.java文件。但无论哪种方式,都必须经过编译器编译成.class文件后才能启动程序。
// 定义接口
interface Subject {
void doSomething();
}
// 实现接口的具体类
class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
// 静态代理类
class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void doSomething() {
System.out.println("ProxySubject is doing something before real subject.");
realSubject.doSomething();
System.out.println("ProxySubject is doing something after real subject.");
}
}
// 测试
public class StaticProxyExample {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.doSomething();
}
}
优点:
业务类只需要关注业务逻辑本身,保证了业务类的重用性。
缺点:
由于代理类和被代理类都实现了主题接口,它们都有相同的方法,导致大量代码重复。同时如果主题接口新增了一个方法,那么代理类与被代理类也都需要实现这个方法,增加了维护代码的复杂度。
jdk动态代理
jdk可以使用java.lang.reflect.Proxy来生成代理对象。然后通过实现InvocationHandler接口在invoke方法中调用具体的方法。
Proxy.newProxyInstance方法来实例化动态生成的代理类,而这个方法中的参数分别代表的含义是:
ClassLoader loader: 被代理类的类加载器
Class<?>[] interfaces: 被代理类实现的接口
InvocationHandler h: 实现指定接口InvocationHandler的实现类
// 定义接口
interface Subject {
void doSomething();
}
// 实现接口的具体类
class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
// InvocationHandler实现类
class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DynamicProxy is doing something before real subject.");
Object result = method.invoke(target, args);
System.out.println("DynamicProxy is doing something after real subject.");
return result;
}
}
// 测试
public class JdkDynamicProxyExample {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicProxy(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler
);
proxySubject.doSomething();
}
}
优点:
jdk动态代理不需要自己写代理类的优点,由程序在运行时自动生成。
缺点:
jdk动态代理只能代理接口。
由于Java是一种静态类型语言,对于已经存在的类,无法在运行时动态地修改其行为。因此,JDK动态代理只能基于接口来创建代理对象,利用接口的抽象性质,在运行时动态地生成代理类并实现接口方法的调用。
cglib动态代理
cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。用它实现动态代理可以摆脱必须实现接口的限制。
cglib生成代理对象的类是Enhancer,对于普通的类,Enhancer会生成被代理类的子类,对于接口代理,Enhancer会生成实现了指定接口的代理类。它提供的create方法用来动态地生成代理类及其对象。
cglib提供了一个MethodInterceptor接口可以通过intercepcreatet方法来处理代理类对象上的方法调用。
使用步骤:
- 创建Enhancer对象,并设置被代理类的类对象(即Class对象)为其父类。
- 实现MethodInterceptor接口,编写自定义的拦截器,用于处理方法调用。
- 将自定义的拦截器对象设置为Enhancer对象的回调对象。
- 调用Enhancer对象的create()方法创建代理对象。
- 客户端调用代理对象的方法时,代理对象会调用拦截器的intercept()方法,并将被代理方法的相关信息传递给该方法。
- 在intercept()方法中,可以编写自定义的逻辑来处理方法调用,比如记录日志、检查权限等。
// 实现类
class RealSubject {
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
// MethodInterceptor实现类
class DynamicProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("DynamicProxy is doing something before real subject.");
Object result = proxy.invokeSuper(obj, args);
System.out.println("DynamicProxy is doing something after real subject.");
return result;
}
}
// 测试
public class CglibDynamicProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new DynamicProxy());
RealSubject proxySubject = (RealSubject) enhancer.create();
proxySubject.doSomething();
}
}
优点:
不需要被代理类实现接口,可以直接对类进行代理。可以代理非公共类。由于使用了字节码技术,因此CGLIB代理的性能相对较高。
缺点:
无法代理静态方法,只能代理非final的实例方法。