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

核心目标

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

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

基本原理

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

//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("后置处理.....");
	}
}


代理关系注册:



	
	
	
	
	
		
			
			
			
			
		
	


测试:

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




你可能感兴趣的:(java设计模式)