一:采用AOP的原因
代码混乱:越来越多的非业务需求(日志和验证等)加入后, 原有的业务方法急剧膨胀. 每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点.
代码分散: 以日志需求为例, 只是为了满足这个单一需求, 就不得不在多个模块(方法)里多次重复相同的日志代码. 如果日志需求发生变化, 必须修改所有模块.
二:基于aspectj注解声明切面
实例:定义一个bean,为其添加日志
(1)定义一个bean
import org.springframework.stereotype.Component;
@Component
public class Student {
private String name;
public void setName(String name) {
this.name = name;
}
public int study(int i,int j) {
// TODO Auto-generated method stub
System.out.println("good study");
return i/j;
}
}
(2)创建切面及通知
package com.wh.spring.impl;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
//把这个类声明为一个切面,需要把该类放入到IOC容器中,再声明为一个切面
//使用@Order指定切面的优先级,数值越小,优先级越高
@Aspect
@Order(1)
@Component
public class LoggingAspect {
//定义一个方法,用于声明切入点表达式,一般该方法不需要填入其他代码
//使用@Pointcut声明切入点表达式
//其他通知引用方法名引入该切入点
@Pointcut("execution(* com.wh.spring.aop.Student.*(..))")
public void declareJointPointExpression() {
}
//声明该方法是一个前置通知
//spring..表示com.wh.spring包及其子包
@Before("declareJointPointExpression()")
public void beforeMethord(JoinPoint joinPoint) {
String methordName=joinPoint.getSignature().getName();
List<Object> args=Arrays.asList(joinPoint.getArgs());
System.out.println("the methord before "+methordName+" "+args);
}
//后置通知:在目标方法执行后(无论是否发生异常),执行的通知
//后置通知中不能访问目标方法的执行结果
@After("declareJointPointExpression()")
public void afterMethord(JoinPoint joinPoint) {
String methordName=joinPoint.getSignature().getName();
System.out.println("The methord "+methordName+" ends");
}
//返回通知,方法正常结束后执行的代码
//返回通知是可以访问到方法的返回值
@AfterReturning(value="declareJointPointExpression()",
returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
String methordName=joinPoint.getSignature().getName();
System.out.println("The methord "+methordName+" ends with "+result);
}
//在目标方法出现异常时会执行该代码,可以访问到异常对象,且可以指定在出现指定异常时才执行通知代码
//指定ex的类型
@AfterThrowing(value="declareJointPointExpression()",
throwing="ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex) {
String methordName=joinPoint.getSignature().getName();
System.out.println("The methord "+methordName+" throw "+ex);
}
//环绕通知需要携带ProceedingJoinPoint类型的参数
//环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法,且环绕通知必须有返回值
//返回值即为目标方法的返回值
//不常用环绕通知
@Around("declareJointPointExpression()")
public Object aroundMethord(ProceedingJoinPoint pjd) {
System.out.println("aroundMethord");
Object result=null;
String methordName=pjd.getSignature().getName();
//执行目标方法
try {
//前置通知
System.out.println(methordName+" begins with "+Arrays.asList(pjd.getArgs()));
result=pjd.proceed();
//返回通知
System.out.println(methordName+" ends with "+result);
} catch (Throwable e) {
// 异常通知
System.out.println("发生异常:"+e);
}
// 后置通知
System.out.println("the methord "+methordName+" ends with around");
return 100;
}
}
(3)使aspectj注解起作用,自动为匹配的类生成代理对象
在spring配置文件中加入
<!-- 使aspectj注解起作用,自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
三:基于配置文件的AOP
(1)配置切面bean
<!-- 配置切面的bean-->
<bean id="loggingAspect" class="com.wh.spring.xml.LoggingAspect"></bean>
(2)配置切点,切面及通知
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* com.wh.spring.xml.Student.*(..))" id="pointcut"/>
<!-- 配置切面及通知 -->
<aop:aspect ref="loggingAspect" order="0">
<aop:before method="beforeMethord" pointcut-ref="pointcut"/>
<aop:after method="afterMethord" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
<aop:around method="aroundMethord" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
四:其他
(1)切面的顺序由@Order(1)或order="0"指定,数字越小,优先级越高
(2)切点的重用
//定义一个方法,用于声明切入点表达式,一般该方法不需要填入其他代码
//使用@Pointcut声明切入点表达式
//其他通知引用方法名引入该切入点
@Pointcut("execution(* com.wh.spring.aop.Student.*(..))")
public void declareJointPointExpression() {
}