AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
为了更好地理解AOP,就需要对AOP的相关术语有一些了解
名称 | 说明 |
---|---|
Joinpoint(连接点) | 指能被拦截到的点,在Spring中只有方法能被拦截。 |
Pointcut(切点) | 指要对哪些连接点进行拦截,即被增强的方法。 |
Advice(通知) | 指拦截后要做的事情,即切点被拦截后执行的方法。 |
Aspect(切面) | 切点+通知称为切面 |
Target(目标) | 被代理的对象 |
Proxy(代理) | 代理对象 |
Weaving(织入) | 生成代理对象的过程 |
AspectJ是一个基于Java语言的AOP框架,在Spring框架中建议使用AspectJ实现AOP。 接下来我们写一个AOP入门案例:dao层的每个方法结束后都可以打印一条日志:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
</dependencies>
package com.example.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void add(){
System.out.println("用户新增");
}
public void delete(){
System.out.println("用户删除");
}
public void update(){
System.out.println("用户修改");
}
}
package com.example.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
public class MyAspectJAdvice {
// 后置通知
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("切点方法名:"+joinPoint.getSignature().getName());
System.out.println("目标对象:"+joinPoint.getTarget());
System.out.println("打印日志···"+joinPoint.getSignature().getName()+"方法被执行了!"); }
}
bean.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描包 -->
<context:component-scan base-package="com.example"/>
<!-- 通知对象 -->
<bean id="myAspectJAdvice" class="com.example.aspect.MyAspectJAdvice"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspectJAdvice">
<!-- 配置切点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.example.dao.UserDao.* (..))"/>
<!-- 配置后置通知 -->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
import com.example.SpringConfig;
import com.example.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserDaoTest {
@Test
public void testAdd(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.add();
}
@Test
public void testDelete(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.delete();
}
}
运行结果分别为
AOP有以下几种常用的通知类型:
通知类型 | 描述 |
---|---|
前置通知 | 在方法执行前添加功能 |
后置通知 | 在方法正常执行后添加功能 |
异常通知 | 在方法抛出异常后添加功能 |
最终通知 | 无论方法是否抛出异常,都会执行该通知 |
环绕通知 | 在方法执行前后添加功能 |
// 前置通知
public void myBefore(){
System.out.println("前置通知···");
}
// 异常通知
public void myAfterThrowing(Exception e){
System.out.println("异常通知···");
System.out.println(e.getMessage());
}
// 最终通知
public void myAfter(){
System.out.println("最终通知···");
}
// 环绕通知
public Object myAround(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前···");
// 执行方法
Object obj = point.proceed();
System.out.println("环绕后···");
return obj;
}
<!-- 配置AOP -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspectJAdvice">
<!-- 配置切点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.example.dao.UserDao.* (..))"/>
<!-- 配置后置通知 -->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
<!-- 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointcut"/>
<!-- 异常通知 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/>
<!-- 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointcut"/>
<!-- 环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
OK,这里我们测试用户新增方法 ,确实是得出来我们想要的结果了
切点表达式:访问修饰符 返回值 包名.类名.方法名(参数列表)
参数列表
全通配: * *..*.*(..)
我们可以为切点配置多个通知,形成多切面,比如希望dao层的每个方法结束后都可以打印日志并发送邮件:
package com.example.aspect;
import org.aspectj.lang.JoinPoint;
public class MyAspectJAdvice2 {
// 后置通知
public void myAfterReturning(JoinPoint point){
System.out.println("发送邮件···");
}
}
在上面的基础上配置多一个切面即可
<aop:aspect ref="myAspectJAdvice2">
<aop:pointcut id="myPointcut2" expression="execution(* com.example.dao.UserDao.*(..))"/>
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut2"/>
</aop:aspect>
OK,确实是打印了发送邮件,因此该多切面配置成功,下面接着讲解用另外几种方法实现AOP ,让我们一起学习啪
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。