设计模式_代理模式_由浅入深

来源:互联网 发布:博库数据服务平台 编辑:程序博客网 时间:2024/05/01 20:02

核心目标

以最小的代价给目标方法加上前置后置处理功能。

比如某账本类的查询方法,想在查询前增加鉴权,和查询后记录查询日志。

基本原理

在目标类前面加一个代理类,将直接调用目标方法改为调用代理类的同名方法,再由代理类的同名方法调用目标方法,前/后置逻辑写在代理类的同名方法中。

//TO-DO 添加示意图

核心理论和技术

1、代理模式

2、反射

3、JDK动态代理

4、Cglib动态代理

5、spring AOP面向切面编程

6、回调模式

技术实现举例:逻辑由易至难,开发由难至易

以账本为例,具有查询和更新两个方法,要在这两个方法前后加上前置和后置处理

IAccount接口:

package com.common;public interface IAccount {public void queryAccount();public void updateAccount();}

AccountImpl1实现:

package com.common;public class AccountImpl1 implements IAccount {public void queryAccount() {System.out.println("查询账户.......");}public void updateAccount() {System.out.println("更新账户......");}}

AccountImpl2实现2:

package com.common;public class AccountImpl2 implements IAccount {public void queryAccount() {System.out.println("当前账户名为:匿名账户");}public void updateAccount() {System.out.println("更新账户名为:张三");}}
菜鸟:天下无招

设计思路

      修改账本实现类

      新增两个方法:前置方法、后置方法

      在执行查询和更新前后,调用前置和后置方法

package com.general;import com.common.IAccount;public class AccountImpl implements IAccount {public void queryAccount() {beforeOperate();System.out.println("查询账户.......");afterOperate();}public void updateAccount() {beforeOperate();System.out.println("更新账户......");afterOperate();}public void beforeOperate(){System.out.println("前置处理.....");}public void afterOperate(){System.out.println("后置处理.....");}}

优点:

      逻辑简单,不费脑

缺点:

     核心业务代码与辅助性代码高耦合

     前置和后置属于通用代码,调用代码重复开发

     前后置内容容易随需求发生改变,代码修改会造成核心业务风险


大虾:静态代理看到武功秘籍,功力大增

设计思路:

       1、使用代理设计模式

       2、新增代理,即新增一个跟被代理账本1实现了相同账本接口的代理类,具备相同的方法

       3、绑定代理关系,即将被代理的账本实现类作为自身的一个属性

       4、代理类新增前置和后置处理方法

       5、强化需要被代理的方法,即真正的业务处理仍由被代理的对方受理,但在将请求转发给被代理对象之前和之后,分别加上前置和后置处理逻辑

       6、修改业务调用逻辑,即将直接调用被代理对象的方法,改为调用代理类的同名方法


package com.staticProxy.staticProxy1;import com.common.AccountImpl1;import com.common.IAccount;public class AccountProxy1 implements IAccount{private AccountImpl1 accountImpl;public AccountProxy1(AccountImpl1 accountImpl){this.accountImpl=accountImpl;}public void queryAccount() {beforeOperate();accountImpl.queryAccount();afterOperate();System.out.println();}public void updateAccount() {beforeOperate();accountImpl.updateAccount();afterOperate();System.out.println();}public void beforeOperate(){System.out.println("前置处理.....");}public void afterOperate(){System.out.println("后置处理.....");}public AccountImpl1 getAccountImpl() {return accountImpl;}public void setAccountImpl(AccountImpl1 accountImpl) {this.accountImpl = accountImpl;}}


测试:

package com.staticProxy.staticProxy1;import com.common.AccountImpl1;import com.common.IAccount;public class test {public static void main(String[] args) {AccountImpl1 accountImpl=new AccountImpl1();IAccount account=new AccountProxy1(accountImpl);account.queryAccount();account.updateAccount();}}


若再来一个账本实现类,则有需要写一个代理类,而且除了代理的实现类不一样外,其余全部一样

package com.staticProxy.staticProxy2;import com.common.AccountImpl2;import com.common.IAccount;public class AccountProxy2 implements IAccount{private AccountImpl2 accountImpl;public AccountProxy2(AccountImpl2 accountImpl){this.accountImpl=accountImpl;}public void queryAccount() {beforeOperate();accountImpl.queryAccount();afterOperate();System.out.println();}public void updateAccount() {beforeOperate();accountImpl.updateAccount();afterOperate();System.out.println();}public void beforeOperate(){System.out.println("前置处理.....");}public void afterOperate(){System.out.println("后置处理.....");}public AccountImpl2 getAccountImpl() {return accountImpl;}public void setAccountImpl(AccountImpl2 accountImpl) {this.accountImpl = accountImpl;}}

测试:

package com.staticProxy.staticProxy2;import com.common.AccountImpl2;import com.common.IAccount;public class test {public static void main(String[] args) {AccountImpl2 accountImpl=new AccountImpl2();IAccount account=new AccountProxy2(accountImpl);account.queryAccount();account.updateAccount();}}

优点:

       1、前后置辅助代码跟业务代码完全解耦

       2、修改前后置方法不影响原核心业务

缺点:

       1、每代理一个类,就得新增一个代理类,开发量大

       2、需要修改被代理方法的调用代码,若被调用的地方很多,改造量大


牛人:动态代理,悟性高,会借力

设计思路:

       1、代理模式必备要素:要实现代理模式,抽象出来无外乎五样东西

            1.1 有一个代理类,用于写前置和后置处理方法

            1.2 有一个被代理类,不关心具体是什么,以object抽象

            1.3 需要绑定代理关系,即将被代理类跟代理类绑定

            1.4 需要知道被强化的方法,不关心具体方法名,以method抽象,不关心传入参数,以args[]抽象,不关心返回参数,以object抽象,可以通过反射来调用

            1.5 每个被代理对象需要一个专用的前置代理实例,不关心代理实例是否有真身(java类),只要存在于内存中,具备被代理对象的所有方法即可

            1.6 代理实例的方法不需实现任何业务逻辑,只需要接收调用请求,拿到被调用的方法名、参数即可,然后将方法、参数和代理实例本身,传给代理类的回调函数,使用反射原理统一加强处理

      2、动态代理简要流程

            2.1 生成代码已有两套现成的,直接拿来用

                  2.1.1 jdk动态代理,只能给实现了接口的类动态生成代理(java自带,所以首选该方案)

                  2.1.2 Cglib动态代理,能给没有实现接口的类动态生成代理,实现接口的也支持(第三方代码,所以次选)

                           全称Code generation library 代码生成类库

            2.2 生成流程

                  2.2.1 代理类和被代理类绑定(属性)

                  2.2.2 使用jdk或者Cglib,根据代理类、被代理类、回调方法,动态生成一个代理实例,该实例只存在于内存中,负责具体的代理工作

                  2.2.3 动态生成的代理实例,具有被代理对象的所有方法,该方法被调用时,会回调预设的回调方法(jdk是invoke(),Cglib是intercept())

                  2.2.3 代理类需要有一个回调方法,该方法实现被代理方法的强化,即加上前置后后置方法

借助JDK动态代理(被代理的类必须实现接口)

package com.jdkDynamicProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class AccountJdkDynamicProxy implements InvocationHandler{private Object proxyedObject;public Object getProxy(Object proxyedObject){this.proxyedObject=proxyedObject;return Proxy.newProxyInstance(proxyedObject.getClass().getClassLoader(), proxyedObject.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {beforeOperate();Object result=method.invoke(proxyedObject, args);afterOperate();System.out.println();return result;}public void beforeOperate(){System.out.println("前置处理.....");}public void afterOperate(){System.out.println("后置处理.....");}public Object getProxyedObject() {return proxyedObject;}public void setProxyedObject(Object proxyedObject) {this.proxyedObject = proxyedObject;}}

测试:写一次代理类,动态代理多个实现类

 package com.jdkDynamicProxy;import com.common.AccountImpl1;import com.common.AccountImpl2;import com.common.IAccount;public class test1 {public static void main(String[] args) {AccountJdkDynamicProxy proxy=new AccountJdkDynamicProxy();/*代理账本1*/IAccount account=new AccountImpl1();IAccount accountProxy=(IAccount)proxy.getProxy(account);accountProxy.queryAccount();accountProxy.updateAccount();/*代理账本2*/account=new AccountImpl2();accountProxy=(IAccount)proxy.getProxy(account);accountProxy.queryAccount();accountProxy.updateAccount();}}

借Cglib动态代理(不要求被代理的类必须实现接口)

package com.cglibDynamicProxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class AccountCglibDynamicProxy implements MethodInterceptor {private Object proxyedObject;public Object getProxy(Object proxedObject){this.proxyedObject=proxedObject;Enhancer enhancer=new Enhancer();enhancer.setSuperclass(proxyedObject.getClass());enhancer.setCallback(this);return enhancer.create();}public Object intercept(Object arg0, Method arg1, Object[] arg2,MethodProxy proxy) throws Throwable {beforeOperate();Object result=proxy.invokeSuper(arg0, arg2);afterOperate();System.out.println();return result;}public void beforeOperate(){System.out.println("前置处理.....");}public void afterOperate(){System.out.println("后置处理.....");}public Object getProxyedObject() {return proxyedObject;}public void setProxyedObject(Object proxyedObject) {this.proxyedObject = proxyedObject;}}
测试:
package com.cglibDynamicProxy;import com.common.AccountImpl2;import com.common.AccountImpl4;public class test {public static void main(String[] args) {/*使用代理_非接口类*/AccountCglibDynamicProxy proxy=new AccountCglibDynamicProxy();AccountImpl4 account=new AccountImpl4();AccountImpl4 accountProxy=(AccountImpl4)proxy.getProxy(account);accountProxy.queryAccount();accountProxy.updateAccount();/*使用代理_接口类*/AccountImpl2 account2=new AccountImpl2();AccountImpl2 accountProxy2=(AccountImpl2)proxy.getProxy(account2);accountProxy2.queryAccount();accountProxy2.updateAccount();}}

优点:

        代理类只需写一份,具体被代理的代理实例动态生成,开发量小

缺点:

        1、接口类和非接口类,需要不同的实现

        2、依赖于JDK和Cglib的代理规范,属于重开发

        3、代理类编写复杂,技术要求高,未实现POJO编程

        4、被代理对象的所有方法均被代理,可自定义程度低


大牛:拳无拳意无意,无意之中是真意:Spring的AOP面向切面编程

设计思路:

       1、spring的POJO编程思想,即Plain Ordinary Java Object简单的Java对象

       2、业务类,就写业务方法

       3、代理类,就写前置和后置方法

       4、依赖关系由xml配置文件维护

       5、spring封装jdk和cglib,对用户不可见

代理类:

package com.springDynamicProxy;public class AspectBean {public void beforeOperate(){System.out.println("前置处理.....");}public void afterOperate(){System.out.println("后置处理.....");}}


代理关系注册:

<?xml version="1.0" encoding="utf-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop      http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"><bean id="accountImpl1" class="com.common.AccountImpl1" /><bean id="accountImpl4" class="com.common.AccountImpl4" /><bean id="aspectBean" class="com.springDynamicProxy.AspectBean" /><!-- 对Test类进行AOP拦截 --><aop:config><aop:aspect id="TestAspect" ref="aspectBean"><!--配置切面--><aop:pointcut id="businessService"expression="(execution(* com.common.AccountImpl1.queryAccount(..))) or (execution(* com.common.AccountImpl4.queryAccount(..)))"/><aop:before pointcut-ref="businessService" method="beforeOperate" /><aop:after pointcut-ref="businessService" method="afterOperate" /></aop:aspect></aop:config></beans>


测试:

package com.springDynamicProxy;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.common.AccountImpl4;import com.common.IAccount;public class test {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("com/springDynamicProxy/test.xml");/*接口类:使用JDK代理*/IAccount accountImpl1=(IAccount) ctx.getBean("accountImpl1");System.out.println(accountImpl1.getClass().getSimpleName());System.out.println("super class is "+accountImpl1.getClass().getSuperclass());accountImpl1.queryAccount();/*非接口类:使用Cglib代理*/AccountImpl4 accountImpl4=(AccountImpl4)ctx.getBean("accountImpl4");System.out.println(accountImpl4.getClass().getSimpleName());System.out.println("super class is "+accountImpl4.getClass().getSuperclass());accountImpl4.queryAccount();}}

优点:

       1、不直接依赖于JDK和Cglib的规范,真正实现POJO编程

       2、代理关系统一配置,维护简单,使用灵活

       3、代理类编写简单,开发量小



1 0