开始使用AspectJ
1.maven依赖
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>4.11version>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.2.5.RELEASEversion>
- dependency>
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-aspectsartifactId>
- <version>5.2.5.RELEASEversion>
- dependency>
- 插件
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-pluginartifactId>
- <version>3.1version>
- <configuration>
- <source>1.8source>
- <target>1.8target>
- configuration>
- plugin>
- plugins>
- build>
引入 AOP 约束
在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签, 均是 AspectJ 框架使用的,而非 Spring 框架本身在实现
AOP 时使用的。 AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
2. AspectJ 基于注解的 AOP 实现(掌握)
项目结构如下:
2.1 实现步骤
-
Step1:定义业务接口与实现类
接口:
- package com.bjpowernode.ba01;
- public interface SomeService {
- void doSome(String name,Integer age);
- }
接口实现类:
- package com.bjpowernode.ba01;
- //目标类
- public class SomeServiceImpl implements SomeService {
- @Override
- public void doSome(String name,Integer age) {
- //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
- System.out.println("====目标方法doSome()====");
- }
- public void doOther(String name,Integer age) {
- //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间
- System.out.println("====目标方法doSome()====");
- }
- }
Step2:定义切面类此时我们用的是 @Before
- package com.bjpowernode.ba01;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import java.util.Date;
- /**
- * @Aspect : 是aspectj框架中的注解。
- * 作用:表示当前类是切面类。
- * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
- * 位置:在类定义的上面
- */
- @Aspect
- public class MyAspect {
- /**
- * 定义方法,方法是实现切面功能的。
- * 方法的定义要求:
- * 1.公共方法 public
- * 2.方法没有返回值
- * 3.方法名称自定义
- * 4.方法可以有参数,也可以没有参数。
- * 如果有参数,参数不是自定义的,有几个参数类型可以使用。
- */
- /**
- * @Before: 前置通知注解
- * 属性:value ,是切入点表达式,表示切面的功能执行的位置。
- * 位置:在方法的上面
- * 特点:
- * 1.在目标方法之前先执行的
- * 2.不会改变目标方法的执行结果
- * 3.不会影响目标方法的执行。
- */
- @Before(value = "execution(public void com.bjpowernode.ba01.SomeServiceImpl.doSome(String,Integer))")
- public void myBefore(){
- //就是你切面要执行的功能代码
- System.out.println("前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }
- //可以省略 public
- /*@Before(value = "execution(void com.bjpowernode.ba01.SomeServiceImpl.doSome(String,Integer))")
- public void myBefore(){
- //就是你切面要执行的功能代码
- System.out.println("1=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }*/
- //使用 <*..>的方法表明不管上级多少目录的SomeServiceImpl的方法
- /* @Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
- public void myBefore(){
- //就是你切面要执行的功能代码
- System.out.println("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }*/
- //其中返回值也可以用 * 号代替
- /*@Before(value = "execution(* *..SomeServiceImpl.*(..))")
- public void myBefore(){
- //就是你切面要执行的功能代码
- System.out.println("3=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }*/
- //可以使用 * 号作为通配符,只要是do开头的都可以被动态代理识别
- /*@Before(value = "execution(* do*(..))")
- public void myBefore2(){
- //就是你切面要执行的功能代码
- System.out.println("4=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }*/
- //形式参数可以被 <..> 代替
- /*@Before(value = "execution(* com.bjpowernode.ba01.*ServiceImpl.*(..))")
- public void myBefore2(){
- //就是你切面要执行的功能代码
- System.out.println("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }*/
- }
Step3:声明目标对象切面类对象 此时我们用的配置文件,在文件种需要注册aspectj的自动代理生成器对象
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理 对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的 自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并 生成代理。
- "1.0" encoding="UTF-8"?>
- <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
- https://www.springframework.org/schema/aop/spring-aop.xsd">
- <bean id="someService" class="com.bjpowernode.ba01.SomeServiceImpl" />
- <bean id="myAspect" class="com.bjpowernode.ba01.MyAspect" />
- <aop:aspectj-autoproxy />
- beans>
2.2 [掌握]@Before 前置通知-方法有 JoinPoint 参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参 数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。 不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该 参数。
- package com.bjpowernode.ba01;
-
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
-
- import java.util.Date;
-
- /**
- * @Aspect : 是aspectj框架中的注解。
- * 作用:表示当前类是切面类。
- * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
- * 位置:在类定义的上面
- */
- @Aspect
- public class MyAspect {
- /**
- * 定义方法,方法是实现切面功能的。
- * 方法的定义要求:
- * 1.公共方法 public
- * 2.方法没有返回值
- * 3.方法名称自定义
- * 4.方法可以有参数,也可以没有参数。
- * 如果有参数,参数不是自定义的,有几个参数类型可以使用。
- */
-
-
- /**
- * @Before: 前置通知注解
- * 属性:value ,是切入点表达式,表示切面的功能执行的位置。
- * 位置:在方法的上面
- * 特点:
- * 1.在目标方法之前先执行的
- * 2.不会改变目标方法的执行结果
- * 3.不会影响目标方法的执行。
- */
-
-
- /**
- * 指定通知方法中的参数 : JoinPoint
- * JoinPoint:业务方法,要加入切面功能的业务方法
- * 作用是:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。
- * 如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
- * 这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数
- */
- @Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
- public void myBefore(JoinPoint jp){
- //获取方法的完整定义
- System.out.println("方法的签名(定义)="+jp.getSignature());
- System.out.println("方法的名称="+jp.getSignature().getName());
- //获取方法的实参
- Object args [] = jp.getArgs();
- for (Object arg:args){
- System.out.println("参数="+arg);
- }
- //就是你切面要执行的功能代码
- System.out.println("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date());
- }
- }
代码执行结果如下;(执行的还是上述方法)