基于AspectJ的AOP的使用
其实就是指的Spring+AspectJ整合,不过Spring已经将AspectJ收录到自身的框架中,并且底层织入依然是采取的动态织入方式
添加依赖
<!--基于AspectJ的aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
编写目标类和目标方法
- 编写接口和实现类(目标对象)
UserService接口
UserServiceImpl实现类
- 配置目标类,将目标类交给spring IoC容器管理
<context:component-scan base-package="sourcecode.ioc" />
使用XML实现
实现步骤
- 编写通知(增强类,一个普通的类)
public class MyAdvice {
public void log(){
System.out.println("记录日志...");
}
}
- 配置通知,将通知类交给spring IoC容器管理
<!--配置通知、增强-->
<bean name="myAdvice" class="cn.spring.advice.MyAdvice"></bean>
- 配置AOP 切面
<!--配置通知、增强-->
<bean name="myAdvice" class="cn.spring.advice.MyAdvice"></bean>
<!-- AOP配置 -->
<aop:config>
<aop:aspect ref="myAdvice">
<!-- method:指定要增强的方法,也就是指定通知类中的增强功能方法 -->
<!-- pointcut:指定切入点,需要通过表达式来指定-->
<aop:before method="log" pointcut="execution(void cn.spring.dao.UserDaoImpl.insert())"/>
</aop:aspect>
</aop:config>
切入点表达式
- 切入点表达式的格式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 表达式格式说明
execution:必须要
修饰符:可省略
返回值类型:必须要,但是可以使用*通配符
包名
多级包之间使用.分割
包名可以使用*代替,多级包名可以使用多个*代替
如果想省略中间的包名可以使用 ..
类名
可以使用*代替
也可以写成*DaoImpl
方法名
也可以使用*号代替
参数
参数使用*代替
如果有多个参数,可以使用..代替
通知类型
通知类型(五种):前置通知、后置通知、最终通知、环绕通知、异常抛出通知
- 前置通知:
执行时机:目标对象方法之前执行通知
配置文件:<aop:before method=“before” pointcut-ref=“myPointcut” />
应用场景:方法开始时可以进行校验
- 后置通知:
执行时机:目标对象方法之后执行通知,有异常则不执行了
配置文件:<aop:after-returning method=“afterReturning” pointcut-ref=“myPointcut” />
应用场景:可以修改方法的返回值
- 最终通知:
执行时机:目标对象方法之后执行执行通知,有没有异常都会执行
配置文件:<aop:after method=“after” pointcut-ref=“myPointcut” />
应用场景:例如像释放资源
- 环绕通知:
执行时机:目标对象方法之前和之后都会执行
配置文件:<aop:around method=“around” pointcut-ref=“myPointcut” />
应用场景:事务、统计代码执行时机
- 异常抛出通知:
执行时机:在抛出异常后通知
配置文件:<aop:after-throwing method=“afterThrowing” pointcut-ref=“myPointcut” />
应用场景:包装异常
使用注解实现
实现步骤
- 编写切面类(注意不是通知类,因为该类中可以指定切入点)
/**
* 切面类(通知+切入点)
*/
// @Aspect:标记该类是一个切面类
@Component("myAspect")
@Aspect
public class MyAspect {
//@Before: 标记该方法是一个前置通知
//value: 切入点表达式
@Before(value = "execution(* *..*.*DaoImpl.*(..))")
public void log() {
System.out.println("记录日志...");
}
}
- 配置切面类
<context:component-scan base-package="com.spring"/>
- 开启AOP自动代理
<!-- AOP基于注解的配置-开启自动代理 -->
<aop:aspectj-autoproxy />
环绕通知注解配置
@Around
作用:
把当前方法看成环绕通知。属性:
value:
用于指定切入点表达式,还可以指定切入点表达式的引用
@Around(value = "execution(* *.*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = joinPoint.getArgs();
//前置通知:开启事务beginTransaction()
//执行方法
rtValue = joinPoint.proceed(args);
//后置通知:提交事务commit();
} catch (Throwable e) {
//异常通知:回滚事务rollback();e.printStackTrace();
} finally {
//最终通知:释放资源release();
}
return rtValue;
}
定义通用切入点
使用@PointCut注解在切面类中定义一个通用的切入点,其他通知可以引用该切入点
//@Aspect:标记该类是一个切面类
@Aspect
public class MyAspect {
//@Before:标记该方法是一个前置通知
//@Value:切入点表达式
//@Before(value = "execution(* *..*.*DaoImpl.*(..))")
@Before(value="MyAspect.fn()")
public void log() {
System.out.println("记录日志...");
}
//@Before(value = "execution(* *..*.*DaoImpl.*(..))")
@Before(value="MyAspect.fn()")
public void validate() {
System.out.println("进行后台校验...");
}
//通过@Pointcut定义一个通用的切入点
@Pointcut(value = "execution(* *..*.*DaoImpl.*(..))")
public void fn() {
}
}
纯注解方式
@Configuration
@ComponentScan(basePackages="com.aaa")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}