1、开发环境搭建
在Spring_Demo项目中创建名为“AspectJ”的module模块,并在lib文件夹中引入如下jar包,具体如下:
然后在com.ccff.spring.aspectj.pojo包下创建名为“Role”的POJO类用于演示,具体代码如下所示:
package com.ccff.spring.aspectj.pojo;
public class Role {
private Long roleId;
private String roleName;
private String roleNote;
public Role() {
}
public Role(Long roleId, String roleName, String roleNote) {
this.roleId = roleId;
this.roleName = roleName;
this.roleNote = roleNote;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleNote() {
return roleNote;
}
public void setRoleNote(String roleNote) {
this.roleNote = roleNote;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleNote='" + roleNote + '\'' +
'}';
}
}
2、选择连接点
Spring是方法级别的AOP框架,而我们主要也是以某个类的某个方法作为连接点,用动态代理的理论来说,就是要拦截哪个方法织入对应AOP通知。为了更好的演示,首先在com.ccff.spring.aspectj.service包下创建名为“RoleService”的接口,具体代码如下:
package com.ccff.spring.aspectj.service;
import com.ccff.spring.aspectj.pojo.Role;
public interface RoleService {
void printRole(Role role);
}
这个接口十分简单,接下来为该接口提供一个名为“RoleServiceImpl”的实现类,具体代码如下:
package com.ccff.spring.aspectj.service;
import com.ccff.spring.aspectj.pojo.Role;
import org.springframework.stereotype.Component;
@Component
public class RoleServiceImpl implements RoleService {
@Override
public void printRole(Role role) {
System.out.println(role.toString());
}
}
这个类没什么特别的,只是这个时候如果把printRole方法作为AOP的连接点,那么用动态地阿里的语言就是要为类RoleServiceImpl生成代理对象,然后拦截printRole方法,于是可以产生各种AOP通知方法。
3、创建切面
选择好了连接点就可以创建切面了,对于动态代理的概念而言,它就如同一个拦截器在Spring中只要使用@Aspect注解一个类,那么Spring IoC容器就会认为这是一个切面了。在com.ccff.spring.aspectj.aspect包下创建名为“RoleAspect”的切面,并使用注解@Aspect注解这个类,具体代码如下:
package com.ccff.spring.aspectj.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class RoleAspect {
@Before("execution(* com.ccff.spring.aspectj.service.RoleServiceImpl.printRole(..))")
public void before(){
System.out.println("Before方法执行......");
}
@After("execution(* com.ccff.spring.aspectj.service.RoleServiceImpl.printRole(..))")
public void after(){
System.out.println("After方法执行......");
}
@AfterReturning("execution(* com.ccff.spring.aspectj.service.RoleServiceImpl.printRole(..))")
public void afterReturning(){
System.out.println("AfterReturning方法执行......");
}
@AfterThrowing("execution(* com.ccff.spring.aspectj.service.RoleServiceImpl.printRole(..))")
public void afterThrowing(){
System.out.println("AfterThrowing方法执行......");
}
}
上面的代码中并没有环绕通知,各个注解详细信息如下表所示:
注解 | 通知 | 备注 |
---|---|---|
@Before | 在被代理对象的方法前调用 | 前置通知 |
@Around | 在被代理对象的方法封装起来,并用环绕通知取代它 | 环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法,后续会讨论 |
@After | 在被代理对象的方法后调用 | 后置通知 |
@AfterReturning | 在被代理对象的方法正常返回后调用 | 返回通知,要求被代理对象的方法执行过程中没有发生异常 |
@AfterThrowing | 在被代理对象的方法抛出异常后调用 | 异常通知,要求被代理对象的方法执行过程中发生异常 |
4、定义切点
上一小节讨论了切面的组成,但是并没有详细讨论Spring是如何判断是否需要拦截方法的,毕竟并不是所有的方法都需要使用AOP编程,这就是一个确定连接点的问题。在切面中的注解定义了execution的正则表达式,Spring是通过这个正则表达式判断是否需要拦截你的方法的,这个表达式是:
execution(* com.ccff.spring.aspectj.service.RoleServiceImpl.printRole(..))
依次对这个表达式做出分析:
execution: 代表执行方法的时候会触发。
*: 代表任意返回类型的方法。
com.ccff.spring.aspectj.service.RoleServiceImpl:<