代理对象与目标对象

1. 定义:代理对象和目标对象

1.1 目标对象(Target Object)

  • 目标对象是指 被增强的原始对象,即需要通过 AOP 切面(Aspect)增强功能的业务对象(原始类)。
  • 增强逻辑(Advice)最终是应用在目标对象的方法上的。
  • 目标对象是实际的业务逻辑持有者,它的功能由我们开发时编写的类直接实现。

例子:

public class UserService {
    public void createUser() {
        System.out.println("Creating user...");
    }
}
  • UserService 是目标对象,定义了业务逻辑。

1.2 代理对象(Proxy Object)

  • 代理对象是 Spring AOP 框架生成的 动态代理类实例,它是目标对象的增强版本。
  • 代理对象会包装目标对象,并在执行目标对象方法时:
    • 增加切面中的增强逻辑(如日志、事务等)。
    • 将调用转发到目标对象的方法。
  • 代理对象在 Spring 应用上下文中取代了目标对象,用户通过代理对象间接访问目标对象的功能。

代理对象的特点:

  • 它的外部表现与目标对象相同(方法签名一致)。
  • 它在方法调用时,可以插入增强逻辑(Advice)。

例子:
如果为 UserService 添加一个切面增强功能(例如日志记录),Spring 会生成一个代理对象来拦截方法调用。

UserService proxy = (UserService) context.getBean(UserService.class);
proxy.createUser();

2. 两者的区别

特性目标对象(Target Object)代理对象(Proxy Object)
定义原始的、未增强的业务对象Spring AOP 框架生成的动态代理对象
是否真实存在是真实存在的类实例(用户定义的类)是由 Spring 动态生成的新实例
增强逻辑(Advice)不包含增强逻辑包含增强逻辑,方法调用可能被拦截
访问方式只能通过代理对象间接访问直接被容器返回,外部调用的方法实际上由代理处理
与 Spring 的关系原始业务逻辑的实现者通过 Spring AOP 动态生成的代理实现
实际工作执行被调用的具体业务方法拦截方法调用,并决定是否执行增强逻辑或目标对象
反射行为目标对象的方法是用户定义的,直接反射可访问代理对象的方法是动态生成的,可能有额外逻辑

3. Spring 中是如何生成代理对象的

Spring AOP 中的代理对象生成有两种方式,分别基于 JDK 动态代理CGLIB 动态代理
代理的选择由目标对象的类型决定:

3.1 JDK 动态代理

  • 使用 Java 提供的动态代理机制(java.lang.reflect.Proxy)。
  • 要求目标对象实现一个或多个接口。
  • 代理对象是目标对象实现的接口的一个实现类。

优点:

  • 轻量级,直接基于接口生成代理。

缺点:

  • 目标对象必须实现接口,如果是纯类无法使用。

示例:

public interface UserService {
    void createUser();
}

public class UserServiceImpl implements UserService {
    @Override
    public void createUser() {
        System.out.println("Creating user...");
    }
}
  • Spring 会为 UserServiceImpl 生成一个动态代理对象,它实现了 UserService 接口。

3.2 CGLIB 动态代理

  • 使用 CGLIB(Code Generation Library)生成目标对象的子类作为代理对象。
  • 不要求目标对象实现接口,可以代理普通的类。
  • 代理对象是目标对象的子类,并通过方法重写(方法增强)来实现切面功能。

优点:

  • 不需要目标对象实现接口,可以直接增强普通类。

缺点:

  • 比 JDK 动态代理稍微重一些。

示例:

public class UserService {
    public void createUser() {
        System.out.println("Creating user...");
    }
}
  • Spring 会为 UserService 生成一个代理子类,并在方法上织入切面逻辑。

3.3 Spring 如何选择代理方式

Spring 默认通过 JDK 动态代理生成代理对象。如果目标对象没有实现接口,则自动切换为 CGLIB 动态代理。

可以通过以下配置强制使用 CGLIB 动态代理:

@EnableAspectJAutoProxy(proxyTargetClass = true)

4. 代理对象和目标对象的实际差异

在 Spring AOP 的运行时动态代理中,外部用户调用的其实是代理对象而非目标对象。以下是一些细节差异:

4.1 方法调用流程

未增强(目标对象直接调用):
UserService userService = new UserService();
userService.createUser();

方法调用的流程:

  • 直接调用目标对象的 createUser() 方法。
  • 输出:Creating user...
增强(通过代理对象调用):
UserService proxy = (UserService) context.getBean(UserService.class);
proxy.createUser();

方法调用的流程:

  1. 调用代理对象的 createUser() 方法。
  2. 代理对象拦截方法调用。
  3. 代理对象决定是否执行切面增强逻辑(如前置通知、后置通知等)。
  4. 代理对象将调用转发到目标对象的 createUser() 方法。

4.2 是否可以直接访问目标对象

  • 直接通过 Spring 容器获取的 Bean 是代理对象
    Spring 容器会将代理对象注册为 Bean,用户通过 @AutowiredgetBean() 获取的 Bean 实际上是代理对象。

  • 通过 AOP 上下文访问目标对象
    如果需要直接访问目标对象(绕过代理),可以通过 Spring 提供的 AopContext

    UserService target = (UserService) AopContext.currentProxy();
    

4.3 代理对象与目标对象的相互关系

  • 代理对象包含目标对象
    代理对象会将对业务方法的调用最终转发给目标对象。

  • 目标对象不知道代理对象的存在
    目标对象的代码完全独立,不需要感知代理对象或 Spring 的存在。


5. 示例:代理对象和目标对象的工作过程

目标对象

public class UserService {
    public void createUser() {
        System.out.println("Creating user...");
    }
}

切面

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.UserService.*(..))")
    public void logBefore() {
        System.out.println("Logging before method execution...");
    }
}

运行时流程

  1. Spring 生成一个代理对象(通过 JDK 动态代理或 CGLIB)。
  2. 用户调用代理对象的 createUser() 方法。
  3. 代理对象拦截该调用,并触发切面逻辑(前置通知)。
  4. 代理对象将方法调用转发给目标对象。
  5. 目标对象执行原始业务逻辑。

6. 总结

代理对象目标对象
Spring 动态生成的增强版本。开发者定义的原始业务逻辑类。
包含切面逻辑(通知)。不包含切面逻辑,只有业务逻辑。
用户通过代理对象间接访问目标对象方法。仅通过代理对象间接调用。
可以插入增强逻辑(如日志记录、事务)。无法直接应用增强逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值