1. 添加 jar 包
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
2. 写 Spring 的配置文件,把注解扫面打开,写接口和实现类,测试类
1. applicationContext.xml
<context:component-scan base-package="www.xq.spring"></context:component-scan>
2. 接口
public interface AopTest {
public int add(int i,int j);
public int sub(int i,int j);
}
3. 实现类
@Component
public class AopTestImpl implements AopTest {
public int add(int i, int j) {
int rs = i + j;
System.out.println("add rs: " + rs);
return rs;
}
public int sub(int i, int j) {
int rs = i - j;
System.out.println("sub rs: " + rs);
return rs;
}
}
4. 测试类
public class RunMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
AopTest aopTest = (AopTest) ctx.getBean("aopTestImpl");
aopTest.add(1, 4);
aopTest.sub(5, 1);
}
}
3. 定义切面类,LoggerAspect
/**
* 要让这个类为切面类
* 1. 首先让其加入到 Spring 的 ioc 容器中去,加注解 @Component
* 2. 其次要声明它是切面类,加注解 @Aspect
* @author lenovo
*
*/
@Aspect
@Component
public class LoggerAspect {
Logger logger = Logger.getLogger(this.getClass());
//定义切面方法,这里有一个关键,告诉 Spring,这个方法在哪个类方法前还是后切入,明确切入点
//说白了,这个方法要在切入点被切入进去并执行
@Before("execution(public int www.xq.spring.aop.AopTestImpl.add(int,int))")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println("xxxx方法执行之前,记录日志");
}
}
4. spring 的配置文件中,让 @Before 注解起作用
<!--
让切面类里的方法上的注解起作用,具体什么作用呢:未被切入的类生产一个代理类(子类),
代理类中加了切面类的方法,说白了,这个代理类,就偷梁换柱了
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
可以看到在指定的切入点,切入了切面类的方法,讲分离的日志代码和业务代码在运行时合起来了!
5. 继续完善切面类
public class LoggerAspect {
Logger logger = Logger.getLogger(this.getClass());
//定义切面方法,这里有一个关键,告诉 Spring,这个方法在哪个类方法前还是后切入,明确切入点
//说白了,这个方法要在切入点被切入进去并执行
@Before("execution(public int www.xq.spring.aop.AopTestImpl.add(int,int))")
public void beforeMethod(JoinPoint joinPoint) {
//通过方法里加一个参数,链接点的对象,可以获取到切入点的那个方法的信息
String methodName = joinPoint.getSignature().getName();//拿到方法名
List<Object> args = Arrays.asList(joinPoint.getArgs());//拿到方法参数
logger.info(methodName + "方法执行之前,记录日志!方法的参数" + args);
}
}
6. 一个方法实现了,扩展到多个方法,用 * 代替 add 方法名,表示任意方法:
@Before("execution(public int www.xq.spring.aop.AopTestImpl.*(int,int))")
7. 运行结果
2019-03-19 20:25:08,481 INFO [www.xq.spring.aop.LoggerAspect.beforeMethod(LoggerAspect.java:35)] add方法执行之前,记录日志!方法的参数[1, 4]
add rs: 5
2019-03-19 20:25:08,483 INFO [www.xq.spring.aop.LoggerAspect.beforeMethod(LoggerAspect.java:35)] sub方法执行之前,记录日志!方法的参数[5, 1]
sub rs: 4
最后,对@Before注解在解释一下:
- @Before和它注解的这个方法在AOP里叫:通知(advice),这个我们除了@Before这个叫前置通知(在切入目标方法执行之前执行)外,还有:
- @After后置通知(在目标方法执行之后执行)
- @AfterReturning返回通知(在目标方法返回结果之后执行)
- @AfterThrowing异常通知(在目标方法跑出异常之后执行)
- @Around环绕通知(围绕着方法执行)
@Before注解后的括号的内容叫AspectJ表达式,这里还可进一步使用通配符*
//public int 换成 * : 任何修饰符和返回值类型
@Before("execution(int int www.xq.spring.aop.AopTestImpl.*(int,int))")
//AopTestImpl换成*,表示cn.ybzy.springdemo包里的所有类
@Before("execution(int int www.xq.spring.aop.*.*(int,int))")
//还可用两个点儿表示任意参数
@Before("execution(int int www.xq.spring.aop.*.*(*,*))")
很多个方法都是相同的切入点表达式,可以像提公因式样的提出来:
@Pointcut("execution(* www.xq.spring.aop.*.*(*,*))")
public void joinPointExpression() {}
@Before("joinPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
....
}