代理模式
代理模式(Proxy Pattern):一般是指一个对象为另一个对象提供一种代理,从而通过代理对象来控制非代理对象的访问,代理对象在客户端和目标对象之间起到中介作用。
代理模式属于结构型设计模式,可以分为静态代理和动态代理两种类型,而动态代理中又分为 JDK 动态代理和 CGLIB 代理两种。
1. 静态代理模式
标准的静态代理模式需要定义一个接口,然后代理对象与被代理对象都需要实现标准接口,并重写标准接口中定义的方法,被代理对象本身需要实现真正的业务逻辑,而代理对象中一般是在调用被代理对象的前后新增一些其它逻辑处理。
示例:我们以买火车票来举例说明。
package cn.liangyy.proxy.staticproxy;
/**
* 标准接口
*/
public interface Travel {
/**
* 买火车票
*/
void buyTrainticket();
}
复制代码
package cn.liangyy.proxy.staticproxy;
/**
* 被代理对象
*/
public class TravelPerson implements Travel {
/**
* 买火车票
*/
@Override
public void buyTrainticket() {
System.out.println("西安到成都");
System.out.println("早上10:00出发");
}
}
复制代码
package cn.liangyy.proxy.staticproxy;
/**
* 代理类
*/
public class TravelAgency implements Travel {
private TravelPerson travelPerson; //被代理对象
public TravelAgency(TravelPerson travelPerson) {
this.travelPerson = travelPerson;
}
/**
* 买火车票
*/
@Override
public void buyTrainticket() {
before();
//调用被代理对象的原方法
this.travelPerson.buyTrainticket();
after();
}
private void before(){
System.out.println("付定金");
}
private void after(){
System.out.println("付尾款");
}
}
复制代码
package cn.liangyy.proxy.staticproxy;
/**
* 静态代理模式-测试
*/
public class TestStaticProxy {
public static void main(String[] args) {
TravelAgency travelAgency = new TravelAgency(new TravelPerson());
travelAgency.buyTrainticket();
}
}
复制代码
这就是一个静态代理的实现方式,可以看到还是非常方便的,但是静态代理却有其局限性:
代理对象需要显示的声明被代理对象,如果说后面想要修改代理对象,则需要修改源代码,不符合开闭原则。被代理对象如果新增了其它方法(接口新增了方法),那么代理对象也需要同步修改,不便于后期维护。所以为了解决静态代理的局限性,就有了动态代理。
2. 动态代理模式
2.1 JDK 动态代理
JDK 动态代理是 JDK 内置的一种动态代理的实现方式,使用 JDK 动态代理必须满足两个条件:
示例:
package cn.liangyy.proxy.jdkproxy;
/**
* 标准接口
*/
public interface Travel {
/**
* 买火车票
*/
void buyTrainticket();
}
复制代码
package cn.liangyy.proxy.jdkproxy;
/**
* 被代理类
*/
public class JdkTravelPerson implements Travel {
/**
* 买火车票
*/
@Override
public void buyTrainticket() {
System.out.println("西安到成都");
System.out.println("早上10:00出发");
}
}
复制代码
package cn.liangyy.proxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类
*/
public class JdkTravelAgency implements InvocationHandler {
//被代理对象,即示例中的 JdkTravelPerson
private Object target;
//动态获取代理对象
public Object getInstance(Object target){
//target就是被代理对象
this.target = target;
Class<?> clazz = target.getClass();
//创建并返回代理对象
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target,args);
after();
return obj;
}
private void before(){
System.out.println("付定金");
}
private void after(){
System.out.println("付尾款");
}
}
复制代码
package cn.liangyy.proxy.jdkproxy;
/**
* JDK动态代理模式-测试
*/
public class TestJdkProxy {
public static void main(String[] args) {
Travel travel = (Travel) new JdkTravelAgency().getInstance(new JdkTravelPerson());
travel.buyTrainticket();
}
}
复制代码
2.2 CGLIB 动态代理
CGLIB 是通过继承被代理对象来实现,和 JDK 动态代理需要实现 InvocationHandler 接口一样,CGLIB 也要求代理对象必须要实现 MethodInterceptor 接口,并重写其唯一的方法 intercept。
CGLIB jar包下载地址
示例:
package cn.liangyy.proxy.cglibproxy;
/**
* 被代理对象类
*/
public class CglibTravelPerson {
public void buyTrainticket() {
System.out.println("cglib:西安到成都");
System.out.println("cglib:早上9:00出发");
}
}
复制代码
package cn.liangyy.proxy.cglibproxy;
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 CglibTravelAgency implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();//相当于 JDK 动态代理中的 Proxy 类
enhancer.setSuperclass(clazz);//设置为即将生成的代理类的父类
enhancer.setCallback(this);//设置回调对象
return enhancer.create();//相当于JDK动态代理的 Proxy.newProxyInstance 方法,生成新的字节码文件,并加载到 JVM 中
}
/**
*
* @param o - CBLIG 生成的代理对象
* @param method - 被代理对象中被拦截的方法
* @param objects - 方法中的参数
* @param methodProxy - 代理对象中对应的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before() {
System.out.println("付定金");
}
private void after() {
System.out.println("付尾款");
}
}
复制代码
package cn.liangyy.proxy.cglibproxy;
/**
* cglib动态代理模式-测试
*/
public class TestCglibProxy {
public static void main(String[] args){
CglibTravelPerson cglibTravelPerson = (CglibTravelPerson) new CglibTravelAgency().getInstance(CglibTravelPerson.class);
cglibTravelPerson.buyTrainticket();
}
}
复制代码
JDK 和 CGLIB 动态代理对比
JDK 动态代理是通过接口来实现,CGLIB 则是通过继承被代理对象来实现,也就是说如果使用 JDK 动态代理,则要求被代理对象一定要有接口;而如果使用 CGLIB 动态代理,因为是通过继承来实现,所以无法代理 final 和 private 修饰的方法。
JDK 和 CGLIB 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,CGLIB 代理实现更复杂,生成代理类的效率比 JDK 代理低,但是因为 JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,故而 CGLib 执行效率更高。
代理模式优点
代理模式缺点
评论