`

设计模式之代理模式

阅读更多

 

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

 

 

分享到:
评论

相关推荐

    设计模式之代理模式proxy

    **设计模式之代理模式(Proxy Pattern)** 设计模式是软件工程中的一种最佳实践,它是在特定情境下解决常见问题的模板。代理模式是其中一种行为设计模式,它的核心思想是为一个对象提供一个替身或者代理,以控制对...

    深入浅出设计模式之代理模式

    根据提供的标题“深入浅出设计模式之代理模式”与描述“将《Head First 设计模式》(中文版)按章节进行了分割,每章一个文件,方便大家下载”,我们可以推测出这部分内容主要聚焦于介绍和解释代理模式这一重要的设计...

    设计模式之代理模式视频教学

    代理模式是一种在软件设计中广泛使用的行为设计模式,它的核心思想是为...通过观看"设计模式之代理模式视频教学",你可以系统地学习代理模式的理论知识和实践技巧,提升自己的设计能力,更好地应对复杂的软件开发挑战。

    大话设计模式之代理模式

    大话设计模式之代理模式 经典代码 C#类

    Java设计模式之代理模式

    Java设计模式之代理模式 1.代理模式 1.1 静态代理 1.2 动态代理 1.3.代理模式使用原因和应用方面

    .net实现设计模式之代理模式

    代理模式是一种常用的设计模式,它在软件工程中扮演着重要的角色,特别是在.NET框架下。代理模式的核心思想是为一个对象提供一个替代品或代表,这个替代品能够控制对原对象的访问,使得客户端代码可以通过代理对象与...

    Java设计模式之代理模式(结构)

    ### Java设计模式之虚拟代理模式详解 #### 一、引言 在软件工程领域,设计模式作为一种被广泛接受的最佳实践,对于提高代码质量和可维护性起着至关重要的作用。其中,“代理模式”作为结构型设计模式之一,在解决...

    设计模式之代理模式demo

    代理模式是一种常用的设计模式,它在软件开发中起到了中介或者代表的作用。代理模式的主要目的是为其他对象提供一种代理以控制对这个对象的访问。通过引入代理,我们可以增加新的功能,如缓存、日志记录、访问控制等...

    JAVA设计模式之代理模式实例

    代理模式是设计模式的一种,它提供了一种对目标对象进行增强或者控制访问的方式。在本实例中,我们将深入探讨Java中的代理模式及其应用。 代理模式的核心思想是为一个对象创建一个代理对象,这个代理对象在客户端和...

    面向对象23种设计模式之代理模式

    代理模式是面向对象设计模式中的一个关键概念,它在软件工程中扮演着重要角色,用于在客户端和目标对象之间创建一种代理关系,以提供额外的功能或控制。在代理模式中,代理类作为真实对象的代表,它可以在客户端与...

    设计模式之代理模式Proxy

    代理模式是设计模式中的一种结构型模式,它在对象交互中起到了中介的作用,允许通过代理对象来控制对原对象的访问。代理模式的核心思想是为一个对象提供一个替身,以便增加新的功能或者控制对原对象的访问。这种模式...

    Java设计模式之代理模式[收集].pdf

    Java设计模式之代理模式[收集].pdf

Global site tag (gtag.js) - Google Analytics