开始使用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());
    }
}

代码执行结果如下;(执行的还是上述方法)

posted @ 2022-11-05 20:56  a-tao必须奥利给  阅读(295)  评论(0编辑  收藏  举报