文章目录
- AspectJ
- 通知类型(通知(Advice):在方法执行前或执行后要做的动作)
- 连接点(JoinPoint)【***】
- 使用AspectJ实现Spring AOP的方式有两种:
- 1. 基于XML配置开发AspectJ
- 案例实现
- 2. 基于注解开发AspectJ【***】
- AspectJ通知注解【***】
- 案例
AspectJ
AspectJ是一个基于Java语言的AOP框架。从Spring 2.0以后引入了AspectJ的支持。目前的Spring框架,建议使用AspectJ实现Spring AOP。
AOP面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
通知类型(通知(Advice):在方法执行前或执行后要做的动作)
- 环绕通知
环绕通知(org.aopalliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。- 前置通知
前置通知(org.springframework.aop.MethodBeforeAdvice)是在目标方法执行前实施增强,可应用于权限管理等功能。- 后置返回通知
后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。- 后置(最终)通知
后置通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源。- 异常通知
异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。- 引入通知
引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。
连接点(JoinPoint)【***】
程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理(和方法有关的前前后后都是连接点)
JoinPoint对象方法 | 描述 |
Signature getSignature() | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object getTarget() | 获取被代理的对象 |
Object getThis() | 获取代理对象 |
Object[] getArgs() | 获取传入目标方法的参数对象(输入的参数列表) |
joinpoint.proceed() | 在环绕通知around中使用,用于启动目标方法执行的(环绕通知=前置+目标方法执行+后置通知) |
joinPoint.getSignature().getName() | 获取目标对象被增强的方法 |
使用AspectJ实现Spring AOP的方式有两种:
- 基于XML配置开发AspectJ
- 基于注解开发AspectJ。
1. 基于XML配置开发AspectJ
注意:基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在applicationContext.xml的<aop:config>
元素内。
案例实现
- pom.xml加入AspectJ的依赖
<!--AspectJ 实现Spring AOP 方式: 1、aspectJ基于XML开发 切面编程AOP 事物回滚使用-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
- 创建被代理类(目标类)
public interface UserDao {
public void save();
public void delete();
}
public class UserDaoImpl implements UserDao{
@Override
public void save() {
// int i = 100/0;//测试异常抛出 虚拟机终止程序运行
System.out.println("save user .....");
}
@Override
public void delete() {
System.out.println("delete user .....");
}
}
- 创建切面类
/*切面类中定义的一个个方法叫通知
advice 通知 --- 是增强的方法,应用于被代理类方法的额外功能
*/
/*
AspectJ中的切入点匹配的执行点称作连接的(JoinPoint),
在通知方法中可以声明一个JoinPoint类型的参数。通过JoinPoint可以访问连接点的细节
1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
2.Signature getSignature() :获取连接点的方法签名对象;
3.java.lang.Object getTarget() :获取连接点所在的目标对象;
4.java.lang.Object getThis() :获取代理对象本身;
ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
5.java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
6.java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的参数替换原来的参数。
* */
public class MyAspect {
// 前置通知 advice
public void before(JoinPoint joinPoint){
// 被代理对象
joinPoint.getTarget();
// 目标对象被增强的方法
//获取方法签名对象 public void save() 【除了{}方法体以外都是方法签名】
joinPoint.getSignature().getName();
System.out.println("前置通知,用于权限控制。。"+ joinPoint.getTarget()+" ----methodName---"+joinPoint.getSignature().getName());
}
//后置通知
public void afterReturning(JoinPoint joinPoint){
System.out.println("后置通知,清除操作过的文件"+ joinPoint.getTarget()+" ----methodName---"+joinPoint.getSignature().getName());
}
// 环绕通知 ProceedingJoinPoint是joinPoint的子类
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知,方法执行之前,开启事务");
joinPoint.proceed(); //让方法继续执行
System.out.println("环绕通知,方法执行之后,关闭事务");
}
public void afterThrowing(Throwable e) throws Throwable {
System.out.println("异常通知:方法出现异常调用,处理异常"+e.getMessage());
}
public void after(JoinPoint joinPoint) {
System.out.println("最终通知 管理数据库连接等清理工作");
}
}
- 创建配置文件applicationContext.xml,编写相关配置
<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">
<!-- 被代理类 目标类 -->
<bean id="userDao" class="com.xgf.aop.aspectj.xml.UserDaoImpl"/>
<!--切面类Advice 干什么事情-->
<bean id="myAspect" class="com.xgf.aop.aspectj.xml.MyAspect"/>
<!-- 基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所
有这些定义都必须在<aop:config>元素内。【***】 -->
<aop:config>
<!--引入切面类 -->
<aop:aspect ref="myAspect">
<!-- 执行的方法 在什么地方 pointcut切点
execution(返回值类型 包名.类名.方法名(参数))
(..)代表任意参数
-->
<aop:pointcut id="mycut" expression="execution(* com.xgf.aop.aspectj.xml.*.*(..))"/>
<!-- 切入时机 -->
<!--前置通知 方法执行之前-->
<aop:before method="before" pointcut-ref="mycut"/>
<!--后置返回通知 方法执行之后执行,方法正常结束才调用,出现异常不会调用-->
<aop:after-returning method="afterReturning" pointcut-ref="mycut"/>
<!--环绕通知 -->
<aop:around method="around" pointcut-ref="mycut"/>
<!--异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="mycut" throwing="e"/>
<!-- 最终(后置)通知 不管方法是否出现异常都会执行,进行资源释放等清理工作 -->
<aop:after method="after" pointcut-ref="mycut"/>
</aop:aspect>
</aop:config>
</beans>
- 编写测试类
public class AspectJXMLTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("com/xgf/aop/aspectj/xml/applicationContext.xml");
/* 代理类 */
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.save();
userDao.delete();
}
}
- 运行结果
2. 基于注解开发AspectJ【***】
注意:一定要在applicationContext.xml中配置<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
才能让aop起作用。
AspectJ通知注解【***】
注解名称 | 描述 |
@Aspect | 用于定义一个切面,注解在切面类上 |
@Pointcut | 用于定义切入点表达式。在使用时,需要定义一个切入点方法。该方法是一个返回值void,且方法体为空的普通方法 |
@Before | 用于定义前置通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@AfterReturning | 用于定义 后置返回通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@Around | 用于定义 环绕通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@AfterThrowing | 用于定义异常通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式。另外,还有一个throwing属性用于访问目标方法抛出的异常,该属性值与异常通知方法中同名的形参一致 |
@After | 用于定义后置(最终)通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
|
案例
- pom.xml导入依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
- 创建被代理类
public interface UserDao {
public void save();
public void delete();
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void save() {
//int i = 100/0;//异常抛出 虚拟机终止程序运行
System.out.println("save 保存 ......");
}
@Override
public void delete() {
System.out.println("delete 删除 ......");
}
}
- 创建切面类
// @Component配置 启动容器会实例化类 名字首字母小写myAspect
// @Aspect配置切面
@Aspect
@Component
public class MyAspect {
/* 配置切点 - pointcut切点
execution(返回值类型 包名.类名.方法名(参数))
(..)代表任意参数
*/
@Pointcut("execution(* com.xgf.aop.aspectj.annotation.*.*(..))")
public void mycut(){// 切点的id
}
// 通知 advice
//@Before("mycut()") //当有多个值时用value 只有一个属性的时候直接写
@Before(value="mycut()")
public void before(JoinPoint joinPoint){
//JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
// 获取被代理对象
joinPoint.getTarget();
// 目标对象被增强的方法
joinPoint.getSignature().getName();
System.out.println("前置通知,用于权限控制 被代理对象: "+ joinPoint.getTarget()+" ----方法名methodName---"+joinPoint.getSignature().getName());
}
@AfterReturning("mycut()")
public void afterReturning(JoinPoint joinPoint){
System.out.println("后置返回通知,清除操作过的文件 被代理对象:"+ joinPoint.getTarget()+" ----方法名methodName---"+joinPoint.getSignature().getName());
}
@Around("mycut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知,方法执行之前,开启事务");
joinPoint.proceed();//启动目标方法执行
System.out.println("环绕通知,方法执行之后,关闭事务");
}
@AfterThrowing(value = "mycut()",throwing = "e")
public void afterThrowing(Throwable e) throws Throwable {
System.out.println("异常通知:方法出现异常调用,处理异常"+e.getMessage());
}
@After("mycut()")
public void after(JoinPoint joinPoint) {
System.out.println("最终通知 管理数据库连接等清理工作");
}
}
- 创建applicationContext.xml配置文件
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
一要有定,不然不起作用!
<?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:context="http://www.springframework.org/schema/context"
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/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">
<!--基于注解开发 扫描包-->
<!-- 容器启动的时候 加载配置文件xml 读取com.xgf.aop.aspectj.annotation包识别注解 -->
<context:component-scan base-package="com.xgf.aop.aspectj.annotation"/>
<!--使aop起作用,开启aop自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- 编写测试类
public class AspectJAnnotationTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("com/xgf/aop/aspectj/annotation/applicationContext.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
//调用方法
userDao.save();
userDao.delete();
}
}
- 执行结果