开发配置:Eclipse + jdk 1.8 + Tomcat 7.0
Spring AOP自身也有一个实现aop的框架,但这里使用的是AspectJ来实现aop。
使用AspectJ来实现aop有两种方法,一种是注解的方式,另一种是xml的方式,这里说的是基于注解的方式。
AOP有五种通知,分别是
@Befor:前置通知,在方法执行之前执行 @After:后置通知,在方法执行之后执行 @AfterRunning:返回通知,在方法返回结果之后执行 @AfterThrowing:异常通知,在方法抛出异常之后执行 @Around:环绕通知,围绕着方法执行
项目结构:
第一个红框是源码,第二个红框是spring的配置文件,第三个红框是jar包。
一、需要的jar包:
这三个是AspectJ的jar包:aopalliance-1.0.jar
aspectjweaver-1.9.0.jar
spring-aspects-4.3.8.RELEASE.jar
这些事spring的核心包:
commons-logging-1.2.jar
spring-aop-4.3.8.RELEASE.jar
spring-beans-4.3.8.RELEASE.jar
spring-context-4.3.8.RELEASE.jar
spring-core-4.3.8.RELEASE.jar
spring-expression-4.3.8.RELEASE.jar
二、bean.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> <!-- 扫描com.qw.apo.impl包及其子包下面的所有的类的注解 --> <context:component-scan base-package="com.qw.aop.impl"/> <!-- 使 AspjectJ 的注解其作用:自动为匹配的类生成代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
三、接口类、接口实现类、切面类、测试类
接口类:AtithmeticCalculator
package com.qw.aop.impl; public interface AtithmeticCalculator { int add(int i,int j);
int sub(int i,int j);}
接口实现类:
package com.qw.aop.impl; import org.springframework.stereotype.Component; @Component public class AtithmeticCalculatorImpl implements AtithmeticCalculator { @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } }
切面类:
package com.qw.aop.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; /** * 申明这个类是切面,需要两个注解,切面可以有多个通知 * @Component:把该类放入到ioc容器中 * @Aspect:申明该类为切面 * @Order :指定切面的优先级,数字越小,优先级越高。 * @author 12873 * */ @Order(1) @Aspect @Component public class LoggingAspect { /** * 定义一个方法,用于声明切入点表达式,一般的,该方法中再不需要填入其他的代码 * 这个就相当于定义了一个公共的常亮,其他的切面的类也可以使用,只要把类名加上即可,如果不在同一个包,报名也要加上。 * 如:同包的切面类引用:LoggingAspect.declareJointPointExpression() */ @Pointcut("execution(public int com.qw.aop.impl.AtithmeticCalculator.add(int, int))") public void declareJointPointExpression(){} /** * @Before:申明该方法是前置通知:在目标方法开始之前执行 * execution:执行条件,当有方法匹配这里面的条件之后,就执行该方法 * JoinPoint:获取执行方法中的各种参数 */ @Before("declareJointPointExpression()")//这个里面是引用了上面的那个方法 public void beforeMethod(JoinPoint joinPoint){ //执行的方法名 String methodName = joinPoint.getSignature().getName(); //执行的方法的参数 List<Object> args = Arrays.asList(joinPoint.getArgs()); //执行的方法所在的类的名称 String className = joinPoint.getTarget().getClass().getName(); System.out.println("前置通知,执行的方法名:"+ methodName + ",方法的参数:"+ args + ",方法所在的类名:" + className); } //后置通知:在目标方法执行后(无论是否发生异常),执行的通知 //在后置通知中还不能访问目标方法执行的结果 @After("execution(public int com.qw.aop.impl.AtithmeticCalculator.add(int, int))") public void afterMethod(JoinPoint joinPoint){ //执行的方法名 String methodName = joinPoint.getSignature().getName(); //执行的方法的参数 List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("后置通知,执行的方法名:"+ methodName + ",方法的参数:"+ args); } /** * 返回通知: * 1、在方法正常结束后执行的通知 * 2、返回通知是可以访问到方法的返回值的 * @param joinPoint */ @AfterReturning(value="execution(public int com.qw.aop.impl.AtithmeticCalculator.add(int, int))",returning="result") public void afterRunning(JoinPoint joinPoint,Object result){ //执行的方法名 String methodName = joinPoint.getSignature().getName(); //执行的方法的参数 List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("返回通知,执行的方法名:"+ methodName + ",方法的参数:"+ args +",返回值是:"+result); } /** * 异常通知,可以访问到目标方法的异常对象,并且可以指定出现特定异常时才执行该通知代码。 * @param joinPoint */ @AfterThrowing(value="execution(public int com.qw.aop.impl.AtithmeticCalculator.*(int, int))",throwing="ex") public void afterThrowing(JoinPoint joinPoint,Exception ex){ //执行的方法名 String methodName = joinPoint.getSignature().getName(); //执行的方法的参数 List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("异常通知,执行的方法名:"+ methodName + ",方法的参数:"+ args + ",异常:"+ex); } /** * 环绕通知 * 环绕通知需要携带ProceedingJoinPoint 类型参数 * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint 类型的参数可以决定是否执行目标方法 * 且环绕通知必须有返回值,返回值即为目标方法的返回值 */ @Around("execution(public int com.qw.aop.impl.AtithmeticCalculator.*(int, int))") public Object around(ProceedingJoinPoint pjd){ Object result = null; try { //前置通知 System.out.println("前置通知:"+pjd.getSignature().getName()); //执行目标方法 result = pjd.proceed(); //返回通知 System.out.println("返回通知:"+pjd.getSignature().getName()); } catch (Throwable e) { //异常通知 System.out.println("异常通知:"+pjd.getSignature().getName() + ",异常:" + e); throw new RuntimeException(e); } //后置通知 System.out.println("后置通知:"+pjd.getSignature().getName()); return result; } }
测试类:
package com.qw.aop.impl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); AtithmeticCalculator calculator = context.getBean(AtithmeticCalculator.class); int result = calculator.add(1, 4); System.out.println("result:"+result); result = calculator.sub(1, 4); System.out.println("result:"+result); } }
执行结果:
四月 17, 2018 11:11:05 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3f91beef: startup date [Tue Apr 17 23:11:05 CST 2018]; root of context hierarchy 四月 17, 2018 11:11:05 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [bean.xml] 方法执行之前执行,执行的方法名:add,方法的参数:[1, 4] result:5 result:-3
第一个add的方法执行了切面类里面的前置通知方法了,后面的sub方法没有执行,是因为add的方法匹配了切面类里面的前置通知方法,而sub方法没有匹配到。
源码资源:https://download.csdn.net/download/java_xuetu/10358576
2018-04-18补充,源码里面没有的一点小补充,一点补充:
1、execution切入点表达式
@Before("execution(public int com.qw.aop.impl.AtithmeticCalculator.add(int, int))")
其中execution是切入点表达式。
public int com.qw.aop.impl.AtithmeticCalculator.add(int, int) 是表达式的内容
1) public:匹配所有的目标类的public方法,不写则匹配所有的访问权限
2) int:方法的返回值类型,* 代表所有的类型
3) com.qw.aop.impl.AtithmeticCalculator.add:匹配这个包下面的这个类的add方法,可以在其中任意位置替换成 * ,如:com.qw.aop.impl.AtithmeticCalculator.* :匹配这个包下面这个类的所有方法。
4) (int, int) :匹配的参数,必须是两个int的参数才行,如果想要匹配任意参数,可以写成 (..)
比如:execution(public int com.qw.aop.impl.AtithmeticCalculator.*(int, int))
匹配AtithmeticCalculator类下面的任意参数为两个int的方法。