AOP技術是spring框架的一個重要特征。通過該特性能夠在函數運行之前,之后,或者異常處理的時候執行我們需要的一些操作。
下面我們就是需要拋開AOP,Spring這樣成型的框架不用,而僅僅使用java反射機制中的Proxy,InvocationHandler來實現類似Spring框架的攔截器的效果。
動態代理DynamicProxy
首先,在設計這個攔截器框架之前,我們需要明白java中動態代理是什么?我想如果早就清楚請直接跳過,如果需要了解,那我想你手邊最好有一個javadoc的電子書。
Java.lang.reflect.Proxy是反射包的成員之一。具體說明請查javadoc。
用法就是比如有一個對象,我們需要在調用它提供的方法之前,干點別的什么,就不能直接調用它,而是生成一個它的代理,這個代理有這個對象所提供的所有接口方法,我們通過直接調用代理的這些方法,來實現:函數既能像原來對象的那樣工作,又能在函數運行過程前后加入我們自己的處理。
這個類有個非常重要的函數用來實現某個類的代理:
1
Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader,
2
Class<?>[] interfaces,
3
InvocationHandler h) throws IllegalArgumentException
參數有點迷惑人,解釋下:
ClassLoader 是類加載器,這個參數用來定義代理類,一般使用原對象的即可,也可以為null用上下文解決。
Class<?>[] 接口數組,就是我們需要這個代理能夠提供原來的類的什么函數。如果全部則直接class.getInterfaces()來解決.
InvocationHandler 調用處理器,這個就是如果你調用代理的方法,那么這個處理器就會被關聯過來,處理調用這個函數的整個過程。這個接口只定義了一個方法:
1
public Object invoke(Object proxy, Method method,
2
Object[] args) throws Throwable;
參數中proxy就是你調用的代理,method指的是你調用的代理的那個方法,args是傳給該方法的參數。
我們生成某個類的代理步驟,一般需要先考慮我們在調用這個類的函數的時候(之前,或者之后)如何處理某些事情,因此我們首先考慮的就是如何實現InvocationHandler這個接口。
讓我們做一個實踐,做這么一個調用處理器:任何使用此處理器的代理在調用它的任何方法的時候,都打印被代理的類的類名+“.”+方法名+”(“+參數+”,”+參數+...+”)”。
步驟1: 定義接口IUser
1
package com.cyh.proxy.sample;
2
3
public interface IUser
{
4
public String getName();
5
6
public void setName(String name);
7
}
步驟2: 寫IUser接口的實現類User
1 package com.cyh.proxy.sample.impl;
2
3 import com.cyh.proxy.sample.IUser;
4
5 public class User implements IUser {
6 String name;
7
8 public User(String name) {
9 this.name = name;
10 }
11
12 public String getName() {
13 return name;
14 }
15
16 public void setName(String name) {
17 this.name = name;
18 }
19 }
步驟3: 寫TraceHandler實現調用處理器InvocationHandler,即在invoke()方法里我們要打印被代理的類的類名+“.”+方法名+”(“+參數+”,”+參數+...+”)”。
1 package com.cyh.proxy.sample.impl;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 public class TraceHandler implements InvocationHandler {
7 private Object target;
8
9 public TraceHandler(Object target) {
10 this.target = target;
11 }
12
13 public Object invoke(Object proxy, Method method, Object[] args)
14 throws Throwable {
15
16 // print implicit argument
17 System.out.print(target.getClass().getName());
18 // print method name
19 System.out.print("." + method.getName() + "(");
20 // print explicit arguments
21 if (args != null) {
22 for (int i = 0; i < args.length; i++) {
23 System.out.print(args[i]);
24 if (i < args.length - 1) {
25 System.out.print(",");
26 }
27 }
28 }
29 System.out.println(")");
30
31 return method.invoke(this.target, args);
32 }
33 }
步驟4: 最后,讓我們寫測試類ProxyTest
1 package com.cyh.proxy.sample.test;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 import com.cyh.proxy.sample.IUser;
7 import com.cyh.proxy.sample.impl.TraceHandler;
8 import com.cyh.proxy.sample.impl.User;
9
10 public class ProxyTest {
11 User user;
12
13 public ProxyTest() {
14 user = new User("LaraCroft");
15
16 ClassLoader classLoader = user.getClass().getClassLoader();
17 Class[] interfaces = user.getClass().getInterfaces();
18 InvocationHandler handler = new TraceHandler(user);
19 IUser proxy = (IUser) Proxy.newProxyInstance(classLoader, interfaces,
20 handler);
21
22 proxy.setName("David Beckham");
23 }
24
25 public static void main(String[] args) {
26 new ProxyTest();
27 }
28
29 }
好了,所有代碼寫好了,運行一下,測試結果是:
com.cyh.proxy.impl.User.setName(David Beckham)
講一下運行原理:
首先我們初始化了user對象,user.name = = “LaraCroft”;
然后創建了user對象的代理proxy。
注意這里:Proxy.newProxyInstance()函數的返回值使用接口IUser轉型的,你或許會想到
用User來做強制類型轉換,但是會拋出下面的異常
Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to com.cyh.proxy.impl.User
因為:代理類是實現了User類的所有接口,但是它的類型是$Proxy0,不是User。
最后,我們調用代理的setName()方法:
proxy.setName("David Beckham");
代理在執行此方法的時候,就好觸發調用處理器 TraceHandler,并執行 TraceHandler的invoke()方法,然后就會打印:
com.cyh.proxy.impl.User.setName(David Beckham)
攔截器框架的實現
好了,關于代理的知識我們講完了,我們可以考慮如何實現這個攔截器的框架,所謂攔截器就是在函數的運行前后定制自己的處理行為,也就是通過實現InvocationHandler達到的。
設計思路
我們來理清一下思路,在使用一個攔截器的時候?什么是不變的,什么是變化的?
不變的:
每次都要創建代理
攔截的時間:函數執行之前,之后,異常處理的時候
變化的:
每次代理的對象不同
攔截器每次攔截到執行時的操作不同
好了,廢話少說,看類圖:
圖中:
DynamicProxyFactory 和它的實現類,是一個工廠,用來創建代理
Interceptor 這個接口用來定義攔截器的攔截處理行為配合DynamicProxyInvocationHandler達到攔截效果
DynamicProxyInvocationHandler 調用處理器的實現,它有兩個成員,一個是Object target指的是被代理的類,另一個是Interceptor interceptor就是在invoke()方法執行target的函數之前后,異常處理時,調用interceptor的實現來達到攔截,并處理的效果。
代碼實現
步驟1: 定義接口DynamicProxyFactory
1 package com.cyh.proxy.interceptor;
2
3 public interface DynamicProxyFactory {
4 /**
5 * 生成動態代理,并且在調用代理執行函數的時候使用攔截器
6 *
7 * @param clazz
8 * 需要實現的接口
9 * @param target
10 * 實現此接口的類
11 * @param interceptor
12 * 攔截器
13 * @return
14 */
15 public <T> T createProxy(T target, Interceptor interceptor);
16 }
步驟2: 定義接口Interceptor
1 package com.cyh.proxy.interceptor;
2
3 import java.lang.reflect.Method;
4
5 public interface Interceptor {
6 public void before(Method method, Object[] args);
7
8 public void after(Method method, Object[] args);
9
10 public void afterThrowing(Method method, Object[] args, Throwable throwable);
11
12 public void afterFinally(Method method, Object[] args);
13 }
14
步驟3: 實現接口DynamicProxyFactory
1 package com.cyh.proxy.interceptor.impl;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 import com.cyh.proxy.interceptor.DynamicProxyFactory;
7 import com.cyh.proxy.interceptor.Interceptor;
8
9 public class DynamicProxyFactoryImpl implements DynamicProxyFactory {
10 /**
11 * 生成動態代理,并且在調用代理執行函數的時候使用攔截器
12 *
13 * @param target
14 * 需要代理的實例
15 * @param interceptor
16 * 攔截器實現,就是我們希望代理類執行函數的前后,
17 * 拋出異常,finally的時候去做寫什么
18 */
19 @Override
20 @SuppressWarnings("unchecked")
21 public <T> T createProxy(T target, Interceptor interceptor) {
22 // 當前對象的類加載器
23 ClassLoader classLoader = target.getClass().getClassLoader();
24 // 獲取此對象實現的所有接口
25 Class<?>[] interfaces = target.getClass().getInterfaces();
26 // 利用DynamicProxyInvocationHandler類來實現InvocationHandler
27 InvocationHandler handler = new DynamicProxyInvocationHandler(target,
28 interceptor);
29
30 return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
31 }
32 }
步驟4: 實現調用處理器
1 package com.cyh.proxy.interceptor.impl;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 import com.cyh.proxy.interceptor.Interceptor;
7
8 /**
9 * 動態代理的調用處理器
10 *
11 * @author chen.yinghua
12 */
13 public class DynamicProxyInvocationHandler implements InvocationHandler {
14 private Object target;
15 private Interceptor interceptor;
16
17 /**
18 * @param target
19 * 需要代理的實例
20 * @param interceptor
21 * 攔截器
22 */
23 public DynamicProxyInvocationHandler(Object target,
24 Interceptor interceptor) {
25 this.target = target;
26 this.interceptor = interceptor;
27 }
28
29 /**
30 * @param proxy
31 * 所生成的代理對象
32 * @param method
33 * 調用的方法示例
34 * @args args 參數數組
35 * @Override
36 */
37 public Object invoke(Object proxy, Method method, Object[] args)
38 throws Throwable {
39 Object result = null;
40
41 try {
42 // 在執行method之前調用interceptor去做什么事
43 this.interceptor.before(method, args);
44 // 在這里我們調用原始實例的method
45 result = method.invoke(this.target, args);
46 // 在執行method之后調用interceptor去做什么事
47 this.interceptor.after(method, args);
48 } catch (Throwable throwable) {
49 // 在發生異常之后調用interceptor去做什么事
50 this.interceptor.afterThrowing(method, args, throwable);
51 throw throwable;
52 } finally {
53 // 在finally之后調用interceptor去做什么事
54 interceptor.afterFinally(method, args);
55 }
56
57 return result;
58 }
59
60 }
好了,目前為止,這個框架算完成了,怎么用呢?
接下來我們完成測試包。
完成測試
步驟1: 首先,給需要代理的類定義一個接口Service
1 package com.cyh.proxy.interceptor.test;
2
3 public interface Service {
4 public String greet(String name);
5 }
步驟2: 實現這個接口,編寫類ServiceImpl
1 package com.cyh.proxy.interceptor.test;
2
3 public class ServiceImpl implements Service {
4 @Override
5 public String greet(String name) {
6 String result = "Hello, " + name;
7 System.out.println(result);
8 return result;
9 }
10 }
步驟3: 實現攔截器接口Interceptor,編寫類InterceptorImpl
1
package com.cyh.proxy.interceptor.test;
2
3
import java.lang.reflect.Method;
4
5
import com.cyh.proxy.interceptor.Interceptor;
6
7
public class InterceptorImpl implements Interceptor
{
8
@Override
9
public void after(Method method, Object[] args)
{
10
System.out.println("after invoking method: " + method.getName());
11
}
12
13
@Override
14
public void afterFinally(Method method, Object[] args)
{
15
System.out.println("afterFinally invoking method: " + method.getName());
16
}
17
18
@Override
19
public void afterThrowing(Method method, Object[] args,
20
Throwable throwable)
{
21
System.out.println("afterThrowing invoking method: "
22
+ method.getName());
23
}
24
25
@Override
26
public void before(Method method, Object[] args)
{
27
System.out.println("before invoking method: " + method.getName());
28
}
29
}
步驟4:編寫測試類TestDynamicProxy
1 package com.cyh.proxy.interceptor.test;
2
3 import com.cyh.proxy.interceptor.DynamicProxyFactory;
4 import com.cyh.proxy.interceptor.Interceptor;
5 import com.cyh.proxy.interceptor.impl.DynamicProxyFactoryImpl;
6
7 public class TestDynamicProxy {
8 public TestDynamicProxy() {
9 DynamicProxyFactory dynamicProxyFactory = new DynamicProxyFactoryImpl();
10 Interceptor interceptor = new InterceptorImpl();
11 Service service = new ServiceImpl();
12
13 Service proxy = dynamicProxyFactory.createProxy(service, interceptor);
14 // Service proxy = DefaultProxyFactory.createProxy(service,
15 // interceptor);
16 proxy.greet("iwindyforest");
17 }
18
19 public static void main(String[] args) {
20 new TestDynamicProxy();
21 }
22 }
好了,整個測試包完成了,讓我們運行下看看運行結果:
before invoking method: greet
Hello, iwindyforest
after invoking method: greet
afterFinally invoking method: greet
完善設計
現在,讓我們回顧一下:接口DynamicProxyFactory,真的需要么?
它只是一個工廠,負責生產代理的,但是我們并沒有過多的要求,因此可以說它的實現基本上是不變的。鑒于此,我們在使用createProxy()函數的時候,只需要一個靜態方法就可以了,沒有必要再初始化整個類,這樣才比較方便么。
因此,我在com.cyh.proxy.interceptor.impl包里加了一個默認的工廠DefaultProxyFactory:
1 package com.cyh.proxy.interceptor.impl;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 import com.cyh.proxy.interceptor.Interceptor;
7
8 public class DefaultProxyFactory {
9 @SuppressWarnings("unchecked")
10 public static <T> T createProxy(T target, Interceptor interceptor) {
11 // 當前對象的類加載器
12 ClassLoader classLoader = target.getClass().getClassLoader();
13 // 獲取此對象實現的所有接口
14 Class<?>[] interfaces = target.getClass().getInterfaces();
15 // 利用DynamicProxyInvocationHandler類來實現InvocationHandler
16 InvocationHandler handler = new DynamicProxyInvocationHandler(target,
17 interceptor);
18
19 return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
20 }
21 }
參考書籍:
Core java Volume I
深入淺出JDK6.0