3.1、场景模拟
3.1.1、声明接口
声明计算器接口Calculator,包含加减乘除的抽象方法
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
3.1.2 、创建实体类
public class CalculatorPureImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部 result = " + result);
return result;
}
}
3.1.3、改变需求
为了提高系统安全,现需要加入日志功能,用户的每一步操作,都需要计入日志里。
传统做法:
public class CalculatorLogImpl implements Calculator {
@Override
public int add(int i, int j) {
System.out.println("[日志] 用户xxx操作 add 方法开始了,参数是:" + i + "," + j);
int result = i + j;
System.out.println("方法内部 result = " + result);
System.out.println("[日志] add 方法结束了,结果是:" + result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("[日志]用户xxx操作 sub 方法开始了,参数是:" + i + "," + j);
int result = i - j;
System.out.println("方法内部 result = " + result);
System.out.println("[日志] sub 方法结束了,结果是:" + result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("[日志]用户xxx操作 mul 方法开始了,参数是:" + i + "," + j);
int result = i * j;
System.out.println("方法内部 result = " + result);
System.out.println("[日志] mul 方法结束了,结果是:" + result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("[日志]用户xxx操作 div 方法开始了,参数是:" + i + "," + j);
int result = i / j;
System.out.println("方法内部 result = " + result);
System.out.println("[日志] div 方法结束了,结果是:" + result);
return result;
}
}
3.1.4、 提出问题
①现有代码缺陷
针对带日志功能的实现类,我们发现有如下缺陷:
-
对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
-
附加功能分散在各个业务功能方法中,不利于统一维护
②解决思路
解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。
③困难
解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引入新的技术。
3.2、AOP概念以及相关术语
3.2.1、概述
AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
①切面
需要添加额外功能的类
②通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
-
前置通知:在被代理的目标方法前执行
-
返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
-
异常通知:在被代理的目标方法异常结束后执行(死于非命)
-
后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
-
环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所
③切点
切面的功能插入到哪里去执行的路径。
3.2.2、作用
-
简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
-
代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。
3.3 基于注解的AOP
3.3.1 配置工作
-
需要的依赖
aspectjrt-1.5.3.jar
aspectjweaver-1.9.9.jar
spring-aspects-5.2.22.RELEASE.jar
-
配置
创建 applicationContext.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开始基于注解的AOP -->
<context:component-scan base-package="com.AOP.test"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
-
功能编码
-
功能接口
package com.AOP.test; public interface Calculator { 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.AOP.test; import org.aspectj.lang.annotation.Aspect; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; @Component public class CalculatorImpl implements Calculator{ @Override public int add(int i, int j) { int result = i + j; System.out.println("方法内部 result = " + result); return result; } @Override public int sub(int i, int j) { int result = i - j; System.out.println("方法内部 result = " + result); return result; } @Override public int mul(int i, int j) { int result = i * j; System.out.println("方法内部 result = " + result); return result; } @Override public int div(int i, int j) { int result = i / j; System.out.println("方法内部 result = " + result); return result; } }
-
-
切面编码
//@Aspect表示这个类是一个切面类 @Aspect //@Component注解保证这个切面类能够放入IOC容器 也可以换成@controller等 @Component public class LoggerAspect { //切点 要添加改切面功能的类的路径 @Pointcut("execution(* com.AOP.test.CalculatorImpl.*(..))") public void poincut() { } //前置通知 @Before("poincut()") public void beforeMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args); } //后置通知 @After("poincut()") public void afterMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); System.out.println("Logger-->后置通知,方法名:"+methodName+",参数:"+args); } //返回通知 @AfterReturning(value = "poincut()",returning = "result") public void afterReturningMethod(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result); } //异常通知 @AfterThrowing(value = "poincut()", throwing = "ex") public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){ String methodName = joinPoint.getSignature().getName(); System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex); } //环绕通知 @Around("poincut()") public Object aroundMethod(ProceedingJoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); String args = Arrays.toString(joinPoint.getArgs()); Object result = null; try { System.out.println("环绕通知-->目标对象方法执行之前"); //目标对象(连接点)方法的执行 result = joinPoint.proceed(); System.out.println("环绕通知-->目标对象方法返回值之后"); } catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("环绕通知-->目标对象方法出现异常时"); } finally { System.out.println("环绕通知-->目标对象方法执行完毕"); } return result; }
-
测试
public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext3.xml"); Calculator calculator = ac.getBean(Calculator.class); calculator.div(1, 3);//连接点 }
-
结果
环绕通知-->目标对象方法执行之前 Logger-->前置通知,方法名:div,参数:[1, 3] 方法内部 result = 0 环绕通知-->目标对象方法返回值之后 环绕通知-->目标对象方法执行完毕 Logger-->后置通知,方法名:div,参数:[1, 3] Logger-->返回通知,方法名:div,结果:0