模拟计算器的加减乘除,在每次调用方法前后进行模拟日志输出。
- 配置自动扫包 和 自动完成创建代理织入切面 spring.xml
<context:component-scan base-package="per.tan"/>
<aop:aspectj-autoproxy/>
- 原始业务接口 Calc
public interface Calc {
Integer add(Integer num1, Integer num2);
Integer min(Integer num1, Integer num2);
Integer mul(Integer num1, Integer num2);
Integer div(Integer num1, Integer num2);
}
- 原始业务实现类 CalcImpl
@Component
public class CalcImpl implements Calc {
@Override
public Integer add(Integer num1, Integer num2) {
Integer result = num1 num2;
System.out.println("执行业务,完成了一次加法!");
return result;
}
@Override
public Integer min(Integer num1, Integer num2) {
Integer result = num1 - num2;
System.out.println("执行业务,完成了一次减法!");
return result;
}
@Override
public Integer mul(Integer num1, Integer num2) {
Integer result = num1 * num2;
System.out.println("执行业务,完成了一次乘法!");
return result;
}
@Override
public Integer div(Integer num1, Integer num2) {
Integer result = num1 / num2;
System.out.println("执行业务,完成了一次除法!");
return result;
}
}
- 切面类 LogAspect
@Component
@Aspect
public class LogAspect {
//指定位置,方法通配符 * ,参数通配符 .. 。
@Before(value = "execution(public Integer per.tan.aop.CalcImpl.*(..))")
public void before(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(name "之前记录日志[Before]" ",参数为:" args);
}
@After(value = "execution(public Integer per.tan.aop.CalcImpl.*(..))")
public void after(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println(name "之后记录日志[After]");
}
@AfterReturning(value = "execution(public Integer per.tan.aop.CalcImpl.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String name = joinPoint.getSignature().getName();
System.out.println(name "之后记录日志[AfterReturning],并拿到返回值:" result);
}
@AfterThrowing(value = "execution(public Integer per.tan.aop.CalcImpl.*(..))", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println(name "之后记录日志[afterThrowing],异常信息为:" e);
}
}
- 测试 Test
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Calc calc = context.getBean(Calc.class);
calc.add(10, 2);
System.out.println();
calc.min(10, 2);
System.out.println();
calc.min(10, 2);
System.out.println();
calc.div(10, 2);
System.out.println();
calc.div(10, 0);
}
}
- 测试结果
D:Java_JDKJDK8binjava.exe ...
五月 16, 2021 4:19:15 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7daf6ecc: startup date [Sun May 16 16:19:15 CST 2021]; root of context hierarchy
五月 16, 2021 4:19:15 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring.xml]
add之前记录日志[Before],参数为:[10, 2]
执行业务,完成了一次加法!
add之后记录日志[After]
add之后记录日志[AfterReturning],并拿到返回值:12
min之前记录日志[Before],参数为:[10, 2]
执行业务,完成了一次减法!
min之后记录日志[After]
min之后记录日志[AfterReturning],并拿到返回值:8
min之前记录日志[Before],参数为:[10, 2]
执行业务,完成了一次减法!
min之后记录日志[After]
min之后记录日志[AfterReturning],并拿到返回值:8
div之前记录日志[Before],参数为:[10, 2]
执行业务,完成了一次除法!
div之后记录日志[After]
div之后记录日志[AfterReturning],并拿到返回值:5
div之前记录日志[Before],参数为:[10, 0]
div之后记录日志[After]
div之后记录日志[afterThrowing],异常信息为:java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at per.tan.aop.CalcImpl.div(CalcImpl.java:36)
- 相关概念
- 切面对象:根据切面抽象出来的对象,CalcImpl中所有方法中需要加入日志的部分,抽象成一个切面类LogAspect。
- 通知:切面对象具体执行的代码,即非业务代码,LogAspect对象打印日志的代码。
- 目标:被横切的对象,即CalcImpl,将通知加入其中。
- 代理:切面对象,通知,目标混合之后的结果,即使用JDK动态代理机制创建的对象。
- 连接点:需要被横切的位置,即通知要插入业务代码的具体位置。
Q.E.D.