开始使用AspectJ-实现步骤@Aspect,@Before,还有其中的JoinPoint参数
开始使用AspectJ
1.maven依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
插件
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</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 注解,并按通知类型与切入点,将其织入,并 生成代理。
<?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: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"> <!--把对象交给spring容器,由spring容器统一创建,管理对象--> <!--声明目标对象--> <bean id="someService" class="com.bjpowernode.ba01.SomeServiceImpl" /> <!--声明切面类对象--> <bean id="myAspect" class="com.bjpowernode.ba01.MyAspect" /> <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。 创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象 所以目标对象就是被修改后的代理对象. aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。 --> <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());
}
}
代码执行结果如下;(执行的还是上述方法)