代理模式熊咽,顧名思義,就是通過代理去完成某些功能横殴。比如被因,你需要購買火車票,不想跑那么遠到火車站售票窗口買衫仑,可以去附近的火車票代售點買梨与,或者到攜程等第三方網(wǎng)站買文狱。這個時候,我們就把火車站叫做目標(biāo)對象或者委托對象瞄崇,也可以叫被代理對象壕曼,而火車票代售點和攜程就叫做代理對象等浊。
一、靜態(tài)代理
靜態(tài)代理是最簡單的代理模式筹燕。需要定義一個接口,然后委托類和代理類分別實現(xiàn)這個接口
//待實現(xiàn)的接口
public interface UserManager {
public void getName(String name);
public void getId(int id);
}
//委托類
public class UserManagerImpl implements UserManager {
@Override
public void getName(String name) {
System.out.println("UserManagerImpl.getName:" + name);
}
@Override
public void getId(int id) {
System.out.println("UserManagerImpl.getId:" + id);
}
}
//代理類
public class UserManagerProxy implements UserManager {
UserManager userManager;
public UserManagerProxy(UserManager userManager) {
this.userManager = userManager;
}
@Override
public void getName(String name) {
System.out.println("before getName");
userManager.getName(name);
System.out.println("after getName");
}
@Override
public void getId(int id) {
userManager.getId(id);
}
public static void main(String[] args) {
UserManagerProxy proxy = new UserManagerProxy(new UserManagerImpl());
proxy.getName("zhangsan");
}
}
//before getName
//UserManagerImpl.getName:zhangsan
//after getName
可以看到过咬,在編譯成class之前糠涛,就已經(jīng)確定了委托類UserManagerImpl和代理類UserManagerProxy。因此集漾,才叫靜態(tài)代理。這樣雖然定義比較方便具篇,實現(xiàn)也簡單凌埂,但是有一個弊端。當(dāng)接口再新加一個方法時瞳抓,委托類和代理類都需要同步地去實現(xiàn)方法,因此維護起來比較麻煩栓霜。而動態(tài)代理解決了這個問題横蜒。
二、JDK動態(tài)代理
動態(tài)代理分為JDK動態(tài)代理和cglib動態(tài)代理丛晌。動態(tài)是指,代理類是通過反射等機制動態(tài)生成的抚垄,委托類和代理類的關(guān)系在運行時才確定。他們的主要區(qū)別就是督勺,JDK動態(tài)代理需要實現(xiàn)接口渠羞,而cglib是通過繼承來實現(xiàn)的智哀,不需要定義接口荧恍。
JDK動態(tài)代理,需要定義一個類去實現(xiàn)InvocationHandler接口
public class LogHandler implements InvocationHandler {
private Object targetObj;
public Object newProxyObject(Object targetObj){
this.targetObj = targetObj;
return Proxy.newProxyInstance(
targetObj.getClass().getClassLoader(), //獲取委托類的類加載器
targetObj.getClass().getInterfaces(), //獲取委托類實現(xiàn)的所有接口
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret;
try {
System.out.println("before method");
ret = method.invoke(targetObj, args);
System.out.println("after method");
} catch (IllegalAccessException e) {
e.printStackTrace();
System.out.println("error");
throw e;
}
return ret;
}
}
public class TestProxy {
public static void main(String[] args) {
LogHandler logHandler = new LogHandler();
UserManager o = (UserManager)logHandler.newProxyObject(new UserManagerImpl());
o.getName("ls");
o.getId(2);
}
}
/**
運行結(jié)果如下:
before method
UserManagerImpl.getName:ls
after method
before method
UserManagerImpl.getId:2
after method
*/
JDK動態(tài)代理其實是在運行時動態(tài)生成了一個代理類去實現(xiàn)接口摹菠,只是隱藏了這個過程骗爆,我們不知道而已。
class $JDKProxy implements UserManager{}
需要注意的是煮寡,實現(xiàn)JDK動態(tài)代理的一個前提就是犀呼,需要定義一個接口幸撕,然后委托類去實現(xiàn)這個接口外臂。那如果我不想定義接口,只定義一個委托類能不能實現(xiàn)呢貌矿?這就需要用到cglib代理了罪佳。(因為cglib是通過繼承方式)
三、cglib動態(tài)代理
需要定義一個類實現(xiàn)MethodInterceptor接口(注意菇民,這個類可不是代理類,也不是委托類哦)阔馋。
//委托類娇掏,不需要實現(xiàn)接口
public class CgTarget {
public void getContent(){
System.out.println("cglib被代理類getContent方法");
}
}
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib開始");
Object invoke = method.invoke(target, objects);
System.out.println("cglib結(jié)束");
return invoke;
}
//代理方法
public Object getCglibProxy(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
//設(shè)置增強類的父類,即被代理類
enhancer.setSuperclass(target.getClass());
//設(shè)置回調(diào)函數(shù)
enhancer.setCallback(this);
//返回創(chuàng)建的代理類
return enhancer.create();
}
}
public class TestCglib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
CgTarget o = (CgTarget)cglibProxy.getCglibProxy(new CgTarget());
o.getContent();
}
}
/**
打印結(jié)果如下:
cglib開始
cglib被代理類getContent方法
cglib結(jié)束
*/
可以看到,cglib動態(tài)代理是通過Enhancer類的create方法創(chuàng)建了代理類下梢。其實,其內(nèi)部是通過繼承委托類來動態(tài)生成代理類的孽江。它隱藏了以下過程
class $cglibProxy extends CgTarget{}
因此,委托類不能定義成final類型的辆琅,因為final修飾的類是不能被繼承的这刷。
了解spring AOP的同學(xué)應(yīng)該知道,AOP是面向切面編程暇屋,在管理事物的時候會用到。其實昙衅,AOP就是通過動態(tài)代理來實現(xiàn)的本辐,具體是用的JDK動態(tài)代理還是cglib動態(tài)代理漱抓,感興趣的小伙伴可以繼續(xù)深入研究哦奖亚。