深入理解代理模式及其实现

这里主要是分析如下3种:静态代理、JDK动态代理、CGLIB动态代理
代理是设计模式的一种,代理类为委托类提供消息预处理,消息转发,事后消息处理等功能。Java中的代理分为三种角色:代理类(ProxySubject)、委托类(RealSubject)、接口(Subject)
三者关系可以表示如下图:
在这里插入图片描述
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。

1.why proxy

  • 开闭原则:尽量做到对修改关闭,对扩展开放,比如现在有一个委托类,如果想要使用委托类还需要一些其他条件(比如权限),自然最好的做法是使用代理类,这样就不用修改原委托类,而是增加了一个代理类,符合开闭原则。
  • 单一职责原则:就是每个类的功能尽可能单一。为什么要单一,因为只有功能单一这个类被改动的可能性才会最小,就拿刚才的例子来说,如果你将权限判断放在当前类里面,当前这个类就既要负责自己本身业务逻辑、又要负责权限判断,那么就有两个导致该类变化的原因,现在如果权限规则一旦变化,这个类就必需得改,显然这不是一个好的设计。

2.静态代理

按照代理创建的时期,可以分为动态代理和静态代理:

  • 静态代理:由程序员或者自动生成工具生成代理类,然后进行代理类的编译和运行。在代理类、委托类运行之前,代理类已经以.class的格式存在。
  • 动态代理:在程序运行时,由反射机制动态创建而成。

下面举个例子:
新建一个接口,UserService.java, 只有一个方法add()。

package com.adam.java.basic;
 
public interface UserService {
	public abstract void add();
}

建一个该接口的实现类UserServiceImpl.java

package com.adam.java.basic;
public class UserServiceImpl implements UserService {
 
	@Override
	public void add() {
		System.out.println("----- add -----");
	}
}

建一个代理类ProxyUserService.java

package com.adam.java.basic;
public class ProxyUserService implements UserService  {
 
	private UserService  userService  ;
    
    public ProxyUserService (UserService  userService ) {
        this.userService  = userService ;
    }
    
    @Override
    public void add() {
        System.out.println("Before add...");
        userService.add();
        System.out.println("After add...");
    }
}

测试类

package com.adam.java.basic;
public class DynamicProxyTest {
 
	public static void main(String[] args) {
		UserService userService = new UserServiceImpl();
		ProxyUserService proxy = new ProxyUserService(userService);
		proxy.add();
	}
}

从上面的例子可知,静态代理的特点是,每个被代理对象都需要和一个代理类紧密联系在一起,如果有n种代理模式,m种被代理对象,则需要写m*n个代理类。因此有如下缺点:

  • 由于代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  • 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了

3.动态代理

鉴于上述代理类和目标类的耦合程度太高,所以才提出了动态代理。
下面说一下JDK动态代理的大致思路:从目标出发,要让被代理类和代理模式解耦,于是引入了一个 中间handler(InvocationHandler),代理类由Proxy通过一系列反射技术代为生成,生成的代理类$proxy继承了Proxy,并提供了一个以InvocationHandler为参数的默认构造函数,因此在调用proxy.newProxyInstance()的时候,其内部就将InvocationHandler实现类传递到了代理类中,在执行代理对象的方法的时候,其实际执行的却是InvocationHandler的invoke()方法。

总结一句话,就是把 代理功能逻辑(代理执行方法)同 委托类 分开,用户在编写代理模式时,只需要编写 代理handler(负责代理功能逻辑) 和实现了subject接口的委托类即可,代理类交由Proxy通过反射技术来自动生成其对应的代理类。

【为什么JDK动态代理只支持对接口的代理】
由于java的单继承,动态生成的代理类已经继承了Proxy类,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口。

参考链接:https://blog.csdn.net/yhl_jxy/article/details/80586785

4.CGLIB代理

https://blog.csdn.net/flyfeifei66/article/details/81481222#cglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86
与JDK动态代理不同,CGLIB支持对类的代理,它主要是通过字节码生成技术,修改原有的被代理类的字节码来生成其子类(相当于是代理类继承了被代理类,所以被代理类不能用final修饰,否则无法继承)。

5.总结

  • cglib是直接继承了原有类,实现了Factory接口,而jdk是实现了自己的顶层接口,继承了Proxy接口
  • 两者都使用了运行时动态字节码生成技术,只是cglib用的是直接通过字节码方式,而jdk动态代理用的是反射
  • cglib可以针对类代理,jdk只能针对接口代理(单继承Proxy原因)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值