IDEA项目实践——Spring当中的切面AOP

这篇具有很好参考价值的文章主要介绍了IDEA项目实践——Spring当中的切面AOP。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

IDEA创建项目的操作步骤以及在虚拟机里面创建Scala的项目简单介绍

IDEA项目实践——创建Java项目以及创建Maven项目案例、使用数据库连接池创建项目简介

IDEWA项目实践——mybatis的一些基本原理以及案例

IDEA项目实践——动态SQL、关系映射、注解开发

IDEA项目实践——Spring框架简介,以及IOC注解

文章目录

系列文章目录

一 AOP介绍

1.0 不使用切面编程额开发方式

项目 aop_leadin2

1.1 AOP简介

1.2 AOP的作用

 1.3 两种代理

1.4 面向切面编程的好处

 1.5 AOP编程术语(掌握)

 1.6 AspectJ对AOP的实现(掌握) 

1.6.0 AspectJ简介

1.6.1 AspectJ 的通知类型(理解)

1.6.2 AspectJ 的切入点表达式(掌握)

二 不使用AOP的开发方式的案例讲解

2.1 加入Maven项目spring-aop-leadin1

 2.2 创建接口SomeService

 2.3 编辑接口的实现类SomeServiceImpl

2.3.1 优化实现类的方案一

2.3.2 优化实现类的方案二

2.4 编辑测试类 

 三 使用JDK动态代理开发AOP项目

3.1 创建Maven项目【此处使用的是JDK动态代理】

3.2 加入一个 handler文件夹原来存放JDK代理

3.3 编辑一个测试类二

 四 使用spring-aop-aspectj的框架来实现AOP编程

4.1 创建Maven项目

4.2 导入依赖 

4.3  AspectJ 基于注解的 AOP 实现(掌握)

(1) 实现步骤

A、 Step1:定义业务接口与实现类​编辑

B、Step2:定义切面类

C、Step3:声明目标对象切面类对象

D、 Step4:注册 AspectJ 的自动代理

E、 Step5:测试类中使用目标对象的 id

(2) [掌握]@Before 前置通知-方法有 JoinPoint 参数

(3) [掌握]@AfterReturning 后置通知-注解有 returning 属性

(4) [掌握]@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数

(5) [了解]@AfterThrowing 异常通知-注解中有 throwing 属 性

(6) [了解]@After 最终通知

(7) @Pointcut 定义切入点

总结


前言

本文主要讲解spring当中的AOP切面编程介绍以及相关案例的使用,以及aspectj基于注解的AOP实现。

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

一 AOP介绍

1.0 不使用切面编程额开发方式

先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要再写两个非业务方法。非业务方法也称为交叉业务逻辑:

  • doTransaction():用于事务处理

  • doLog():用于日志处理

然后,再使接口方法调用它们。接口方法也称为主业务逻辑。

接口:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

项目 aop_leadin2

当然,也可以有另一种解决方案:将这些交叉业务逻辑代码放到专门的工 具类或处理类中,由主业务逻辑调用。

 IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

1.1 AOP简介

AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。

面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,如下图所示:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

1.2 AOP的作用

AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

变成切面编程,主体的代码不会动。

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 1.3 两种代理

AOP的底层,就是采用动态代理的方式实现的。采用了两种代理:JDK动态代理【主要了解】、CGLIB动态代理。

  • JDK动态代理:使用Proxy,Method,InvocationHandler【类】创建代理对象;要求目标类必须实现接口。
  • CGLIB动态代理:它是一个第三方的工具库,创建代理对象的原理,是通过继承目标类,创建子类,子类去重写方法,实现增强。子类就是代理对象;要求目标类不能是final【最终类】的,方法也不能是final的,可以不实现接口。【子父类的关系】

AOP为 Aspect Oriented Programming的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP 容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。

若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余"代码,还大大干扰了主业务逻辑---转账。 

这些可以使用切面来实现

1.4 面向切面编程的好处

1.减少重复

2. 专注业务

 注意:面向切面编程只是面向对象编程的一种补充,

使用AOP减少重复代码,专注业务实现。

 IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 1.5 AOP编程术语(掌握)

1)切面(Aspect)

切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强

2)连接点(JoinPoint)

连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。 

3)切入点(Pointcut)

切入点【切入的位置,真正实现切入的地方】指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的

4)目标对象(Target)

目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。上例中的StudentServicelmpl的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。

5)通知(Advice)

通知表示切面的执行时间,Advice也叫增强。上例中的 MyInvocationHandler就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 

切入点定义切入的位置,通知定义切入的时间。

 1.6 AspectJ对AOP的实现(掌握) 

对于AOP这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,Aspect]也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将Aspect的对于AOP的实现也引入到了自己的框架中

在Spring 中使用AOP开发时,一般使用Aspect的实现方式。

1.6.0 AspectJ简介

Aspect是一个优秀面向切面的框架,它扩展了Java语言,提供了强大的切面实现。

官网地址:The AspectJ Project | The Eclipse Foundation

AspetJ是 Eclipse的开源项目,官网介绍如下: 

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

a seamless aspect-oriented extension to the Javatm programming language(一种基于 Java 平台的面向切面编程的语言)

Java platform compatible(兼容 Java 平台,可以无缝扩展)

easy to learn and use(易学易用)

1.6.1 AspectJ 的通知类型(理解)

AspectJ 中常用的通知有五种类型:

(1)前置通知

(2)后置通知【方法执行完成之后执行,方法异常就会执行不到】

(3)环绕通知【前后都执行】

(4)异常通知【发生异常的时候通知】

(5)最终通知【不论发生不发生异常都执行】

1.6.2 AspectJ 的切入点表达式(掌握)

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern 
declaring-type-pattern?namepattern(param-pattern) throws-pattern?) 

解释:

  • modifiers-pattern] 访问权限类型
  • ret-type-pattern 返回值类型
  • declaring-type-pattern 包名类名
  • name-pattern(param-pattern) 方法名(参数类型和参数个数)
  • throws-pattern 抛出异常类型
  • ?表示可选的部分

以上表达式共 4 个部分。

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表 达式中明显就是方法的签名。注意,表达式中黑色文字表示可省略部分,各部 分间用空格分开。在其中可以使用以下符号:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

举例:

  • execution(public * *(..)) 指定切入点为:任意公共方法。
  • execution(* set*(..)) 指定切入点为:任何一个以“set”开始的方法。
  • execution(* com.xyz.service..(..)) 指定切入点为:定义在 service 包里的任意类的任意方法。
  • execution(* com.xyz.service...(..)) 指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现 在类名中时,后面必须跟“*”,表示包、子包下的所有类。
  • execution(* ..service..*(..)) 指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
  • execution(* .service..*(..)) 指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
  • execution(* .ISomeService.(..)) 指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
  • execution(* ..ISomeService.(..)) 指定所有包下的 ISomeSerivce 接口中所有方法为切入点
  • execution(* com.xyz.service.IAccountService.*(..)) 指定切入点为:IAccountService 接口中的任意方法。
  • execution(* com.xyz.service.IAccountService+.*(..)) 指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有 实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
  • execution(* joke(String,int))) 指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可 以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。
  • execution(* joke(String,*))) 指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数 可以是任意类型,如 joke(String s1,String s2)和 joke(String s1,double d2) 都是,但 joke(String s1,double d2,String s3)不是。
  • execution(* joke(String,..))) 指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有 任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)都是。
  • execution(* joke(Object)) 指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类 型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
  • execution(* joke(Object+))) 指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型 或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也 是。

二 不使用AOP的开发方式的案例讲解

2.1 加入Maven项目spring-aop-leadin1

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

添加的依赖如下:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.26</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.26</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

 加入依赖之后需要更新一下。

 2.2 创建接口SomeService

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,javaIDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 输入接口的代码:

package com.ambow.service;

public interface SomeService {
    //实现业务方法
    void doSome();
    //实现其他的方法
    void doOther();
}

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 2.3 编辑接口的实现类SomeServiceImpl

在IDEA里面的快捷键创建接口的实现类

alt+enter输入实现类的名称以及实现类所在的包 

package com.ambow.service.impl;

import com.ambow.service.SomeService;
import com.ambow.service.util.ServiceUtil;

import java.util.Date;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        //不加入aop时如果要在开始之前加入---需要每次都要修改硬编码
        //System.out.println("-------开始日志"+ new Date()+"-----");
        System.out.println("开始日志"+ new Date());       
        System.out.println("======doSome方法=======");
        System.out.println("结束日志"+ new Date());
        
    }

    @Override
    public void doOther() {
        System.out.println("开始日志"+ new Date());
        System.out.println("======doOther方法=======");
        System.out.println("结束日志"+ new Date());
    }
}

2.3.1 优化实现类的方案一

直接在代码里面加入方法的优化方法

package com.ambow.service.impl;

import com.ambow.service.SomeService;
import com.ambow.service.util.ServiceUtil;

import java.util.Date;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        //优化一下,第一种方法
        beginLog();
        System.out.println("======doSome方法=======");
        endLog();        
    }

    @Override
    public void doOther() {
        //优化一下,第一种方法
        beginLog();
        System.out.println("======doOther方法=======");
        //优化一下,第一种方法
        endLog();
    }
    //写两个日志开始于结束的方法,直接调用方法
    public void beginLog(){
        System.out.println("开始日志"+ new Date());
    }

    public void endLog(){
        System.out.println("结束日志"+ new Date());
    }
}

2.3.2 优化实现类的方案二

在项目里面编写一个ServiceUtil类,将两个方法放在这个类里面。

package com.ambow.service.util;

import java.util.Date;

public class ServiceUtil {
    public static void beginLog(){
        System.out.println("开始日志"+ new Date());
    }

    public static void endLog(){
        System.out.println("结束日志"+ new Date());
    }
}

修改原来接口实现类的代码

package com.ambow.service.impl;

import com.ambow.service.SomeService;
import com.ambow.service.util.ServiceUtil;

import java.util.Date;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        //优化的第二种方法
        ServiceUtil.beginLog();
        System.out.println("======doSome方法=======");    
        //优化的第二种方法
        ServiceUtil.endLog();
    }

    @Override
    public void doOther() {
        //优化的第一种方法        
        ServiceUtil.beginLog();
        System.out.println("======doOther方法=======");
        //优化的第二种方法
        ServiceUtil.endLog();
    }
}

2.4 编辑测试类 

package com.ambow.test;

import com.ambow.service.SomeService;
import com.ambow.service.impl.SomeServiceImpl;
import org.junit.Test;

public class AopLeadinTest {
    @Test
    public void test01(){
        SomeService someService = new SomeServiceImpl();
        someService.doSome();
    }
}

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

这种传统方式的维护较为困难。 

 三 使用JDK动态代理开发AOP项目

3.1 创建Maven项目【此处使用的是JDK动态代理】

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 将原来的spring-aop-leadin1项目里面的接口和util拿过来,编辑一个测试类

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,javaIDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

3.2 加入一个 handler文件夹原来存放JDK代理

加入一个新的类——handler.MylnvocationHandler

 IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 使得这个类继承InvocationHandler类

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,javaIDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 接口实现类的代码段:

package com.ambow.handler;

import com.ambow.util.ServiceUtil;
import org.omg.DynamicAny.DynAnyPackage.InvalidValueHelper;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//JDK动态代理的代理对象InvalidValueHelper

public class MylnvocationHandler implements InvocationHandler {
    private Object target;//此处为目标对象【业务层service】
    //加入target的构造方法或者set方法都可以


    public MylnvocationHandler(Object target) {
        this.target = target;
    }

//    public void setTarget(Object target) {
//        this.target = target;
//    }

    //Method是反射【指的对应的目标对象方法】,需要加入目标对象参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //原来的代码实现是怎么样还是怎么样,不改变原来的代码,只是在前后加入代理
        ServiceUtil.beginLog();//前置功能模块

        //可以理解为Method将doSome()方法封装为一个对象,使用invoke唤醒对象
        //相当于原来的target.doSome()【原来的功能】
        Object result = method.invoke(target, args);

        ServiceUtil.endLog();//后置功能模块
        return null;
    }
}

接口的定义与前面的相同。

接口实现类SomeServiceImpl的代码:

package com.ambow.service.impl;

import com.ambow.service.SomeService;
import com.ambow.util.ServiceUtil;
//原始的功能代码并未修改
//相当于将业务代码与日志代码分离开来,维护较为方便
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("======doSome方法=======");
    }

    @Override
    public void doOther() {
        System.out.println("======doOther方法=======");
    }
}

3.3 编辑一个测试类二

package com.ambow.test;

import com.ambow.handler.MylnvocationHandler;
import com.ambow.service.SomeService;
import com.ambow.service.impl.SomeServiceImpl;
import org.junit.Test;

import java.lang.reflect.Proxy;

public class JDKProxyTest {
    @Test
    public void test01(){
        SomeService someService = new SomeServiceImpl();
        someService.doSome();
    }

    //测试JDK动态代理
    //效果:原来的代码不改动,实现功能的增强
    @Test
    public void test02(){
        //1.创建目标对象
        SomeService target = new SomeServiceImpl();
        //2.创建handler,将target传进来
        MylnvocationHandler handler = new MylnvocationHandler(target);
        //3.创建代理对象
        //此处的代理对象应该与SomeService相同,是这个SomeService类的实现类
        //Proxy代理类的(newProxyInstance)方法创建新的代理实例
        //target.getClass().getClassLoader()获得类加载器
        //target.getClass().getInterfaces()目标对象实现的接口
        //最后将handler传进来
        SomeService proxy = (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), handler);
        //4.跳过代理对象来实现类的方法,实现功能
        proxy.doSome();
    }
}

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 四 使用spring-aop-aspectj的框架来实现AOP编程

4.1 创建Maven项目

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

项目架构:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

4.2 导入依赖 

(1)Maven依赖 

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.26</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.26</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

(2) 引入 AOP 约束

在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。

AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。

4.3  AspectJ 基于注解的 AOP 实现(掌握)

AspectJ 提供了以注解方式对于 AOP 的实现。

(1) 实现步骤

A、 Step1:定义业务接口与实现类IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

或者将前面jdk-proxy里面的service拿过来。 

B、Step2:定义切面类

类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

创建一个aspect的MyAspect类

package com.ambow.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//定义切面类:功能增强
//加入注解,说明是一个切面
@Aspect
public class MyAspect {
    //引入Before前置通知
    //属性:value切入点表达式[切入到目标类的那个方法上去],表达切面的执行位置
    //execution(参数类型  执行的包名称.类名称.类的方法(里面的参数可以任意))
    //位置:在方法的定义之前
    //
    @Before(value = "execution(* com.ambow.service.impl.SomeServiceImpl.doSome(..))")
    public void myBefore(){
        System.out.println("前置通知:在目标方法之前执行,例如:日志");
    }
}
C、Step3:声明目标对象切面类对象

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

此处为配置文件applicationContext.xml文件内容 

<?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">

    <!--声明目标类对象-->
    <bean id="someService" class="com.ambow.service.impl.SomeServiceImpl"/>

    <!--声明切面类对象-->
    <bean id="myAspect" class="com.ambow.aspect.MyAspect"/>

    <!--声明自动代理生成器 -> 创建代理 -->
    <aop:aspectj-autoproxy/>
</beans>
D、 Step4:注册 AspectJ 的自动代理

此处做一声明

原来的方式需要我们自己写

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

现在直接自己自动代理生成 

在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文 件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注 解,并按通知类型与切入点,将其织入,并生成代理。IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

<aop:aspectj-autoproxy />的底层是由 AnnotationAwareAspectJAutoProxyCreator 【注释感知 AspectJ 自动代理创建器】实现的。从其类名就可看出, 是基于 AspectJ 的注解适配自动代理生成器。

其工作原理是,通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。

E、 Step5:测试类中使用目标对象的 id

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 编辑测试类

package com.ambow.test;

import com.ambow.service.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AspectjTest {
    @Test
    public void test01(){
        //从容器里面获取对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //创建接口对象
        SomeService someService = (SomeService) context.getBean("someService");
        //接口调用方法
        someService.doSome();
    }
}

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

加入打印的类,接着运行既可以看到调用的类

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

(2) [掌握]@Before 前置通知-方法有 JoinPoint 参数

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。

不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 此处修改一下接口的声明

package com.ambow.service;

public interface SomeService {
    void doSome(String name,int age);
    void doOther();
}

接口类的修改:

package com.ambow.service.impl;

import com.ambow.service.SomeService;
//原始的功能代码并未修改
//相当于将业务代码与日志代码分离开来,维护较为方便
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name,int age) {
        //此处打印输出一下具体的参数
        System.out.println("======doSome方法=======" + name + ":" + age);
    }

    @Override
    public void doOther() {
        System.out.println("======doOther方法=======");
    }
}

测试类修改:

@Test
    public void test02(){
        //从容器里面获取对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //创建接口对象
        SomeService someService = (SomeService) context.getBean("someService");
        //打印一下获取的类
        System.out.println(someService.getClass());
        //接口调用方法,此时需要传递参数
        someService.doSome("张三",18);
    }
package com.ambow.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//定义切面类:功能增强
//加入注解,说明是一个切面
@Aspect
public class MyAspect {
    @Before(value = "execution(* com.ambow.service.impl.SomeServiceImpl.doSome(..))")
    // 1、JoinPoint: 表示连接点的方法
    public void myBefore(JoinPoint jp){
        System.out.println("连接点方法的定义:"+ jp.getSignature());
        System.out.println("连接点方法的参数个数:"+ jp.getArgs().length);

        Object[] args= jp.getArgs();
        for (Object arg : args) {
            System.out.println(arg);
        }
        System.out.println("前置通知:在目标方法之前执行,例如:日志");
    }
}

再次运行测试 

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

(3) [掌握]@AfterReturning 后置通知-注解有 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外, 还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

接口增加方法:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 重写的接口方法:【此时的接口有返回值】

package com.ambow.service;

public interface SomeService {
    void doSome(String name,int age);
    //此处就会有返回值类型
    String doOther(String name,int age);
}

实现方法:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 重写的接口实现类的方法:【此处需要修改原来的接口返回值类型并且给接口一个返回的参数】

@Override
    public String doOther(String name,int age) {
        System.out.println("======doOther方法======="+ name + ":" + age);
        return "abcd";
    }

定义切面:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 切面新增的后置通知代码:

//后置通知,返回还要加入
    @AfterReturning(value = "execution(* com.ambow.service.impl.SomeServiceImpl.doOther(..))",returning = "result")
    public void myAfterReturning(Object result){
        //修改目标方法的执行结果
        if (result !=null){
            //强制转换为String
            String s = (String) result;
            //转换为大写
            result = s.toUpperCase();
        }
        System.out.println("后置通知:在目标方法之后执行,例如:执行事务处理(切面)" +result);
    }

编写测试类test03:

@Test
    public void test03(){
        //从容器里面获取对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //创建接口对象
        SomeService someService = (SomeService) context.getBean("someService");
        //打印一下获取的类
        System.out.println(someService.getClass());
        //接口调用方法,此时需要传递参数
        someService.doOther("李四",28);
    }

 执行结果:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

(4) [掌握]@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值, Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强 方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

接口增加方法:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

package com.ambow.service;

public interface SomeService {
    void doSome(String name,int age);
    //此处就会有返回值类型
    String doOther(String name,int age);
    String doFirst(String name,int age);
}

 接口方法的实现:

 IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 接口实现类的新增部分:

@Override
    public String doFirst(String name, int age) {
        System.out.println("======doFirst方法======="+ name + ":" + age);
        return "doFirst";
    }

定义切面:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 切面新增的方法:

//环绕通知——>环绕通知=前置+目标方法执行+后置通知
    @Around(value = "execution(* com.ambow.service.impl.SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        //增强功能
        System.out.println("环绕通知:在目标方法之前执行,例如:输出日志");
        //,proceed方法就是用于启动目标方法执行
        Object obj = pjp.proceed();//调用目标方法
        //增强的功能
        System.out.println("环绕通知:在目标方法之后执行,例如:处理事务");
        return obj;
    }
@Test
    public void test04(){
        //1.获取到Spring的容器
        //使用技巧:连续按住shift两次就可以看到类,ctrl+h可以查看ApplicationContext接口
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从容器当中取出对象
        //创建接口对象
        SomeService someService = (SomeService) ctx.getBean("someService");
        //接口调用方法,此时需要传递参数
        someService.doFirst("王五",22);
    }

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java  

(5) [了解]@AfterThrowing 异常通知-注解中有 throwing 属 性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

增加业务方法:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

package com.ambow.service;

public interface SomeService {
    void doSome(String name,int age);
    //此处就会有返回值类型
    String doOther(String name,int age);
    String doFirst(String name,int age);
    void doSecond();
}

方法实现:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

@Override
    public void doSecond() {
        System.out.println("======doSecond方法========" + (10/0));
    }

此处人为制造异常

定义切面:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 异常通知的方法:

//异常通知
    @AfterThrowing(value = "execution(* com.ambow.service.impl.SomeServiceImpl.doSecond(..))",throwing = "ex")
    public void myAfterThrowing(Throwable ex){
        //把异常发生的时间,位置,原因记录到数据库,日志文件等等,
        //可以在异常发生时,把异常信息通过短信,邮件发送给开发人员。
        System.out.println("异常通知:在目标方法抛出异常的时候执行,异常原因:" + ex.getMessage());
    }
 @Test
    public void test05(){
        //1.获取到Spring的容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从容器当中取出对象
        //创建接口对象
        SomeService someService = (SomeService) ctx.getBean("someService");
        //接口调用方法,此时需要传递参数
        someService.doSecond();
    }

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 此时如果前面的(10/0)换成(10/2)则不会在处理异常

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

(6) [了解]@After 最终通知

无论目标方法是否抛出异常,该增强均会被执行。

增加方法:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

package com.ambow.service;

public interface SomeService {
    void doSome(String name,int age);
    //此处就会有返回值类型
    String doOther(String name,int age);
    String doFirst(String name,int age);
    void doSecond();
    void doThird();
}

方法实现:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

@Override
    public void doThird() {
        System.out.println("=======doThird方法=======" + (10/0));
    }

定义切面:

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

//最终通知
    @After(value = "execution(* com.ambow.service.impl.SomeServiceImpl.doThird(..))")
    public void myAfter(){
        System.out.println("最终通知:总是会被执行的方法");
    }
@Test
    public void test06(){
        //1.获取到Spring的容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从容器当中取出对象
        //创建接口对象
        SomeService someService = (SomeService) ctx.getBean("someService");
        //接口调用方法,此时需要传递参数
        someService.doThird();
    }

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java 

(7) @Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。

其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切 入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

//相当于给切入点表达式,起一个别名
    @Pointcut(value = "execution(* com.ambow.service.impl.SomeServiceImpl.*(..))")
    public void myPointcut() {
    }

修改环绕通知:

//环绕通知——>环绕通知=前置+目标方法执行+后置通知
    @Around(value = "myPointcut()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        //增强功能
        System.out.println("环绕通知:在目标方法之前执行,例如:输出日志");
        //,proceed方法就是用于启动目标方法执行
        Object obj = pjp.proceed();//调用目标方法
        //增强的功能
        System.out.println("环绕通知:在目标方法之后执行,例如:处理事务");
        return obj;
    }

 IDEA项目实践——Spring当中的切面AOP,IDEA软件的使用与相关项目的操作,intellij-idea,spring,java

 

总结

以上就是今天的内容~

欢迎大家点赞👍,收藏⭐,转发🚀,
如有问题、建议,请您在评论区留言💬哦。

最后:转载请注明出处!!文章来源地址https://www.toymoban.com/news/detail-635587.html

到了这里,关于IDEA项目实践——Spring当中的切面AOP的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • spring之面向切面:AOP(2)

    学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需写作干货注入能量… 热爱写作,愿意让自己成为更好

    2024年02月04日
    浏览(47)
  • Spring-AOP(面向切面)

    功能接口 实现类 在含有日志输出的实现类中可以了解到:与核心业务功能没有关系的日志输出加杂在模块中,对核心业务功能有干扰。 思路:解耦 , 将附加功能从业务功能模块中抽取出来 概念 二十三种设计模式中的一种,属于结构型模式,它的作用就是通过提供一个代理

    2024年02月16日
    浏览(42)
  • 注解 + spring aop切面+ SpringEl

    2024年02月09日
    浏览(48)
  • javaee spring 测试aop 切面

    spring配置文件

    2024年02月09日
    浏览(39)
  • Spring-aop面向切面

    1、理解必要的专业术语  先看看上面图,这是我的个人理解。(画的丑,主打真实)         1)Advice,通知/增强:类方法中提出来的共性功能(大白话就是提出来的重复代码)         2)Pointcut,切入点/切点:通知返回的方法         3)连接点:方法         4)织入:

    2023年04月15日
    浏览(95)
  • 26.Spring-AOP(切面编程)

    目录 一、Spring-AOP。 (1)AOP的简介。  (2)AOP的底层实现-动态代理。  (2.1)JDK的动态代理。  (2.2)cglib的动态代理。  (3)AOP的相关概念。  (4)xml配置——AOP的快速入门。 (5) xml配置AOP详解。 (5.1)切点表达式的写法。  (5.2)通知的类型。 (5.3)切点表达式的

    2023年04月24日
    浏览(51)
  • spring多个aop切面之间的顺序

    业务中通常会定义多个注解来进行切面,那么当一个方法有多个注解的时候,此时这些切面是以什么顺序来执行的呢。 spring 的 aop 切面有点像鸡蛋的层次 最外面的切面包裹着里面的切面 捞个网图 既然是属于spring的,那用的肯定是spring自己的顺序控制方法 可以用 Ordered接口

    2024年02月10日
    浏览(42)
  • [SSM]Spring面向切面编程AOP

    目录 十五、面向切面编程AOP 15.1AOP介绍 15.2AOP的七大术语 15.3切点表达式 15.4使用Spring的AOP 15.4.1准备工作 15.4.2基于AspectJ的AOP注解式开发 15.4.3基于XML配置方式的AOP(了解) 15.5AOP的实际案例:事务处理 15.6AOP的实际案例:安全日志 IoC使软件组件松耦合。AOP让你能够捕捉系统中经

    2024年02月15日
    浏览(48)
  • 认识 spring AOP (面向切面编程) - springboot

    本篇介绍什么是spring AOP, AOP的优点,使用场景,spring AOP的组成,简单实现AOP 并 了解它的通知;如有错误,请在评论区指正,让我们一起交流,共同进步! 本文开始 AOP: 面向切面编程,也就是面向某一类编程,对某一类事情进行统一处理; spring AOP: 是实现了AOP这种思想的一

    2024年02月14日
    浏览(50)
  • Spring AOP(面向切面编程)和方法拦截

    Spring AOP(面向切面编程)和方法拦截 Spring是一款广泛使用的Java开发框架,提供了丰富的功能和工具,用于简化企业级应用程序的开发。其中一个重要的特性是面向切面编程(AOP)和方法拦截。本文将介绍Spring AOP和方法拦截的概念、工作原理以及在实际开发中的应用。 在软

    2024年02月05日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包