一、註解
註解是插入到源碼中用於某種工具處理的標籤。這裏我們使用將要Spring AOP來讀取並處理它們。
1.定義一個註解接口。
package com.test.common;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 元註解,表明該註解只能用於方法。
@Target({ElementType.METHOD})
// 表明註解將會被載入到虛擬機中,可以由反射代碼獲取到註解內容
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {
String desc() default "無描述信息";
}
@Target是一個元註解。它註解了MethodLog註解。
該註解可以指定任意數量的元素類型,如@Target({ElementType.TYPE,ElementType.METHOD})。下表展示其可能取值情況:
ANNOTATION_TYPE 註解類型聲明 | CONSTRUCTOR 構造器 |
PACKAGE 包 | FIELD 成員域(包括enum常量) |
TYPE 類(包括enum)及接口(包括註解類型) | PARAMETER 方法或構造器參數 |
METHOD 方法 | LOCAL_VARIABLE 本地變量 |
@Retention元註解指定註解保留多久。默認值爲RetentionPolicy.CLASS.
其取值與含義如下:
SOURCE 不包括在CLASS文件中的註解
CLASS class文件中的註解,但是虛擬機將不會載入它們
RUNTIME class文件中的註解,並由虛擬機載入。可以通過反射API獲得它們
2.註解方法
在方法上使用註解。
@MethodLog(desc = "這是一個測試Action")
public String HelloWorld() {
Map result = new HashMap();
result.put("returnMessage", service.getInfo());
resultObj = JSONObject.fromObject(result);
return "resultObj";
}
3.使用反射API獲取註解
MethodLog log = method.getAnnotation(MethodLog.class);
String desc = log.desc();
其中method是使用反射API獲取的方法對象。
二、Spring AOP
方案1:
1.切面類AspectBean
package com.test.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AspectBean {
public void doAfter(JoinPoint jp) {
System.out.println("log Ending method: "
+ jp.getTarget().getClass().getName() + "."
+ jp.getSignature().getName());
}
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long time = System.currentTimeMillis();
Object retVal = pjp.proceed();
time = System.currentTimeMillis() - time;
System.out.println("process time: " + time + " ms");
return retVal;
}
public void doBefore(JoinPoint jp) {
System.out.println("log Begining method: "
+ jp.getTarget().getClass().getName() + "."
+ jp.getSignature().getName());
}
public void doThrowing(JoinPoint jp, Throwable ex) {
System.out.println("method " + jp.getTarget().getClass().getName()
+ "." + jp.getSignature().getName() + " throw exception");
System.out.println(ex.getStackTrace());
}
}
2.在applicationContex.xml中添加advice掃描路徑,將切面類添加爲bean
<context:component-scan base-package="com.test"
use-default-filters="false">
<context:include-filter type="regex"
expression="com.test.manager.*" />
<context:include-filter type="regex"
expression="com.test.advice.*" />
</context:component-scan>
3.在applicationContex.xml中添加切面配置
注意,爲防止action出錯必須添加proxy-target-class="true"
<aop:config proxy-target-class="true">
<aop:aspect id="TestAspect" ref="aspectBean">
<!--配置com.test.manager.action包下所有類或接口的所有方法 -->
<aop:pointcut id="businessService"
expression="execution(* com.test.manager.action.*.*(..))" />
<aop:before pointcut-ref="businessService" method="doBefore" />
<aop:after pointcut-ref="businessService" method="doAfter" />
<aop:around pointcut-ref="businessService" method="doAround" />
<aop:after-throwing pointcut-ref="businessService"
method="doThrowing" throwing="ex" />
</aop:aspect>
</aop:config>
4.struts.xml中添加相關配置以防止action中的field注入失敗
<!-- 允許spring來創建Action、Interceptror和Result,無此項時開啓AOP則注入失敗 -->
<constant name="struts.objectFactory.spring.autoWire.alwaysRespect"
value="true" />
5.在切面類中利用反射獲取MethodLog註解
若要在AspectBean中獲取註解信息,則在方法中添加如下代碼:
import java.lang.reflect.Method;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import com.test.common.MethodLog;
//....一些其他import代碼.....
public void doBefore(JoinPoint jp) {
Signature signature = jp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
<p> Method method = methodSignature.getMethod();</p> if(method.getAnnotation(MethodLog.class)!=null){
System.out.println(method.getAnnotation(MethodLog.class).desc());
}
}
方案2:
1.切面類
可以通過實現MethodInterceptor AfterReturningAdvice ThrowsAdvice MethodBeforeAdvice四個接口來寫切面類,他們提供了四個方法,擁有更便捷的參數。
通過靈活使用MethodInterceptor,可以只用它來實現我們需要的日誌功能。
package com.test.advice;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.test.common.MethodLog;
public class AroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation methodInterceptor) throws Throwable {
Method method = methodInterceptor.getMethod();
MethodLog log = method.getAnnotation(MethodLog.class);
if (log != null) {
String desc = log.desc();
System.out.println(desc);
}
Object obj = null;
try {
// ---methodInterceptor.proceed之前可以添加前置操作,相當於MethodBeforeAdvice
System.out.println("before");
// 目標方法執行
obj = methodInterceptor.proceed();
// ---methodInterceptor.proceed之後可以添加後續操作,相當於AfterReturningAdvice
System.out.println("after");
} catch (Exception e) {
// 在執行目標對象方法的過程中,如果發生異常,可以在catch中捕獲異常,相當於ThrowsAdvice
System.out.println("exception");
}
return obj;
}
}
2.在applicationContex.xml中添加advice掃描路徑,將切面類添加爲bean
<context:component-scan base-package="com.test"
use-default-filters="false">
<context:include-filter type="regex"
expression="com.test.manager.*" />
<context:include-filter type="regex"
expression="com.test.advice.*" />
</context:component-scan>
3.在applicationContext.xml添加配置
<aop:config proxy-target-class="true">
<aop:aspect id="TestAspect" ref="aspectBean">
<!-- 配置com.test.manager.action包下所有類或接口的所有方法 -->
<aop:pointcut id="businessService"
expression="execution(* com.test.manager.action..*.*(..))" />
<aop:before pointcut-ref="businessService" method="doBefore" />
<aop:after pointcut-ref="businessService" method="doAfter" />
<aop:around pointcut-ref="businessService" method="doAround" />
<aop:after-throwing pointcut-ref="businessService"
method="doThrowing" throwing="ex" />
</aop:aspect>
</aop:config>
4.struts.xml中添加相關配置以防止action中的field注入失敗
<!-- 允許spring來創建Action、Interceptror和Result,無此項時開啓AOP則注入失敗 -->
<constant name="struts.objectFactory.spring.autoWire.alwaysRespect"
value="true" />
附:pointcut expression表達式詳解
Pointcut可以有下列方式來定義或者通過and && or || 和!的方式進行組合:args()@args()execution()this()target()@target()within()@within()@annotation
excution
通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:方法的操作權限
ret-type-pattern:返回值
declaring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:參數名
throws-pattern:異常
其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值爲任意類型;方法名任意;參數不作限制的所有方法。
舉例說明:
任意公共方法的執行:execution(public * *(..))
任何一個以“set”開始的方法的執行:execution(* set*(..))
AccountService 接口的任意方法的執行:execution(* com.xyz.service.AccountService.*(..))
定義在service包裏的任意方法的執行:execution(* com.xyz.service.*.*(..))
定義在service包和所有子包裏的任意類的任意方法的執行:execution(* com.xyz.service..*.*(..))
定義在pointcutexp包和所有子包裏的JoinPointObjP2類的任意方法的執行:execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
其它例子:
pointcutexp包裏的任意類:within(com.test.spring.aop.pointcutexp.*)
pointcutexp包和所有子包裏的任意類:within(com.test.spring.aop.pointcutexp..*)
實現了Intf接口的所有類,如果Intf不是接口,限定Intf單個類:this(com.test.spring.aop.pointcutexp.Intf)
帶有@Transactional標註的所有類的任意方法:
@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)
帶有@Transactional標註的任意方法:@annotation(org.springframework.transaction.annotation.Transactional)
注: @within和@target針對類的註解,@annotation是針對方法的註解
參數帶有@Transactional標註的方法:@args(org.springframework.transaction.annotation.Transactional)
參數爲String類型(運行時決定)的方法:args(String)
可以通過args來綁定參數,這樣就可以在通知(Advice)中訪問具體參數了。
<aop:config>
<aop:aspect id="TestAspect" ref="aspectBean">
<aop:pointcut id="businessService"
expression="execution(* com.test.manager.action.*.*(String,..)) and args(msg,..)" />
<aop:after pointcut-ref="businessService" method="doAfter"/>
</aop:aspect>
</aop:config>
TestAspect的doAfter方法中就可以訪問msg參數,但這樣以來AService中的barA()和BServiceImpl中的barB()就不再是連接點,因爲execution(* com.spring.service.*.*(String,..))只配置第一個參數爲String類型的方法。其中,doAfter方法定義如下:
public void doAfter(JoinPoint jp,String msg)