本文参考实验楼教程:https://www.shiyanlou.com/courses/578/learning/?id=1940
AspectJ是基于注解(Annotation)的,所以需要JDK5.0版本以上。本文实验环境延用之Spring Aop:一、四种advice 的实验环境。
AspectJ支持的注解类型如下:
- @Before
- @After
- @AfterReturning
- @AfterThrowing
- @Around
1、准备工作
首先定义一个简单的bean,CustomerBo
实现了ICustomerBo
接口。ICustomerBo
接口代码如下:
package com.shiyanlou.spring.aop.aspectj;
/**
* Created by Administrator on 2019/11/2.
*/
public interface ICustomerBo {
void addCustomer();
void deleteCustomer();
String addCustomerReturnValue();
void addCustomerThrowException() throws Exception;
void addCustomerAround(String name);
}
CustomerBo.java
代码如下:
package com.shiyanlou.spring.aop.aspectj;
/**
* Created by Administrator on 2019/11/2.
*/
public class CustomerBo implements ICustomerBo {
@Override
public void addCustomer() {
System.out.println("addCustomer() is running...");
}
@Override
public void deleteCustomer() {
System.out.println("deleteCustomer() is running...");
}
@Override
public String addCustomerReturnValue() {
System.out.println("addCustomerReturnValue() is running...");
return "abc";
}
@Override
public void addCustomerThrowException() throws Exception {
System.out.println("addCustomerThrowException() is running...");
throw new Exception("Generic Error!");
}
@Override
public void addCustomerAround(String name) {
System.out.println("addCustomerAround() is running, args: " + name);
}
}
2、简单的AspectJ,Advice和Pointcut结合在一块的
首先在没有引入AspectJ之前,Advice和Pointcut是混在一块的,步骤如下:
- 创建一个Aspect类
- 配置Spring配置文件
由于接下来要使用aspectj的jar包,首先要添加maven依赖,需要在pom.xml中添加:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
注:这在之前已经添加过了,这里这是说明他们的用途。
创建AspectJ类,LoggingAspect.java
如下:
package com.shiyanlou.spring.aop.aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* Created by Administrator on 2019/11/2.
*/
@Aspect
public class LoggingAspect {
@Before("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer(..))")
public void logBefore(JoinPoint joinPoint){
System.out.println("logBefore() is running...");
System.out.println("wei:" + joinPoint.getSignature().getName());
System.out.println("***********");
}
@After("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.deleteCustomer(..))")
public void logAfter(JoinPoint joinPoint){
System.out.println("logAfter() is running...");
System.out.println("wei:" + joinPoint.getSignature().getName());
System.out.println("***********");
}
}
解释:
- 必须使用
@Aspect
在LoggingAspect
之前声明,以便被框架扫描到; - 此例中Advice和Poincut结合在一起,
LoggingAspect
类中的logBefore
和logAfter
即为Advice,要注入的代码,即它们上方的代码为Pointcut表达式,即定义了切入点。上例中@Before
中代码表示在执行com.shiyanlou.spring.aop.aspectj.CustomerBo
的addCustomer
方法前注入logBefore
代码; - 需要在LoggingAspect类的方法前加上
@Before
,@After
等注释; -
execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer(..))
是Aspect的Pointcut表达式:
*
代表任意返回类型- 后面定义要拦截的方法名(全路径:包名.类名.方法名)
(..)
代表匹配参数:任意多个任意类型的参数,若确认没有参数可以写()
;还可以用(*)
代表任意一个类型的参数,(*,String)
表示匹配两个参数,第一个任意类型,第二个必须是String
类型。
- AspectJ表达式可以对整个包定义,例如:
execution(public * com.shiyanlou.spring.aop.aspectj.*.*(..))
表示切入点是com.shiyanlou.spring.aop.aspectj
整个包的所有类的所有方法。
配置SpringAopAspectJ.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
<bean id="customerBo" class="com.shiyanlou.spring.aop.aspectj.CustomerBo"/>
<bean id="loggingAspect" class="com.shiyanlou.spring.aop.aspectj.LoggingAspect"/>
</beans>
<aop:aspectj-autoproxy/>
代表启动AspectJ支持,这样Spring会自动寻找@Aspect
注释过的类,其他配置一致;
执行App.java
,如下:
package com.shiyanlou.spring;
import com.shiyanlou.spring.aop.advice.CustomerService;
import com.shiyanlou.spring.aop.aspectj.CustomerBo;
import com.shiyanlou.spring.aop.aspectj.ICustomerBo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class App
{
private static ApplicationContext context;
public static void main( String[] args ) {
context = new ClassPathXmlApplicationContext("SpringAopAspectJ.xml");
ICustomerBo cust = (ICustomerBo) context.getBean("customerBo");
cust.addCustomer();
System.out.println("-----------------------");
cust.deleteCustomer();
}
}
执行结果如下:
logBefore() is running...
wei:addCustomer
***********
addCustomer() is running...
-----------------------
deleteCustomer() is running...
logAfter() is running...
wei:deleteCustomer
***********
3、将Advice和Pointcut 分开
需要三步骤:
- 创建Pointcut
- 创建Advice
- 配置Spring的配置文件
1)、定义Pointcut
创建PointcutsDefinitions.java
,如下:
package com.shiyanlou.spring.aop.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
/**
* Created by Administrator on 2019/11/2.
*/
@Aspect
public class PointcutsDefinitions {
@Pointcut("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.*(..))")
public void customreLog(){
}
}
解释:
- 类声明之前加上
@Aspect
注释,一遍被框架扫描到; -
@Pointcut
定义了切入点声明,指定需要注入代码的位置(即在哪个类的哪个方法中注入额外代码),上例中指定的切入点为CustomerBo.java
类中的所有方法;但是往往实际应用中,我们需要切入的是一整个业务逻辑层(简单点说就是某个包),例如@Pointcut("execution(public * com.shiyanlou.spring.aop.aspectj.*.*(..))")
,表示的是com.shiyanlou.spring.aop.aspectj
包中所有类的所有方法。 - 方法
customreLog
只是一个签名,在Advice中可以用此签名代替切入点表达式,多以不需要写方法体,只起到助记功能,例如此处代表操作CustomerBo
类中的切入点。
2)、修改LoggingAspect.java
package com.shiyanlou.spring.aop.aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* Created by Administrator on 2019/11/2.
*/
@Aspect
public class LoggingAspect {
@Before("com.shiyanlou.spring.aop.aspectj.PointcutsDefinitions.customreLog()")
public void logBefore(JoinPoint joinPoint){
System.out.println("logBefore() is running...");
System.out.println("wei:" + joinPoint.getSignature().getName());
System.out.println("***********");
}
@After("com.shiyanlou.spring.aop.aspectj.PointcutsDefinitions.customreLog()")
public void logAfter(JoinPoint joinPoint){
System.out.println("logAfter() is running...");
System.out.println("wei:" + joinPoint.getSignature().getName());
System.out.println("***********");
}
}
解释:
@Before
和@After
注释使用PointcutsDefinitions
中的方法签名代替Pointcut表达式找到相应的切入点, 即通过签名找到PointcutsDefinitions
方法customreLog
前定义的Pointcut表达式;- 对于
PointcutsDefinitions
来说,它的主要职责是定义Pointcut即切入点,可以在其中定义多个切入点,并且使用便于记忆的方法签名表示;- 单独定义Pointcut表达式的好处有,一是使用了有意义的方法名,二是Pointcut表达式可以被多个Advice共享,修改一处其他所有使用的地方均被修改。
3)、配置Spring配置文件
配置的SpringAopAspectJ.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
<bean id="customerBo" class="com.shiyanlou.spring.aop.aspectj.CustomerBo"/>
<bean id="loggingAspect" class="com.shiyanlou.spring.aop.aspectj.LoggingAspect"/>
</beans>
App.java
不变,运行如下:
logBefore() is running...
wei:addCustomer
***********
addCustomer() is running...
logAfter() is running...
wei:addCustomer
***********
-----------------------
logBefore() is running...
wei:deleteCustomer
***********
deleteCustomer() is running...
logAfter() is running...
wei:deleteCustomer
***********