AOP(Aspect Oriented Programming 面向切面编程)是一种通过运行期动态代理实现代码复用的机制,是对传统OOP(Object Oriented Programming,面向对象编程 )的补充。
那么,如果在Spring 中进行AOP的配置与增强实现呢???
流程
1.添加jar包,Build Path
2.创建Spring配置文件application.xml.
3.代码框架
4.在application.xml文件配置
扫描目录:
<context:component-scan base-package="com.jd"></context:component-scan>
配置文件:
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
配置作用一会再说。proxy-target-class是动态代理方式,false是JDK代理,true是CGLIB代理,默认为JDK代理。
5.类之间关系
Methods是待增强的目标类
import org.springframework.stereotype.Service;
@Service
public class Methods implements IMethods {
public int add(int x,int y) {
System.out.println("方法内容");
return x+y;
}
}
IMethods是Methods实现的接口。
public interface IMethods {
public int add(int x,int y);
}
6.增强类MethodsAspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class MethodsAspect {
@Before("execution(int *(int,int))")
public void before(JoinPoint joinPoint) {
Object target = joinPoint.getTarget(); //获取代理的对象
System.out.println(target+"target!!!before");
}
}
此类中写的是对目标类方法的增加,需添加注释 @Aspect。这里就可以结合前面application.xml中的配置信息一起说了。当容器扫描到这个带有@Aspect注解的类时,会将@Before等注解括号中的切点表达式与其他扫描过的类中的方法进行匹配,如果匹配成功,动态代理会生成相应的代理类,调用目标类中的方式时使用代理类来对目标类进行增强。
7.测试代码
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
// IMethods methods = applicationContext.getBean(Methods.class); // !!! 有风险
IMethods methods = applicationContext.getBean(IMethods.class); // 正确
}
代码中提示的那一行容易出错,当动态代理为JDK时,创建代理对象的代理类与增强的目标类Methods 已无关系,不能通过applicationContext.getBean(Methods.class)这种方式获取到代理类。因为代理类同样继承于接口,可以通过这种方式获取到代理类对象,但是仍需注意,如果多个类都实现了这个接口,那么通过这个接口就不能唯一定位到代理类,同样会出错,还是不太懂的可以看看我的上一篇博客的末尾。注意:即使proxy-target-class设置为false,如果目标类没有声明接口,则Spring将自动使用CGLib生成代理对象。
增强
下面着重对增强类进行研究。
到底怎么增强?通过何种方式?如何进行匹配?
增强方式
共5种增强方式,用不同的注解来区分
1.前置增强 @Before
2.后置增强 @After
3.返回增强 @AfterReturning
4.异常增强 @AfterThrowing
5.环绕增强 @Around
1.前置增强
在方法执行前执行的代码内容。
@Before("execution(int *(int,int))")
public void before(JoinPoint joinPoint) {
Object target = joinPoint.getTarget(); //获取目标对象
System.out.println(target+"target!!!@Before");
System.out.println(joinPoint.getArgs()); //获取参数列表
System.out.println(joinPoint.getSignature()); //增强的方法
}
传递来的JoinPoint对象在多个增强方法中都有,可以获取如代码等信息。
2.后置增强
在方法执行后执行的代码内容。
@After("execution(int *(int,int))")
public void after(JoinPoint joinPoint) {
Object target = joinPoint.getTarget();
System.out.println(target+"target!!!@After");
}
与前置增强区别不大。
3.返回增强
在方法执行返回值之后执行
@AfterReturning(value="execution(int *(int,int))",returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
Object target = joinPoint.getTarget();
System.out.println(target+"target!!!@AfterReturning");
System.out.println(result); //返回值
}
注解需配置returning属性,值为方法中的第二个参数值的名字。通过此参数可以获取到返回值。若方法抛出异常则无法执行返回增强。
4.异常增强
在方法抛出异常之后执行
@AfterThrowing(value="execution(int *(int,int))",throwing="e")
public void afterThrowing(JoinPoint joinPoint,Exception e) {
Object target = joinPoint.getTarget();
System.out.println(target+"target!!!@AfterThrowing");
System.out.println(e.getMessage());
}
注解需配置throwing属性,值为方法中的第二个参数值的名字。通过此参数可以获取到抛出的异常。可通过配置第二个参数类型来实现当抛出参数异常类的子类时才会执行异常增强,否则不执行。注意:如果方法抛出异常被try-catch,则依然不执行
前四种增强执行顺序
try {
try {
doBefore();// @Before注解所修饰的方法
method.invoke();// 执行目标对象内的方法
} finally {
doAfter();// @After注解所修饰的方法
}
doAfterReturning();// @AfterReturning注解所修饰的方法
} catch (Exception e) {
doAfterThrowing();// @AfterThrowing注解所修饰的方法
}
5.环绕增强
对前四个增强的组合
@Around("execution(int *(int,int))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object target = proceedingJoinPoint.getTarget(); //获取代理的对象
System.out.println(proceedingJoinPoint.getArgs()); //获取参数列表
System.out.println(proceedingJoinPoint.getSignature()); //增强的方法
Object result = null; //存储返回的结果
try {
try {
System.out.println(target+"target!!!@Before");// 前置增强
result = proceedingJoinPoint.proceed();// 执行目标对象内的方法
} finally {
System.out.println(target+"target!!!@After");// 后置增强
}
System.out.println(target+"target!!!@AfterReturning");// 返回增强
} catch (Exception e) {
System.out.println(target+"target!!!@AfterThrowing");// 异常增强
}
return result; //返回结果
}
方法的参数与前面四个均不同,且只有这个具有返回值,其它返回值均为void。但是实现的功能是差不多的,各种增强在代码中的不同位置执行,与前四种增强的执行流程基本一样。result的值即为前面返回增强需要的参数值。调整catch括号中的类可以实现前面异常增强的参数功能。注意的地方在于:proceedingJoinPoint.proceed()要将返回的结果保存并最终ruturn,这个是方法返回的结果。