使用代理对象实现AOP虽然可以满足需求,但是较为复杂,而Spring提供一种简单的实现AOP的方法AspectJ
同样的计算器的DEMO
首先配置applicationContext.xml
<!-- 配置自动扫描的包 --> <context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan> <!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在计算器实现类ArithmeticCalculatorImpl上加注解@Component("arithmeticCalculator")
创建类LoggingAspect作为计算器的一个切面,添加注解@Aspect和@Component
在LoggingAspect里添加一个方法beforeMethod即前置通知
/** * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码 */@Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); }
切入点表达式execution(public <返回类型>int <接口方法,*为通配符(..)为参数,代表这个接口的每个实现类的每个方法>com.atguigu.spring.aop.ArithmeticCalculator.*(..))这里也可以指定某一个方法,比如add(..)切入点表达式还可以使用|| && !这些连接符execution(public * *.add(..))||execution(public * *.div(..))
连接点参数类型JoinPoint可以访问更多细节比如方法名和参数对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点. 完成这些就可以运行main方法
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator"); System.out.println(arithmeticCalculator.getClass().getName()); int result = arithmeticCalculator.add(1, 2); System.out.println("result:" + result); result = arithmeticCalculator.div(1000, 10); System.out.println("result:" + result);
类似的可以添加其他通知
AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
日志切面详细代码
/** * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高 */ @Order(2) @Aspect @Component public class LoggingAspect { /** * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. * 使用 @Pointcut 来声明切入点表达式. * 后面的其他通知直接使用方法名来引用当前的切入点表达式. */ @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))") public void declareJointPointExpression(){} /** * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码 */ @Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); Object [] args = joinPoint.getArgs(); System.out.println("The method " + methodName + " begins with " + Arrays.asList(args)); } /** * 在方法执行之后执行的代码. 无论该方法是否出现异常 */ @After("declareJointPointExpression()") public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends"); } /** * 在方法法正常结束受执行的代码 * 返回通知是可以访问到方法的返回值的! */ @AfterReturning(value="declareJointPointExpression()", returning="result") public void afterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " ends with " + result); } /** * 在目标方法出现异常时会执行的代码. * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码 */ @AfterThrowing(value="declareJointPointExpression()", throwing="e") public void afterThrowing(JoinPoint joinPoint, Exception e){ String methodName = joinPoint.getSignature().getName(); System.out.println("The method " + methodName + " occurs excetion:" + e); } }
环绕通知示例代码
Spring4.0支持@order注解当有多个切面的时候可以使用@order来为切面的执行顺序进行排序
比如添加一个验证切面添加注解@Order(1),日志切面添加注解@Order(2)
时间: 2024-10-26 16:57:09