代理三要素
一个典型的代理模式通常有三个角色,这里称之为代理三要素。
-
共同的接口
-
真实对象
-
代理对象
在代理模式中,其实就是为了让代理对象完成真实对象的过程,同时可以在不侵入真实对象的基础上,对真实对象处理的业务过程进行扩展或者扩充
Java 静态代理
静态代理其实是通过封装第二方代码进行的业务逻辑嵌套处理的方式,可以创建一个代理类实现和目标方法相同的方法,通过让代理类持有真实对象,然后在原代码中调用代理类方法,来达到添加我们需要业务逻辑的目的。
/**
* @program: blogDemo
* @description: 共同接口
* @author: Henry.Wang
* @create: 2019-03-11 22:51
**/
public interface Action {
void doSomeString();
}
/**
* @program: blogDemo
* @description: 代理对象
* @author: Henry.Wang
* @create: 2019-03-11 22:54
**/
public class ProxyObject implements Action {
private Action realObject; //聚合真实对象
public ProxyObject(Action realObject) {
this.realObject = realObject;
}
@Override
public void doSomeString() {
System.out.println("代理对象执行业务方法...... 调用真实对象的业务方法");
System.out.println("在真实对象处理之前,进行打印");
realObject.doSomeString();
System.out.println("在真实对象处理之后,进行打印");
}
}
/**
* @program: blogDemo
* @description: 真实对象
* @author: Henry.Wang
* @create: 2019-03-11 22:52
**/
public class RealObject implements Action{
@Override
public void doSomeString() {
System.out.println("真实对象处理业务.......");
}
}
/**
* @program: blogDemo
* @description:
* @author: Henry.Wang
* @create: 2019-03-11 22:59
**/
public class main {
public static void main(String[] args) {
Action realObject = new RealObject();
Action proxyObject = new ProxyObject(realObject);
proxyObject.doSomeString();
}
}
输出结果:
代理对象执行业务方法...... 调用真实对象的业务方法
在真实对象处理之前,进行打印
真实对象处理业务.......
在真实对象处理之后,进行打印
静态代理的优点和缺点
- 优点: 代码无侵入性
- 缺点: 显而易见,当出现多个真实对象,且需要代理的目标方法不同时,只有两种方式
- 做多个代理对象,分别代理不同的真实对象。
- 同一个代理对象,分别实现不同的方法,这样会让代理对象是分臃肿,而且当方法签名相同时,需要做过多的逻辑判断。
Java动态代理
通过使用动态代理,我们可以通过在运行时,动态生成一个持有RealObject、并实现代理接口的Proxy,同时注入我们相同的扩展逻辑。哪怕你要代理的RealObject是不同的对象,甚至代理不同的方法,都可以动过动态代理,来扩展功能。简单理解,动态代理就是我们上面提到的方案一,只不过这些proxy的创建都是自动的并且是在运行期生成的。
一般我们使用动态代理有两种,JDK和cglib,这两者的区别在于
-
JDK动态代理需要目标对象实现业务接口,代理类需实现InvocationHandler接口
-
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。
使用cglib代理的对象则无需实现接口,达到代理类无侵入。
-
二者都是在运行时生成代理的class文件,不过一个是代理类$Proxy0 ,一个是真是的实现类字节码。
动态代理生成的类为 lass com.sun.proxy.$Proxy4,cglib代理生成的类为
class com.cglib.xxx实现类 E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB552188b6。
-
cglib代理无需实现接口,通过生成实现类字节码实现代理,比反射稍快,不存在性能问题,但JDK的动态代理是生成接口的Proxy代理类,通过调用反射的方法调用目标方法,大量的反射会消耗性能。
-
cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。
-
cglib需要实现MethodInterceptor接口。这个接口是为了生成的实现类内置的callback方法提供统一对目标方法进行统一的调用
下面我们分别使用JDK和cglib进行动态代理
JDK动态代理
public interface Action {
void doSomeString();
}
public class DynamicProxyHandler implements InvocationHandler {
Object realObject;
public DynamicProxyHandler(Object realObject) {
this.realObject = realObject;
}
/**
* @description
* @param proxy 被代理后的对象
* @param method 将要被执行的方法信息(反射)
* @param args 方法参数
* @return java.lang.Object
* @author Henry.Wang
* @date 2019/3/11 23:55
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理对象执行业务方法...... 调用真实对象的业务方法");
System.out.println("JDK动态代理在真实对象处理之前,进行打印");
method.invoke(this.realObject, args);
System.out.println("JDK动态代理在真实对象处理之后,进行打印");
return null;
}
}
public class RealObject implements Action {
@Override
public void doSomeString() {
System.out.println("真实对象处理业务.......");
}
}
public class main {
public static void main(String[] args) {
//定义真实对象
Action action = new RealObject();
//关键步骤
//实例化代理对象,实例化代理对象的过程其实是在运行时动态生成的class文件
//newProxyInstance 需要三个参数
//1. 类加载器
// 2. 被代理对象实现的所有接口数组,这样就可以生成出所有的需要代理的方法
// 3. 代理对象 具体的实现了 InvocationHandler 的对象,在生成$Proxy0 代理类时,通过构造方法注入
// InvocationHandler,用于调用invoke方法,便于反射调用。
// 其实这一步生成的对象与静态代理很想,只是一个用了反射调用,一个是直接调用
Action proxy = (Action) Proxy.newProxyInstance(
action.getClass().getClassLoader(), action.getClass().getInterfaces(),
new DynamicProxyHandler(action)
);
//由此可以看出 Proxy.newProxyInstance生成的 $Proxy0 对象实现了Action,可以直接强转
proxy.doSomeString();
}
}
在网上找到了一个proxy生成的代理对象的反编译代码
可以看出来,生成的$Proxy0 继承了proxy并且实现了Action,通过构造方法注入了定义的InvocationHandler实现,底层其实就是通过InvocationHandler调用invoke方法。
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Action {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void doSomething() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
...
static {
try {
...
m3 = Class.forName("Action").getMethod("doSomething", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
cglib动态代理
首先需要引入cglib的依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
public class RealObject {
public void doSomeString() {
System.out.println("cglib代理的真实对象处理业务.......");
}
public void doSomeString1() {
System.out.println("cglib代理的真实对象处理业务.......");
}
}
public class ProxyIntercept implements MethodInterceptor {
//1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
System.out.println("cglib动态代理对象执行业务方法...... 调用真实对象的业务方法");
System.out.println("cglib动态代理在真实对象处理之前,进行打印");
methodProxy.invokeSuper(o,objects);
System.out.println("cglib动态代理在真实对象处理之后,进行打印");
return null;
}
}
public class main {
public static void main(String[] args) {
//动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例
Enhancer enchancer = new Enhancer();//Enhancer是CGLib的字节码增强器
enchancer.setSuperclass(RealObject.class);//设置被代理类为父类
enchancer.setCallback(new ProxyIntercept());//设置回调
RealObject o = (RealObject) enchancer.create();
o.doSomeString1();
}
}