Spring AOP详解
2017-07-07 12:42
387 查看
Spring AOP简介
AOP(Aspect Oriented Programming)把软件系统分为两个部分:核心关注点和横切关注点核心关注点:业务处理的主要流程
横切关注点:特点是经常发生在核心关注点的多处,而各处基本相似
典型应用场景:权限认证、日志
需求
1.实现一个计算器接口,需要有计算加减乘除的方法2.为了便于日后核查问题,需要在日志总记录每次计算的入参及计算结果
基本接口
public interface ArithmeticCalculator { int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j); }
基本接口的实现
public class ArithmeticCalculatorImpl implements ArithmeticCalculator { public int add(int i, int j) { int result = i + j; return result; } public int sub(int i, int j) { int result = i - j; return result; } public int mul(int i, int j) { int result = i * j; return result; } public int div(int i, int j) { int result = i / j; return result; } }
版本1--2B青年写法
手动撸代码,下下策public class ArithmeticCalculatorImpl implements ArithmeticCalculator { public int add(int i, int j) { System.out.println("The method add begins with [" + i + ", " + j + "]"); int result = i + j; System.out.println("The method add end with " + result); return result; } public int sub(int i, int j) { System.out.println("The method sub begins with [" + i + ", " + j + "]"); int result = i - j; System.out.println("The method sub end with " + result); return result; } public int mul(int i, int j) { System.out.println("The method mul begins with [" + i + ", " + j + "]"); int result = i * j; System.out.println("The method mul end with " + result); return result; } public int div(int i, int j) { System.out.println("The method div begins with [" + i + ", " + j + "]"); int result = i / j; System.out.println("The method div end with " + result); return result; } }
版本2--文艺青年写法(代理模式)
基于代理模式的实现增加了一个代理类,在代理类中增加了前置方法和后置方法,不失为一种轻量级解决方案。public class ArithmeticCaculatorProxy implements ArithmeticCalculator { //要代理的对象 private ArithmeticCalculator arithmeticCalculator; public ArithmeticCaculatorProxy(ArithmeticCalculator arithmeticCalculator){ this.arithmeticCalculator = arithmeticCalculator; } private void before(int ... args) { System.out.println("The method add begins with " + Arrays.toString(args)); } private void after(int ... args) { System.out.println("The method add ends with " + Arrays.toString(args)); } public int add(int i, int j) { before(i, j); int result = arithmeticCalculator.add(i, j); after(result); return result; } public int sub(int i, int j) { before(i, j); int result = arithmeticCalculator.sub(i, j); after(result); return result; } public int mul(int i, int j) { before(i, j); int result = arithmeticCalculator.mul(i, j); after(result); return result; } public int div(int i, int j) { before(i, j); int result = arithmeticCalculator.div(i, j); after(result); return result; } } public class Main { public static void main(String[] args) { ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl(); ArithmeticCaculatorProxy proxy = new ArithmeticCaculatorProxy(arithmeticCalculator); proxy.add(1, 5); System.out.println("----------"); proxy.sub(5, 3); System.out.println("----------"); proxy.mul(3, 7); System.out.println("----------"); proxy.div(9, 3); } }
版本3--装B青年写法(动态代理)
这种方式简单粗暴,直接对代理对象下手。public class ArithmeticCaculatorProxy { //要代理的对象 private ArithmeticCalculator target; public ArithmeticCaculatorProxy(ArithmeticCalculator target){ this.target = target; } public ArithmeticCalculator getProxy() { ArithmeticCalculator proxy = null; ClassLoader classLoader = target.getClass().getClassLoader(); Class[] interfaces = new Class[]{ArithmeticCalculator.class}; InvocationHandler handler = new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); System.out.println("The method " + methodName + " begins with" + Arrays.asList(args)); System.out.println("Invoke..."); //执行方法 Object result = method.invoke(target, args); //日志 System.out.println("The method " + methodName + " ends with " + result); return result; } }; proxy = (ArithmeticCalculator) Proxy.newProxyInstance(classLoader, interfaces, handler); return proxy; } } public class Main { public static void main(String[] args) { ArithmeticCalculator target = new ArithmeticCalculatorImpl(); ArithmeticCalculator proxy = new ArithmeticCaculatorProxy(target).getProxy(); proxy.add(1, 5); System.out.println("----------"); proxy.sub(5, 3); System.out.println("----------"); proxy.mul(3, 7); System.out.println("----------"); proxy.div(9, 3); } }
版本4--武林高手写法之一(Spring AOP注解)
package com.umgsai.aop.level4; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; /** * Created by shangyidong on 17/6/29. */ //指定切面的优先级,当有多个切面时,数值越小优先级越高 @Order(1) //把这个类声明为一个切面:需要把该类放入到IOC容器中。再声明为一个切面. @Aspect @Component public class LoggingAspect { /** * 声明切入点表达式,一般在该方法中不再添加其他代码。 * 使用@Pointcut来声明切入点表达式。 * 后面的通知直接使用方法名来引用当前的切入点表达式。 */ @Pointcut("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))") public void declareJoinPointExpression() {} /** *前置通知,在目标方法开始之前执行。 *@Before("execution(public int com.spring.aop.impl.ArithmeticCalculator.add(int, int))")这样写可以指定特定的方法。 * @param joinpoint */ @Before("declareJoinPointExpression()") //这里使用切入点表达式即可。后面的可以都改成切入点表达式。如果这个切入点表达式在别的包中,在前面加上包名和类名即可。 public void beforeMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println("前置通知:The method "+ methodName +" begins with " + args); } /** *后置通知,在目标方法执行之后开始执行,无论目标方法是否抛出异常。 *在后置通知中不能访问目标方法执行的结果。 * @param joinpoint */ @After("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(int, int))") public void afterMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); //List<Object>args = Arrays.asList(joinpoint.getArgs()); 后置通知方法中可以获取到参数 System.out.println("后置通知:The method "+ methodName +" ends "); } /** *返回通知,在方法正常结束之后执行。 *可以访问到方法的返回值。 * @param joinpoint * @param result 目标方法的返回值 */ @AfterReturning(value="execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))", returning="result") public void afterReturnning(JoinPoint joinpoint, Object result) { String methodName = joinpoint.getSignature().getName(); System.out.println("返回通知:The method "+ methodName +" ends with " + result); } /** *异常通知。目标方法出现异常的时候执行,可以访问到异常对象,可以指定在出现特定异常时才执行。 *假如把参数写成NullPointerException则只在出现空指针异常的时候执行。 * @param joinpoint * @param e */ @AfterThrowing(value="execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))", throwing="e") public void afterThrowing(JoinPoint joinpoint, Exception e) { String methodName = joinpoint.getSignature().getName(); System.out.println("异常通知:The method "+ methodName +" occurs exception " + e); } /** * 环绕通知类似于动态代理的全过程,ProceedingJoinPoint类型的参数可以决定是否执行目标方法。 * @param point 环绕通知需要携带ProceedingJoinPoint类型的参数。 * @return 目标方法的返回值。必须有返回值。 */ /*不常用 @Around("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))") public Object aroundMethod(ProceedingJoinPoint point) { Object result = null; String methodName = point.getSignature().getName(); try { //前置通知 System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs())); //执行目标方法 result = point.proceed(); //翻译通知 System.out.println("The method "+ methodName +" ends with " + result); } catch (Throwable e) { //异常通知 System.out.println("The method "+ methodName +" occurs exception " + e); throw new RuntimeException(e); } //后置通知 System.out.println("The method "+ methodName +" ends"); return result; } */ } public class Main { public static void main(String[] args) { //创建spring IOC容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-level4.xml"); //从IOC容器中获取bean实例 ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class); int result = arithmeticCalculator.add(4, 6); System.out.println(result); result = arithmeticCalculator.sub(4, 6); System.out.println(result); result = arithmeticCalculator.mul(4, 6); System.out.println(result); result = arithmeticCalculator.div(4, 0); System.out.println(result); } }
此时需要在Spring的配置文件中加入以下注解
<!-- 使AspectJ注解起作用:自动为匹配的类生产代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
Spring AOP注解参数详解
@Aspect 声明切面类,放入IOC容器中
@Order 指定切面的优先级,当有多个切面时,数值越小优先级越高
@Pointcut 声明切入点
@Before 前置通知
@After 后置通知
@AfterReturning 返回通知
@AfterThrowing 异常通知
@Around 环绕通知
版本5--武林高手写法之二(Spring AOP配置)
@Component("loggingAspect") public class LoggingAspect { public void beforeMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object> args = Arrays.asList(joinpoint.getArgs()); System.out.println("前置通知:The method "+ methodName +" begins with " + args); } public void afterMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); //List<Object>args = Arrays.asList(joinpoint.getArgs()); 后置通知方法中可以获取到参数 System.out.println("后置通知:The method "+ methodName +" ends "); } public void afterReturning(JoinPoint joinpoint, Object result) { String methodName = joinpoint.getSignature().getName(); System.out.println("返回通知:The method "+ methodName +" ends with " + result); } public void afterThrowing(JoinPoint joinpoint, Exception e) { String methodName = joinpoint.getSignature().getName(); System.out.println("异常通知:The method "+ methodName +" occurs exception " + e); } public Object aroundMethod(ProceedingJoinPoint point) { Object result = null; String methodName = point.getSignature().getName(); try { //前置通知 System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs())); //执行目标方法 result = point.proceed(); //翻译通知 System.out.println("The method "+ methodName +" ends with " + result); } catch (Throwable e) { //异常通知 System.out.println("The method "+ methodName +" occurs exception " + e); throw new RuntimeException(e); } //后置通知 System.out.println("The method "+ methodName +" ends"); return result; } } @Component("validationAspect") public class ValidationAspect { public void validateArgs(JoinPoint joinPoint) { System.out.println("validate:" + Arrays.asList(joinPoint.getArgs())); } } public class Main { public static void main(String[] args) { //创建spring IOC容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-level5.xml"); //从IOC容器中获取bean实例 ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class); int result = arithmeticCalculator.add(4, 6); System.out.println(result); result = arithmeticCalculator.sub(4, 6); System.out.println(result); result = arithmeticCalculator.mul(4, 6); System.out.println(result); //result = arithmeticCalculator.div(4, 0); //System.out.println(result); } }
xml配置文件如下
<!-- 配置AOP --> <aop:config> <!-- 配置切点表达式 --> <aop:pointcut expression="execution(* com.umgsai.aop.level5.ArithmeticCalculator.*(..))" id="pointcut"/> <!-- 配置切面及通知,使用order指定优先级 --> <aop:aspect ref="loggingAspect" order="1"> <!-- 环绕通知 --> <!-- <aop:around method="aroundMethod" pointcut-ref="pointcut"/> --> <!-- 前置通知 --> <aop:before method="beforeMethod" pointcut-ref="pointcut"/> <!-- 后置通知 --> <aop:after method="afterMethod" pointcut-ref="pointcut"/> <!-- 异常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/> <!-- 返回通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/> </aop:aspect> <aop:aspect ref="validationAspect" order="2"> <!-- 前置通知 --> <aop:before method="validateArgs" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
这里有两个切面类,通过配置文件中的order即可控制优先级.
项目源码: http://git.oschina.net/umgsai/spring-aop
相关文章推荐
- Spring aop学习详解
- Spring AOP 的proxy详解
- spring AOP详解二
- Spring AOP 详解
- spring aop表达式详解
- Spring AOP三种拦截方式举例详解
- spring AOP详解四
- Spring AOP 详解
- Spring AOP的代理模式详解
- 详解Spring AOP
- Spring AOP 详解
- Spring AOP 详解
- spring AOP详解一
- Spring AOP 详解
- Spring AOP 详解
- Spring AOP 详解
- Spring AOP详解
- Spring AOP 详解 【转】
- Spring AOP 详解
- Spring AOP 详解(转载)