本文屬于系列文章《設(shè)計模式》氢哮,附上文集鏈接
本文集代理模式請看此處
前言
上一篇寫的那個代理模式觅赊,是屬于靜態(tài)代理。假設(shè)一種場景,假設(shè)我們要為很多類的很多方法添加前置和后置方法(別忘了代理模式是為了控制)粱腻,按照靜態(tài)代理的方法,我們就得為所有類的所有方法一個一個地去實現(xiàn)需求斩跌,想想這工程量就覺得可怕绍些,所以就有了很優(yōu)美的實現(xiàn)方式,叫做動態(tài)代理滔驶。
JDK代理
且看代碼
// 抽象接口
public interface MyInterface {
void hello(int i, int j);
void hello(int i, int j, int k);
}
// 接口實現(xiàn)類遇革,也是我們的代理目標類
public class MySubject implements MyInterface{
@Override
public void hello(int i, int j) {
System.out.println("hello(): "+i+"-"+j);
}
@Override
public void hello(int i, int j, int k) {
System.out.println("hello(): "+i+"-"+j+"~"+k);
}
}
// 這個是關(guān)鍵,被代理對象方法的實際執(zhí)行地方揭糕,通過反射實現(xiàn)
public class MyInvocationHandler implements InvocationHandler{
// 被代理的目標對象
private Object target;
MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
/\*\*
\* proxy 代理的目標對象
\* method 代理的目標對象的方法
\* args萝快,方法的參數(shù)
\*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法: " + method.getName() + "執(zhí)行前");
StringBuilder sb = new StringBuilder("方法的參數(shù):");
for (Object arg : args) {
sb.append(arg.toString()+" ");
}
System.out.println(sb.toString());
Object result = method.invoke(target, args);
System.out.println("方法: " + method.getName() + "執(zhí)行后");
return result;
}
}
// 場景類
public class Client {
public static void main(String[] args) {
MyInterface mySubject = new MySubject();
MyInvocationHandler handler = new MyInvocationHandler(mySubject);
MyInterface mySubjectProxy = (MyInterface) Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),
mySubject.getClass().getInterfaces(), handler);
proxy.getProxyObject();
mySubjectProxy.hello(5,98);
System.out.println("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*");
mySubjectProxy.hello(5, 23, 54);
}
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98
hello(): 5-98
方法: hello執(zhí)行后
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
方法: hello執(zhí)行前
方法的參數(shù):5 23 54
hello(): 5-23~54
方法: hello執(zhí)行后
比較關(guān)鍵的就三個地方:
- 一、被代理的目標對象必須實現(xiàn)接口著角,即上文的<code>MySubject</code>和<code>MyInterface</code>揪漩。
- 二、實現(xiàn)InvocationHandler接口的handler類吏口,即上文中的<code>public class MyInvocationHandler implements InvocationHandler</code>奄容。方法的參數(shù)說明在上面也有了冰更,這里就不在闡述了。
- 三昂勒、使用Proxy類獲取到目標對象的代理對象蜀细,即上文中的
Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),mySubject.getClass().getInterfaces(), handler);
之前也想過Proxy是怎么實現(xiàn)這個代理過程的,結(jié)果在這里看到了戈盈。
回到上面的代碼奠衔,其實上面的代碼還可以封裝下,比如變成下面的樣子:
// 接口類塘娶,接口實現(xiàn)類归斤,handler類都沒變化,引入下面的類
// 簡單的動態(tài)代理類刁岸,傳入一個目標對象參數(shù)脏里,得到其代理對象
public class DynamicProxy{
private MyInvocationHandler handler;
private Object target;
public DynamicProxy(Object _target) {
this.target = _target;
handler = new MyInvocationHandler(_target);
}
public Object getProxyObject(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
}
}
// 場景類
public class Client {
public static void main(String[] args) {
MyInterface mySubject = new MySubject();
**DynamicProxy proxy = new DynamicProxy(mySubject);
MyInterface mySubjectProxy = (MyInterface) proxy.getProxyObject();**
mySubjectProxy.hello(5,98);
System.out.println("******************");
mySubjectProxy.hello(5, 23, 54);
}
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98
hello(): 5-98
方法: hello執(zhí)行后
*********************
方法: hello執(zhí)行前
方法的參數(shù):5 23 54
hello(): 5-23~54
方法: hello執(zhí)行后
上面場景類的代碼,用自然語言解釋挺流暢的虹曙,就是創(chuàng)建代理類對象迫横,通過代理類對象獲取被代理對象的代理對象。反正封裝方法多種多樣根吁,上面其實其中一種员淫,有意者多試試唄合蔽。
上面就是JDK代理击敌,但是JDK代理有一個限制,就是上面說到的關(guān)鍵點的第一點拴事,JDK代理無法脫離接口而實現(xiàn)沃斤,要為某個目標對象實現(xiàn)JDK代理,該目標對象至少實現(xiàn)一個接口刃宵,那如果要為沒實現(xiàn)接口的類實現(xiàn)代理衡瓶,怎么辦,就是我們的Cglib代理牲证。
cglib代理
且看代碼
// 我們的代理目標類哮针,這次是沒有實現(xiàn)接口的
public class MySubject {
public void hello(int i, int j) {
System.out.println("hello(): "+i+"-"+j);
}
public void hello(int i, int j, int k) {
System.out.println("hello(): "+i+"-"+j+"~"+k);
}
}
// cglib代理類
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("方法: " + method.getName() + "執(zhí)行前");
StringBuilder sb = new StringBuilder("方法的參數(shù):");
for (Object arg : args) {
sb.append(arg.toString() + " ");
}
System.out.println(sb.toString());
Object obj = methodProxy.invokeSuper(o, args);
System.out.println("方法: " + method.getName() + "執(zhí)行后");
return obj;
}
}
// 場景類
public class Client {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MySubject.class);
enhancer.setCallback(cglibProxy);
// 得到代理對象
MySubject o = (MySubject) enhancer.create();
o.hello(5, 98);
System.out.println("******************");
o.hello(5, 23, 54);
}
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98
hello(): 5-98
方法: hello執(zhí)行后
**********************
方法: hello執(zhí)行前
方法的參數(shù):5 23 54
hello(): 5-23~54
方法: hello執(zhí)行后
cglib是提供動態(tài)代理的第三方庫,并不是jdk給我們代理坦袍,所以需要自己解決導(dǎo)包的問題十厢。
這里,cglib設(shè)計的幾個核心類捂齐,分別是:
- net.sf.cglib.proxy.Enhancer – 主要的增強類
- net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類蛮放,它是Callback接口的子接口,需要用戶實現(xiàn)
- net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類奠宜,可以方便的實現(xiàn)對源對象方法的調(diào)用
核心方法就是<code>public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable </code>,第一個參數(shù)是代理對象包颁,第二和第三個參數(shù)分別是攔截的方法和方法的參數(shù)瞻想,第四個是方法的代理對象。這個就是cglib代理娩嚼。
以上就是動態(tài)代理蘑险。
水平有限,難免有錯岳悟,還請評論區(qū)指責(zé)下