浅谈Java【代理设计模式】以及原理解刨
前言:设计模式源于生活
什么是代理模式
为其他对象提供一种代理,控制对这个对象的访问
白话文:为某个对象实现动态增强
为什么要使用代理模式
中介隔离:在某些情况下,一个客户类不想或不能直接引用一个委托对象,而代理类对象可以在客户类与委托类之间起到中介的作用,其特征代理类
与委托类实现的是相同接口
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
代理模式实现原理
代理模式主要包含三种角色,有抽象角色,委托角色、代理角色
抽象角色:可以是接口,也可以是抽象类
委托角色:真实主题角色,具体业务逻辑执行的地方
代理角色:里面包含了真实主题角色引用,负责对真实主题角色执行前或后进行操作处理
代理模式应用场景
日志收集
SpringAop
动态事务开关
全局捕获异常
过滤器
RPC远程调用
代理模式的分类
静态代理和动态代理模式
静态代理模式
静态代理是由程序员手动创建或工具生成代理类的源码,再编译成代理类。
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
一句话,自己手写代理类就是静态代理。
静态代理的缺陷
基于人工手写代理类,后期被代理类的增多,代理类也会随之增多
基于接口模式实现方式
主题:
/**
* 主题
*/
public interface OrderService {
void addOrder();
void updateOrder();
}
实现接口:
/**
* 被代理类
*/
public class OrderServiceImpl implements OrderService {
public void addOrder() {
System.out.println("执行新增业务逻辑方法");
}
public void updateOrder() {
System.out.println("执行修改业务逻辑方法");
}
}
代理类:
/**
* 代理类
* <p>
* 缺点:每次有新的接口,代理类也就需要加入一个方法,造成代码的冗余性了
* 优点:解耦合了,因为只需要关注业务逻辑代码,不需要关心其他的操作
*/
public class OrderServiceProxy implements OrderService {
private OrderServiceImpl orderService;
public OrderServiceProxy(OrderServiceImpl orderService) {
this.orderService = orderService;
}
public void addOrder() {
System.out.println("开启事务");
orderService.addOrder();
System.out.println("提交事务");
}
public void updateOrder() {
System.out.println("开启事务");
orderService.updateOrder();
System.out.println("提交事务");
}
}
测试类:
public class Test {
public static void main(String[] args) {
OrderService orderServiceProxy = new OrderServiceProxy(new OrderServiceImpl());
orderServiceProxy.addOrder();
System.out.println("--");
orderServiceProxy.updateOrder();
}
}
运行结果图:
基于继承模式实现方式
主题:
/**
* 主题
*/
public interface OrderService {
void addOrder();
void updateOrder();
}
实现接口:
/**
* 被代理类
*/
public class OrderServiceImpl implements OrderService {
public void addOrder() {
System.out.println("执行新增业务逻辑方法");
}
public void updateOrder() {
System.out.println("执行修改业务逻辑方法");
}
}
代理类:
public class OrderProxy extends OrderServiceImpl {
@Override
public void addOrder() {
System.out.println("开启事务");
super.addOrder();
System.out.println("提交事务");
}
@Override
public void updateOrder() {
System.out.println("开启事务");
super.updateOrder();
System.out.println("提交事务");
}
}
测试类:
public class Test {
public static void main(String[] args) {
OrderProxy orderProxy = new OrderProxy();
orderProxy.addOrder();
System.out.println("--");
orderProxy.updateOrder();
}
}
运行效果图:
jdk动态代理模式
jdk动态代理执行步骤:
1.创建被代理类,和接口类
2.通过实现invcation接口来调用proxy方法,实现动态创建代理类的实例
简单来说:动态代理:通过程序动态生成代理,无需手工添加
主题:
public interface OrderService {
void addOrder();
}
实现接口:
public class OrderServiceImpl implements OrderService {
@Override
public void addOrder() {
System.out.println("执行新增订单逻辑");
}
}
jdk动态代理:
public class JdkInvocationHandler implements InvocationHandler {
private Object target;
/**
* 目标对象-被代理类
*
* @param target
*/
public JdkInvocationHandler(Object target) {
this.target = target;
}
/**
* @param proxy jdk动态代理生成的代理类
* @param method 接口中的方法哦(不是真正目标方法)
* @param args 代理的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理开始执行");
//调用目标方法--java反射执行目标方法
Object invoke = method.invoke(target, args);
System.out.println("jdk动态代理结束执行");
return invoke;
}
public <T> T getProxy() {
/**
* 三个参数
* ClassLoader loader, 读取代理类class文件 -- 类加载器
* Class<?>[] interfaces 基于该接口拼成代理类源代码
* InvocationHandler h 就是this
*/
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
测试类:
public class Test {
public static void main(String[] args) {
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderService proxy = jdkInvocationHandler.getProxy();
proxy.addOrder();
}
}
效果图:
原理分析:
添加代码,获取jdk自动生成的calss文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
1.使用反编译工具打开该proxy.class文件
通过生成的代理类都是以$Proxy开头,0是第一个生成的代理类,代理类会被很多业务代理进行调用,所以这里的0是通过自增的形式,防止代理类重复
2.分析生成的代理类,反射的自定义方法
首先了解一下invoke的三个参数的意义
第一个:代理类 对号入座:本身就是代理类,所以这里就是this
第二个:被代理类 对号入座:m3就是通过反射获取被代理类的接口和方法等
第三个:参数 对号入座:我这里就没参数了 所以null
上图不是我们真正执行代理类的地方,它是通过super关键字通过调用父类的回调类,执行我们自定义配置代理类,进去瞧一波,接着走到下一步
首先从我们注释上可以了解到,method并不是我们真正的目标方法,而target才是,可能有人会疑问,那么target是从哪里传进来的,可以看我图中所标记的地方,在类中定义了一个全局变量,通过构造方法的形式,将外部对象的引用传递给我们类中的全局变量,并通过类中的invoke方法执行我们的真正的目标对象,在执行目标之前我们是已经完成了对对象的预处理和末尾处理
以上就是jdk动态代理执行原理
注意:由于java不能实现多继承,这里已经继承了Proxy类,所以不能在继承其他的类了,所以jdk动态代理只支持接口代理,不支持继承实现类的代理
cglib动态代理
jdk动态代理与cglib动态代理的区别
jdk动态代理:通过走回调拦截,实现接口生成的代理类,使用反射执行目标方法
原理:
1.拼接java源代码
2.将java源代码编译为class文件
3.通过类加载器读取class文件到内存中
4.采用java的反射机制执行目标方法
cglib动态代理:采用继承模式生成代理类(相当于直接重写被代理方法,不使用反射),底层基于ASN字节码技术实现
原理:
1.直接采用ASN字节码技术生成class文件
2.通过内加载器读取class文件到内存中
3.采用fastClass索引机制执行目标对象方法,比反射机制效率高
得出CGlib的效率比jdk动态代理效率高
主题
public interface OrderService {
void saveOrder(String name);
}
实现类
public class OrderServiceImpl implements OrderService {
public void saveOrder(String name) {
System.out.println("执行添加订单业务逻辑" + name);
}
}
cglib代理类
public class CGLibMethodIntercetor implements MethodInterceptor {
/**
* @param o cglib生成好的代理对象
* @param method 目标方法
* @param objects 参数
* @param methodProxy 代理
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开启事务");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("提交事务");
return invoke;
}
}
测试类
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class); //这里其实用到了被代理类的引用
enhancer.setCallback(new CGLibMethodIntercetor()); //这里是拦截回调,简单说就是在执行真正目标方法之前和之后进行额外的增强
//创建代理对象
OrderService o = (OrderService) enhancer.create();
o.saveOrder("computer");
}
}
效果图
源码分析
添加代码,输出class文件到指定目录
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
一、首先执行完了之后,指定目录会有这三个class文件
第一个文件是cglib的索引文件,能够快速找到代理类
第二个是代理类啦
第三个是fastClass索引文件
打开反编译工具,打开代理类的class文件,可以看到class文件是通过继承的形式来实现代理的
然后接着可以看到,saveOrder是我们需要被代理的方法,在进入目标对象之前,会先执行Method接口,这个接口是直接指定到我自定义的cglib动态代理类,然后会执行增强处理,然后再执行目标对象
可能这里会有人好奇,引用怎么设置进来的,就是通过我setCallback方法设置设置进来
进入save方法,程序会先拿到被代理类的引用,当被代理类引用不为NULL的时候,会通过intercept方法,执行代理
类
在执行真正目标方法之前,先增强,开启事务,然后在执行目标对象
fastClass基本概念
相当于对类中的所有方法生成一个索引值,直接根据索引调用方法
入口进入
底层会自动拦截回调到执行intercept方法,实现对目标方法的增强,然后会通过methodProxy.invokeSuper,执行调用真实目标方法
进来之后,会先初始化FastClass对象,在进入看一下,就会明白了
初始化方法,fastClassInfo对象是否为空,如果为空则进行初始化,继续探索一下fastCalssInfo对象里面到底有啥
FastClass f1:对代理对象索引
FastClass f2:代理类的索引
int i1:对代理对象的索引值
int i2:代理类的索引值
现在知道了对象里面有哪些属性之后,在接着往回看到,第一次加载进入初始化方法,会将FastClass对象各个属性进行赋值,f1 和 f2 属性 也就是代理对象和被代理对象的引用,那么i1.getIndex就是通过方法名称加参数类型进行签名,得出的索引值,i2的index值也是同理获取
getIndex其实就是通过对方法名称和参数类型进行签名然后得出HashCode值,通过switch找到相应的hashcode值返回最终的索引值出去
然后初始化完成之后,可以看到下图代理类的i2索引值是19
通过索引值去生成的代理FastClass类找到19的索引值,然后可以看到返回的是我们的saveOrder方法
通过方法名称,再去生成的代理类里面找到代理类生成的方法名称,通过super回调直接找到目标真正目标方法
如下图,找到写的目标真正方法类
执行真正的目标对象方法
以上就是cglib代理类源码分析.
推荐阅读