代理模式详解
目录
1. 代理模式简介
1.1 定义
代理模式(Proxy Pattern)是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对象的情况下,通过代理对象来增强目标对象的功能。
1.2 核心思想
- 控制访问:代理对象控制对目标对象的访问
- 功能增强:在不修改目标对象的前提下,增加额外功能
- 透明性:客户端通过代理对象访问目标对象,感觉不到代理的存在
1.3 适用场景
- 远程代理:为远程对象提供本地代表
- 虚拟代理:创建开销大的对象时使用
- 安全代理:控制对原始对象的访问权限
- 智能引用:在访问对象时执行额外的操作
- 缓存代理:为开销大的运算结果提供临时存储
1.4 代理模式分类
静态代理
// 接口
public interface UserService {
void saveUser(String user);
}
// 目标对象
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String user) {
System.out.println("保存用户: " + user);
}
}
// 代理对象
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void saveUser(String user) {
System.out.println("代理前处理");
userService.saveUser(user);
System.out.println("代理后处理");
}
}
动态代理
- JDK动态代理:基于接口的动态代理
- CGLIB动态代理:基于继承的动态代理
2. 核心流程
2.1 静态代理流程
2.2 动态代理流程
JDK动态代理流程
CGLIB动态代理流程
2.3 代理模式实现步骤
2.3.1 静态代理实现步骤
步骤1:定义业务接口
// 定义目标对象和代理对象的共同接口
public interface UserService {
void saveUser(String user);
void deleteUser(String userId);
String findUser(String userId);
}
步骤2:实现目标类
// 创建目标对象的具体实现
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String user) {
System.out.println("保存用户: " + user);
// 模拟数据库操作
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void deleteUser(String userId) {
System.out.println("删除用户: " + userId);
}
@Override
public String findUser(String userId) {
System.out.println("查找用户: " + userId);
return "用户信息: " + userId;
}
}
步骤3:创建静态代理类
// 手动创建代理类,实现相同的接口
public class UserServiceStaticProxy implements UserService {
private UserService target; // 目标对象
public UserServiceStaticProxy(UserService target) {
this.target = target;
}
@Override
public void saveUser(String user) {
// 前置处理
System.out.println("代理前:开始保存用户");
long startTime = System.currentTimeMillis();
// 调用目标方法
target.saveUser(user);
// 后置处理
long endTime = System.currentTimeMillis();
System.out.println("代理后:保存用户完成,耗时: " + (endTime - startTime) + "ms");
}
@Override
public void deleteUser(String userId) {
System.out.println("代理前:开始删除用户");
target.deleteUser(userId);
System.out.println("代理后:删除用户完成");
}
@Override
public String findUser(String userId) {
System.out.println("代理前:开始查找用户");
String result = target.findUser(userId);
System.out.println("代理后:查找用户完成");
return result;
}
}
步骤4:客户端调用
public class StaticProxyClient {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建代理对象
UserService proxy = new UserServiceStaticProxy(target);
// 通过代理对象调用方法
proxy.saveUser("张三");
proxy.deleteUser("001");
String user = proxy.findUser("001");
System.out.println("查找结果: " + user);
}
}
2.3.2 JDK动态代理实现步骤
步骤1:定义业务接口(同上)
步骤2:实现目标类(同上)
步骤3:创建调用处理器
// 实现InvocationHandler接口
public class UserServiceInvocationHandler implements InvocationHandler {
private Object target; // 目标对象
public UserServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置处理
System.out.println("动态代理前:调用方法 " + method.getName());
long startTime = System.currentTimeMillis();
// 通过反射调用目标方法
Object result = method.invoke(target, args);
// 后置处理
long endTime = System.currentTimeMillis();
System.out.println("动态代理后:方法 " + method.getName() + " 执行完成,耗时: " + (endTime - startTime) + "ms");
return result;
}
}
步骤4:创建动态代理工厂
// 动态代理工厂类
public class DynamicProxyFactory {
public static Object createProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口数组
new UserServiceInvocationHandler(target) // 调用处理器
);
}
// 泛型版本,提供类型安全
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, Class<T> interfaceClass) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class<?>[]{interfaceClass},
new UserServiceInvocationHandler(target)
);
}
}
步骤5:客户端调用
public class DynamicProxyClient {
public static void main(String[] args) {
// 创建目标对象
UserService target = new UserServiceImpl();
// 创建动态代理对象
UserService proxy = (UserService) DynamicProxyFactory.createProxy(target);
// 通过代理对象调用方法
proxy.saveUser("李四");
proxy.deleteUser("002");
String user = proxy.findUser("002");
System.out.println("查找结果: " + user);
// 使用泛型版本
UserService proxy2 = DynamicProxyFactory.createProxy(target, UserService.class);
proxy2.saveUser("王五");
}
}
2.3.3 CGLIB动态代理实现步骤
步骤1:添加CGLIB依赖
<!-- Maven依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
步骤2:实现目标类(不需要接口)
// 目标类,不需要实现接口
public class UserServiceCglib {
public void saveUser(String user) {
System.out.println("CGLIB保存用户: " + user);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void deleteUser(String userId) {
System.out.println("CGLIB删除用户: " + userId);
}
public String findUser(String userId) {
System.out.println("CGLIB查找用户: " + userId);
return "CGLIB用户信息: " + userId;
}
}
步骤3:创建方法拦截器
// 实现MethodInterceptor接口
public class UserServiceMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 前置处理
System.out.println("CGLIB代理前:调用方法 " + method.getName());
long startTime = System.currentTimeMillis();
// 调用目标方法(使用MethodProxy,性能更好)
Object result = proxy.invokeSuper(obj, args);
// 后置处理
long endTime = System.currentTimeMillis();
System.out.println("CGLIB代理后:方法 " + method.getName() + " 执行完成,耗时: " + (endTime - startTime) + "ms");
return result;
}
}
步骤4:创建CGLIB代理工厂
// CGLIB代理工厂类
public class CglibProxyFactory {
public static Object createProxy(Class<?> targetClass) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(targetClass);
// 设置回调
enhancer.setCallback(new UserServiceMethodInterceptor());
// 创建代理对象
return enhancer.create();
}
// 泛型版本
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<T> targetClass, Class<T> interfaceClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setInterfaces(new Class<?>[]{interfaceClass});
enhancer.setCallback(new UserServiceMethodInterceptor());
return (T) enhancer.create();
}
}
步骤5:客户端调用
public class CglibProxyClient {
public static void main(String[] args) {
// 创建CGLIB代理对象
UserServiceCglib proxy = (UserServiceCglib) CglibProxyFactory.createProxy(UserServiceCglib.class);
// 通过代理对象调用方法
proxy.saveUser("赵六");
proxy.deleteUser("003");
String user = proxy.findUser("003");
System.out.println("查找结果: " + user);
}
}
2.3.4 通用代理工具类
创建统一的代理工具类
// 通用代理工具类
public class ProxyUtils {
/**
* 创建代理对象(自动选择代理方式)
*/
public static Object createProxy(Object target) {
if (target.getClass().getInterfaces().length > 0) {
// 有接口,使用JDK动态代理
return createJdkProxy(target);
} else {
// 无接口,使用CGLIB动态代理
return createCglibProxy(target);
}
}
/**
* 创建JDK动态代理
*/
public static Object createJdkProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return handleMethod(target, method, args);
}
}
);
}
/**
* 创建CGLIB动态代理
*/
public static Object createCglibProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return handleMethod(target, method, args);
}
});
return enhancer.create();
}
/**
* 统一的方法处理逻辑
*/
private static Object handleMethod(Object target, Method method, Object[] args) throws Throwable {
System.out.println("代理前:调用方法 " + method.getName());
long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("代理后:方法 " + method.getName() + " 执行完成,耗时: " + (endTime - startTime) + "ms");
return result;
}
}
使用通用工具类
public class UniversalProxyClient {
public static void main(String[] args) {
// 测试有接口的目标对象
UserService userService = new UserServiceImpl();
UserService userProxy = (UserService) ProxyUtils.createProxy(userService);
userProxy.saveUser("通用代理用户");
// 测试无接口的目标对象
UserServiceCglib userServiceCglib = new UserServiceCglib();
UserServiceCglib cglibProxy = (UserServiceCglib) ProxyUtils.createProxy(userServiceCglib);
cglibProxy.saveUser("通用代理CGLIB用户");
}
}
2.3.5 实现步骤总结
| 代理类型 | 步骤1 | 步骤2 | 步骤3 | 步骤4 | 步骤5 |
|---|---|---|---|---|---|
| 静态代理 | 定义接口 | 实现目标类 | 手动创建代理类 | 实现代理逻辑 | 客户端调用 |
| JDK动态代理 | 定义接口 | 实现目标类 | 创建InvocationHandler | 使用Proxy.newProxyInstance | 客户端调用 |
| CGLIB动态代理 | 无需接口 | 实现目标类 | 创建MethodInterceptor | 使用Enhancer.create | 客户端调用 |
关键要点:
- 静态代理:需要手动编写代理类,但性能最好
- JDK动态代理:需要接口,使用反射调用
- CGLIB动态代理:不需要接口,使用字节码技术
- 选择策略:有接口用JDK,无接口用CGLIB,性能要求高用静态代理
3. 重难点分析
3.1 重难点一:动态代理的实现原理
难点分析
- 字节码生成:动态代理需要在运行时生成字节码
- 反射机制:需要理解Java反射的工作原理
- 类加载机制:动态生成的类需要被正确加载
解决方案
// JDK动态代理实现
public class DynamicProxyExample {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("代理前处理");
Object result = method.invoke(target, args);
System.out.println("代理后处理");
return result;
}
}
);
proxy.saveUser("张三");
}
}
3.2 重难点二:JDK动态代理 vs CGLIB动态代理
对比分析
| 特性 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 基于 | 接口 | 继承 |
| 性能 | 较慢(反射调用) | 较快(直接调用) |
| 限制 | 必须有接口 | 目标类不能是final |
| 依赖 | JDK自带 | 需要第三方库 |
| 生成方式 | 接口实现 | 子类继承 |
选择策略
// 选择策略示例
public class ProxyFactory {
public static Object createProxy(Object target) {
// 如果有接口,使用JDK动态代理
if (target.getClass().getInterfaces().length > 0) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyInvocationHandler(target)
);
} else {
// 否则使用CGLIB动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MyMethodInterceptor(target));
return enhancer.create();
}
}
}
3.3 重难点三:代理模式的性能优化
性能问题
- 反射调用开销:JDK动态代理使用反射,性能较低
- 字节码生成开销:动态生成类需要时间
- 内存占用:代理类会占用额外内存
优化策略
// 1. 缓存代理对象
public class ProxyCache {
private static final Map<Class<?>, Object> proxyCache = new ConcurrentHashMap<>();
public static Object getProxy(Object target) {
return proxyCache.computeIfAbsent(target.getClass(),
clazz -> createProxy(target));
}
}
// 2. 使用CGLIB的FastClass机制
public class FastClassExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new FastClassInterceptor());
enhancer.setUseFastClass(true); // 启用FastClass
UserService proxy = (UserService) enhancer.create();
}
}
4. Spring中的源码分析
4.1 Spring AOP中的代理实现
核心接口分析
// Spring AOP核心接口
public interface AopProxy {
Object getProxy();
Object getProxy(ClassLoader classLoader);
}
// JDK动态代理实现
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
private final AdvisedSupport advised;
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
}
CGLIB动态代理实现
// CGLIB动态代理实现
class CglibAopProxy implements AopProxy, Serializable {
private final AdvisedSupport advised;
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
if (rootClass == null) {
throw new AopConfigException("Target class cannot be null");
}
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// 验证类
validateClassIfNecessary(proxySuperClass, classLoader);
// 配置CGLIB增强器
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
enhancer.setCallbacks(callbacks);
enhancer.setCallbackTypes(types);
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " +
this.advised.getTargetClass() + ": " + ex.getMessage(), ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
}
4.2 Spring代理选择策略
DefaultAopProxyFactory源码分析
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
/**
* 判断是否没有用户提供的代理接口
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
代理选择规则
- 强制使用CGLIB:
proxyTargetClass = true - 优化模式:
optimize = true - 无接口:目标类没有实现接口
- 默认策略:有接口使用JDK,无接口使用CGLIB
4.3 Spring AOP执行流程
核心执行流程
// JdkDynamicAopProxy的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// 处理equals方法
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// 处理hashCode方法
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// 处理DecoratingProxy接口
return AopProxyUtils.advisedTarget(this.advised);
}
Object retVal;
if (this.advised.exposeProxy) {
// 暴露代理对象到ThreadLocal
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 没有拦截器,直接调用目标方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 创建方法调用对象,执行拦截器链
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// 处理返回值
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isAssignableFrom(targetClass)) {
retVal = target;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
5. 面试高频点
5.1 基础概念类问题
Q1: 什么是代理模式?有哪些类型?
答案要点:
- 代理模式是一种结构型设计模式
- 为其他对象提供代理以控制访问
- 类型:静态代理、动态代理(JDK、CGLIB)
Q2: 静态代理和动态代理的区别?
答案要点:
- 静态代理:编译时确定,需要手动编写代理类
- 动态代理:运行时生成,通过反射或字节码技术
- 性能:静态代理性能更好,动态代理更灵活
5.2 实现原理类问题
Q3: JDK动态代理的实现原理?
答案要点:
// 核心实现步骤
1. 通过Proxy.newProxyInstance()创建代理对象
2. 代理对象实现目标接口
3. 所有方法调用都转发到InvocationHandler.invoke()
4. 在invoke()方法中通过反射调用目标方法
Q4: CGLIB动态代理的实现原理?
答案要点:
// 核心实现步骤
1. 通过Enhancer创建代理类
2. 代理类继承目标类
3. 重写目标方法,添加拦截逻辑
4. 使用MethodInterceptor处理方法调用
5.3 Spring AOP类问题
Q5: Spring AOP使用哪种代理方式?
答案要点:
- 默认策略:有接口用JDK,无接口用CGLIB
- 强制CGLIB:设置
proxyTargetClass = true - 选择依据:目标类是否有接口、是否强制使用CGLIB
Q6: Spring AOP的执行流程?
答案要点:
1. 创建代理对象(JdkDynamicAopProxy或CglibAopProxy)
2. 方法调用时进入invoke()方法
3. 获取拦截器链(Advisor链)
4. 创建MethodInvocation对象
5. 执行拦截器链(责任链模式)
6. 调用目标方法
7. 返回结果
5.4 性能优化类问题
Q7: 如何优化动态代理的性能?
答案要点:
- 缓存代理对象:避免重复创建
- 使用CGLIB:比JDK动态代理性能更好
- 减少反射调用:使用FastClass机制
- 合理使用:避免过度使用代理
Q8: 代理模式有什么缺点?
答案要点:
- 性能开销:反射调用和字节码生成
- 内存占用:代理类占用额外内存
- 调试困难:代理对象增加调试复杂度
- 限制条件:CGLIB不能代理final类
5.5 实际应用类问题
Q9: 在项目中如何选择代理方式?
答案要点:
// 选择策略
if (有接口 && 不需要强制CGLIB) {
使用JDK动态代理;
} else if (无接口 || 强制CGLIB) {
使用CGLIB动态代理;
} else if (性能要求极高) {
考虑静态代理;
}
Q10: 如何实现一个简单的AOP框架?
答案要点:
// 核心组件
1. 切面定义:@Aspect注解
2. 切点定义:@Pointcut注解
3. 通知定义:@Before、@After等
4. 代理工厂:创建代理对象
5. 拦截器链:执行切面逻辑
5.6 源码分析类问题
Q11: Spring如何选择代理方式?
答案要点:
// DefaultAopProxyFactory.createAopProxy()
if (config.isOptimize() ||
config.isProxyTargetClass() ||
hasNoUserSuppliedProxyInterfaces(config)) {
// 使用CGLIB
return new ObjenesisCglibAopProxy(config);
} else {
// 使用JDK动态代理
return new JdkDynamicAopProxy(config);
}
Q12: 代理对象是如何创建的?
答案要点:
- JDK:通过
Proxy.newProxyInstance()创建 - CGLIB:通过
Enhancer.create()创建 - Spring:通过
AopProxyFactory统一创建
总结
代理模式是Java开发中非常重要的设计模式,特别是在Spring AOP中广泛应用。掌握代理模式的原理、实现方式和在Spring中的应用,对于理解AOP机制和提升代码质量具有重要意义。
关键要点
- 理解原理:掌握静态代理和动态代理的实现原理
- 区分场景:知道什么时候使用哪种代理方式
- 性能考虑:了解代理模式的性能影响和优化方法
- Spring集成:理解Spring AOP中代理模式的应用
- 实际应用:能够在项目中合理使用代理模式
通过深入学习代理模式,可以更好地理解面向切面编程的思想,提升代码的可维护性和扩展性。
2576

被折叠的 条评论
为什么被折叠?



