什么是aop?
aop(Aspect Oriented Programming)面向切面編程。是oop面向對象思想的一種補充和延續。也是Spring框架的一種重要的組件!
Spring中Aop代理是由SpringIOC容器負責生成、管理,其依賴關系也是由IOC容器負責處理的。
在Spring中,默認情況下是使用java動態代理技術來實現
當需要代理的類不是接口類型的時候,Spring會自動切換為CGLIB來進行代理,也可以強制的選擇使用CGLIB來進行代理
可以看一下SpringAOP的源代碼
package org.springframework.aop.framework; import java.io.Serializable; import java.lang.reflect.Proxy; import org.springframework.aop.SpringProxy; @SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override
//創建Aop代理 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//判斷配置是否有效,配置中有代理類信息,判斷有沒有用戶自己定義的解析接口 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); }
//如果是接口,或者Proxy的類,返回默認的JDK動態代理對象 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); }
//配置中有用戶自己指定的代理信息,則使用cglib進行代理 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } }
首先從通過配置信息解析有沒有指定的解析方法。
如果配置信息中沒有的話,就使用默認的JdkDynamicAopProxy()來進行代理
好了,現在知道了Spring在什么情況下使用什么代理。下面開始深入了解一下jdk代理和cglib代理吧
動態代理是jdk1.5引進的新的技術。動態代理的類位於Java.lang.reflect包下
先舉個栗子!
BookStore.java
public interface BookStore { public void addBook(); public void deleteBook(); }
還有一個實現該接口的實現類
BookStoreImpl.java
package test; public class BookStoreImpl implements BookStore { public void addBook() { System.out.println("增加圖書方法。。。"); } public void deleteBook() { System.out.println("我現在要刪除一個書"); } }
BookStoreProxy.java
package test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class BookStoreProxy implements InvocationHandler { private Object target; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("代理事務開始啦"); result = method.invoke(target, args); System.out.println("代理事務結束啦"); return result; } }
測試一下
package test; public class TestProxy { public static void main(String[] args) { BookStoreProxy proxy = new BookStoreProxy(); BookStore bs = (BookStore) proxy.bind(new BookStoreImpl()); bs.addBook(); System.out.println("-------------------"); bs.deleteBook(); } }
輸出結果
代理事務開始啦 增加圖書方法。。。 代理事務結束啦 ------------------- 代理事務開始啦 我現在要刪除一個書 代理事務結束啦
從測試的過程中可以看到,我們沒有改變原來的那個BookStore接口和BookStoreImpl實現類。
但是實現了方法的注入
這就是動態代理了,它的作用有以下幾點
1.Proxy類的代碼量可以降下來,不會因為業務的增大而龐大起來
2.可以實現aop編程,雖然靜態代理也可以實現
3.降低耦合性,通過參數就可以判斷真實的類,不需要事先實例化,比較靈活
jdk代理很好啊,但是仔細想一下還是有缺陷啊,Proxy類只能代理接口類
所以cglib(Code generation Library)就出現了,完美的拓展了proxy的性能,它可以代理接口的和類。碉堡了
再舉個栗子!
action.java
package cglib.test; public class Action { public void playGame(){ System.out.println("上班玩游戲..."); } public void smoke(){ System.out.println("上班抽煙..."); } }
ActionFactory.java
package cglib.test; public class ActionFactory { public static Action action = new Action(); public static Action getActionInstance(){ return action; } }
ActionTest.java
package cglib.test; import org.junit.Before; import org.junit.Test; public class ActionTest { @Before public void setUp() throws Exception { } @Test public void test() { Action action = ActionFactory.getActionInstance(); action.playGame(); action.smoke(); } }
好了,運行沒有問題。playGame和smoke兩個動作可以完成,但是新的需求來了
老板說,只有他能在上班的時候玩游戲!之前的類不能動!
用jdk中的動態代理當然可以實現,但是我們這沒有Action的接口。要是使用jdk動態代理的話,我們還需要創建新的接口!老板說了不能動原來的類。
所以我們選用cglib
再搞一個
ActionCglibProxy.java
package cglib.test; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class ActionCglibProxy implements MethodInterceptor { private String name; private Enhancer enhancer = new Enhancer(); public ActionCglibProxy(String name) { super(); this.name = name; } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { if ("zhyonk".equals(name)) { Object result = proxy.invokeSuper(obj, args); return result; } System.out.println(name + "你不可以抽煙哦"); return null; } public Action getAction(){ enhancer.setSuperclass(Action.class); enhancer.setCallback(this); return (Action) enhancer.create(); } }
測試一下
@Test public void TestCglibAction() { ActionCglibProxy proxy = new ActionCglibProxy("老板"); Action action = proxy.getAction(); action.playGame(); action.smoke(); }
成功代理了,這個過程中沒有用到接口。
通過Enhancer進行對類的注入。
getAction()返回的是原有的被代理類Action的子類
暫時就是這樣啦!