架构探险笔记5-使框架具备AOP特性(下)
開(kāi)發(fā)AOP框架
借鑒SpringAOP的風(fēng)格,寫(xiě)一個(gè)基于切面注解的AOP框架。在進(jìn)行下面的步驟之前,確保已經(jīng)掌了動(dòng)態(tài)代理技術(shù)。
定義切面注解
/*** 切面注解*/ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect {/*注解*/Class<? extends Annotation> value(); }通過(guò)@Target(ElementType.TYPE)來(lái)設(shè)置該注解只能應(yīng)用在類上。該注解中包含一個(gè)名為value的屬性,它是一個(gè)注解類,用來(lái)定義Controller這類注解。
在使用切面注解之前,我們需要先搭建一個(gè)代理框架。
搭建代理框架
繼續(xù)在框架中添加一個(gè)名為Proxy的接口
/*** 代理接口*/ public interface Proxy {/*** 執(zhí)行鏈?zhǔn)酱?/span>*/Object doProxy(ProxyChain proxyChain) throws Throwable; }這個(gè)接口中包括了一個(gè)doProxy方法,傳入一個(gè)ProxyChain,用于執(zhí)行“鏈?zhǔn)酱怼辈僮鳌?/p>
所謂鏈?zhǔn)酱?#xff0c;也就是說(shuō),可將多個(gè)代理通過(guò)一條鏈子串起來(lái),一個(gè)個(gè)地區(qū)執(zhí)行,執(zhí)行順序取決于添加到鏈上的先后順序。
package com.autumn.aop;import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List;/*** 代理鏈*/ public class ProxyChain {private final Class<?> targetClass; //代理類private final Object targetObject; //目標(biāo)對(duì)象private final Method targetMethod; //目標(biāo)方法private final MethodProxy methodProxy; //方法代理private final Object[] methodParams; //方法參數(shù)private List<Proxy> proxyList = new ArrayList<Proxy>(); //代理列表private int proxyIndex = 0; //代理索引public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {this.targetClass = targetClass;this.targetObject = targetObject;this.targetMethod = targetMethod;this.methodProxy = methodProxy;this.methodParams = methodParams;this.proxyList = proxyList;}public Class<?> getTargetClass() {return targetClass;}public Method getTargetMethod() {return targetMethod;}public Object[] getMethodParams() {return methodParams;}public Object doProxyChain() throws Throwable{Object methodResult;if (proxyIndex<proxyList.size()){ //如果代理索引小于代理列表大小//從列表中取出相應(yīng)的Proxy對(duì)象,調(diào)用器doProxy方法methodResult = proxyList.get(proxyIndex++).doProxy(this);}else { //所有代理遍歷完后methodResult = methodProxy.invokeSuper(targetObject,methodParams); //執(zhí)行目標(biāo)對(duì)象業(yè)務(wù) }return methodResult;} }在ProxyChain類中,我們定義了一系列的成員變量,包括targetClass(目標(biāo)類)、targetObject(目標(biāo)對(duì)象)、targetMethod(目標(biāo)方法)、methodProxy(方法代理)、methodParams(方法參數(shù)),此外還包括proxyList(代理列表)、proxyIndex(代理索引),這些成員變量在構(gòu)造器中進(jìn)行初始化,并提供了幾個(gè)重要的獲值方法。
需要注意的是MethodProxy這個(gè)類,它是CGLib開(kāi)源項(xiàng)目為我們提供的一個(gè)方法代理對(duì)象,在doProxyChain方法中被使用。
doProxyChain方法中,proxyIndex來(lái)充當(dāng)對(duì)象的計(jì)數(shù)器,若尚未達(dá)到proxyList的上限,則從proxyList中取出相應(yīng)的Proxy對(duì)象,并調(diào)用其doProxy方法。在Proxy接口中的實(shí)現(xiàn)中會(huì)提供相應(yīng)的橫切邏輯,并調(diào)用doProxyChain方法,隨后將再次調(diào)用當(dāng)前ProxyChain對(duì)象的doProxyChain方法,直到proxyIndex達(dá)到proxyList的上限為止,最后調(diào)用methodProxy的invokeSuper方法,執(zhí)行目標(biāo)對(duì)象的業(yè)務(wù)邏輯。
在pom.xml中添加CGLib的Maven依賴:
<!--不能超過(guò)3.0版本,這里用2.2--><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2</version></dependency>現(xiàn)在我們需要寫(xiě)一個(gè)類,讓它提供一個(gè)創(chuàng)建代理對(duì)象的方法,輸入一個(gè)目標(biāo)類和一組Proxy接口實(shí)現(xiàn),輸出一個(gè)代理對(duì)象,將該類命名為ProxyManager,讓它來(lái)創(chuàng)建愛(ài)你所有的代理對(duì)象,代碼如下:
/*** 代理管理器*/ public class ProxyManager {public static <T> T createProxy(final Class<T> targetClass, final List<Proxy> proxyList){return (T) Enhancer.create(targetClass, new MethodInterceptor() {@Overridepublic Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {return new ProxyChain(targetClass,targetObject,targetMethod,methodProxy,methodParams,proxyList).doProxyChain();}});} }使用CGLib提供的Enhancer.create()方法來(lái)創(chuàng)建代理對(duì)象,將intercept的參數(shù)傳入ProxyChain的構(gòu)造器中即可。
誰(shuí)來(lái)調(diào)用ProxyManager呢?當(dāng)然是切面類了,因?yàn)樵谇忻骖愔?#xff0c;需要在目標(biāo)方法被調(diào)用的前后增加相應(yīng)的邏輯。我們有必要寫(xiě)一個(gè)抽象類,讓它提供一個(gè)模板方法,并在該抽象類的具體實(shí)現(xiàn)中擴(kuò)展相應(yīng)的抽象方法。我們將該抽象命名為AspectProxy。代碼如下
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Method;/*** 切面代理*/ public abstract class AspectProxy implements Proxy{private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class);/*** 執(zhí)行鏈?zhǔn)酱?/span>*/@Overridepublic Object doProxy(ProxyChain proxyChain) throws Throwable {Object result = null;Class<?> cls = proxyChain.getTargetClass();Method method = proxyChain.getTargetMethod();Object[] params = proxyChain.getMethodParams();begin(); //代理開(kāi)始時(shí)執(zhí)行begin方法try {if (intercept(cls,method,params)){ //是否攔截此方法before(cls,method,params); //目標(biāo)方法執(zhí)行前代理方法before執(zhí)行result = proxyChain.doProxyChain(); //執(zhí)行目標(biāo)方法after(cls,method,result); //目標(biāo)方法執(zhí)行后代理方法after執(zhí)行}else {result = proxyChain.doProxyChain(); //執(zhí)行目標(biāo)方法 }}catch(Exception e){logger.error("proxy failure",e);error(cls,method,params,e); //拋出異常時(shí)代理方法error執(zhí)行throw e;}finally{end(); //代理結(jié)束時(shí)執(zhí)行方法end }return null;}/*方法開(kāi)始前執(zhí)行*/public void begin(){}/*** 攔截* @param cls 目標(biāo)類* @param method 目標(biāo)方法* @param params 目標(biāo)方法參數(shù)* @return 返回是否攔截*/public boolean intercept(Class<?> cls, Method method, Object[] params) throws Throwable {return true;}/*** 前置增強(qiáng)* @param cls 目標(biāo)類* @param method 目標(biāo)方法* @param params 目標(biāo)方法參數(shù)*/public void before(Class<?> cls, Method method, Object[] params) throws Throwable {}/*** 后置增強(qiáng)* @param cls 目標(biāo)類* @param method 目標(biāo)方法* @param result 目標(biāo)方法返回結(jié)果*/public void after(Class<?> cls, Method method, Object result) throws Throwable {}/*** 拋出增強(qiáng)* @param cls 目標(biāo)類* @param method 目標(biāo)方法* @param params 目標(biāo)方法參數(shù)* @param e 異常* @throws Throwable*/public void error(Class<?> cls, Method method, Object[] params, Exception e) throws Throwable {}/*方法結(jié)束后執(zhí)行*/public void end(){} }注意這里的AspectProxy類中的doProxy方法,我們從proxyChain參數(shù)中獲取了目標(biāo)類、目標(biāo)方法與方法參數(shù),隨后通過(guò)一個(gè)try...catch...finally代碼塊來(lái)實(shí)現(xiàn)調(diào)用框架,從框架中抽象出一系列的“鉤子方法”,這些抽象方法可在AspectProxy的子類中有選擇性的實(shí)現(xiàn),例如ControllerAspect
/*** 攔截所有Controller方法*/ @Aspect(Controller.class) public class ControllerAspect extends AspectProxy{private static final Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class);private long begin; //方法開(kāi)始時(shí)間/*** 前置增強(qiáng)* @param cls 目標(biāo)類* @param method 目標(biāo)方法* @param params 目標(biāo)方法參數(shù)*/@Overridepublic void before(Class<?> cls, Method method, Object[] params) throws Throwable {LOGGER.debug("---------begin---------");LOGGER.debug(String.format("class: %s",cls.getName()));LOGGER.debug(String.format("method: %s",method.getName()));begin = System.currentTimeMillis();}/*** 后置增強(qiáng)* @param cls 目標(biāo)類* @param method 目標(biāo)方法* @param result 目標(biāo)方法返回結(jié)果*/@Overridepublic void after(Class<?> cls, Method method, Object result) throws Throwable {LOGGER.debug(String.format("time: %ds", System.currentTimeMillis()-begin));LOGGER.debug("---------end---------");} }這里只實(shí)現(xiàn)了before與after方法,就可以在目標(biāo)方法執(zhí)行前后添加其他需要執(zhí)行的代碼了。
那么這樣就結(jié)束了嗎?當(dāng)然不是。我們需要在整個(gè)框架里使用ProxyManager來(lái)創(chuàng)建代理對(duì)象,并將該代理對(duì)象放入框架底層的BeanMap中,隨后才能通過(guò)IOC將被代理的對(duì)象注入到其他對(duì)象中。
加載AOP框架
按照之前的套路,為了加載AOP框架,我們需要一個(gè)名為AopHelper的類,然后將其添加到HelperLoader類中。
在AOPHelper中我們需要獲取所有的目標(biāo)類及其被攔截的切面類實(shí)例,并通過(guò)ProxyManager.createProxy方法來(lái)創(chuàng)建代理對(duì)象,最后將其放入BeanMap中。
首先,需要為BeanHelper類添加一個(gè)setBean方法,用于將Bean實(shí)例放入Bean?Map中(將實(shí)例替換為代理類用),代碼如下:
package org.smart4j.framework.helper;import org.smart4j.framework.util.ReflectionUtil;import java.util.HashMap; import java.util.Map; import java.util.Set;/*** Bean助手類**/ public class BeanHelper {/*** 定義bean映射(用于存放Bean類與Bean實(shí)例的映射關(guān)系)*/private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>, Object>();static{//獲取所有Controller和Service將類和實(shí)例放入Map中 }/*** 獲取Bean映射* @return*/public static Map<Class<?>, Object> getBeanMap() { return BEAN_MAP; }/*** 根據(jù)Class獲取bean實(shí)例* @param cls bean實(shí)例所屬的類* @param <T> 類的實(shí)例對(duì)象* @return*/public static <T> T getBean(Class<T> cls){}/*** 設(shè)置Bean實(shí)例 - 手動(dòng)將cls - obj放入到BEANMAP中去(更多用于將實(shí)例替換為代理對(duì)象)* @param cls 類* @param obj 實(shí)例*/public static void setBean(Class<?> cls,Object obj){BEAN_MAP.put(cls,obj);} }然后,我們需要擴(kuò)展AspectProxy抽象類的所有具體類getClassSetBySuper(),此外,還需要獲取帶有Aspect注解的所有類(即切面),因此需要在ClassHelper中添加一下兩個(gè)方法:
public class ClassHelper {/*** 定義類集合*/private static final Set<Class<?>> CLASS_SET;static {String basePackage = ConfigHelper.getAppBasePackage();CLASS_SET = ClassUtil.getClassSet(basePackage);}/*** 獲取應(yīng)用包名下的所有類* @return*/public static Set<Class<?>> getClassSet(){}/*** 獲取所有Controller類* @return*/public static Set<Class<?>> getControllerClassSet(){}/*** 獲取所有Service類* @return*/public static Set<Class<?>> getServiceClassSet(){}/*** 獲取應(yīng)用包名下的所有bean類(Controller和Service)* @return*/public static Set<Class<?>> getBeanClassSet(){}/*** 獲取應(yīng)用包名下某父類(接口)的所有子類(或?qū)崿F(xiàn)類)* @param superClass* @return*/public static Set<Class<?>> getClassSetBySuper(Class<?> superClass){Set<Class<?>> classSet = new HashSet<Class<?>>();for (Class<?> cls:CLASS_SET){if (superClass.isAssignableFrom(cls)&&superClass.equals(cls)){classSet.add(cls);}}return classSet;}/*** 獲取應(yīng)用包名下帶有注解的所有類* @param annotationClass* @return*/public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass){Set<Class<?>> classSet = new HashSet<Class<?>>();for (Class<?> cls : CLASS_SET){if (cls.isAnnotationPresent(annotationClass)){classSet.add(cls);}}return classSet;} }有了上面連個(gè)方法后,createTargetClassSet方法獲取Aspect注解中設(shè)置的注解類,若該注解不是Aspect類,則可調(diào)用ClassHelper.getClassSetByAnnotation方法獲取相應(yīng)的類,并把這些類放入目標(biāo)類集合中,最終返回這個(gè)集合。
然后我們用createProxyMap方法獲取代理類及其目標(biāo)類直接按的映射關(guān)系,一個(gè)代理類可對(duì)應(yīng)一個(gè)或多個(gè)目標(biāo)類。需要強(qiáng)調(diào)的是,這里所說(shuō)的代理類指的是切面類。
AOP順序:
代理類需要擴(kuò)展AspectProxy抽象類(通過(guò)getClassSetBySuper獲取所有子類(即切面)),還需要帶有Aspect注解,只有滿足這兩個(gè)條件,才能根據(jù)Aspect注解中所定義的注解屬性去獲取該注解所對(duì)應(yīng)的目標(biāo)類集合(通過(guò)createTargetClassSet獲取目標(biāo)類集合),然后才能建立代理類與目標(biāo)類集合之間的映射關(guān)系,最終返回這個(gè)映射關(guān)系。
一旦獲取了代理類與目標(biāo)類集合之間的映射關(guān)系,createTargetMap方法就能根據(jù)這個(gè)關(guān)系分析出目標(biāo)類與代理對(duì)象列表之間的映射關(guān)系。
最后通過(guò)AOPHelper的靜態(tài)代碼塊初始化整個(gè)AOP框架,獲取代理類及其目標(biāo)類集合的映射關(guān)系,進(jìn)一步獲取目標(biāo)類與代理對(duì)象列表的映射關(guān)系,進(jìn)而遍歷這個(gè)映射關(guān)系,從中獲取目標(biāo)類與代理對(duì)象列表,調(diào)用ProxyManager.createProxy方法獲取代理對(duì)象,調(diào)用BeanHelper.setBean方法,將該代理對(duì)象重新放入BeanMap中。
import org.smart4j.framework.aop.Aspect; import org.smart4j.framework.aop.AspectProxy; import org.smart4j.framework.aop.Proxy; import org.smart4j.framework.aop.ProxyManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.lang.annotation.Annotation; import java.util.*;/*** 方法攔截助手類*/ public class AopHelper {private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);static{try{Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap(); //獲取Map<代理類,Set<目標(biāo)類>>Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap); //獲取Map<目標(biāo)類,List<代理實(shí)例>>for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()){ //遍歷Map<目標(biāo)類,List<代理實(shí)例>>Class<?> targetClass = targetEntry.getKey(); //目標(biāo)類List<Proxy> proxyList = targetEntry.getValue(); //代理類Object proxy = ProxyManager.createProxy(targetClass,proxyList); //根據(jù)目標(biāo)類和代理集合創(chuàng)建一個(gè)代理BeanHelper.setBean(targetClass,proxy); //將Bean容器中目標(biāo)類對(duì)應(yīng)的實(shí)體替換成代理 }}catch (Exception e){LOGGER.error("aop failure",e);}}/*** 根據(jù)Aspect注解(切點(diǎn))獲取所有的代理目標(biāo)類集合(目標(biāo)類為Controller等注解的類)* @param aspect 代理類注解,用來(lái)指定目標(biāo)類的注解 例如:@Aspect(Controller.class)* @return 返回Aspect注解中指定value注解的目標(biāo)類 例如:帶Controller注解的所有類* 例如Aspect(Controller.class)是指獲取所有Controller注解的類*/private static Set<Class<?>> createTargetClassSet(Aspect aspect){Set<Class<?>> targetClassSet = new HashSet<Class<?>>();Class<? extends Annotation> annotation = aspect.value(); //獲取值(也是注解)if (annotation!=null && !annotation.equals(Aspect.class)){ //獲取的value注解不為null,且注解不為AspecttargetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)); //加入所有value(切點(diǎn))指定的注解的類 }return targetClassSet; //返回所有目標(biāo)類 }/*** 創(chuàng)建所有Map<代理類,Set<代理目標(biāo)類>>的映射關(guān)系* @return Map<代理類,Set<代理目標(biāo)類>>* @throws Exception*/private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception{Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>(); //結(jié)果集<代理類,Set<代理目標(biāo)類>>//獲取所有的AspectProxy的子類(代理類集合),即切面,/*這個(gè)是入口,根據(jù)基類來(lái)查找所有的切面(代理類),獲取所有的切面!!!*/Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);for (Class<?> proxyClass : proxyClassSet){ //遍歷代理類(切面),如ControllerAspectif (proxyClass.isAnnotationPresent(Aspect.class)){ //驗(yàn)證基類為AspectProxy且要有Aspect注解的才能為切面。如果代理類的的注解為Aspect(也就是說(shuō)代理類一定要都切點(diǎn)(注解)才能是切面),例如ControllerAspect代理類的注解為@Aspect(Controller.class)Aspect aspect = proxyClass.getAnnotation(Aspect.class); //獲取代理類(切面)的注解/*根據(jù)注解獲取所有的目標(biāo)類*/Set<Class<?>> targetClassSet = createTargetClassSet(aspect); //獲取所有的代理目標(biāo)類集合 proxyMap.put(proxyClass,targetClassSet); //加入到結(jié)果集Map<代理類,Set<代理目標(biāo)類>>中 }}return proxyMap;}/*** 將Map<代理類,Set<目標(biāo)類>> proxyMap轉(zhuǎn)為Map<目標(biāo)類,List<代理類>> targetMap* @param proxyMap Map<代理類,Set<目標(biāo)類>>* @return Map<目標(biāo)類,List<代理類實(shí)例>>* @throws Exception*/private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>,List<Proxy>>(); //class - list鍵值對(duì)的mapfor (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry:proxyMap.entrySet()){ //遍歷cls - set鍵值對(duì)的mapClass<?> proxyClass = proxyEntry.getKey(); //獲取代理clsSet<Class<?>> targetClassSet = proxyEntry.getValue(); //獲取目標(biāo)Setfor (Class<?> targetClass:targetClassSet){ //遍歷目標(biāo)SetProxy proxy = (Proxy) proxyClass.newInstance(); //實(shí)例化代理類if (targetMap.containsKey(targetClass)){ //如果Map<Class<?>,List<Proxy>>包含該目標(biāo)類targetMap.get(targetClass).add(proxy); //直接將代理類添加到對(duì)應(yīng)目標(biāo)類的Map中}else{List<Proxy> proxyList = new ArrayList<Proxy>(); //如果沒(méi)有 proxyList.add(proxy);targetMap.put(targetClass,proxyList);}}}return targetMap;} }最后,需要注意的是,AOPHelper要在IOCHelper之前加載,因?yàn)槭紫刃枰ㄟ^(guò)AopHelper獲取代理對(duì)象,然后才能通過(guò)IOCHelper進(jìn)行依賴注入。否則的話,IOCHelper先加載就會(huì)導(dǎo)致Bean容器中的是代理,而B(niǎo)ean中注入的是原本的實(shí)例對(duì)象。所以注入一定要最后進(jìn)行(IOCHelper一定要最后加載)。
源碼?
轉(zhuǎn)載于:https://www.cnblogs.com/aeolian/p/9931922.html
總結(jié)
以上是生活随笔為你收集整理的架构探险笔记5-使框架具备AOP特性(下)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 使用Cloud application
- 下一篇: solidity智能合约[17]-动态长