首页 > 其他分享 >Spring - AOP - 底层原理

Spring - AOP - 底层原理

时间:2024-07-18 18:28:06浏览次数:18  
标签:调用 对象 Spring 代理 接口 AOP 方法 public 底层

目录:

Spring - AOP - 底层原理

1. 代理模式

代理模式是一种比较好的理解的设计模式。简单来说就是:

  1. 使用代理对象来增强目标对象(target obiect),这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
  2. 将核心业务代码和非核心的公共代码分离解耦,提高代码可维护性,让被代理类专注业务降低代码复杂度。
    • 被代理类专注业务
    • 代理类非核心的公共代码

通常用代理实现比如拦截器,事务控制,还有测试框架 mock、用户鉴权、日志、全局异常处理等功能

  • 代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。

举个例子:客户找老板, 让秘书登记来访人员和时间, 记录来访用时,并且让秘书拒绝老板不想见的客户。 秘书充当代理角色。

在这里插入图片描述

/**
 * CEO 类代表首席执行官,用于模拟与客户会面的行为。
 */
public class CEO {
    /**
     * 与指定名称的客户进行会面。
     * 
     * @param name 客户的名称。
     *             说明:该参数用于指定与CEO会面的客户的名字,以便在会面过程中进行引用。
     */
    public void meeting(String name) {
        System.out.println("CEO 接见客户:" + name);
    }
}


/**
 *=========================================================================
 * CEO代理类,用于控制对CEO会议的访问。
 * 该类继承自CEO类,以实现对特定人员(如张三)的访问控制。
 */
public class CEOProxy extends CEO{
    /**
     * 代理会议访问方法。
     * 在允许访问之前,此方法会记录来访者的姓名。
     * 如果来访者是张三,则允许进入会议;否则,不允许进入。
     *
     * @param name 来访者的姓名。
     */
    @Override
    public void meeting(String name) {
        // 记录来访人员,以便于会议管理和安全控制。
        System.out.println("记录来访人员:" + name);
        // 判断来访者是否为张三,张三是被允许直接参加会议的人。
        if (name.equals("张三")){
            System.out.println("允许进入");
            // 如果是张三,則调用CEO的会议方法,允许进入会议。
            super.meeting(name);
        }
    }
}


/**
 *=========================================================================
 * 测试代理类的示例。
 * 该类演示了如何使用代理模式来控制对CEO对象的访问。
 */
public class TestProxy {
    /**
     * 程序入口点。
     * 创建一个CEO代理对象,并通过代理对象来调用会议方法。
     * @param args 命令行参数,本例中未使用。
     */
    public static void main(String[] args) {
        // 创建CEO的代理对象
        CEO ceo = new CEOProxy();
        // 通过代理对象安排会议
        ceo.meeting("张三");
    }
}

2. 静态代理

  • 静态代理是一种设计模式,其中代理类是在编译时期就确定的,也就是说,代理类的源代码在编译时就已经存在,而不是在运行时动态生成的。这种代理模式通常用于需要在方法调用前后添加额外功能的场景,例如日志记录、权限检查、事务管理等。

在Java中,静态代理通常涉及以下元素:

  1. 接口:定义了一组方法,这些方法将由代理类和真实主题类实现。
  2. 真实主题类:实现了接口,并提供了具体的功能实现。
  3. 代理类:也实现了相同的接口,它包含对真实主题类的引用。代理类的主要职责是封装对真实主题的调用,并在调用前后添加额外的操作。

下面是一个简单的Java静态代理模式的示例:

// 定义接口
public interface Subject {
    void doAction();
}

// 目标类
public class RealSubject implements Subject {
    @Override
    public void doAction() {
        System.out.println("Real subject is doing action.");
    }
}

// 代理类
public class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doAction() {
        System.out.println("Proxy: Before calling real subject.");
        realSubject.doAction();
        System.out.println("Proxy: After calling real subject.");
    }
}

// 测试类
public class Client {
    public static void main(String[] args) {
        // 创建真实主题对象,该对象是代理主题所代理的实际执行者
        RealSubject realSubject = new RealSubject();
        
        // 创建代理主题对象,并将真实主题作为参数传入,代理主题将通过该真实主题执行操作
        ProxySubject proxySubject = new ProxySubject(realSubject);
        
        // 通过代理主题对象调用方法,此时代理主题将会在调用真实主题的方法前后添加额外的操作
        proxySubject.doAction();

    }
}

在这个例子中,Subject 是一个接口,RealSubject 是实现了这个接口的真实主题类,而 ProxySubject 是代理类,它也实现了 Subject 接口,并持有 RealSubject 的引用。当 ProxySubjectdoAction 方法被调用时,它会先执行一些前置操作,然后调用 RealSubjectdoAction 方法,最后执行一些后置操作。

静态代理的优点是结构清晰,易于理解和实现。然而,它的缺点也很明显,如果需要代理的对象很多,那么需要为每一个对象都创建一个代理类,这会导致代码量的增加和维护成本的上升。此外,如果接口中的方法数量增加,那么所有的代理类都需要进行修改,这违反了开闭原则(Open/Closed Principle)。因此,在实际应用中,动态代理往往更受欢迎,因为它可以避免这些问题。

3. JDK 动态代理

  • JDK 动态代理是 Java 反射 API 中的一个重要特性,它允许你在运行时动态地创建一个实现了指定接口的代理类。这个代理类可以用来包装一个目标对象,从而在调用目标对象的方法前后添加额外的行为,这在面向切面编程(AOP)和中间件设计模式中非常有用。

要使用 JDK 动态代理,你需要了解以下几个关键概念:

  1. java.lang.reflect.InvocationHandler:这是所有动态代理类必须实现的接口。当代理对象的方法被调用时,InvocationHandler 接口的 invoke() 方法会被调用,该方法接收三个参数:

    • proxy:代理对象本身。
    • method:正在调用的方法的 Method 对象。
    • args:调用方法时传递的参数数组。
  2. java.lang.reflect.Proxy:这是一个工具类,用于创建代理实例。它有一个静态方法 newProxyInstance(),接受以下参数:

    • ClassLoader loader:用于加载代理类的类加载器。

    • Class<?>[] interfaces:代理类需要实现的接口列表。

    • InvocationHandler h:处理代理方法调用的 InvocationHandler 实例。

下面是一个使用 JDK 动态代理的基本示例:

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

// 定义接口
public interface MyInterface {
    void doSomething();
}

// 目标类
public class RealObject implements MyInterface {
    public void doSomething() {
        System.out.println("Real object doing something.");
    }
}

// 代理类
public class DynamicProxy implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call");
        Object result = method.invoke(target, args);
        System.out.println("After method call");
        return result;
    }
}

// 测试类
public class Main {
    public static void main(String[] args) {
        /*
         * 创建一个真实对象,该对象实现了MyInterface接口。
         * 这个真实对象将被代理对象在需要时调用,以执行实际的业务逻辑。
         */
        MyInterface realObject = new RealObject();

        /*
         * 创建一个代理对象,该代理对象实现了MyInterface接口。
         * 代理对象在调用方法时,会将方法调用转发给真实对象。
         * 使用Proxy.newProxyInstance动态地创建一个代理类,该代理类实现了MyInterface接口。
         * 参数说明:
         * - Main.class.getClassLoader():指定代理类的类加载器,确保代理类和真实对象使用相同的类加载器。
         * - new Class[]{MyInterface.class}:指定代理类实现的接口列表,此处指明代理类将实现MyInterface接口。
         * - new DynamicProxy(realObject):一个InvocationHandler实现,代理对象的方法调用将被转发到这个InvocationHandler的invoke方法。
         */
        MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
                Main.class.getClassLoader(),
                new Class[]{MyInterface.class},
                new DynamicProxy(realObject)
        );

        /*
         * 通过代理对象调用方法。
         * 此时,方法的实际执行者是动态代理中的InvocationHandler,它会根据需要调用真实对象的方法。
         */
        proxyObject.doSomething();
    }

    }
}

在这个例子中,我们首先定义了一个接口 MyInterface 和一个实现了该接口的 RealObject 类。然后我们创建了一个 DynamicProxy 类来实现 InvocationHandler 接口,这个类会在方法调用前后打印一些信息。最后,我们在 main 方法中使用 Proxy.newProxyInstance() 方法创建了 MyInterface 的代理对象,并调用了它的 doSomething 方法。当这个方法被调用时,DynamicProxyinvoke 方法就会被执行,从而实现了在方法调用前后的增强行为。

4. CGlib 动态代理

  • CGLIB(Code Generation Library)是一个强大的高性能的代码生成库,它可以在运行期扩展Java类与实现Java接口。CGLIB的实质是字节码技术,它可以用来为一个类创建子类,因此它要求代理的目标对象必须是可继承的(即没有声明为final)。CGLIB常被用于那些不希望因为引入接口而修改现有代码的情况下,或者当无法访问目标源代码时。

CGLIB的工作原理

  • CGLIB通过字节码技术为一个类创建子类,并在子类中加入拦截器(interceptor),这样就可以在方法调用前后执行自定义的代码。拦截器可以理解为一种特殊的适配器,它允许你控制对目标对象方法的调用。

使用CGLIB进行动态代理

要使用CGLIB进行动态代理,你需要以下步骤:

  1. 添加CGLIB依赖到你的项目中(Spring 已集成)。如果你使用Maven,可以在pom.xml文件中添加如下依赖:

       <dependency>
           <groupId>cglib</groupId>
           <artifactId>cglib</artifactId>
           <version>3.3.0</version>
       </dependency>
       
    
  2. 创建一个增强的目标对象,通常这个对象不是接口的实现。

  3. 创建一个Enhancer实例,并设置其回调(Callback)和目标对象。

  4. 调用Enhancercreate()方法来创建代理对象。

  5. 使用代理对象调用目标方法。

下面是一个使用CGLIB动态代理的例子:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

// 定义接口
public interface Service {
    void execute();
}

// 目标类
public class ServiceImpl implements Service {
    @Override
    public void execute() {
        System.out.println("Executing service...");
    }
}

// 代理类
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method execution");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method execution");
        return result;
    }
}

// 测试类
public class main {
    public static void main(String[] args) {
        // 创建一个Enhancer实例,用于生成CGLIB代理对象
        Enhancer enhancer = new Enhancer();
        
        // 设置代理对象的基类为ServiceImpl
        // 这里的ServiceImpl是一个具体的服务实现类,代理对象将继承它的行为
        enhancer.setSuperclass(ServiceImpl.class);
        
        // 设置回调机制,这里使用CglibProxy实现
        // 当代理对象的方法被调用时,实际上会触发CglibProxy中的方法拦截器
        enhancer.setCallback(new CglibProxy());
        
        // 创建ServiceImpl的代理对象
        // 这里的代理对象在功能上等同于ServiceImpl,但多了动态织入的逻辑
        Service proxyService = (Service) enhancer.create();
        
        // 调用代理对象的方法,实际上会触发CglibProxy中的逻辑
        // 这里执行的具体行为取决于CglibProxy的实现
        proxyService.execute();

    }
}

在这个例子中,我们定义了一个服务接口Service和它的实现类ServiceImpl。然后我们创建了一个CglibProxy类来实现MethodInterceptor接口,这个类会在方法调用前后打印一些信息。接下来,我们使用Enhancer创建了ServiceImpl的代理对象,并调用了它的execute方法。当这个方法被调用时,CglibProxyintercept方法就会被执行,从而实现了在方法调用前后的增强行为。

5. 注意事项

  • CGLIB代理的对象不能是final类或final方法,因为CGLIB是通过继承来实现动态代理的。
  • CGLIB在性能上可能比JDK动态代理稍慢,因为它涉及到字节码的生成和解析。但在某些场景下,如无法使用接口的情况,CGLIB是一个很好的选择。

标签:调用,对象,Spring,代理,接口,AOP,方法,public,底层
From: https://blog.csdn.net/weixin_43320796/article/details/140524127

相关文章

  • 【2024】springboot O2O生鲜食品订购
     博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大......
  • Springboot项目远程部署gitee仓库(docker+Jenkins+maven+git)
    创建一个Springboot项目,勾选web将该项目创建git本地仓库,再创建远程仓库推送上去创建TestController@RestController@RequestMapping("/test")publicclassTestController{    @GetMapping("/hello")    publicStringsayHelloJenkins(){        ret......
  • SpringBoot增加验证码
    一、加入验证码依赖包com.github.whvcseeasy-captcha1.6.2二、实现验证码控制层@GetMapping("/common/kaptcha")publicvoiddefaultKaptcha(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse)throwsException{httpServletRespo......
  • spring 服务端如何设置 Last-Modified If-Modified-Since
    在Spring框架中,设置HTTP响应头Last-Modified和处理请求头If-Modified-Since是优化缓存和减少不必要数据传输的常用策略。SpringMVC提供了灵活的机制来实现这一点。设置Last-Modified响应头要在SpringMVC中设置Last-Modified响应头,你可以在你的Controller方法中返回一个Respons......
  • 基于springboot的小区物业管理系统
    全文内容包括:1、采用技术;2、系统功能;3、系统截图;4、配套内容。索取方式见文末微信号,欢迎关注收藏!一、采用技术语言:Java1.8框架:SpringBoot数据库:MySQL5.7、8.0开发工具:IntelliJIDEA旗舰版其他:Maven3.8以上二、系统功能物业人员管理:负责物业员工的信息录入、编辑、权限分......
  • 基于SpringBoot的宠物领养系统-07863(免费领源码+开发文档)可做计算机毕业设计JAVA、PHP
    摘 要21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对宠物领养系统......
  • spring 服务端如何设置 If-None-Match 和 ETAG
    在Spring框架中,特别是使用SpringMVC或SpringBoot时,设置ETag和处理If-None-Match请求头通常是通过一些自定义的逻辑来实现的,因为SpringMVC本身不直接提供自动化的ETag生成和验证机制。不过,你可以通过以下几种方式来实现:1.使用拦截器(Interceptor)或过滤器(Filter)你可以创建一个......
  • SpringBoot增加管理后台登录拦截器验证用户登录
    一、增加拦截器@ComponentpublicclassAdminLoginInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecto)throwsException{StringrequestServletPath=request.getS......
  • 使用Spring Boot和OpenAPI构建RESTful API文档化系统
    使用SpringBoot和OpenAPI构建RESTfulAPI文档化系统大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在当今的软件开发中,构建RESTfulAPI是非常常见的任务。随着微服务架构的流行,API的文档化和管理变得尤为重要。本文将介绍如何利用SpringBoot和OpenAPI(S......
  • springboot整合mqtt
    安装emqxhttps://blog.csdn.net/weixin_41542513/article/details/134328627springboot整合mqtt1、引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration&l......