欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式之代理模式

程序员文章站 2022-05-04 22:22:10
...

 

在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使用中会总结

 

 

相关标签: 代理模式