一、注解
注解是插入到源码中用于某种工具处理的标签。这里我们使用将要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)