一、實現自己的AOP
在之前一篇隨筆中已經詳細講解了java的動態代理機制,我們也知道了AOP的底層其實就是基於動態代理機制實現的,所以這里先自己實現一下AOP
public class DynamicProxy implements InvocationHandler { // 要代理的對象 private Object target; // 將構造方法禁用掉,不讓外部通過new來得到DynamicProxy對象 private DynamicProxy() { }; /** * 返回一個動態的代理對象 * * @param object * @return */ public static Object newInstance(Object object) { DynamicProxy proxy = new DynamicProxy(); proxy.target = object; // 通過Proxy的newProxyInstance方法來得到一個代理對象 Object result = Proxy.newProxyInstance(object.getClass() .getClassLoader(), object.getClass().getInterfaces(), proxy); return result; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // // 只有方法名為add和delete時候才引入日志 // if(method.getName().equals("add") || method.getName().equals("delete")) // { // Logger.logInfo("動態代理類"); // } // 根據LogAnnotation來判斷,如果被標注了注解,則輸出日志 if(method.isAnnotationPresent(LogAnnotation.class)) { LogAnnotation log = method.getAnnotation(LogAnnotation.class); Logger.logInfo(log.value()); } Object object = method.invoke(target, args); return object; } }
就如上篇隨筆所說,動態代理類必須要實現InvocationHandler的這個接口,我們的這個類當然也要實現這個接口了。然后在里面定義了一個私有的Object屬性,表示我們要代理的對象。這里我們將這個類的構造方法禁用掉,使其不能通過外部直接new出來一個對象,然后我們寫一個newInstance的方法來給我們的代理對象賦初值,並且返回的就是我們的代理對象。我們看看在beans.xml中的配置文件
<!-- 如果要對static方法進行注入,可以通過factory-method屬性來制定方法名字,並通過構造函數的方式傳入參數 -->
<bean id="userDAOProxy" class="com.xiaoluo.proxy.DynamicProxy" factory-method="newInstance">
<constructor-arg ref="userDAO"/>
</bean>
因為我們的DynamicProxy類的對象以及代理對象是通過static方法來進行注入的,因此我們如果要對其進行注入的話,需要通過 factory-method 這個屬性來給我們的靜態方法進行屬性注入,通過 <constructor-arg>來講參數傳遞進去,這樣我們的userDAOProxy就是一個代理對象了。
二、通過Annotation來配置我們的AOP
我們要將AOP的schema引入,如果使用注解的話,還要開啟AOP的自動代理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 通過annotation來進行bean的創建 --> <context:annotation-config/> <!-- 對以com.xiaoluo開頭的包進行掃描 --> <context:component-scan base-package="com.xiaoluo"/> <!-- 開啟AOP的自動代理,只要加入了@Aspect標簽就自動代理 --> <aop:aspectj-autoproxy/> </beans>
然后我們來看看我們的切面類:
@Component("logAspect") // 將該切面類由Spring托管 @Aspect // 聲明該類是一個Aspect,切面類 public class LogAspect { /** * execution(* com.xiaoluo.dao.*.add*(..)) * 第一個*表示任意返回值 * 第二個*表示com.xiaoluo.dao下的所有類 * 第三個*表示所有以add開頭的方法 * (..)表示方法接收的任何參數 */ /* * Before在方法執行前執行 */ @Before("execution(* com.xiaoluo.dao.*.add*(..))||" + "execution(* com.xiaoluo.dao.*.delete*(..))||" + "execution(* com.xiaoluo.dao.*.update*(..))") public void logStart(JoinPoint jp) { // 得到該JoinPoint的類 System.out.println(jp.getTarget()); // 得到該JoinPoint的方法 System.out.println(jp.getSignature()); // 得到該JoinPoint的方法的名字 System.out.println(jp.getSignature().getName()); Logger.logInfo("日志開始"); } /* * After在方法執行只會執行 */ @After("execution(* com.xiaoluo.dao.*.add*(..))||" + "execution(* com.xiaoluo.dao.*.delete*(..))||" + "execution(* com.xiaoluo.dao.*.update*(..))") public void logEnd() { Logger.logInfo("日志結束"); } /* * Around包含了這個方法的執行 * * Logger.logInfo("開始執行Around日志"); * pjp.proceed(); * Logger.logInfo("結束了Around日志");的執行順序為: * * 首先執行 Logger.logInfo("開始執行Around日志");,接着執行方法,因為在方法執行要執行 * Before,所以先執行完Before再執行方法,接着執行 Logger.logInfo("結束了Around日志"); * 最后執行After * */ @Around("execution(* com.xiaoluo.dao.*.add*(..))||" + "execution(* com.xiaoluo.dao.*.delete*(..))||" + "execution(* com.xiaoluo.dao.*.update*(..))") public void logAround(ProceedingJoinPoint pjp) throws Throwable { Logger.logInfo("開始執行Around日志"); pjp.proceed(); Logger.logInfo("結束了Around日志"); } }
因為Spring的AOP使用的是第三方的jar包,所以我們這里還要引入三個AOP的jar文件:
aopalliance-1.0.jar aspectjrt-1.7.3.jar aspectjweaver-1.7.3.jar
這樣我們的基於注解的AOP就配置好可以使用了。
三、基於XML的AOP配置
如果基於XML的AOP配置,我們的beans.xml配置如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 通過annotation來進行bean的創建 --> <context:annotation-config/> <!-- 對以com.xiaoluo開頭的包進行掃描 --> <context:component-scan base-package="com.xiaoluo"/> <!-- 通過xml方式來配置AOP --> <aop:config> <!-- 聲明一個切面 --> <aop:aspect id="myLogAspect" ref="logAspect"> <!-- 聲明在哪些位置我要加入這個切面 --> <aop:pointcut id="logPoint" expression="execution(* com.xiaoluo.dao.*.add*(..))|| execution(* com.xiaoluo.dao.*.delete*(..))|| execution(* com.xiaoluo.dao.*.update*(..))"/> <aop:before method="logStart" pointcut-ref="logPoint"/> <aop:after method="logEnd" pointcut-ref="logPoint"/> <aop:around method="logAround" pointcut-ref="logPoint"/> </aop:aspect> </aop:config> </beans>
我們這里當然也要聲明我們的切面類:
@Component("logAspect") // 將該切面類由Spring托管 public class LogAspect { public void logStart(JoinPoint jp) { // 得到該JoinPoint的類 System.out.println(jp.getTarget()); // 得到該JoinPoint的方法 System.out.println(jp.getSignature()); // 得到該JoinPoint的方法的名字 System.out.println(jp.getSignature().getName()); Logger.logInfo("日志開始"); } public void logEnd() { Logger.logInfo("日志結束"); } public void logAround(ProceedingJoinPoint pjp) throws Throwable { Logger.logInfo("開始執行Around日志"); pjp.proceed(); Logger.logInfo("結束了Around日志"); } }
本篇隨筆主要記錄了自己實現AOP的配置以及基於Annotation和XML的方式來配置我們的AOP