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