Spring使用ProxyFactoryBean创建代理对象

开发 架构
在Spring中创建AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。这提供了对切入点、应用的任何通知及其顺序的完全控制。但是,如果你不需要这种控制,则有更简单的选项更可取。

概述

在Spring中创建AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。这提供了对切入点、应用的任何通知及其顺序的完全控制。但是,如果你不需要这种控制,则有更简单的选项更可取。

和其他Spring FactoryBean实现一样,ProxyFactoryBean引入了一个间接层。如果定义了一个名为foo的ProxyFactoryBean,那么引用foo的对象不会看到ProxyFactoryBean实例本身,而是看到一个由ProxyFactoryBean中的getObject()方法实现创建的对象。此方法创建封装目标对象的AOP代理。

使用ProxyFactoryBean或另一个支持IoC的类来创建AOP代理的最重要的好处之一是,IoC也可以管理Advice和Pointcut。这是一个强大的特性,可以实现某些用其他AOP框架难以实现的方法。例如,通知本身可以引用应用程序对象(目标之外,它应该在任何AOP框架中可用),受益于依赖项注入提供的所有可插拔性。

ProxyFactoryBean 属性

和Spring提供的大多数FactoryBean实现一样,ProxyFactoryBean类本身就是一个JavaBean。它的属性用于:

  • 指定要代理的目标
  • 指定是否使用CGLIB
    一些关键属性继承自org.springframework.aop.framework.ProxyConfig (Spring中所有AOP代理工厂的超类)。这些关键属性包括:
  • proxyTargetClass: 如果要代理的是目标类,而不是目标类的接口,则为true。如果该属性值设置为true,则创建CGLIB代理。
  • optimize: 控制是否对通过CGLIB创建的代理应用激进的优化。除非你完全理解相关AOP代理如何处理优化,否则不应该轻松地使用此设置。目前仅用于CGLIB代理。它对JDK动态代理没有影响。
  • frozen: 如果代理配置被冻结,则不再允许更改该配置。无论是作为轻微的优化,还是当你不希望调用者在创建代理后能够操作代理,这都是有用的。此属性的默认值为false,因此允许更改(例如添加额外的通知)。
  • exposeProxy: 确定是否应该在ThreadLocal中暴露当前代理,以便目标可以访问它。如果目标需要获取代理,而exposeProxy属性被设置为true,那么可以使用AopContext.currentProxy()方法。

ProxyFactoryBean特有的其他属性包括:

  • proxyInterfaces: 接口名称的字符串数组。如果没有提供,则使用目标类的CGLIB代理
  • interceptorNames: 建议器、拦截器或其他要应用的建议器名称的字符串数组。 这些名称是当前工厂中的bean名称,包括来自祖先工厂的bean名称。这里不能提到bean引用,因为这样做会导致ProxyFactoryBean忽略通知的单例设置。
    你可以在拦截器名称后面加上星号(*)。这样做的结果是,所有advisor bean的名称都以要应用的星号之前的部分开头。你可以在使用“全局”建议器中找到使用此功能的例子。
  • singleton: 不管getObject()方法被调用的频率如何,工厂是否应该返回一个对象。有几个FactoryBean实现提供了这样的方法。默认值为true。

基于JDK和CGLIB的代理

​ProxyFactoryBean如何选择为特定目标对象(要代理的对象)创建基于JDK的代理或基于CGLIB的代理的最终文档。

如果要代理的目标对象的类(以下简称为目标类)没有实现任何接口,则创建一个基于cglib的代理。这是最简单的场景,因为JDK代理是基于接口的,没有接口意味着JDK代理根本不可能。你可以插入目标bean并通过设置interceptorNames属性来指定拦截器列表。注意,即使将ProxyFactoryBean的proxyTargetClass属性设置为false,也会创建一个基于cglib的代理。

如果目标类实现了一个(或多个)接口,则创建的代理类型取决于ProxyFactoryBean的配置。

如果ProxyFactoryBean的proxyTargetClass属性被设置为true,则会创建一个基于cglib的代理。这是有道理的,也符合最小意外原则。即使ProxyFactoryBean的proxyInterfaces属性被设置为一个或多个完全限定的接口名,proxyTargetClass属性被设置为true这一事实也会导致基于cglib的代理生效。

如果将ProxyFactoryBean的proxyInterfaces属性设置为一个或多个完全限定的接口名,则创建一个基于jdk的代理。创建的代理实现了在proxyInterfaces属性中指定的所有接口。如果目标类碰巧实现了比proxyInterfaces属性中指定的更多的接口,那也没什么问题,但返回的代理不会实现这些额外的接口。

如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类确实实现了一个(或多个)接口,那么ProxyFactoryBean会自动检测到目标类实际上至少实现了一个接口,并且创建了一个基于jdk的代理。实际被代理的接口是目标类实现的所有接口。实际上,这相当于为目标类的proxyInterfaces属性提供一个列表,其中包含目标类实现的所有接口。然而,这样做工作量大大减少,也不容易出现排版错误。

代理接口

考虑一个使用ProxyFactoryBean的简单例子。这个例子涉及:

  • 考虑一个使用ProxyFactoryBean的简单例子。这个例子涉及:
  • 顾问(Advisor)和拦截器(Interceptor)用来提供通知。
  • 一个AOP代理bean定义,用来指定目标对象(personTarget bean)、代理的接口和要应用的建议。
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>

<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
</bean>

<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
</bean>

<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<property name="target" ref="personTarget"/>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>

注意,interceptorNames属性接受一个字符串列表,其中包含当前工厂中拦截器或顾问的bean名称。你可以使用顾问,拦截器,返回之前,之后,并抛出建议对象。

前面给出的person bean定义可以代替person实现,如下所示:

Person person = (Person) factory.getBean("person");

同一个IoC上下文中的其他bean可以表达对它的强类型依赖,就像普通Java对象一样。如下面的例子所示:

<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>

这个例子中的PersonUser类公开了一个Person类型的属性。就它所关心的而言,AOP代理可以透明地代替“真实的”人实现。但是,它的类将是一个动态代理类。

代理类

如果需要代理一个类,而不是一个或多个接口,该怎么办?

想象一下,在我们前面的例子中,没有Person接口。我们需要建议一个名为Person的类,它没有实现任何业务接口。在这种情况下,你可以配置Spring使用CGLIB代理,而不是动态代理。为此,将前面给出的ProxyFactoryBean的proxyTargetClass属性设置为true。虽然最好是针对接口而不是类进行编程,但在处理遗留代码时,能够建议不实现接口的类可能很有用。(一般来说,Spring不是规定性的。虽然它使应用良好的实践变得容易,但它避免了强制使用特定的方法。)

如果你愿意,你可以在任何情况下强制使用CGLIB,即使你有接口。

CGLIB代理通过在运行时生成目标类的子类来工作。Spring将这个生成的子类配置为将方法调用委托给原始目标。子类用于实现装饰器模式,在建议中编织。

CGLIB代理一般应该对用户透明。但是,有一些问题需要考虑:

  • Final方法无法提供建议,因为它们不能被覆盖
  • 不需要将CGLIB添加到类路径中。从Spring 3.2开始,CGLIB被重新打包并包含在Spring -core JAR中。换句话说,基于cglib的AOP可以“开箱即用”,JDK的动态代理也是如此

使用“全局”顾问(Advisor)

通过在拦截器名称后面附加一个星号,所有bean名称与星号之前的部分匹配的顾问都被添加到顾问链中。如果你需要添加一组标准的“全局”顾问,这可以派上用场。下面的例子定义了两个全局advisor:

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"/>
<property name="interceptorNames">
<list>
<value>global*</value>
</list>
</property>
</bean>
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
责任编辑:姜华 来源: 今日头条
相关推荐

2023-08-02 10:48:47

SpringBean反射

2009-11-05 13:16:59

WCF代理

2021-03-08 08:40:25

Spring Bean 创建单例对象

2024-04-01 08:38:57

Spring@AspectAOP

2024-04-30 08:20:54

代理工厂类Spring

2020-10-18 08:51:18

Spring Boot

2023-05-11 12:40:00

Spring控制器HTTP

2021-05-14 00:00:15

JavaScript开发代码

2023-10-08 10:14:12

2024-02-19 00:00:00

DockerNginx反向代理

2024-02-06 18:05:54

微服务SpringCloud

2021-07-27 22:56:00

JavaScript编程开发

2014-12-31 09:56:29

Ehcache

2011-05-25 17:10:39

ibmdw

2024-09-05 09:35:58

CGLIBSpring动态代理

2009-12-29 14:09:17

ADO.NET通用接口

2009-09-28 14:49:08

Spring DMOSGi服务

2022-07-05 14:19:30

Spring接口CGLIB

2011-07-14 18:18:32

Domino

2013-06-24 10:21:47

面向对象Web应用JavaScript
点赞
收藏

51CTO技术栈公众号