文章目录
前言
提示:这里可以添加本文要记录的大概内容:
Spring有两大核心,AOP和IOC,本文着重讲解AOP。
一、AOP是什么?
AOP是Aspect Oriented Programming,即面向切面编程。
什么是面向切面呢?
要理解AOP的概念,我们先用OOP举例,OOP大家都知道,面向对象编程。比如一个业务组件BookService,它有几个业务方法:
- createBook:添加新的Book;
- updateBook:修改Book;
- deleteBook:删除Book。
对每个业务方法,例如,createBook(),除了业务逻辑,还需要安全检查、日志记录和事务处理,它的代码像这样:
public class BookService {
public void createBook(Book book) {
securityCheck();
Transaction tx = startTransaction();
try {
// 核心业务逻辑
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
}
log("created book: " + book);
}
}
然后你就会发现增删改的方法都需要做这些判断,很繁琐,解决办法有两种:
一种可行的方式是使用Proxy模式,将某个功能,例如,权限检查,放入Proxy中,但是这种方式的缺点是比较麻烦,必须先抽取接口,然后,针对每个方法实现Proxy。
所以第二种方法AOP应运而生了。
AOP技术看上去比较神秘,但实际上,它本质就是一个动态代理,让我们把一些常用功能如权限检查、日志、事务等,从每个业务方法中剥离出来。
二、使用步骤
1.引入Aspect依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${
spring.version}</version>
</dependency>
2.编写代码
这里我先模拟一个简单的场景,用最简洁的代码教会大家。
假设我们进行了一条数据的更新(update),需要记录日志,如果报错需要抛出异常,这时候我们可以用到前置通知(其实是在插入成功后,后置通知)和异常通知
2.1 使用注解+AOP方式实现切面
首先我们定义一个注解类AdminOnly
@Retention(RetentionPolicy.RUNTIME) //运行期
@Target(ElementType.METHOD) //作用在方法上
public @interface AdminOnly {
}
然后就可以编写切面类了,
@Aspect
@Component
public class CheckUserAspect {
//注解方式,方法其实是个空壳
//此时注解就跟此方法绑定了,后续使用注解的方法都会被带上切面所标识的方法
@Pointcut("@annotation(com.tao.aop.annotation.AdminOnly)")
private void checkAdmin(){
}
@Before("checkAdmin()") // 前置通知,checkAdmin()跟拦截器进行绑定
private void Before(){
System.out.println("前置通知...日志记录");
}
@AfterThrowing( value = "checkAdmin()",throwing = "e") //异常通知 checkAdmin()跟拦截器进行绑定
private void afterThrowing(Throwable e) {
System.out.println("报错后通知...");
System.out.println(e.getMessage());
}
}
UserService类
@Service
public class UserService {
@AdminOnly //注:要想被切面的方法需要加上这个注解
public void update() {
System.out.println("进行了update操作...");
}
}
此时使用测试类,调用UserService中的update方法,你会发现结果是:
前置通知…权限校验
进行了update操作…
已经加上了AOP操作。接下来讲解通过execution来划定范围
2.2 使用execution+AOP方式实现切面
切面类 CheckUserAspect ,看得出,我把service包下的所有类,所有方法都拦截了
@Aspect
@Component
public class CheckUserAspect {
//表达式方式
// execution 代表的意思
/*
* 第一个 * 号代表返回值为任意类型
*第二位代表 包名
*第三位 .* 代表任意类
*第四位 .*(..) 表示任何方法名的任何参数
*
* */
@Pointcut("execution(* com.tao.aop.service.*.*(..))")
private void checkAdmin(){
//此方法是空壳
}
@Before("checkAdmin()") // 前置通知,checkAdmin()跟拦截器进行绑定
private void Before(){
System.out.println("前置通知...日志记录");
}
@AfterThrowing( value = "checkAdmin()",throwing = "e") //异常通知 checkAdmin()跟拦截器进行绑定
private void afterThrowing(Throwable e) {
System.out.println("报错后通知...");
System.out.println(e.getMessage());
}
}
UserService1类
@Service
public class UserService1 {
public void update() throws Exception {
System.out.println("进行了update操作...");
throw new Exception("自造异常");
}
}
前置通知以及我自己模拟的异常通知都触发了,结果如下:
前置通知…权限校验
进行了update操作…
报错后通知…
自造异常
3. 写在最后
还有一些拦截通知类型,我就不一一举例,感兴趣的同学可以关注公众号,探讨及提问。
@Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了;
@After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行;
@AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码;
@AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码;
@Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能。
总结
总的来说,AOP已经是开发必备或者说面试必问,老生常谈的问题了,如果想要详细源码或者不懂的地方关注公众号,回复AOP实例源码 即可获取全部源码
如果您有Java方面的问题(不局限于此文章的问题),欢迎公众号提出您的问题,我将在第一时间为您解答~
有惊喜哟~