`

设计模式之代理模式

阅读更多

 

在Spring AOP的实现中用到了JDK的代理模式,有必要将代理模式整理一下。

 

将按照“为什么使用代理模式”、“怎么样使用代理模式”来安排整个文章。

1、为什么要使用代理模式

 

一个完成的系统有时候可以划分为系统需求和业务需求两种。业务需求即与业务相关的逻辑需求,而系统需求即如安全检查、前期校验等与系统相关的需求,而且系统需求一般穿插于业务需求中,需要在业务开始之前或者是开始之后插入系统需求。这个时候就需要有代理模式。

代理模式是为了实现业务需求与系统需求之间的解耦,使得业务专注于自己的业务逻辑设计、系统需求专注于自己的系统设计,两者互不干涉,当需要结合使用时采用该模式即可实现两者的结合。

 

举例说明。

有一个业务逻辑,实现的功能是“表演”功能,接口如下:

 

package com.springframework.aop;

public interface IPerformer{
	public void perform();
}

 其实现类代码如下:

 

 

package com.springframework.aop;

public class PerformerImpl implements IPerformer{
	@Override
	public void perform(){
		System.out.println("perform...");
	}
}

 

 

现在要在“表演”之前观众要“坐下”,“关掉手机”,在“表演”之后要“起立鼓掌”,这些都是系统需求,不管演出者是谁,要演出的节目是什么,观众都要这样做。

观众的实现代码如下:

 

package com.springframework.aop;
public class Audience{
	//表演之前
	public static void takeSeat(){
		System.out.println("before perform, take seat...");
	}
	//表演之前
	public static void turnOffCellPhone(){
		System.out.println("before perform, turn off CellPhone...");
	}
	//表演之后
	public static void applaud(){
		System.out.println("after perform, applaud...");
	}
}

 现在需要在表演的时候添加观众的行为,也就是要把两者结合。如果按照一般的写法,我们会这样写:

package com.springframework.aop;

public class PerformerImpl implements IPerformer{
	
	@Override
	public void perform(){
		//观众落座
		Audience.takeSeat();
		//关掉手机
		Audience.turnOffCellPhone();
		
		//表演者实际表演内容
		System.out.println("perform...");
		
		//观众鼓掌
		Audience.applaud();
	}
}

这样写不是不可以,但是可以发现在为原来的performer实现类中充斥着太多的观众的行为,侵入性很强,耦合很严重,这个时候就需要代理模式来解决。

 

2、代理模式解决问题

 

代理模式的实现就是重新写一个代理类,实现和被代理类一样的接口,但是具体的实现方法会发生改变,具体怎么样改变通过代码一目了然。

package com.springframework.aop;

public class PerformerImplProxy implements IPerformer{

	private IPerformer performer;

        public PerformerImplProxy(IPerformer performer){
		this.performer = performer;
	}
	  @Override
	public void perform(){
		//观众落座
		Audience.takeSeat();
		//关掉手机
		Audience.turnOffCellPhone();
		
		//表演者实际表演内容
		performer.perform();
		
		//观众鼓掌
		Audience.applaud();
	}
}

当我在外部调用时调用的也是实际的代理类,如下所示:

package com.springframework.test;

public class Test{
	public static void main(String[] args){
		IPerformer performer = new PerformerImplProxy(new PerformerImpl());
		performer.perform();
	}
	
}

 如果业务需求(表演者)或者系统需求(观众)发生变化,需要添加或者修改功能,则只需要修改相关的实际实现类即可,调用者和代理类都不需要修改,而且业务需求和系统需求也不会有耦合。

 

以上是静态代理的实现,静态代理解决了业务需求和系统需求之间的耦合,但是当我表演接口中添加一个需求,假如说是化妆(makeUp),需要在实现类中实现makeUp,并且在代理类中也要实现该具体方法,这样的话就会比较麻烦。因此有了动态代理。

 

3、动态代理解决问题

 静态代理的特点是在前期已经将代理类规定好,具体代理的是哪个类接口,具体代理的是接口的哪个方法,这些都事先已经写好了。

而动态代理是在运行时来决定代理的是哪个类,代理的是哪个方法,事先并不知道。

 

动态代理需要实现InvocationHandler接口,代码如下:

package com.springframework.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class PerformanceHandler implements InvocationHandler{
	
	//需要被代理的目标类
	private Object target;
	
	public PerformanceHandler(Object target){
		this.target = target;
	}
	
	@Override
	public Object invoke(Object obj, Method method, Object[] args) throws InvocationTargetException,IllegalAccessException{
		Audience.takeSeat();
		Audience.turnOffCellPhone();
		method.invoke(target, args);
		Audience.applaud();
		return null;
	}
}

可以发现在该接口的实现中并没有指定具体的代理类是什么,它针对的是一个Object类型的目标类。所以具体的目标类需要在调用方指定。

 然后在调用端使用Proxy动态创建代理类,然后调用相应功能即可,代码如下:

package com.springframework.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.springframework.aop.*;

public class Test{
	public static void main(String[] args){	
		//使用动态代理来实现
		//具体的需要代理的目标类
		IPerformer target = new PerformerImpl();
		InvocationHandler handler = new PerformanceHandler(target);
	        //生成代理实例
		IPerformer performer = (IPerformer)Proxy.newProxyInstance(target.getClass().getClassLoader(),
					                                       target.getClass().getInterfaces(),
									                                 handler);
		performer.perform();
	}
}

这样当我的表演接口中添加化妆(makeUp)方法时,只需要(1)修改IPerformer接口,(2)修改其实现类PerformerImpl类,而代理类不需要做任何修改。

以上介绍的JDK提供的动态代理已经解决了静态代理的问题,但是JDK提供的动态代理方式也有一定的缺点,那就是只能为接口实现代理,无法为没有接口的直接实现类实现代理,这种类型的需要使用cglib代理方式来实现。

4、CGLib动态代理来解决问题

CGLib采用底层字节码技术,为需要代理的目标类创建子类,然后再子类创建的过程中,将需要加入的其他系统需求织入到子类中,就完成了对目标类的代理。

CGLib需要实现MethodInterceptor接口,代码如下:

package com.springframework.aop;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;



public class PerformanceCGLibHandler implements MethodInterceptor{
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class clazz){
                 //设置父类
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
                 //生成子类
		return enhancer.create();
	}
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable{
		Audience.takeSeat();
		Audience.turnOffCellPhone();
		proxy.invokeSuper(obj, args);
		Audience.applaud();
		return null;
	}
}

 然后调用方调用如下:

package com.springframework.test;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class Test{
	public static void main(String[] args){
		PerformanceCGLibHandler cglibProxy = new PerformanceCGLibHandler();
		PerformerImpl perfromerimpl = (PerformerImpl)cglibProxy.getProxy(PerformerImpl.class);
		
		perfromerimpl.perform();
		perfromerimpl.makeUp();	
	}
	
}

 可以发现我没有用接口类,而是用的它的具体实现类,解决了JDK只能代理接口的缺点,但是有一个问题就是,因此CGLib的实现原理是生成目标类的子类,所以如果目标类中有final方法的话是不能被继承的,也就不能用CGLib代理方式来实现。这也是CGLib的一个缺点。

 

 Spring框架的AOP实现原理就是JDK动态代理和CGLib动态代理,默认会使用JDK动态代理,但是如果没有实现接口类的话会使用CGLib动态代理。如果一开始默认想要使用的就是CGLib动态代理,则需进行一下配置,具体在Spring Aop使用中会总结

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics