Spring AOP

发布于:2025-03-23 ⋅ 阅读:(26) ⋅ 点赞:(0)

一、代理模式

1、什么是代理模式?

代理模式是一种结构型的设计模式,其核心思想是通过一个“代理对象”来控制对真是对象的访问。从而在不修改原始对象的前提下,实现对目标对象的功能增强访问控制。他是面向对象设计中实现“间接访问”的经典模式


2、代理模式的核心结构

  • Subject 抽象主题
    • 定义真实对象和代理对象的共同接口,客户端通过该接口操作真实对象
  • RealSubject 真实主题
    • 实际执行业务逻辑的对象,是代理模式最重要访问的目标
  • Proxy 代理
    • 持有真实对象的引用,负责控制对真实对象的访问,并可在调用真实对象前后插入额外的逻辑。

3、典型应用场景

  1. 虚拟代理
    延迟加载大资源(如图片、文件),直到真正需要时才创建真实对象。
    示例:网页中图片的占位符,滚动到可见区域时才加载真实图片。

  2. 保护代理
    控制对真实对象的访问权限(如身份验证)。
    示例:敏感操作需验证用户角色后才能执行。

  3. 远程代理
    为不同地址空间的对象提供本地代理(如 RPC 调用)。
    示例:分布式系统中通过代理对象调用远程服务。

  4. 日志/监控代理
    记录方法调用信息或性能指标。
    示例:记录接口耗时、调用次数等。


4、代理模式 vs 装饰器模式

特性 代理模式 装饰器模式
核心目的 控制访问(如权限、延迟加载) 动态添加功能(增强对象行为)
关注点 限制或管理对对象的访问 扩展对象的功能
对象关系 代理类与真实对象通常是1:1关系 装饰器与被装饰对象可多层嵌套
初始化时机 代理类可延迟创建真实对象(如虚拟代理)

装饰器需依赖已存在的被装饰对象


5、总结

代理模式通过“中间层”实现了对真实对象的间接访问,是解决权限控制资源管理功能扩展问题的利器。无论是静态代理还是动态代理(如 JDK/CGLIB),其本质均是通过代理对象间接操作目标对象,从而在调用链中插入自定义逻辑。

1.1、静态代理

1、什么是静态代理

静态代理是手动编写代理类的方式,代理类和被代理类在编译器就已经确定了。每个被代理类都需要一个对应的代理类。通过组合或继承实现方法增强。

2、实现示例

// 接口
public interface UserService {
    void save(String name);
}

// 被代理类(真实对象)
public class UserServiceImpl implements UserService {
    @Override
    public void save(String name) {
        System.out.println("保存用户: " + name);
    }
}

// 静态代理类(手动编写)
public class UserServiceProxy implements UserService {
    private final UserService target;  // 组合被代理对象

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void save(String name) {
        System.out.println("方法调用前");
        target.save(name);  // 调用真实对象的方法
        System.out.println("方法调用后");
    }
}

// 使用
public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(target);
        proxy.save("Alice");
    }
}


1.2、动态代理

1.2.1 jdk动态代理

JDK动态代理是JAVA中基于接口实现代理的一种机制,允许在运行时动态创建代理类。常用于AOP。其核心依赖 java.lang.reflect.Proxy java.lang.reflect.InvocationHandler .


核心方法及参数含义

1、 Proxy.newProxyInstance()

public static Object newProxyInstance(
    ClassLoader loader,          // 类加载器
    Class<?>[] interfaces,       // 被代理类实现的接口
    InvocationHandler handler    // 方法调用处理器
)
  • ClassLoader
    • 通常使用被代理类的类加载器(eg. target.getClass().getClassLoader()),用于加载代理类
  • Class<?>[ ] interface
    • 指定代理类需要实现的接口(eg. target.getClass.getInterfaces()).JDK动态代理需要求被代理对象必须实现至少一个接口
  • InvocationHandler
    • 代理逻辑的核心,所有方法调用会被转发到 InvocationHandler.invoke() 方法。(代理对象真正需要做的事,必须自己指定)

2、InvocationHandler.invoke()

作用:处理代理对象的方法调用,实现增强逻辑

参数:

public Object invoke(
    Object proxy,      // 代理对象本身(通常不直接使用,避免递归调用)
    Method method,     // 被调用的方法对象 --谁调用的代理
    Object[] args      // 被调用的方法参数  --被代理对象方法的参数
) throws Throwable;
示例:

1、定义接口和实现类

public interface UserService {
    void save(String name);
}

public class UserServiceImpl implements UserService {
    @Override
    public void save(String name) {
        System.out.println("保存用户: " + name);
    }
}

2、实现InvocationHandler

public class LogHandler implements InvocationHandler {
    private final Object target;  // 被代理对象

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

    // 创建代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
            target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法调用前: " + method.getName());
        Object result = method.invoke(target, args);  // 反射调用真实方法
        System.out.println("方法调用后");
        return result;
    }
}

3、创建代理对象

public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        LogHandler handler = new LogHandler();
        handler.setTarget(target);
        UserService proxy= (UserService)handler.getProxy();

        // 调用代理方法
        proxy.save("Alice");
    }
}

关键特性
  1. 基于接口:被代理类必须实现接口,否则无法生成代理。

  2. 运行时增强:动态生成字节码,无需手动编写代理类。

  3. 灵活扩展:通过 InvocationHandler 统一处理多个方法的增强逻辑。


1.2.2 cglib 动态代理

二、SpringAOP
 


三、AOP - Schema based 方式

四、AOP - AspectJ 方式

五、注解实现AOP

六、bean的作用域

作用域(Scope) 描述(Description)
singleton (默认)将单个 Bean 定义限定为每个 Spring IoC 容器的单个对象实例
prototype 将单个 Bean 定义限定为任意数量的对象实例。每次通过容器获取该 Bean 时都会创建一个新实例。
request 将单个 Bean 定义限定为单个 HTTP 请求的生命周期。每个 HTTP 请求会基于同一 Bean 定义创建独立的实例。仅适用于支持 Web 的 Spring ApplicationContext。
session 将单个 Bean 定义限定为HTTP 会话(Session)的生命周期。仅适用于支持 Web 的 Spring ApplicationContext。
application 将单个 Bean 定义限定为ServletContext 的生命周期。仅适用于支持 Web 的 Spring ApplicationContext。
websocket 将单个 Bean 定义限定为WebSocket 的生命周期。仅适用于支持 Web 的 Spring ApplicationContext。

关键说明

  1. 默认作用域

    • singleton 是 Spring 容器的默认作用域,适用于无状态的共享 Bean(如工具类、服务类)。

  2. 原型作用域

    • prototype 适用于需要每次获取新实例的场景(如包含状态的临时对象)。

  3. Web 相关作用域

    • requestsessionapplicationwebsocket 仅在 Web 环境中生效(如 Spring MVC 应用)。

  4. 线程安全问题

    • 非单例作用域(如 prototyperequest)的 Bean 需开发者自行管理线程安全。