本文屬于系列文章《設(shè)計(jì)模式》,附上文集鏈接
本文集代理模式請(qǐng)看此處
前言
上一篇寫的那個(gè)代理模式,是屬于靜態(tài)代理。假設(shè)一種場(chǎng)景,假設(shè)我們要為很多類的很多方法添加前置和后置方法(別忘了代理模式是為了控制),按照靜態(tài)代理的方法,我們就得為所有類的所有方法一個(gè)一個(gè)地去實(shí)現(xiàn)需求,想想這工程量就覺得可怕,所以就有了很優(yōu)美的實(shí)現(xiàn)方式,叫做動(dòng)態(tài)代理。
JDK代理
且看代碼
// 抽象接口
public interface MyInterface {
void hello(int i, int j);
void hello(int i, int j, int k);
}
// 接口實(shí)現(xiàn)類,也是我們的代理目標(biāo)類
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);
}
}
// 這個(gè)是關(guān)鍵,被代理對(duì)象方法的實(shí)際執(zhí)行地方,通過反射實(shí)現(xiàn)
public class MyInvocationHandler implements InvocationHandler{
// 被代理的目標(biāo)對(duì)象
private Object target;
MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
/\*\*
\* proxy 代理的目標(biāo)對(duì)象
\* method 代理的目標(biāo)對(duì)象的方法
\* 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;
}
}
// 場(chǎng)景類
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)鍵的就三個(gè)地方:
- 一、被代理的目標(biāo)對(duì)象必須實(shí)現(xiàn)接口,即上文的<code>MySubject</code>和<code>MyInterface</code>。
- 二、實(shí)現(xiàn)InvocationHandler接口的handler類,即上文中的<code>public class MyInvocationHandler implements InvocationHandler</code>。方法的參數(shù)說明在上面也有了,這里就不在闡述了。
- 三、使用Proxy類獲取到目標(biāo)對(duì)象的代理對(duì)象,即上文中的
Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),mySubject.getClass().getInterfaces(), handler);
之前也想過Proxy是怎么實(shí)現(xiàn)這個(gè)代理過程的,結(jié)果在這里看到了。
回到上面的代碼,其實(shí)上面的代碼還可以封裝下,比如變成下面的樣子:
// 接口類,接口實(shí)現(xiàn)類,handler類都沒變化,引入下面的類
// 簡(jiǎn)單的動(dòng)態(tài)代理類,傳入一個(gè)目標(biāo)對(duì)象參數(shù),得到其代理對(duì)象
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);
}
}
// 場(chǎng)景類
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í)行后
上面場(chǎng)景類的代碼,用自然語言解釋挺流暢的,就是創(chuàng)建代理類對(duì)象,通過代理類對(duì)象獲取被代理對(duì)象的代理對(duì)象。反正封裝方法多種多樣,上面其實(shí)其中一種,有意者多試試唄。
上面就是JDK代理,但是JDK代理有一個(gè)限制,就是上面說到的關(guān)鍵點(diǎn)的第一點(diǎn),JDK代理無法脫離接口而實(shí)現(xiàn),要為某個(gè)目標(biāo)對(duì)象實(shí)現(xiàn)JDK代理,該目標(biāo)對(duì)象至少實(shí)現(xiàn)一個(gè)接口,那如果要為沒實(shí)現(xiàn)接口的類實(shí)現(xiàn)代理,怎么辦,就是我們的Cglib代理。
cglib代理
且看代碼
// 我們的代理目標(biāo)類,這次是沒有實(shí)現(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;
}
}
// 場(chǎng)景類
public class Client {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MySubject.class);
enhancer.setCallback(cglibProxy);
// 得到代理對(duì)象
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是提供動(dòng)態(tài)代理的第三方庫,并不是jdk給我們代理,所以需要自己解決導(dǎo)包的問題。
這里,cglib設(shè)計(jì)的幾個(gè)核心類,分別是:
- net.sf.cglib.proxy.Enhancer – 主要的增強(qiáng)類
- net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實(shí)現(xiàn)
- net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實(shí)現(xiàn)對(duì)源對(duì)象方法的調(diào)用
核心方法就是<code>public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable </code>,第一個(gè)參數(shù)是代理對(duì)象,第二和第三個(gè)參數(shù)分別是攔截的方法和方法的參數(shù),第四個(gè)是方法的代理對(duì)象。這個(gè)就是cglib代理。
以上就是動(dòng)態(tài)代理。
水平有限,難免有錯(cuò),還請(qǐng)?jiān)u論區(qū)指責(zé)下