一、代理模式
1、什么是代理模式?
代理模式是一种结构型的设计模式,其核心思想是通过一个“代理对象”来控制对真是对象的访问。从而在不修改原始对象的前提下,实现对目标对象的功能增强或访问控制。他是面向对象设计中实现“间接访问”的经典模式
2、代理模式的核心结构
- Subject 抽象主题
- 定义真实对象和代理对象的共同接口,客户端通过该接口操作真实对象
- RealSubject 真实主题
- 实际执行业务逻辑的对象,是代理模式最重要访问的目标
- Proxy 代理
- 持有真实对象的引用,负责控制对真实对象的访问,并可在调用真实对象前后插入额外的逻辑。
3、典型应用场景
虚拟代理
延迟加载大资源(如图片、文件),直到真正需要时才创建真实对象。
示例:网页中图片的占位符,滚动到可见区域时才加载真实图片。保护代理
控制对真实对象的访问权限(如身份验证)。
示例:敏感操作需验证用户角色后才能执行。远程代理
为不同地址空间的对象提供本地代理(如 RPC 调用)。
示例:分布式系统中通过代理对象调用远程服务。日志/监控代理
记录方法调用信息或性能指标。
示例:记录接口耗时、调用次数等。
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"); } }
关键特性
基于接口:被代理类必须实现接口,否则无法生成代理。
运行时增强:动态生成字节码,无需手动编写代理类。
灵活扩展:通过
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。 关键说明
默认作用域:
singleton
是 Spring 容器的默认作用域,适用于无状态的共享 Bean(如工具类、服务类)。原型作用域:
prototype
适用于需要每次获取新实例的场景(如包含状态的临时对象)。Web 相关作用域:
request
、session
、application
、websocket
仅在 Web 环境中生效(如 Spring MVC 应用)。线程安全问题:
非单例作用域(如
prototype
、request
)的 Bean 需开发者自行管理线程安全。