java 代理模式
代理模式简介
代理模式(Proxy Pattern)是一种结构性模式。在代理模式中,一个类代表另一个类的功能,原来的类为目标对象,新建的类称为代理类,代理类在目标对象的基础上,增强额外的功能操作,即增强原来的功能。
实现方式
代理模式主要有三种形式,分别是静态代理,动态代理(JDK代理)和cglib代理(在内存创建对象而不需要实现接口,也可以属于动态代理范畴)。
代理模式主要包含三个角色:
- 抽象目标对象:通过接口或抽象类声明真实对象和业务方法
- 具体目标对象:实现了抽象目标对象中的具体业务,是代理对象所代表的真实对象
- 代理类(Proxy):代理对象,也实现抽象目标对象的接口,其内部含有对目标对象的引用,它可以访问,控制或者扩展具体对象中的业务。
1.静态代理
静态代理通过实现目标对象的抽象接口或继承抽象父类,并通过组合的方式引入被代理的对象,从而在代理类中为目标对象的业务进行增强。它要求代理对象与目标对象实现相同的接口,然后通过重写业务方法,在重写的方法中调用目标对象的方法实现增强对应的方法。
它的优点是被代理的类不会修改目标对象的功能,就能完成对目标对象中业务的扩展。
缺点是目标对象和代理对象必须要实现同一个接口,一个代理类只能为一个目标对象做代理,而且一旦更改接口方法,目标对象和代理对象都要修改。
缺点: 1、重复性: 需要代理的业务或方法越多,重复的模板代码越多;
2、脆弱性: 一旦改动基础接口,代理类也需要同步修改(因为代理类也实现了基础接口)。
1.接口:
public interface TargetInteface {
void method1();
void method2();
int method3(Integer i);
}
2.具体对象:
public class Target implements TargetInteface {
@Override
public void method1() {
System.out.println(" Target method1 running ...");
}
@Override
public void method2() {
System.out.println("Target method2 running ...");
}
@Override
public int method3(Integer i) {
System.out.println("Target method3 running ...");
return i;
}
}
3.代理类:
public class TargetProxy implements TargetInteface {
private Target target =new Target();
@Override
public void method1() {
System.out.println("执行方法前...");
target.method1();
System.out.println("执行方法后...");
}
@Override
public void method2() {
System.out.println("执行方法前...");
target.method2();
System.out.println("执行方法后...");
}
@Override
public int method3(Integer i) {
System.out.println("执行方法前...");
int method3 = target.method3(i);
System.out.println("执行方法后...");
return method3;
}
}
4.测试:
public static void main(String[] args) {
TargetInteface target = new TargetProxy();
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
输出:

可以看到代理类没有更改目标对象的业务方法,对目标对象的目标方法进行了增强处理。
2.JDK动态代理
JDK动态代理使用反射机制,在程序的执行过程中,创建代理类对象。因此代理类代理的目标类是活动的,可以为很多个目标对象进行代理。
接口和具体对象类都没有变化,代理类需要实现以下三点:
- 必须实现
InvocationHandler接口,表明该类是一个动态代理执行类 - 重写
InvocationHandler接口中的public Object invoke(Object proxy, Method method, Object[] arg2)方法 - 获取代理对象,需要使用
Proxy.newProxyInstance(Class loader, Class[] interfaces, InvocationHandler h)方法去获取Proxy对象实例
动态代理(搭配泛型参数)可以实现了一个代理同时处理 N 种基础接口,一定程度上规避了静态代理的缺点。从原理上讲,静态代理的代理类 Class 文件在编译期生成,而动态代理的代理类 Class 文件在运行时生成,代理类在 coding 阶段并不存在,代理关系直到运行时才确定
链接:https://juejin.cn/post/6974018412158664734
代理类的具体实现代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TargetProxy implements InvocationHandler{
private Target target;
public TargetProxy(Target target) {
this.target = target;
}
//重写invoke方法,使用jdk中的反射完成对方法的增强,proxy为目标对象目标对象target,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行方法前...");
//使用反射执行
Object result = method.invoke(target, args);
System.out.println("执行方法后...");
return result;
}
public Object getTarget(){
//构建一个新的代理类对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
测试类:
public static void main(String[] args) {
//新建一个代理类,传入新建的具体对象
TargetProxy targetProxy=new TargetProxy(new Target());
//利用代理类获取被增强了的目标对象
TargetInteface target = (TargetInteface) targetProxy.getTarget();
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
为了让代理更加通用,看起来更加简洁,也可以使用内部类将代理类写成这个样子:
public class TargetProxy {
public static <T> Object getTarget(T t) {
//新构建了一个 新的 代理类的对象
return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
//比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
System.out.println("执行方法前...");
Object invoke = method.invoke(t, args);
System.out.println("执行方法后...");
return invoke;
}
});
}
}
3.cglib代理
CGLIB(Code Generation Library)是一个开源、高性能、高质量的Code生成类库(代码生成包)。
它可以在运行期扩展Java类与实现Java接口。Hibernate用它实现PO(Persistent Object 持久化对象)字节码的动态生成,Spring AOP用它提供方法的interception(拦截)。
CGLIB的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。但不鼓励大家直接使用ASM框架,因为对底层技术要求比较高。
cglib代理也叫基于子类的动态代理,Cglib提供了比jdk更为强大的动态代理。主要相比jdk动态代理的优势有:
- jdk动态代理只能基于接口,代理生成的对象只能赋值给接口变量,而Cglib就不存在这个问题,Cglib是通过生成子类来实现的,代理对象既可以赋值给实现类,又可以赋值给接口。
- Cglib速度比jdk动态代理更快,性能更好。
cglib代理的实现方法和jdk动态代理的实现方式有些类似,也是实现了一个MethodInterceptor接口,重写了接口中的 public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) 方法,在这个方法中对目标对象的业务方法进行增强处理,然后利用en.create()方法创建子类实例,也就是目标对象实例,因为在cglib中“凭空”生成了一个原目标对象的子类,并把Callback指向了this,也就是当前对象,从而会调用intercept方法。而在intercept方法里,进行了附加功能的执行,最后还是调用了原始目标对象的相应方法。
CGLIB创建动态代理类过程
(1)查找目标类上的所有非final的public类型的方法定义;
(2)将符合条件的方法定义转换成字节码;
(3)将组成的字节码转换成相应的代理的class对象;
(4)实现MethodInterceptor接口,用来处理对代理类上所有方法的请求。
代理类实现代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TargetProxy implements MethodInterceptor{
//创建Enhancer对象,类似于JDK动态代理的Proxy类
private Enhancer enhancer=new Enhancer();
private Target target;
public TargetProxy(Target target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//object表示要进行增强的对象,method表示拦截的方法,即目标对象中的方法
//args表示方法中的参数,methodProxy表示对方法的代理,表示要触发父类的方法对象
System.out.println("执行方法前。。。");
//调用原有方法 invokeSuper方法表示对被代理对象方法的调用,执行的是原始类的方法
Object invoke = methodProxy.invokeSuper(o, args);
// Object invoke = method.invoke(t, args);// 作用等同与上面。执行的是子类的方法
System.out.println("执行方法后。。。");
return invoke;
}
public Object getProxy(){
//设置需要创建子类的类 设置目标类的字节码文件
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
}
同样的,也可以写成这个样子:
public class TargetProxy {
public static <T> Object getProxy(T t) {
Enhancer en = new Enhancer(); //帮我们生成代理对象
en.setSuperclass(t.getClass());//设置要代理的目标类
en.setCallback(new MethodInterceptor() {//代理要做什么
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//object表示要进行增强的对象,method表示拦截的方法,即目标对象中的方法
//args表示方法中的参数,methodProxy表示对方法的代理
System.out.println("执行方法前。。。");
//调用原有方法 invokeSuper方法表示对被代理对象方法的调用,执行的是原始类的方法
Object invoke = methodProxy.invokeSuper(object, args);
// Object invoke = method.invoke(t, args);// 作用等同与上面。执行的是子类的方法
System.out.println("执行方法后。。。");
return invoke;
}
});
return en.create();
}
}
JDK动态代理与CGLIB对比:
JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才生成代理对象。CGLIB动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。
JDK Proxy的优势:
最小化依赖关系、代码实现简单、简化开发和维护、JDK原生支持,比CGLIB更加可靠,随JDK版本平滑升级。而字节码类库通常需要进行更新以保证在新版Java上能够使用。
基于CGLIB的优势:
无需实现接口,达到代理类无侵入,只操作关心的类,而不必为其他相关类增加工作量。高性能。
总结
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
应用场景:AOP、日志框架、全局性异常处理、事务处理等
更多推荐


所有评论(0)