此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.1.10! |
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.1.10! |
如果您使用 Spring IoC 容器(或 )作为
业务对象(你应该是!),你想使用Spring的AOP实现之一。(请记住,工厂 bean 引入了一层间接层,让
它创建不同类型的对象。ApplicationContext
BeanFactory
FactoryBean
Spring AOP 支持还在后盖下使用工厂 bean。 |
在 Spring 中创建 AOP 代理的基本方法是使用 .这样可以完全控制
切入点、任何适用的建议及其顺序。但是,有更简单的
如果不需要此类控制,则更可取的选项。org.springframework.aop.framework.ProxyFactoryBean
Spring AOP 支持还在后盖下使用工厂 bean。 |
基本
与其他 Spring 实现一样,引入了
间接水平。如果定义一个 命名 的 对象
引用看不到实例本身,而是对象
由 .这
方法创建包装目标对象的 AOP 代理。ProxyFactoryBean
FactoryBean
ProxyFactoryBean
foo
foo
ProxyFactoryBean
getObject()
ProxyFactoryBean
使用一个或另一个 IoC 感知的最重要好处之一
创建 AOP 代理的类是建议和切点也可以
由 IoC 管理。这是一个强大的功能,可以实现某些难以实现的方法
与其他 AOP 框架一起实现。例如,建议本身可以参考
应用程序对象(除了目标,它应该在任何 AOP 中都可用
framework),受益于依赖注入提供的所有可插拔性。ProxyFactoryBean
JavaBean 属性
与 Spring 提供的大多数实现一样,该类本身就是一个 JavaBean。其属性用于:FactoryBean
ProxyFactoryBean
-
指定要代理的目标。
-
指定是否使用 CGLIB(稍后将介绍,另请参阅基于 JDK 和 CGLIB 的代理)。
一些关键属性继承自 (Spring 中所有 AOP 代理工厂的超类)。这些关键属性包括
以下内容:org.springframework.aop.framework.ProxyConfig
-
proxyTargetClass
:如果要代理目标类,而不是 目标类的接口。如果此属性值设置为 ,则 CGLIB 代理 创建(但另请参阅基于 JDK 和 CGLIB 的代理)。true
true
-
optimize
:控制是否将主动优化应用于代理 通过 CGLIB 创建。除非您完全 了解相关 AOP 代理如何处理优化。这是目前使用的 仅适用于 CGLIB 代理。它对 JDK 动态代理不起作用。 -
frozen
:如果代理配置为 ,则对配置的更改为 不再允许。这既可以作为轻微的优化,也可以用于这些情况 当您不希望调用方在创建代理后能够操作代理(通过接口)时。此属性的默认值为 ,因此允许进行更改(例如添加其他建议)。frozen
Advised
false
-
exposeProxy
:确定是否应在 中公开当前代理,以便目标可以访问它。如果目标需要获取 代理和属性设置为 ,目标可以使用该方法。ThreadLocal
exposeProxy
true
AopContext.currentProxy()
其他特定属性包括:ProxyFactoryBean
-
proxyInterfaces
:接口名称数组。如果未提供,则 CGLIB 使用目标类的代理(另请参阅基于 JDK 和 CGLIB 的代理)。String
-
interceptorNames
:一个数组,包括 、拦截器或其他建议名称 应用。订购很重要,先到先得。也就是说 列表中的第一个拦截器是第一个能够拦截 调用。String
Advisor
名称是当前工厂中的 Bean 名称,包括祖先的 Bean 名称 工厂。您不能在此处提及 Bean 引用,因为这样做会导致忽略建议的单例设置。
ProxyFactoryBean
可以在拦截器名称后附加星号 ()。这样做会导致 应用名称以星号前部分开头的所有顾问 Bean 要应用。您可以在使用“全局”顾问中找到使用此功能的示例。
*
-
singleton:工厂是否应该返回单个对象,无论如何 通常调用该方法。多种实现提供 这样的方法。默认值为 。如果你想使用有状态的建议 - 对于 例如,对于有状态混合 - 使用原型建议以及单例值 。
getObject()
FactoryBean
true
false
基于 JDK 和 CGLIB 的代理
本节是关于如何选择为特定目标创建基于 JDK 的代理或基于 CGLIB 的代理的权威文档
对象(要代理)。ProxyFactoryBean
关于创建基于 JDK 或 CGLIB 的行为
代理在 Spring 的 1.2.x 和 2.0 版本之间发生了变化。现在
在自动检测接口方面表现出与类相似的语义。ProxyFactoryBean ProxyFactoryBean TransactionProxyFactoryBean |
如果要代理的目标对象的类(以下简称为
目标类)不实现任何接口,基于 CGLIB 的代理是
创建。这是最简单的方案,因为 JDK 代理是基于接口的,并且没有
接口意味着 JDK 代理甚至是不可能的。您可以插入目标 Bean
并通过设置属性来指定拦截器列表。请注意,一个
即使 的属性已设置为 ,也会创建基于 CGLIB 的代理。(这样做没有意义,最好
从 Bean 定义中删除,因为它充其量是多余的,最坏的情况是多余的
令人困惑。interceptorNames
proxyTargetClass
ProxyFactoryBean
false
如果目标类实现一个或多个接口,则代理类型是
created 取决于 .ProxyFactoryBean
如果 的属性已设置为 ,
将创建基于 CGLIB 的代理。这是有道理的,并且符合
最小惊喜原则。即使 的属性已设置为一个或多个完全限定的接口名称,事实
该属性设置为导致基于 CGLIB 的原因
代理生效。proxyTargetClass
ProxyFactoryBean
true
proxyInterfaces
ProxyFactoryBean
proxyTargetClass
true
如果 的属性已设置为一个或多个
完全限定的接口名称,则创建一个基于 JDK 的代理。创建的
代理实现属性中指定的所有接口。如果目标类碰巧实现了比
那些在属性中指定的,一切都很好,但是那些
返回的代理不会实现其他接口。proxyInterfaces
ProxyFactoryBean
proxyInterfaces
proxyInterfaces
如果尚未设置 的属性,但
目标类确实实现了一个(或多个)接口,自动检测目标类实际上实现了这一事实
实现至少一个接口,并创建一个基于 JDK 的代理。接口
实际代理的是目标类的所有接口
实现。实际上,这与提供每个列表相同
目标类为属性实现的接口。然而
它大大减少了工作量,也更不容易出现印刷错误。proxyInterfaces
ProxyFactoryBean
ProxyFactoryBean
proxyInterfaces
关于创建基于 JDK 或 CGLIB 的行为
代理在 Spring 的 1.2.x 和 2.0 版本之间发生了变化。现在
在自动检测接口方面表现出与类相似的语义。ProxyFactoryBean ProxyFactoryBean TransactionProxyFactoryBean |
代理接口
考虑一个简单的实际例子。此示例涉及:ProxyFactoryBean
-
代理的目标 Bean。这是 Bean 的定义 这个例子。
personTarget
-
an 和 an 用于提供建议。
Advisor
Interceptor
-
用于指定目标对象(Bean)的 AOP 代理 Bean 定义, 代理的接口,以及要应用的建议。
personTarget
以下列表显示了该示例:
<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
String
您可能想知道为什么该列表不包含 Bean 引用。原因是
如果 的单例属性设置为 ,则它必须能够
返回独立的代理实例。如果任何顾问本身就是原型,那么
需要返回独立实例,因此必须能够获得
工厂原型的实例。持有参考是不够的。ProxyFactoryBean false |
前面显示的 bean 定义可以用来代替实现,如
遵循:person
Person
-
Java
-
Kotlin
Person person = (Person) factory.getBean("person");
val person = factory.getBean("person") as Person;
同一 IoC 上下文中的其他 Bean 可以表示对它的强类型依赖关系,如 使用普通的 Java 对象。以下示例演示如何执行此操作:
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>
此示例中的类公开 类型的属性。至于
就其关注而言,AOP 代理可以透明地代替“真实”人使用
实现。但是,它的类将是一个动态代理类。这是可能的
将其强制转换为接口(稍后讨论)。PersonUser
Person
Advised
您可以使用匿名
内豆。只是定义不同。这
仅为了完整性而提供建议。以下示例演示如何使用
匿名内豆:ProxyFactoryBean
<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 id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<!-- Use inner bean, not local reference to target -->
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
</property>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
使用匿名内部 Bean 的优点是只有一个对象类型。如果我们愿意,这很有用
防止应用程序上下文的用户获取对 UN-ADVICE 的引用
反对或需要避免 Spring IoC 自动接线的任何歧义。还有,
可以说,一个优势在于定义是独立的。
但是,有时能够从
工厂实际上可能是一个优势(例如,在某些测试场景中)。Person
ProxyFactoryBean
您可能想知道为什么该列表不包含 Bean 引用。原因是
如果 的单例属性设置为 ,则它必须能够
返回独立的代理实例。如果任何顾问本身就是原型,那么
需要返回独立实例,因此必须能够获得
工厂原型的实例。持有参考是不够的。ProxyFactoryBean false |
代理类
如果您需要代理一个类,而不是一个或多个接口,该怎么办?
想象一下,在我们前面的示例中,没有接口。我们需要建议
一个名为 that 的类未实现任何业务接口。在这种情况下,您
可以将 Spring 配置为使用 CGLIB 代理而不是动态代理。为此,请将前面所示的属性设置为 。虽然最好
程序到接口而不是类,能够建议不这样做的类
在处理遗留代码时,实现接口可能很有用。(一般来说,Spring
不是规范性的。虽然它使应用良好做法变得容易,但它避免了强制
特定方法。Person
Person
proxyTargetClass
ProxyFactoryBean
true
如果你愿意,你可以在任何情况下强制使用 CGLIB,即使你有 接口。
CGLIB 代理的工作原理是在运行时生成目标类的子类。Spring 将此生成的子类配置为将方法调用委托给原始目标。这 子类用于实现 Decorator 模式,在建议中编织。
CGLIB 代理通常对用户应该是透明的。但是,也存在一些问题 要考虑:
-
final
类不能代理,因为它们不能扩展。 -
final
方法不能被建议,因为它们不能被覆盖。 -
private
方法不能被建议,因为它们不能被覆盖。 -
不可见的方法通常将私有方法打包在父类中 来自不同的包,不能被建议,因为它们实际上是私有的。
无需将 CGLIB 添加到类路径中。CGLIB 被重新打包并包含
在 JAR 中。换句话说,基于 CGLIB 的 AOP “开箱即用”,也是如此
JDK 动态代理。spring-core |
CGLIB 代理和动态代理之间的性能差异很小。 在这种情况下,性能不应成为决定性的考虑因素。
无需将 CGLIB 添加到类路径中。CGLIB 被重新打包并包含
在 JAR 中。换句话说,基于 CGLIB 的 AOP “开箱即用”,也是如此
JDK 动态代理。spring-core |
使用“全球”顾问
通过在拦截器名称后附加星号,所有顾问的 Bean 名称都匹配 星号之前的部分将添加到顾问链中。这可以派上用场 如果您需要添加一组标准的“全局”顾问。以下示例定义 两位全球顾问:
<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"/>