您的位置:首页 > 其它

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

2015-07-30 10:13 316 查看
核心目标:

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

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

基本原理

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

//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、代理类编写简单,开发量小



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: