Spring进阶-AOP配置xml

简介: Spring进阶-AOP配置xml

1 Spring 的 AOP 简介

1.1 OOP开发思路

1.2 什么是 AOP

AOP 为 Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。


AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.3 AOP 的作用及其优势

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

1.4 AOP 的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

1.5 AOP 相关术语

在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

Target(目标对象):代理的目标对象

Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类


Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点


Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义


Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知


Aspect(切面):是切入点和通知(引介)的结合


Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入


其中,重要的几个概念:


目标对象target:计划被增强的对象


切点pointcut:计划被增强的对象中的方法


通知advice:附加功能


切面类:通知(增强功能)所在的类,称为切面类切面aspect:切点(要增强的方法)+通知(增强的功能) = 切面。

2 基于AOP的开发步骤

1.编写要增强的类,该类称为目标类

2.编写附加功能,附加功能要编写到一个类中。附加功能称为通知,附加功能所在的类被称为切面类

3.将目标类和切面类中的通知配置到一块

3 基于 XML 的 AOP 开发

3.1 快速入门

3.1.1 导入maven

<dependencies>
     <!--导入spring的context坐标,context依赖aop-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>5.0.5.RELEASE</version>
     </dependency>
     <!-- aspectj的织入 -->
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.8.13</version>
     </dependency>
     <!--Spring集成Junit测试-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>5.0.5.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
     </dependency>
 </dependencies>

3.1.2 创建目标接口和目标类

在cn.oldlu.aop包中新建

public interface TargetInterface {
    public void save();
}
public class Target implements TargetInterface {
    @Override
    //save方法就是切点,即计划被增强的方法
    public void save() {
        System.out.println("save()方法正在执行,保存数据中");
    }
}

3.1.3 创建切面类以及通知

在cn.oldlu.aop包中新建

package cn.oldlu.aop;
public class MyAspect {
    //附加功能,被称为通知,通知所在的类称为切面类
    public void startTransactional(){
        System.out.println("开启事物");
    }
    public void submitTransactional(){
        System.out.println("提交事物");
    }
}

3.1.4 将目标类和切面类的对象创建权交给spring

<!--    要增强的对象-->
    <bean id="target" class="cn.oldlu.aop.Target"></bean>
<!--    附加功能-->
    <bean id="myAspect" class="cn.oldlu.aop.MyAspect"></bean>

3.1.5 在 applicationContext.xml 中配置切面

需要先导入aop命名空间

<?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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <!--要被增强的对象,目标对象-->
    <bean class="cn.oldlu.aop.Target"></bean>
    <!--切面类,里面封装了附加功能-->
    <bean id="myAspect" class="cn.oldlu.aop.MyAspect"></bean>
    <!--配置AOP-->
    <aop:config >
        <aop:aspect ref="myAspect">
            <!--配置切面 切面 = 通知(附加功能)+切入点(被增强的方法)-->
            <aop:before  method="startTransactional" pointcut="execution(public void cn.oldlu.aop.Target.save())"></aop:before>
            <aop:after method="submitTransactional" pointcut="execution(public void cn.oldlu.aop.Target.save())"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

上面代码切入点表达式写了两次,可以抽取出公共的

<!--配置AOP-->
<aop:config >
    <aop:aspect ref="myAspect">
        <aop:pointcut id="pc" expression="execution(public void cn.oldlu.aop.Target.save())"/>
        <!--配置切面 切面 = 通知(附加功能)+切入点(被增强的方法)-->
        <aop:before  method="startTransactional" pointcut-ref="pc"></aop:before>
        <aop:after method="submitTransactional" pointcut-ref="pc" ></aop:after>
    </aop:aspect>
</aop:config>

3.1.7 测试代码

import cn.oldlu.target.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class T {
    @Autowired
    private TargetInterface target;
    @Test
    public void 入门案例测试(){
        target.save();
    }
}

3.1.8 测试结果

开启事物
save()方法正在执行,保存数据中
提交事物

3.1.9 可能出现的异常

Unsatisfied dependency expressed through field 'target'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'cn.oldlu.aop.Target#0' is expected to be of type 'cn.oldlu.aop.Target' but was actually of type 'com.sun.proxy.$Proxy14'

原因:默认spring使用JDK代理,基于接口, @Autowired private TargetInterface target; 这里要用接口接受代理对象

3.2 XML 配置 AOP 详解

3.2.1 切点表达式的写法

image.png

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
  • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

例如:

execution(public void com.itheima.aop.Target.method())  
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))//第一个* 表示返回值类型,最常用
execution(* *..*.*(..))

3.2.2 切入点的三种配置方式

<aop:config>
    <!--方式1:配置公共切入点-->
    <aop:pointcut id="pt1" expression="execution(* *(..))"/>
    <aop:aspect ref="myAdvice">
        <!--方式2:配置局部切入点-->
        <aop:pointcut id="pt2" expression="execution(* *(..))"/>
        <!--引用公共切入点-->
        <aop:before method="logAdvice" pointcut-ref="pt1"/>
        <!--引用局部切入点-->
        <aop:before method="logAdvice" pointcut-ref="pt2"/>
        <!--方式3:直接配置切入点-->
        <aop:before method="logAdvice" pointcut="execution(* *(..))"/>
    </aop:aspect>
</aop:config>

20201205112351522.png

3.2.3 通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

环绕通知相当于将要写到配置文件里的通知(前置、后置、异常、最终)整合到一个方法里了

如果在XML中不配置其他四种通知,那么可以使用java代码来配置其他四种通知。

环绕通知

public Object around(ProceedingJoinPoint pjp) throws Throwable {
    //TODO 附加功能
    Object ret = pjp.proceed(pjp.getArgs);  //调用被增强的方法
    //TODO 附加功能
    return ret;
}

3.2.4 在通知中获取目标方法的实参

格式

public void before(JoinPoint jp) throws Throwable {
    Object[] args = jp.getArgs();
}

测试步骤

1.创建实体类

package cn.oldlu.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private Integer age;
}

2.创建目标类

package cn.oldlu.aop;
import cn.oldlu.domain.User;
public interface TargetInterface {
    public void save(User user);
}
package cn.oldlu.aop;
import cn.oldlu.domain.User;
public class Target implements TargetInterface {
    //save方法就是切点,即计划被增强的方法
    @Override
    public void save(User user) {
        System.out.println("save()方法正在执行,保存数据中,姓名:"+user.getName()+"年龄"+user.getAge());
    }
}

3.创建切面类

package cn.oldlu.aop;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
public class MyAspect {
    //附加功能,被称为通知,通知所在的类称为切面类
    public void log(JoinPoint jp){//获取目标方法save中的参数
        Object[] args = jp.getArgs();
        System.out.println("目标方法的实参是:"+ Arrays.toString(args));
    }
}

4.修改切入点

因为save方法多了参数所以参数要写…

<aop:pointcut id="pc" expression="execution(public void cn.oldlu.aop.Target.save(..))"/>

5.测试

import cn.oldlu.aop.TargetInterface;
import cn.oldlu.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class T {
    @Autowired
    private TargetInterface target;
    @Test
    public void 入门案例测试(){
        target.save(new User("tom",3));
    }
}

6.输出结果

save()方法正在执行,保存数据中,姓名:tom年龄3
目标方法的实参是:[User(name=tom, age=3)]

3.2.5 在通知中获取目标方法的返回值

格式,只能用在afterReturning通知中

public void afterReturning(Object result) {
    System.out.println(result);
}

代码

1.修改TargetInterafce中的save方法添加返回值类型为int

package cn.oldlu.aop;
import cn.oldlu.domain.User;
public interface TargetInterface {
    public int save(User user);
}
package cn.oldlu.aop;
import cn.oldlu.domain.User;
public class Target implements TargetInterface {
    //save方法就是切点,即计划被增强的方法
    @Override
    public int save(User user) {
        System.out.println("save()方法正在执行,保存数据中,姓名:"+user.getName()+"年龄"+user.getAge());
        return 1;
    }
}

2.修改配置文件

配置文件中的切点表达式返回值现在为void,改成int,并且修改通知类型为after-returning

<!--配置切入点-->
<aop:pointcut id="pc" expression="execution(public int cn.oldlu.aop.Target.save(..))"/>
<!--<aop:before method="startTrasaction" pointcut-ref="pc"></aop:before>-->
<aop:after-returning method="log" pointcut-ref="pc" returning="result"></aop:after-returning>

3.修改切面类,获取返回值

package cn.oldlu.aop;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
public class MyAspect {
    public void log(JoinPoint jp,Object result){
        Object[] args = jp.getArgs();
        System.out.println("目标方法的实参是:"+ Arrays.toString(args));
        System.out.println("目标方法的返回值是:"+ result);
    }
}

3.2.6 在通知中获取目标方法抛出的异常信息

方式1,在通知类中使用Throwable中接收异常信息

aop配置

<aop:aspect ref="myAdvice">
  <aop:pointcut id="pt4" expression="execution(* *(..))  "/>
    <aop:after-throwing method="afterThrowing" pointcut-ref="pt4" throwing="t"/>
</aop:aspect>

通知类

public void afterThrowing(Throwable t){
    System.out.println(t.getMessage());
}

方式2,使用环绕通知,直接使用trycatch捕获

public Object around(ProceedingJoinPoint pjp) throws Throwable {
    Object ret = pjp.proceed(); //对此处调用进行try……catch……捕获异常,或抛出异常
    return ret;
}
<aop:aspect ref="myAdvice">
    <aop:pointcut id="pt4" expression="execution(* *(..))  "/>
    <aop:around method="around" pointcut-ref="pt4" />
</aop:aspect>


目录
相关文章
|
11月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
889 0
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1712 13
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
3297 1
|
10月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
1137 0
|
12月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
12月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
1465 0
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
1272 25
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
1848 0