Spring5-AOP

本文介绍了AOP的概念,它是面向切面编程,用于分离业务逻辑和系统逻辑,提高代码的可维护性。AOP的实现基于动态代理,SpringAOP支持JDK和CGLIB两种方式。文中详细演示了JDK动态代理的工作原理,并通过代码示例展示了如何创建代理对象。此外,文章还讲解了AOP的相关术语,如连接点、切入点、通知和切面。最后,介绍了AspectJ的使用,包括导入依赖、切入点表达式和注解实现AOP的步骤。
摘要由CSDN通过智能技术生成

目录

​编辑

一:什么是AOP

二:AOP的底层原理

三:演示JDK动态代理模式

 四:AOP操作术语

五:AOP的实现(基于AspectJ)

5.1:导入依赖

5.2:切入点表达式

5.3:注解实现AOP

细节一:相同切入点的抽取

 细节二:有多个增强类对同一个方法进行增强,设置增强类优先级


一:什么是AOP

        AOP(Aspect-Oriented Programming)是一种编程思想,也是其中非常重要的一个特征之一。它基于切面(Aspect)的编程技术,将应用程序中的业务逻辑与系统逻辑分离,提高代码的可复用性、可维护性和可扩展性。

二:AOP的底层原理

AOP的实现原理是基于动态代理

动态代理:具体来说,SpringAOP支持两种不同类型的代理模式:JDK动态代理和CGLIB代理。如果要代理的目标对象实现了至少一个接口,则会使用JDK动态代理;如果目标对象没有实现任何接口,则会使用CGLIB来实现代理对象。

三:演示JDK动态代理模式

        JDK动态代理是一种基于接口的代理技术,通过Proxy.newProxyInstance()方法和InvocationHandler接口实现。在这种代理技术中,代理对象和目标对象必须实现同样的接口。

Proxy.newProxyInstance()方法用于创建一个实现了指定接口的代理对象。方法有三个参数:public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException

  • ClassLoader:类加载器:用于指定生成代理类的加载器;
  • Class[]:要实现的接口:代理类需要实现的接口;
  • InvocationHandler:调用处理器:代理对象的调用会转发到该处理器的invoke()方法中进行处理

  • InvocationHandler:实现这个接口,接口中只声明了一个方法invoke(),该方法用于处理代理对象的方法调用,每次代理对象的方法被调用时,都会触发invoke()方法的执行,并将代理对象、调用的方法对象以及方法参数传入参数中,我们可以在invoke()方法中定义对这些参数的操作,比如记录日志信息、控制访问权限等。
  • invoke中的三个参数:
    • proxy:表示代理对象本身:在invoke方法中,我们可以通过这个参数来调用代理对象的其他方法,或者将代理对象传递给其他方法使用
    • method:正在被代理的方法:通过这个参数,我们可以获取到当前正在被代理的方法信息,如方法名、参数类型
    • args:方法的参数:通过这个参数,,我们可以获取到当前正在被代理的方法的所有参数,这些参数以数组形式传递

 演示:

package com.juju.spring5;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author lcy
 * @version 1.0
 * @date 2023/3/30 17:36
 */
public interface User {
    void add();

    static void main(String[] args) {

    }
}

//被代理对象
class UserImpl implements User {
    @Override
    public void add() {
        System.out.println("add方法被执行了");
    }
}


//代理处理器
class UserProxy implements InvocationHandler {

    //接收一个被代理对象,保存在成员变量object中
    private Object object;

    public UserProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        //method.getName()获取被代理对象的方法名 args:被代理的参数列表
        System.out.println("方法之前..." + method.getName());

        //原来的方法
        //通过反射机制来调用被代理对象的方法
        Object invoke = method.invoke(object, args);
        //方法之后
        System.out.println("方法之后");

        //返回出原来的值之后返回给代理对象
        return invoke;
    }
}

    class Test{
        public static void main(String[] args) {
            //创建被代理对象
            User hello = new UserImpl();

            //创建代理处理器
            //new YanShiProxy代表要处理被代理对象
            UserProxy userProxy = new UserProxy(hello);

            //创建代理对象
            User pp = (User) Proxy.newProxyInstance(
                    //类加载器
                    hello.getClass().getClassLoader(),
                    //获取被代理类实现的接口列表
                    hello.getClass().getInterfaces(),
                    userProxy);
            pp.add();
        }
    }

输出:

 四:AOP操作术语

1:连接点:类中哪些方法可以增强,这些方法被称为切入点

2:切入点:实际被真正增强的方法,称为切入点

3:通知(增强):(1):实际增强的逻辑代码就是通知,加进来的代码

(2):通知有多种:前置通知、后置通知、环绕通知、异常通知(目标方法抛出异常后执行的通知)、最终通知;

4:切面:由切点和通知组成的,通过将切点和通知装在一起,形成一个可复用的模块,以便在程序中的多个位置进行使用

五:AOP的实现(基于AspectJ)

AspectJ是基于Java语言的切面编程框架,它扩展了Java语言本身,提供了丰富的面向切面编程支持。AspectJ可以与spring框架集成使用,是spring AOP的一种实现方式

可以使用xml文件实现

可以使用注解实现

5.1:导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.20.RELEASE</version>
</dependency>


<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>1.9.5</version>
</dependency>

<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.0</version>
</dependency>

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

5.2:切入点表达式

        切入点表达式用于确定哪些方法需要被增强,springAOP支持两种类型的切入点表达式:基于方法签名的切入点和基于注解的切入点表达式

        格式:

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

基于签名的切入点

execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

举例说明:excution(public * com.example.service.UserService.*(..))

代表对com.example.service.UserService类中的所有public方法,并且参数列表是任意类型返回类型是任意类型的的所有类增强

5.3:注解实现AOP

第一步:创建两个类,把增强类中的方法写入add方法之前

public class User {
    public void add(){
        System.out.println("add...方法");
    }
}
public class UserProxy {
    public void proxyUser(){
        System.out.println("方法之前....");
    }
}

第二步:开启注解扫描

xml文件中引入context、aop命名空间

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">
<!--    开启注解扫描-->
    <context:component-scan base-package="com.juju.spring5.ProxyUser"></context:component-scan>

</beans>

第三步:使用注解创建User和UserProxy对象

@Component创建对象

第四步:在增强类上添加@Aspect

第五步:在xml配置文件中开启生成代理对象

<!--    开启AspectJ生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

第六步:在增强类中方法添加前置通知

前置通知:@Before

最终通知:@After

后置通知:@AfterReturning

异常通知:@AfterThrowing

环绕通知:@Around

package com.juju.spring5.ProxyUser;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author lcy
 * @version 1.0
 * @date 2023/3/31 14:12
 */
@Component
@Aspect
public class UserProxy {

    //前置通知
    //Before注解表示作为前置通知
    //里面填入切点表达式,哪个类中的那个方法需要被增强
    @Before(value = "execution(public * com.juju.spring5.ProxyUser.User1.add(..))")
    public void proxyUser(){
        System.out.println("Before--前置....");
    }

    //最终通知
    @After(value = "execution(public * com.juju.spring5.ProxyUser.User1.add(..))")
    public void after(){
        System.out.println("After--最终...");
    }

    //后置通知
    @AfterReturning(value = "execution(public * com.juju.spring5.ProxyUser.User1.add(..))")
    public void after1(){
        System.out.println("AfterReturning--后置...");
    }

    //异常通知
    @AfterThrowing(value = "execution(public * com.juju.spring5.ProxyUser.User1.add(..))")
    public void after2(){
        System.out.println("AfterThrowing--异常...");
    }

    //环绕通知
    //ProceedingJoinPoint
    @Around(value = "execution(public * com.juju.spring5.ProxyUser.User1.add(..))")
    public void after3(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around--环绕之前...");
        //该方法用于调用目标对象的原有方法
        proceedingJoinPoint.proceed();
        System.out.println("Around--环绕之后...");

    }
}

细节一:相同切入点的抽取

        发现上面的切入表达式一样的内容,我们可以@pointcut注解抽取相同的切入点。@pointcut注解定义了一个切入点表达式,可以在其他通知中通过引用该切入点表达式来复用代码

        需要注意的是,@Pointcut注解不能直接使用在通知方法上,必须定义一个独立的方法来使用。同时,切入点表达式可以在其他地方进行引用,例如可以在@Before注解中使用该切入点表达式。这样大大减少代码重复,使AOP的代码更加清晰和简洁

 细节二:有多个增强类对同一个方法进行增强,设置增强类优先级

@order注解:用于指定切面的执行顺序。当有多个切面对同一个连接点进行增强时,可以使用@order注解为它们指定执行先后顺序,数值越小的切面优先级越高,默认优先级为int类型最大值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值