Spring(五)——【面向切面编程AOP】

简介: Spring(五)——【面向切面编程AOP】

目录


Spring的AOP简介


aop是什么?


AOP的作用及其优势


AOP的底层实现


JDK的动态代理


cglib 的动态代理


AOP相关概念


AOP开发明确的事项


基于XML的AOP开发


快速入门


XML配置AOP详解


1.切点表达式的写法


2.通知的类型


基于注解的AOP开发


aop快速入门


注解配置AOP详解


Spring的AOP简介

aop是什么?

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

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



AOP的作用及其优势

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


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


AOP的底层实现

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

AOP的动态代理技术


常用的动态代理技术

JDK代理∶基于接口的动态代理技术

cglib代理:基于父类的动态代理技术

image.png

JDK的动态代理

①目标类接口

1. public interface TargetInterface {
2. public void save();
3. }

②目标类

public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

③增强方法

public class Advice {
    public void before(){
        System.out.println("前置增强....");
    }
    public void afterReturning(){
        System.out.println("后置增强....");
    }
}

④动态代理代码,调用代理对象的方法测试

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        final Target target = new Target();
        //增强对象
        final Advice advice = new Advice();
        //返回值 就是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象类加载器
                target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组
                new InvocationHandler() {
                    //调用代理对象的任何方法  实质执行的都是invoke方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.before(); //前置增强
                        Object invoke = method.invoke(target, args);//执行目标方法
                        advice.afterReturning(); //后置增强
                        return invoke;
                    }
                }
        );
        //调用代理对象的方法
        proxy.save();
    }
}

cglib 的动态代理

①目标类

1. public class Target {
2. public void save() {
3.         System.out.println("save running.....");
4.     }
5. }

②增强方法

public class Advice {
    public void before(){
        System.out.println("前置增强....");
    }
    public void afterReturning(){
        System.out.println("后置增强....");
    }
}

③动态代理代码,调用代理对象方法进行测试

public class ProxyTest {
    public static void main(String[] args) {
        //目标对象
        final Target target = new Target();
        //增强对象
        final Advice advice = new Advice();
        //返回值 就是动态生成的代理对象  基于cglib
        //1、创建增强器
        Enhancer enhancer = new Enhancer();
        //2、设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                advice.before(); //执行前置
                Object invoke = method.invoke(target, args);//执行目标
                advice.afterReturning(); //执行后置
                return invoke;
            }
        });
        //4、创建代理对象
        Target proxy = (Target) enhancer.create();
        proxy.save();
    }
}

AOP相关概念

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

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

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

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

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

Pointcut(切入点)︰所谓切入点是指我们要对哪些Joinpoint进行拦截的定义【真正被增强的方法】

Advice (通知/增强)∶所谓通知是指拦截到Joinpoint之后所要做的事情就是通知【封装增强业务逻辑的方法】

Aspect (切面)︰是切入点和通知(引介)的结合【目标方法(切点)+增强(通知)】

Weaving (织入)︰是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入【将切点和通知结合的过程】


AOP开发明确的事项

1.需要编写的内容

①编写核心业务代码(目标类的目标方法)

②编写切面类,切面类中有通知(增强功能方法)

③在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合


2.AOP技术实现的内容

Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。


3.AOP底层使用哪种代理方式

在spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。


基于XML的AOP开发

快速入门

①导入AOP相关坐标

       <!--导入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.4</version>
        </dependency>

②创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running.....");
    }
}

③创建切面类(内部有增强方法)

public class MyAspect {
        //前置增强方法
    public void before(){
        System.out.println("前置增强..........");
    }
}

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

    <!--目标对象-->
    <bean id="target" class="com.longdi.aop.Target"></bean>
    <!--切面对象-->
    <bean id="myAspect" class="com.longdi.aop.MyAspect"></bean>

⑤在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: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 http://www.springframework.org/schema/aop/spring-aop.xsd
">

配置切点表达式和前置增强的织入关系

    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
        <!--切点+通知-->
<aop:before method="before" pointcut="execution(public void com.longdi.aop.Target.save())"></aop:before>
        </aop:aspect>
    </aop:config>

⑥测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration ( "classpath:applicationContext.xml ")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1 ( ) {
target.save( ) ;
}

XML配置AOP详解

1.切点表达式的写法

表达式语法:

execution([修饰符]返回值类型包名.类名.方法名(参数))

访问修饰符可以省略

返回值类型、包名、类名、方法名可以使用星号*代表任意

包名与类名之间一个点.代表当前包下的类,两个点.表示当前包及其子包下的类参数列表可以使用两个点.表示任意个数,任意类型的参数列表

例如:


execution (public void com.longdi.aop.Target.method())
execution (void com.longdi.aop.Target.* (..))
execution (* com.longdi.aop.*.*(..))
execution (* com.longdi.aop..*.*(..))
execution (* *..*.* ( ..) )

2.通知的类型

通知的配置语法:

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

image.png

public class MyAspect {
    public void before(){
        System.out.println("前置增强..........");
    }
    public void afterReturning(){
        System.out.println("后置增强..........");
    }
    //Proceeding JoinPoint:  正在执行的连接点===切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强....");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强....");
        return proceed;
    }
    public void afterThrowing(){
        System.out.println("异常抛出增强..........");
    }
    public void after(){
        System.out.println("最终增强..........");
    }
}

application.xml:

<aop:before method="before" pointcut="execution(* com.longdi.aop.*.*(..))"/>
<aop:after-returning method="afterReturning" pointcut="execution(* com.longdi.aop.*.*(..))"/>
 <aop:around method="around" pointcut="execution(* com.longdi.aop.*.*(..))"/>
 <aop:after-throwing method="afterThrowing" pointcut="execution(* com.longdi.aop.*.*(..))"/>
 <aop:after method="after" pointcut="execution(* com.longdi.aop.*.*(..))"/>

3. 切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式。

<aop :config>
<!--引用myAspect的Bean为切面对象-->
<aop:aspect ref="myAspect">
<aop:pointcut id="myPointcut" expression="execution(* com.longdi.aop.*.*(..) )" />
<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
    </aop: aspect>
</aop:config>

基于注解的AOP开发

aop快速入门

基于注解的aop开发步骤:

①创建目标接口和目标类(内部有切点)

public interface TargetInterface {
    public void save();
}
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

②创建切面类(内部有增强方法)

public class MyAspect {
    //前置增强方法
    public void before(){
        System.out.println("前置代码增强..........");
    }

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

@Component("target")
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}
@Component("myAspect")
public class MyAspect {
    public void before(){
        System.out.println("前置增强..........");
    }

④在切面类中使用注解配置织入关系

@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("execution(* com.longdi.anno.*.*(..))")
    public void before(){
        System.out.println("前置增强..........");
    }

⑤在配置文件中开启组件扫描和AOP的自动代理

    <!--组件扫描-->
    <context:component-scan base-package="com.longdi.anno"/>
    <!--aop自动代理-->
    <aop:aspectj-autoproxy/>

⑥测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.save();
    }
}

注解配置AOP详解

1.注解通知的类型

通知的配置语法:@通知注解(“切点表达式")

image.png

2.切点表达式的抽取

同xml配置 aop一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在在增强注解中进行引用。具体如下:

@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("myAspect.pointcut()")
    public void before(){
        System.out.println("前置增强..........");
    }
   //定义切点表达式
    @Pointcut("execution(* com.longdi.anno.*.*(..))")
    public void pointcut(){}
}
目录
打赏
0
0
0
0
2
分享
相关文章
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
15天前
|
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
76 25
|
15天前
|
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
67 24
Spring Boot中的WebFlux编程模型
Spring WebFlux 是 Spring Framework 5 引入的响应式编程模型,基于 Reactor 框架,支持非阻塞异步编程,适用于高并发和 I/O 密集型应用。本文介绍 WebFlux 的原理、优势及在 Spring Boot 中的应用,包括添加依赖、编写响应式控制器和服务层实现。WebFlux 提供高性能、快速响应和资源节省等优点,适合现代 Web 应用开发。
82 15
阿里云工程师跟通义灵码结伴编程, 用Spring AI Alibaba来开发 AI 答疑助手
本次分享的主题是阿里云工程师跟通义灵码结伴编程, 用Spring AI Alibaba来开发 AI 答疑助手,由阿里云两位工程师分享。
阿里云工程师跟通义灵码结伴编程, 用Spring AI Alibaba来开发 AI 答疑助手
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
82 8
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
111 5
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
104 8