引言:java
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。算法
在Spring AOP中业务逻辑仅仅只关注业务自己,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,经过对这些行为的分离,咱们但愿能够将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。spring
相关注解介绍以下:编程
@Aspect:做用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每一个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。能够将Pointcut中的方法看做是一个被Advice引用的助记符,由于表达式不直观,所以咱们能够经过方法签名的方式为 此表达式命名。所以Pointcut中的方法只须要方法签名,而不须要在方法体内编写实际代码。
@Around:环绕加强,至关于MethodInterceptor
@AfterReturning:后置加强,至关于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置加强方法,至关于BeforeAdvice的功能,类似功能的还有
@AfterThrowing:异常抛出加强,至关于ThrowsAdvice
一:引入相关依赖api
二:Spring的配置文件 applicationContext.xml 中引入context、aop对应的命名空间;配置自动扫描的包,同时使切面类中相关方法中的注解生效,需自动地为匹配到的方法所在的类生成代理对象。安全
三、建立简单计算器的接口ArithmeticCalculator.java及实现类ArithmeticCalculatorImpl.javaapp
package com.svse.aop; 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); }
package com.svse.aop; import org.springframework.stereotype.Component; //将实现类加入Spring的IOC容器进行管理 @Component("arithmeticCalculator") public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @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; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; } }
四、如今想在实现类中的每一个方法执行前、后、以及是否发生异常等信息打印出来,须要把日志信息抽取出来,写到对应的切面的类中 LoggingAspect.java 中
要想把一个类变成切面类,须要两步,
① 在类上使用 @Component 注解 把切面类加入到IOC容器中
② 在类上使用 @Aspect 注解 使之成为切面类
框架
package com.svse.aop; import java.util.Arrays; 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.springframework.stereotype.Component; import com.tencentcloudapi.vod.v20180717.VodClient; /** * 日志切面 * * @author zhaoshuiqing<br> * @date 2019年6月14日 下午3:03:29 */ @Component @Aspect public class LoggingAspect { //如今想在实现类中的每一个方法执行前、后、以及是否发生异常等信息打印出来,须要把日志信息抽取出来,写到对应的切面的类中 LoggingAspect.java 中 //要想把一个类变成切面类,须要两步, //① 在类上使用 @Component 注解 把切面类加入到IOC容器中 //② 在类上使用 @Aspect 注解 使之成为切面类 /** * 前置通知:目标方法执行以前执行如下方法体的内容 * @param jp */ @Before("execution(* com.svse.aop.*.*(..))") public void beforeMethod(JoinPoint jp){ String methodName =jp.getSignature().getName(); System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); } /** * 返回通知:目标方法正常执行完毕时执行如下代码 * @param jp * @param result */ @AfterReturning(value="execution(* com.svse.aop.*.*(..))",returning="result") public void afterReturningMethod(JoinPoint jp, Object result){ String methodName =jp.getSignature().getName(); System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】"); } /** * 后置通知:目标方法执行以后执行如下方法体的内容,无论是否发生异常。 * @param jp */ @After("execution(* com.svse.aop.*.*(..))") public void afterMethod(JoinPoint jp){ System.out.println("【后置通知】this is a afterMethod advice..."); } /** * 异常通知:目标方法发生异常的时候执行如下代码 */ @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e") public void afterThorwingMethod(JoinPoint jp, NullPointerException e){ String methodName = jp.getSignature().getName(); System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e); } /** * 环绕通知:目标方法执行先后分别执行一些代码,发生异常的时候执行另一些代码 * @return */ /*@Around(value="execution(* com.svse.aop.*.*(..))") public Object aroundMethod(ProceedingJoinPoint jp){ String methodName = jp.getSignature().getName(); Object result = null; try { System.out.println("【环绕通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); //执行目标方法 result = jp.proceed(); System.out.println("【环绕通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result); } catch (Throwable e) { System.out.println("【环绕通知中的--->异常通知】:the method 【" + methodName + "】 occurs exception " + e); } System.out.println("【环绕通知中的--->后置通知】:-----------------end.----------------------"); return result; }*/ }
五、编写MainTest方法进行测试ide
package com.svse.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainTest { public static void main(String[] args) { //ClassPathXmlApplicationContext默认是加载src目录下的xml文件 ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); ArithmeticCalculator arithmeticCalculator =(ArithmeticCalculator) ctx.getBean("arithmeticCalculator"); System.out.println(arithmeticCalculator.getClass()); int result = arithmeticCalculator.add(3, 5); System.out.println("result: " + result); result = arithmeticCalculator.div(5, 0); System.out.println("result: " + result); } }
运行结果:函数式编程
把其它代码都注释掉,把环绕通知的方法释放出来,测试结果以下:
至此AOP注解面向切面记录日志就写完了!