設計模式之代理模式

 

在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使用中會總結

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章