代理设计模式
1. 场景设定和问题复现
1.准备项目
pom.xml
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.3.1</version> <scope>test</scope> </dependency> |
2. 声明功能接口
/** * 功能接口 */ public interface GongNeng { //吃饭 void chifan(); //谈小目标 void tanxiaomb(); } |
3.声明老总目标类实现功能接口
/** * 老总:目标对象类,实现功能接口 */ public class LaoZong implements GongNeng { @Override public void chifan() { //核心功能 System.out.println("老总吃饭..."); } @Override public void tanxiaomb() { //核心功能 System.out.println("老总谈一个亿的小目标..."); } } |
4.声明带预约和联系方式附加功能实现
新需求: 需要在每个方法中,添加控制台输出,输出预约和输出留个联系方式,方便下次联系!
/** * 老总:目标对象类 * 需要在每个方法中,添加控制台输出,输出预约和输出留个联系方式,方便下次联系! */ public class LaoZong implements GongNeng { @Override public void chifan() { System.out.println("预约...."); //核心功能 System.out.println("老总吃饭..."); System.out.println("留个联系方式,方便下次联系...."); } @Override public void tanxiaomb() { System.out.println("预约...."); //核心功能 System.out.println("老总谈一个亿的小目标..."); System.out.println("留个联系方式,方便下次联系...."); } } |
5.代码问题分析
1. 代码缺陷 - 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力 - 附加功能代码重复,分散在各个业务功能方法中!冗余,且不方便统一维护! 2. 解决思路 核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。 将重复的代码统一提取,并且[[动态插入]]到每个业务方法! 3. 技术困难 解决问题的困难:提取重复附加功能代码到一个类中,可以实现 但是如何将代码插入到各个方法中,我们不会,我们需要引用新技术!!! |
6.解决技术代理模式
1. 代理模式
二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。 |
2. 无代理场景
3. 有代理场景
4. 生活中的代理
- 广告商找大明星拍广告需要经过经纪人 - 合作伙伴找大老板谈合作要约见面时间需要经过秘书 - 房产中介是买卖双方的代理 - 太监是大臣和皇上之间的代理 |
5. 相关术语
- 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。(中介) 动词:指做代理这个动作,或这项工作 名词:扮演代理这个角色的类、对象、方法 - 目标:**被代理**“套用”了核心逻辑代码的类、对象、方法。(房东) 代理在开发中实现的方式具体有两种:静态代理,[动态代理技术] |
6.静态代理实现
1. 主动创建代理类
/** * 秘书:静态代理类 */ public class XiaoMi implements GongNeng{ //将被代理的目标对象:老总 LaoZong laoZong; public XiaoMi(LaoZong laoZong) { this.laoZong = laoZong; } @Override public void chifan() { //附加功能由代理类中的代理方法来实现:核心业务前执行的操作 System.out.println("预约...."); //调用目标对象老总的吃饭方法 laoZong.chifan(); //附加功能由代理类中的代理方法来实现:核心业务后执行的操作 System.out.println("留个联系方式,方便下次联系...."); } @Override public void tanxiaomb() { //附加 System.out.println("预约...."); laoZong.tanxiaomb(); //附加 System.out.println("留个联系方式,方便下次联系...."); } } |
测试代码
@Test public void testStaticProxy(){ //创建老总对象:马云 LaoZong yunyun=new LaoZong(); //创建云云的秘书 XiaoMi xm=new XiaoMi(yunyun); //吃饭 xm.chifan(); System.out.println("--------------"); xm.tanxiaomb(); } |
静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。
提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。
7.动态代理
1. 动态代理技术分类
动态代理是一种在运行时动态生成代理对象的技术。它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。 JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口!他会根据目标类的接口动态生成一个代理对象!代理对象和目对象有标相同的接口!(拜把子) cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口!(认干爹) |
2.基于jdk代理技术实现
- 声明功能接口
/** * 功能接口 */ public interface GongNeng { //吃饭 void chifan(); //谈小目标 void tanxiaomb(); } |
2.声明老总目标类实现功能接口
/** * 老总:目标对象类,实现功能接口 */ public class LaoZong implements GongNeng { @Override public void chifan() { //核心功能 System.out.println("老总吃饭..."); } @Override public void tanxiaomb() { //核心功能 System.out.println("老总谈一个亿的小目标..."); } } |
3.定义jdk动态代理工厂类,生成动态代理对象和目标对象实现同一个接口,并调用代理方法 invoke()
/** * jdk动态代理工厂类,生成小秘动态代理对象 */ public class JdkProxyHandler implements InvocationHandler { //代理的目标对象 LaoZong laoZong; public JdkProxyHandler(LaoZong laoZong) { this.laoZong = laoZong; } /** * invoke()设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法 * proxy:代理对象 * method:代理对象需要实现的方法,即其中需要重写的方法 * args:method所对应方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("预约...."); //调用目标对象的方法 Object obj=method.invoke(laoZong,args); System.out.println("留下联系方式,方便下次联系...."); return obj; } } |
4. 测试代码
/** * JDK动态代理 */ @Test public void testJdkProxy(){ //创建老总对象 LaoZong yunyun=new LaoZong(); /** * 创建代理对象,通过jdk动态代理生成代理对象的方法 * Proxy.newProxyInstance():创建一个代理实例 * 其中有三个参数: * 1、classLoader:加载动态生成的代理类的类加载器 * 2、interfaces:目标对象实现的所有接口的class对象所组成的数组 * 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法 */ GongNeng xm=(GongNeng)Proxy.newProxyInstance(TestProxy.class.getClassLoader(),new Class[]{GongNeng.class},new JdkProxyHandler(yunyun)); //通过代理对象调用目标对象的方法,从而扩展附加功能 xm.chifan(); System.out.println("--------------"); xm.tanxiaomb(); } |
3. 基于cglib代理技术,生成动态代理对象
1.引入cglib坐标
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> |
2.定义目标对象的类 (不要实现接口)
/** * 老总:目标对象类 */ public class LaoZong { public void chifan() { //核心功能 System.out.println("老总吃饭..."); } public void tanxiaomb() { //核心功能 System.out.println("老总谈一个亿的小目标..."); } } |
3.定义cglib动态代理工厂类,生成动态代理对象,是目标对象的子类。
并调用代理方法invoke
/** * cglib动态代理工厂类,生成日志动态代理对象 */ public class CglibInterceptor implements MethodInterceptor { /** * intercept设置代理对象实现目标对象方法的过程,即代理类中如何重写目标中的方法 * o :目标对象 * method:目标对象的方法 * MethodProxy:代理对象的方法 * objects:method所对应方法的参数 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("预约...."); //调用目标对象的方法 Object obj=methodProxy.invokeSuper(o,objects); System.out.println("留下联系方式,方便下次联系...."); return obj; } } |
3. 测试代码
/** * Cglib动态代理 */ @Test public void testCglibProxy(){ //创建代理对象,是目标对象的子类 Enhancer e=new Enhancer(); //1.指定父类:目标对象 e.setSuperclass(LaoZong.class); //2.指定callback,指定代理对象调用一个对象的代理方法 e.setCallback(new CglibInterceptor()); LaoZong xm=(LaoZong) e.create(); //通过代理对象调用目标对象的方法,从而扩展附加功能 xm.chifan(); System.out.println("--------------"); xm.tanxiaomb(); } |
注意:运行时会出现异常,原因由于jdk8之后的版本的反射相关功能被限制,导致了异常。
解决方案:编辑配置时手动添加两个参数实现jdk的兼容,选择开启不被允许的反射功能。--add-opens java.base/java.lang=ALL-UNNAMED
8.代理总结
**代理方式可以解决附加功能代码干扰核心代码和不方便统一维护的问题!** 他主要是将附加功能代码提取到代理中执行,不干扰目标核心代码! 但是我们也发现,无论使用静态代理和动态代理(jdk,cglib),程序员的工作都比较繁琐! 需要自己编写代理工厂等! 但是,提前剧透,我们在实际开发中,不需要编写代理代码,我们可以使用[Spring AOP]框架,他会简化动态代理的实现!!! |
原文地址:https://blog.csdn.net/2301_80220193/article/details/142922214
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!