示例代碼:
public interface AopService {
public void doService();
}
public class AopServiceImpl implements AopService {
private String singal;
public String getSingal() {
return singal;
}
public AopServiceImpl(){}
public AopServiceImpl(Stringsingal){
this.singal = singal;
}
@Override
public void doService() {
System.out.println("doservice method is being invoked now!!!");
}
}
public class ProxyFactory implements InvocationHandler {
private Object targetObject;
public ObjectcreateTargetObject(Object targetObject){
this.targetObject =targetObject;
returnProxy.newProxyInstance(this.targetObject.getClass()
.getClassLoader(),
this.targetObject.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Objectarg0, Method method, Object[] args)
throws Throwable{
AopServiceImpl aopservice =(AopServiceImpl)this.targetObject;
Object result = null;
if(aopservice.getSingal()!=null){
result= method.invoke(targetObject, args);
}
return result;
}
}
測試代碼:
public class AopTests {
@Test
public void Tests(){
ProxyFactory pf = newProxyFactory();
// AopService as =(AopService)pf.createTargetObject(new AopServiceImpl());
AopService as =(AopService)pf.createTargetObject(new AopServiceImpl("Frank"));
as.doService();
}
}
JDK動態代理爲什麼必須用接口以及與CGLIB的對比
這 兩天對AOP原理感興趣了,試驗了JDK動態代理與CGLIB動態代理。從Spring的AOP框架介紹中得知對於使用接口的類,Spring使用JDK 動態代理(原來做項目中試圖從Bean強制轉換爲實現類,結果報錯,原來是這麼回事),沒有接口的就使用別的AOP框架aspectj,但這些都是依賴於 Java字節碼工具ASM生成一個原類的新類,調用Callback
但是JDK動態代理爲什麼必須使用接口一直很疑惑,難道原理不是像ASM一樣修改字節碼嗎?帶着這個疑問,開始看JDK的Proxy代碼。使用JDK動態代理的代碼代碼
ITestBean tb= (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(),tb.getClass().getInterfaces(), new TestBeanHander(tb));
於是從創建代理函數看起,即publicstatic Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException ,
通過源碼可以看到,這個類第一步生成一個代理類(注意,這裏的參數就是接口列表),
Class cl =getProxyClass(loader, interfaces);
然後通過代理類找到構造參數爲InvocationHandler的構造函數並生成一個新類。
Constructorcons = cl.getConstructor(constructorParams);//這個有用,在後面細說
return (Object) cons.newInstance(new Object[] { h });
接口起什麼作用呢,於是又看getProxyClass方法的代碼,這個源碼很長,就不細說了。大致分爲三段:
第一:驗證
第二:緩存創建新類的結構,如果創建過,則直接返回。(注意:這裏的KEY就是接口列表)
第三:如果沒有創建過,則創建新類
創建代碼如下
long num;
//獲得代理類數字標識
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
//獲得創建新類的類名$Proxy,包名爲接口包名,但需要注意的是,如果有兩個接口而且不在同一個包下,也會報錯
StringproxyName = proxyPkg + proxyClassNamePrefix + num;
//調用class處理文件生成類的字節碼,根據接口列表創建一個新類,這個類爲代理類,
byte[] proxyClassFile =ProxyGenerator.generateProxyClass(
proxyName, interfaces);
//通過JNI接口,將Class字節碼文件定義一個新類
proxyClass= defineClass0(loader, proxyName,
proxyClassFile, 0,proxyClassFile.length);
根據前面的代碼Constructorcons = cl.getConstructor(constructorParams);
可以猜測到接口創建的新類proxyClassFile 不管採用什麼接口,都是以下結構
public class$Proxy1 extends Proxy implements 傳入的接口{
}
生成新類的看不到源代碼,不過猜測它的執行原理很有可能是如果類是Proxy的子類,則調用InvocationHandler進行方法的Invoke
到現在大家都應該明白了吧,JDK動態代理的原理是根據定義好的規則,用傳入的接口創建一個新類,這就是爲什麼採用動態代理時爲什麼只能用接口引用指向代理,而不能用傳入的類引用執行動態類。
cglib採用的是用創建一個繼承實現類的子類,用asm庫動態修改子類的代碼來實現的,所以可以用傳入的類引用執行代理類
JDK動態代理與CGLIB對比如下:
//JDK動態代理測試代碼
ITestBean tb= new TestBean();
tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(),tb.getClass().getInterfaces(), new TestBeanHander(tb));//這句用接口引用指向,不會報錯
TestBean tmp= (TestBean) tb;//強制轉換爲實現類,將拋出類強制轉換異常
//CGLIB測試代碼
TestProxy tp= new TestProxy();
tb = (ITestBean) tp.getProxy(TestBean.class);
tmp =(TeatBean) tb;//強制轉換爲實現類,不會拋出異常
補充說明,如果在實現類中,接口定義的方法互相調用不會在調用InvocationHandler的invoke方法,JDK動態代理應該不是嵌入到Java的反射機制中,而是在反射機制上的一個調用。
應用舉例如下:
JDK動態代理的簡單使用示例:
如有業務類:
package com.proxy;
public class ForumServiceImplimplements ForumService{
public void removeTopic(int topicId){
System.out.println("模擬刪除記錄"+topicId);
try{
Thread.currentThread().sleep(20);
}catch(Exception e){
throw newRuntimeException(e);
}
}
public voidremoveForum(int forumId){
System.out.println("模擬刪除記錄"+forumId);
try{
Thread.currentThread().sleep(20);
}catch(Exception e){
throw newRuntimeException(e);
}
}
}
1、創建一個實現java.lang.reflect.InvocationHandler接口的代理類,如:
importjava.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PerformanceHandlerimplements InvocationHandler{
private Object target; //要進行代理的業務類的實例
public PerformanceHandler(Object target){
this.target = target;
}
//覆蓋java.lang.reflect.InvocationHandler的方法invoke()進行織入(增強)的操作
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable{
System.out.println("Objecttarget proxy:"+target);
System.out.println("模擬代理加強的方法...");
Object obj = method.invoke(target,args); //調用目標業務類的方法
System.out.println("模擬代理加強的方法執行完畢...");
return obj;
}
}
2、用java.lang.reflect.Proxy.newProxyInstance()方法創建動態實例來調用代理實例的方法:
import java.lang.reflect.Proxy;
public class TestForumService {
public static void main(String args[]){
ForumService target = newForumServiceImpl();//要進行代理的目標業務類
PerformanceHandler handler = new PerformanceHandler(target);//用代理類把目標業務類進行編織
//創建代理實例,它可以看作是要代理的目標業務類的加多了橫切代碼(方法)的一個子類
ForumService proxy =(ForumService)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
proxy.removeForum(10);
proxy.removeTopic(20);
}
}
CGLib動態代理示例:
1、創建一個實現net.sf.cglib.proxy.MethodInterceptor接口的實例來爲目標業務類加入進行代理時要進行的操作或增強:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
/**
*CGlib採用非常底層的字節碼技術,可以爲一個類創建子類,
並在子類中採用方法攔截技術攔截父類方法的調用,並順勢進行增強,即是織入橫切邏輯
* @author tufu
*/
public class CglibProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
//覆蓋MethodInterceptor接口的getProxy()方法,設置
public Object getProxy(Class clazz){
enhancer.setSuperclass(clazz); //設者要創建子類的類
enhancer.setCallback(this); //設置回調的對象
return enhancer.create(); //通過字節碼技術動態創建子類實例,
}
public Objectintercept(Object obj,Method method,Object[] args,
MethodProxyproxy) throws Throwable {
System.out.println("模擬代理增強方法");
//通過代理類實例調用父類的方法,即是目標業務類方法的調用
Object result =proxy.invokeSuper(obj, args);
System.out.println("模擬代理增強方法結束");
return result;
}
}
2、通過java.lang.reflect.Proxy的getProxy()動態生成目標業務類的子類,即是代理類,再由此得到代理實例:
import com.proxy.ForumServiceImpl;
import java.lang.reflect.Proxy;
public class TestCglibProxy {
public static void main(String args[]){
CglibProxy proxy = new CglibProxy();
//動態生成子類的方法創建代理類
ForumServiceImpl fsi =
(ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
fsi.removeForum(10);
fsi.removeTopic(2);
}
}