共计 28119 个字符,预计需要花费 71 分钟才能阅读完成。
上一章通过 @AspectJ
和基于 schema
的切面定义形容了 Spring 对 AOP 的反对。在本章中,咱们探讨了较低级别的 Spring AOP API。对于常见的应用程序,咱们倡议将 Spring AOP 与 AspectJ 切入点一起应用,如上一章所述。
6.1 本节形容了 Spring 如何解决要害切入点概念。
6.1.1 概念
Spring 的切入点模型使切入点重用不受告诉类型的影响。你能够应用雷同的切入点来定位不同的告诉。org.springframework.aop.Pointcut
接口是外围接口,用于将告诉定向到特定的类和办法。残缺的接口如下:
public interface Pointcut {ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();}
将 Pointcut
接口分为两局部,能够重用类和办法匹配的局部以及细粒度的合成操作(例如与另一个办法匹配器执行“联结”)。
ClassFilter
接口用于将切入点限度为给定的一组指标类。如果 matches()
办法始终返回 true
,则将匹配所有指标类。以下清单显示了ClassFilter
接口定义:
public interface ClassFilter {boolean matches(Class clazz);
}
MethodMatcher
接口通常更重要。残缺的接口如下:
public interface MethodMatcher {boolean matches(Method m, Class targetClass);
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}
matchs(Method,Class)
办法用于测试此切入点是否与指标类上的给定办法匹配。创立 AOP 代理时能够执行此评估,以防止须要对每个办法调用进行测试。如果两个参数的 match
办法对于给定的办法返回 true
,并且MethodMatcher
的isRuntime()
办法返回 true
,则在每次办法调用时都将调用三个参数的match
办法。这样,切入点就能够在执行指标告诉之前立刻查看传递给办法调用的参数。
大多数 MethodMatcher
实现都是动态的,这意味着它们的 isRuntime()
办法返回false
。在这种状况下,永远不会调用三参数匹配办法。
如果可能,请尝试使切入点成为动态,以容许 AOP 框架在创立 AOP 代理时缓存切入点评估的后果。
6.1.2 切入点的操作
Spring 反对切入点上的操作(特地是联结和交加)。
联结示意两个切入点匹配其中一个的办法。交加是指两个切入点都匹配的办法。联结通常更有用。你能够通过应用 org.springframework.aop.support.Pointcuts
类中的静态方法或应用同一包中的 ComposablePointcut
类来组成切入点。然而,应用 AspectJ 切入点表达式通常是一种更简略的办法。然而,应用 AspectJ 切入点表达式通常是一种更简略的办法。
6.1.3 AspectJ 表达式切入点
从 2.0 开始,Spring 应用的最重要的切入点类型是org.springframework.aop.aspectj.AspectJExpressionPointcut
。这是一个切入点,该切入点应用 AspectJ 提供的库来解析 AspectJ 切入点表达式字符串。
无关反对的 AspectJ 切入点原语的探讨,请参见上一章。
6.1.4 便捷切入点实现
Spring 提供了几种不便的切入点实现。你能够间接应用其中一些。其余的则打算在特定于应用程序的切入点中被子类化。
动态切入点
动态切入点基于办法和指标类,并且不能思考办法的参数。动态切入点足以满足大多数用处,并且最好。首次调用办法时,Spring 只能评估一次动态切入点。之后,无需在每次办法调用时再次评估切入点(备注:第一次评估后进行缓存)。
本节的其余部分形容了 Spring 附带的一些动态切入点实现。
正则表达式切入点
指定动态切入点的一种显著办法是正则表达式。除了 Spring 之外,还有几个 AOP 框架使之成为可能。org.springframework.aop.support.JdkRegexpMethodPointcut
是一个通用的正则表达式切入点它应用 JDK 中的正则表达式反对。
应用 JdkRegexpMethodPointcut
类,能够提供模式字符串的列表。如果其中任何一个匹配,则切入点的评估后果为true
。(因而,后果实际上是这些切入点的并集。)
以下示例显示如何应用JdkRegexpMethodPointcut
:
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
Spring 提供了一个名为 RegexpMethodPointcutAdvisor
的便捷类,该类使咱们还能够援用一个 Advice
(请记住,Advice
能够是拦截器、前置告诉、异样告诉等)。在幕后,Spring 应用了 JdkRegexpMethodPointcut
。应用RegexpMethodPointcutAdvisor
简化了连贯,因为一个 bean 同时封装了切入点和告诉,如上面的示例所示:
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
你能够将 RegexpMethodPointcutAdvisor
与任何 Advice
类型一起应用。
属性驱动的切入点
动态切入点的一种重要类型是元数据驱动的切入点。这将应用元数据属性的值(通常是源级别的元数据)。
动静切入点
动静切入点比动态切入点更低廉。它们思考了办法参数以及动态信息。这意味着必须在每次办法调用时对它们进行评估,并且因为参数会有所不同,因而无奈缓存后果。
次要示例是 control flow
切入点。
控制流切入点
Spring 控制流切入点在概念上相似于 AspectJ cflow
切入点,只管性能不那么弱小。(目前还没有方法指定一个切入点在与另一个切入点匹配的连接点上面执行。)控制流切入点与以后调用堆栈匹配。例如,如果连接点是由 com.mycompany.web
包中的办法或 SomeCaller
类调用的,则可能会触发。通过应用 org.springframework.aop.support.ControlFlowPointcut
类指定控制流切入点。通过应用 org.springframework.aop.support.ControlFlowPointcut
类指定控制流切入点。
与其余动静切入点相比,控制流切入点在运行时进行评估要低廉得多。在 Java 1.4 中,老本大概是其余动静切入点的五倍。
6.1.5 切入点超类
Spring 提供了有用的切入点超类,以帮忙你实现本人的切入点。因为动态切入点最有用,所以你可能应该子类化 StaticMethodMatcherPointcut
。这仅须要实现一个形象办法(只管你能够笼罩其余办法以自定义行为)。上面的示例显示如何对StaticMethodMatcherPointcut
进行子类化:
class TestStaticPointcut extends StaticMethodMatcherPointcut {public boolean matches(Method m, Class targetClass) {// return true if custom criteria match}
}
动静切入点也有超类。你能够将自定义切入点与任何告诉类型一起应用。
6.1.6 自定义切面
因为 Spring AOP 中的切入点是 Java 类,而不是语言性能(如 AspectJ),所以你能够申明自定义切入点,无论是动态还是动静。Spring 中的自定义切入点能够任意简单。然而,如果能够的话,咱们倡议应用 AspectJ 切入点表白语言。
更高版本的 Spring 可能提供对 JAC 提供的“语义切入点”的反对,例如“所有更改指标对象中实例变量的办法”。
6.2 Spring 中的告诉 API
当初,咱们能够查看 Spring AOP 如何解决告诉。
6.2.1 告诉生命周期
每个告诉都是一个 Spring bean。告诉实例能够在所有告诉对象之间共享,或者对于每个告诉对象都是惟一的。这对应于每个类或每个实例的告诉。
每个类告诉最罕用。实用于个别告诉,例如事物advisors
(切面和告诉组合)。这些不依赖于代理对象的状态或增加新状态。它们仅作用于办法和参数。
每个实例的告诉都适宜引入,以反对mixins
。在这种状况下,告诉将状态增加到代理对象。
你能够在同一 AOP 代理中混合应用共享告诉和基于实例的告诉。
6.2.2 Spring 中告诉类型
Spring 提供了几种告诉类型,并且能够扩大以反对任意告诉类型。本节介绍基本概念和规范告诉类型。
拦挡盘绕告诉
Spring 中最根本的告诉类型是盘绕告诉的拦挡。
对于应用办法拦挡的告诉,Spring 合乎 AOP Alliance 接口。实现 MethodInterceptor
和盘绕告诉的类也应该实现以下接口:
public interface MethodInterceptor extends Interceptor {Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()
办法的 MethodInvocation
参数公开了被调用的办法、指标连接点、AOP 代理和办法的参数。invoke()
办法应返回调用的后果:连接点的返回值。
以下示例显示了一个简略的 MethodInterceptor
实现:
public class DebugInterceptor implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
请留神对 MethodInvocation
的proceed()
办法的调用。这沿着拦截器链向下达到连接点。大多数拦截器都调用此办法并返回其返回值。然而,MethodInterceptor
就像其余的盘绕告诉一样,能够返回不同的值或引发异样,而不是调用 proceed
办法。然而,你没有充沛的理由就不要这样做。
MethodInterceptor
实现提供与其余合乎 AOP Alliance 要求的 AOP 实现的互操作性。本节其余部分探讨的其余告诉类型将实现常见的 AOP 概念,但以特定于 Spring 的形式。只管应用最具体的告诉类型有一个劣势,然而如果你可能想在另一个 AOP 框架中运行切面,则在盘绕告诉应用MethodInterceptor
。请留神,切入点以后无奈在框架之间互操作,并且 AOP Alliance 以后未定义切入点接口。
前置告诉
一种最简略的告诉类型是前置告诉。这个不须要 MethodInvocation
对象,因为它仅仅在进入办法前被调用。
前置告诉的次要长处在于,无需调用 proceed()
办法,因而,不会因忽略而未能沿拦截器链继续前进。
以下清单显示了 MethodBeforeAdvice
接口:
public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method m, Object[] args, Object target) throws Throwable;
}
(只管通常的对象实用于字段拦挡,并且 Spring 不太可能实现它,但 Spring 的 API 设计容许前置告诉。)
请留神,返回类型为void
。告诉能够在连接点执行之前插入自定义行为,但不能更改返回值。如果前置的告诉引发异样,它将停止拦截器链的进一步执行。异样会流传回拦截器链。如果是未查看异样在调用的办法的签名上,则将其间接传递给客户端。否则,它将被 AOP 代理包装在未经查看的异样中。
以下示例显示了 Spring 中的 before
告诉,该告诉计算所有办法调用:
public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {++count;}
public int getCount() {return count;}
}
前置告诉能够被应用于任何切入点。
异样告诉
如果连接点引发异样,则在连接点返回后调用引发告诉。Spring 提供抛出异样告诉。留神这意味着 org.springframework.aop.ThrowsAdvice
接口不蕴含任何办法。这是一个标记接口,示意这个对象实现一个或多个抛出异样告诉办法。这些应采纳以下模式:
afterThrowing([Method, args, target], subclassOfThrowable)
仅最初一个参数是必须的。办法签名能够具备一个或四个参数,具体取决于告诉办法是否对该办法参数感兴趣。接下来的两个清单显示类,它们是抛出异样告诉的示例。
如果引发RemoteException
(包含从子类),则调用以下告诉:
public class RemoteThrowsAdvice implements ThrowsAdvice {public void afterThrowing(RemoteException ex) throws Throwable {// Do something with remote exception}
}
与后面的告诉不同,下一个示例申明了四个参数,以便能够拜访被调用的办法、办法参数和指标对象。如果抛出ServletException
,则调用以下告诉:
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {// Do something with all arguments}
}
最初一个示例阐明如何在解决 RemoteException
和ServletException
的单个类中应用这两种办法。能够将任意数量的异样告诉办法组合到一个类中。以下清单显示了最初一个示例:
public static class CombinedThrowsAdvice implements ThrowsAdvice {public void afterThrowing(RemoteException ex) throws Throwable {// Do something with remote exception}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {// Do something with all arguments}
}
如果
throws-advice
办法自身引发异样,则它将笼罩原始异样(也就是说,它将更改引发给用户的异样)。重写异样通常是RuntimeException
,它与任何办法签名都兼容。然而,如果throws-advice
办法抛出一个已查看的异样,则它必须与指标办法的申明异样匹配,因而在某种程度上与特定的指标办法签名耦合。不要抛出与指标办法签名不兼容的未声明的查看异样!异样告诉能够被应用与任何切入点。
后置返回告诉
在 Spring 中,后置返回告诉必须实现 org.springframework.aop.AfterReturningAdvice
接口,以下清单显示:
public interface AfterReturningAdvice extends Advice {void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}
后置返回告诉能够拜访返回值(无奈批改),调用的办法、办法的参数和指标。
上面的后置返回告诉内容将计数所有未引发异样的胜利办法调用:
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable {++count;}
public int getCount() {return count;}
}
此告诉不会更改执行门路。如果它抛出异样,则抛出的是拦截器链,而不是返回值。
后置返回告诉能够被用于任何切入点。
引入告诉
Spring 将引入告诉视为一种非凡的拦挡告诉。
引入须要实现以下接口的 IntroductionAdvisor
和IntroductionInterceptor
:
public interface IntroductionInterceptor extends MethodInterceptor {boolean implementsInterface(Class intf);
}
从 AOP Alliance MethodInterceptor
接口继承的 invoke()
办法必须实现引入。也就是说,如果被调用的办法在引入的接口上,则引入拦截器负责解决办法调用,不能调用proceed()
。
引入告诉不能与任何切入点一起应用,因为它仅实用于类,而不适用于办法级别。你只能通过 IntroductionAdvisor
应用引入告诉,它具备以下办法:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;}
public interface IntroductionInfo {Class<?>[] getInterfaces();}
没有MethodMatcher
,因而没有与引入告诉相干的Pointcut
。只有类过滤是合乎逻辑的。
getInterfaces()
办法返回此 advisor
引入的接口。
在外部应用 validateInterfaces()
办法来查看引入的接口是否能够由配置的 IntroductionInterceptor
实现。
考虑一下 Spring 测试套件中的一个示例,并假如咱们想为一个或多个对象引入以下接口:
public interface Lockable {void lock();
void unlock();
boolean locked();}
这阐明了混合。咱们心愿可能将告诉对象强制转换为 Lockable
,无论它们的类型和调用锁和解锁办法如何。如果咱们调用lock()
办法,咱们心愿所有的 setter
办法都抛出一个LockedException
。因而,咱们能够增加一个切面,使对象在不理解对象的状况下不可变:AOP 的一个很好的例子。
首先,咱们须要一个 IntroductionInterceptor
来实现沉重的工作。在这种状况下,咱们扩大了 org.springframework.aop.support.DelegatingIntroductionInterceptor
便当类。咱们能够间接实现IntroductionInterceptor
,然而在大多数状况下,最好应用DelegatingIntroductionInterceptor
。
DelegatingIntroductionInterceptor
被设计为将引入委托给所引入接口的理论实现,而暗藏了监听的应用。你能够应用结构函数参数将委托设置为任何对象。默认委托(应用无参数构造函数时)是 this
。因而,在下一个示例中,委托是DelegatingIntroductionInterceptor
的LockMixin
子类。给定一个委托(默认状况下为自身),DelegatingIntroductionInterceptor
实例将查找由委托实现的所有接口(IntroductionInterceptor
除外),并反对针对其中任何一个的引入。诸如 LockMixin
的子类能够调用 suppressInterface(Class intf)
办法来禁止不应公开的接口。然而,无论 IntroductionInterceptor
筹备反对多少个接口,IntroductionAdvisor
被应用管制理论公开哪些接口。引入的接口暗藏了指标对同一接口的任何实现。
因而,LockMixin
扩大了 DelegatingIntroductionInterceptor
并实现了 Lockable
自身。超类会主动抉择能够反对 Lockable
进行引入的办法,因而咱们不须要指定它。咱们能够通过这种形式引入任意数量的接口。
留神 locked
实例变量的应用。这无效地将附加状态增加到指标对象中保留。
上面的示例显示 LockMixin
类:
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {this.locked = true;}
public void unlock() {this.locked = false;}
public boolean locked() {return this.locked;}
public Object invoke(MethodInvocation invocation) throws Throwable {if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {throw new LockedException();
}
return super.invoke(invocation);
}
}
通常,你无需重写 invoke()
办法。通常足以满足 DelegatingIntroductionInterceptor
实现(如果引入了办法,则调用委托办法,否则进行到连接点)。在当前情况下,咱们须要增加一个查看:如果处于锁定模式,则不能调用任何 setter
办法。
所需的引入只须要保留一个不同的 LockMixin
实例并指定引入的接口 (在本例中,仅为Lockable
)。一个更简单的示例可能援用了引入拦截器(将被定义为原型)。在这种状况下,没有与LockMixin
相干的配置,因而咱们应用 new
创立它。以下示例显示了咱们的 LockMixinAdvisor
类:
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {public LockMixinAdvisor() {super(new LockMixin(), Lockable.class);
}
}
咱们能够非常简单地利用此 advisor
程序,因为它不须要配置。(然而,如果没有 IntroductionAdvisor
,则无奈应用IntroductionInterceptor
。) 像通常的介绍一样,advisor
必须是按实例的,因为它是有状态的。对于每个被告诉的对象,咱们须要一个 LockMixinAdvisor
的不同实例,因而也须要 LockMixin
的不同实例。advisor
蕴含被告诉对象状态的一部分。咱们能够像其余任何 advisor
一样,通过应用 Advised.addAdvisor()
办法或 XML 配置(举荐形式)以编程形式利用此advisor
。下文探讨的所有代理创立抉择,包含“主动代理创立器”,都能够正确处理引入和有状态的混合。
6.3 在 Spring 中的 Advisor API
在 Spring 中,Advisor
是只蕴含一个与切入点表达式关联的告诉对象的切面。
除了介绍的非凡状况外,任何 advisor
都能够与任何告诉一起应用。org.springframework.aop.support.DefaultPointcutAdvisor
是最罕用的 advisor
类。它能够与 MethodInterceptor
,BeforeAdvice
或ThrowsAdvice
一起应用。
能够在 Spring 中将 advisor
和advice
类型混合在同一个 AOP 代理中。在一个代理配置中,能够应用盘绕告诉、异样告诉和前置告诉的拦挡。Spring 主动创立必要的拦截器链。
6.4 应用 ProxyFactoryBean
创立 AOP 代理
如果你的业务对象应用 Spring IoC 容器 (一个ApplicationContext
或BeanFactory
)(你应该这样做!),那么你想应用 Spring 的 AOP FactoryBean
实现之一。(请记住,工厂 bean 引入了一个间接层,使它能够创立其余类型的对象。)
Spring AOP 反对还在后盾应用了工厂 bean。
在 Spring 中创立 AOP 代理的根本办法是应用org.springframework.aop.framework.ProxyFactoryBean
。这样能够齐全管制切入点,任何实用的告诉及其程序。然而,如果不须要这样的管制,则有一些更简略的选项比拟可取。
6.4.1 根底
像其余 Spring FactoryBean
实现一样,ProxyFactoryBean
引入了一个间接级别。如果定义一个名为 foo
的ProxyFactoryBean
,则援用 foo
的对象将看不到 ProxyFactoryBean
实例自身,而是看到由 ProxyFactoryBean
中的 getObject()
办法的实现创立的对象。此办法创立一个包装指标对象的 AOP 代理。
应用 ProxyFactoryBean
或另一个反对 IoC 的类创立 AOP 代理的最重要益处之一是告诉和切入点也能够由 IoC 治理。这是一项弱小的性能,能够实现某些其余 AOP 框架难以实现的办法。例如,告诉自身能够援用应用程序对象(除了指标对象,它应该在任何 AOP 框架中都可用),这得益于依赖注入提供的所有可插入性。
6.4.2 JavaBean 属性
与 Spring 提供的大多数 FactoryBean
实现一样,ProxyFactoryBean
类自身也是一个 JavaBean。其属性用于:
- 指定要代理的指标。
- 指定是否应用 CGLIB(稍后介绍,另请参见基于 JDK 和 CGLIB 的代理)。
一些要害属性继承自org.springframework.aop.framework.ProxyConfig
(Spring 中所有 AOP 代理工厂的超类)。这些要害属性包含:
proxyTargetClass
:如果要代替指标类而不是指标类的接口,则为true
。如果此属性值设置为true
,则将创立 CGLIB 代理(另请参见基于 JDK 和 CGLIB 的代理)。optimize
: 管制被动优化是否利用于通过 CGLIB 创立的代理。除非你齐全理解相干的 AOP 代理如何解决优化,否则不要随便应用此设置。以后仅用于 CGLIB 代理。它对 JDK 动静代理有效。frozen
: 如果代理配置被解冻,则不再容许对配置进行更改。当你不心愿调用者在创立代理后 (通过已告诉接口) 可能操作代理时,这对于轻微的优化是十分有用的。此属性的默认值为false
,因而容许进行更改(例如增加其余告诉)。exposeProxy
: 确定以后代理是否应该在ThreadLocal
中裸露,以便指标能够拜访它。如果指标须要获取代理,并且裸露代理属性设置为true
,则指标能够应用AopContext.currentProxy()
办法。
ProxyFactoryBean
特有的其余属性包含:
proxyInterfaces
: 字符串接口名称的数组。如果未提供,则应用指标类的 CGLIB 代理(另请参见基于 JDK 和 CGLIB 的代理)。interceptorNames
:Advisor
,拦截器或要利用的其余告诉名称的字符串数组。程序很重要,先到先得。也就是说,列表中的第一个拦截器是第一个可能拦挡调用的拦截器。- 你能够在拦截器名称后加上星号(
*
)。这样做会导致所有advisor
bean 的应用程序的名称都以要利用的星号之前的局部结尾。你能够在应用 Global Advisors 中找到应用此个性的示例。 singleton
: 无论getObject()
办法被调用的频率如何,工厂是否应返回单个对象。一些FactoryBean
实现提供了这种办法。默认值是true
。如果你想应用有状态告诉—例如,有状态混合—应用原型告诉和单例值false
。
6.4.3 基于 JDK 和 CGLIB 代理
本局部是无关 ProxyFactoryBean
如何抉择为特定指标对象(将被代理)创立基于 JDK 的代理或基于 CGLIB 的代理的权威性文档。
在 Spring 的 1.2.x 版和 2.0 版之间,
ProxyFactoryBean
的行为与创立基于 JDK 或 CGLIB 的代理无关。当初,ProxyFactoryBean
在自动检测接口方面展现了与TransactionProxyFactoryBean
类相似的语义。
如果要代理的指标对象的类(以下简称为指标类)没有实现任何接口,则创立基于 CGLIB 的代理。这是最简略的状况,因为 JDK 代理是基于接口的,并且没有接口意味着甚至无奈进行 JDK 代理。你能够插入指标 bean 并通过设置 interceptorNames
属性来指定拦截器列表。请留神,即便 ProxyFactoryBean
的proxyTargetClass
属性已设置为false
,也会创立基于 CGLIB 的代理。(这样做没有任何意义,最好将其从 bean 定义中删除,因为它充其量是多余,并且在最糟的状况下会造成混同。)
如果指标类实现一个(或多个)接口,则创立的代理类型取决于 ProxyFactoryBean
的配置。
如果 ProxyFactoryBean
的proxyTargetClass
属性已设置为 true
,则将创立基于 CGLIB 的代理。这很有情理,也合乎最小意外准则。即便ProxyFactoryBean
的proxyInterfaces
属性被设置为一个或多个齐全限定的接口名,proxyTargetClass
属性被设置为 true
也会使基于 cglib 的代理失效。
如果 ProxyFactoryBean
的proxyInterfaces
属性被设置为一个或多个齐全限定的接口名称,那么将创立一个基于 jdk 的代理。创立的代理实现了 proxyInterfaces
属性中指定的所有接口。如果指标类碰巧实现了比 proxyInterfaces
属性中指定的更多的接口,那也没什么问题,然而那些额定的接口不是由返回的代理实现的。
如果没有设置 ProxyFactoryBean
的proxyInterfaces
属性,然而指标类实现了一个 (或多个) 接口,那么 ProxyFactoryBean
会自动检测到指标类的确实现了至多一个接口,并创立一个基于 jdk 的代理。理论代理的接口是指标类实现的所有接口。实际上,这与将指标类实现的每个接口的列表提供给 proxyInterfaces
属性雷同。然而,这大大减少了工作量,也不容易呈现书写谬误。
6.4.4 代理接口
思考一个简略的 ProxyFactoryBean
操作示例。此示例波及:
- 代理的指标 bean。这是示例中的
personTarget
bean 定义。 - 用于提供告诉的
Advisor
和拦截器。 - 一个用于指定指标对象(
personTarget
bean)、代理接口和利用告诉的 AOP 代理 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
属性承受一个字符串列表,其中蕴含以后工厂中的拦截器或 advisors
的 bean 名称。你能够应用 advisors
、拦截器、前置告诉、后置告诉和异样告诉对象。advisors
的程序很重要。
你可能想晓得为什么列表不保留 bean 援用。这样做的起因是,如果
ProxyFactoryBean
的singleton
属性被设置为false
,那么它必须可能返回独立的代理实例。如果任何advisors
自身是原型,则须要返回一个独立的实例,因而必须可能从工厂取得原型的实例。
能够应用后面显示的 person
Bean 定义代替Person
实现,如下所示:
Person person = (Person) factory.getBean("person");
与一般 Java 对象一样,在同一 IoC 上下文中的其余 bean 能够表白对此的强类型依赖性。以下示例显示了如何执行此操作:
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>
在此示例中,PersonUser
类裸露了 Person
类型的属性。就其自身而言,AOP 代理能够通明地代替真 person
实现。然而,其类将是动静代理类。能够将其转换为 Advised
接口(稍后探讨)。
你能够应用匿名外部 bean 暗藏指标和代理之间的区别。仅 ProxyFactoryBean
定义不同。该倡议仅出于完整性思考。以下示例显示了如何应用匿名外部 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 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 的长处是只有一个 Person
类型的对象。如果咱们想避免应用程序上下文的用户取得对未告诉对象的援用,或者须要防止应用 Spring IoC 主动拆卸产生歧义,这是很有用的。能够说,还有一个长处是 ProxyFactoryBean
定义是独立的。然而,有时候,可能从工厂取得未告诉的指标实际上可能是一种劣势(例如,在某些测试场景中)。
6.4.5 代理类
如果须要代理一个类,而不是一个或多个接口怎么办?
设想一下,在咱们之前的示例中,没有 Person
接口。咱们须要告诉一个名为 Person
的类,该类没有实现任何业务接口。在这种状况下,你能够将 Spring 配置为应用 CGLIB 代理而不是动静代理。为此,请将后面显示的 ProxyFactoryBean
的proxyTargetClass
属性设置为true
。尽管最好是依据接口而不是类编程,但在解决遗留代码时,告诉没有实现接口的类的能力可能很有用。(一般来说,Spring 是没有规定性的。尽管它使利用良好实际变得容易,但它防止了强制应用特定的形式或办法。)
如果须要,即便有接口,也能够在任何状况下强制应用 CGLIB。
CGLIB 代理通过在运行时生成指标类的 <u> 子类 </u> 来工作。Spring 配置此生成的子类以将办法调用委托给原始指标。子类用于实现 Decorator
模式,并编织在告诉中。
CGLIB 代理通常应答用户通明。然而,有一些问题要思考:
final
的办法不能被告诉,因为它们不能被笼罩 (备注:子类不能笼罩被final
标记办法)。- 无需将 CGLIB 增加到你的类门路中。从 Spring 3.2 开始,CGLIB 被从新打包并蕴含在
spring-core
JAR 中。换句话说,基于 CGLIB 的 AOP 就像 JDK 动静代理一样“开箱即用”。
CGLIB 代理和动静代理之间简直没有性能差别。
在这种状况下,性能不应作为决定性的思考因素。
6.4.6 应用 全局
Advisors
通过向拦截器名称附加星号,所有具备与星号之前的局部相匹配的 bean 名称的 advisor
都会被增加到 advisor
链中。如果你须要增加一组规范的全局 advisor
,这将十分有用。以下示例定义了两个全局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"/>
参考代码:
com.liyong.ioccontainer.starter.ProxyFactoryBeanIocContainer
6.5 简洁的代理定义
特地是在定义事务代理时,你可能会失去许多相似的代理定义。应用父 bean 和子 bean 定义以及外部 bean 定义能够产生更洁净、更简洁的代理定义。
首先,咱们为代理创立父模板,bean 定义,如下所示:
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
它自身从未实例化,因而实际上可能是不残缺的。而后,须要创立的每个代理都是一个子 bean 定义,它将代理的指标包装为一个外部 bean 定义,因为无论如何指标都不会独自应用。以下示例显示了这样的子 bean:
<bean id="myService" parent="txProxyTemplate">
<property name="target">
<bean class="org.springframework.samples.MyServiceImpl">
</bean>
</property>
</bean>
你能够从父模板笼罩属性。在以下示例中,咱们将笼罩事务流传设置:
<bean id="mySpecialService" parent="txProxyTemplate">
<property name="target">
<bean class="org.springframework.samples.MySpecialServiceImpl">
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
请留神,在父 bean 的示例中,咱们通过将 abstract
属性设置为 true
来将父 bean 定义显式标记为形象,如前所述,因而实际上可能不会实例化它。默认状况下,应用程序上下文 (但不是简略的 bean 工厂) 预实例化所有单例。因而,重要的是(至多对于单例 bean),如果你有一个(父)bean 定义仅打算用作模板,并且此定义指定了一个类,则必须确保将 abstract
属性设置为true
。否则,应用程序上下文实际上会尝试对其进行实例化。
6.6 通过 ProxyFactory
编程式地创立 AOP 代理
应用 Spring 以编程形式创立 AOP 代理很容易。这使你能够应用 Spring AOP,而无需依赖 Spring IoC。
由指标对象实现的接口将被主动代理。以下清单显示了应用一个拦截器和一个 advisor
为指标对象创立代理的过程:
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
第一步是结构一个类型为 org.springframework.aop.framework.ProxyFactory
的对象。你能够应用指标对象 (如后面的示例所示) 来创立它,也能够在代替构造函数中指定要代理的接口。
你能够增加告诉 (拦截器是一种专门的告诉)、advisors
,或者同时增加它们,并在ProxyFactory
的生命周期中操作它们。如果增加了IntroductionInterceptionAroundAdvisor
,则能够使代理实现其余接口。
ProxyFactory
上还有一些不便的办法 (继承自AdvisedSupport
),能够增加其余告诉类型,比方before
和throw advice
。AdvisedSupport
是 ProxyFactory
和ProxyFactoryBean
的超类。
在大多数应用程序中,将 AOP 代理创立与 IoC 框架集成在一起是最佳实际。通常,倡议你应用 AOP 从 Java 代码内部化配置。
6.7 操作告诉对象
无论如何创立 AOP 代理,都能够通过应用 org.springframework.aop.framework.Advised
接口来操作它们。任何 AOP 代理都能够转换到这个接口,不论它实现了哪个接口。该接口蕴含以下办法:
Advisor[] getAdvisors();
void addAdvice(Advice advice) throws AopConfigException;
void addAdvice(int pos, Advice advice) throws AopConfigException;
void addAdvisor(Advisor advisor) throws AopConfigException;
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
int indexOf(Advisor advisor);
boolean removeAdvisor(Advisor advisor) throws AopConfigException;
void removeAdvisor(int index) throws AopConfigException;
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
boolean isFrozen();
getAdvisors()
办法为已增加到工厂的每个 advisor
、拦截器或其余告诉类型返回一个advisor
。如果增加了advisor
,则此索引处返回的advisor
是你增加的对象。如果增加了拦截器或其余告诉类型,Spring 会将其包装在带有指向总是返回 true
的切入点的 advisor
中。因而,如果你增加一个 MethodInterceptor
,为这个索引返回的advisor
是一个 DefaultPointcutAdvisor
,它返回你的MethodInterceptor
和一个匹配所有类和办法的切入点。
addAdvisor()
办法可用于增加任何 Advisor
。通常,持有切入点和告诉的advisor
是通用的DefaultPointcutAdvisor
,你能够将其用于任何告诉或切入点(但不用于introduction
)。
默认状况下,即便已创立代理,也能够增加或删除 advisor
或拦截器。惟一的限度是不可能增加或删除一个introduction
advisor
,因为工厂中的现有代理不会显示接口更改。(你能够从工厂获取新的代理来防止此问题。)
以下示例显示了将 AOP 代理投射到 Advised
接口并检查和解决其告诉:
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + "advisors");
// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(new DebugInterceptor());
// Add selective advice using a pointcut
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
在生产中批改业务对象上的告诉是否可取 (没有双关语) 值得狐疑,只管毫无疑问存在非法的应用案例。然而,它在开发中(例如在测试中)十分有用。有时咱们发现以拦截器或其余告诉的模式增加测试代码,并进入咱们要测试的办法调用中十分有用。(例如,在标记回滚事务之前,告诉能够进入为该办法创立的事务,可能是为了运行 SQL 来查看数据库是否被正确更新。)
依据创立代理的形式,通常能够设置解冻标记。在这种状况下,Advised
isFrozen()
办法返回true
,并且任何通过增加或删除来批改告诉的尝试都会导致AopConfigException
。解冻已告诉对象状态的能力在某些状况下十分有用(例如,避免调用代码删除平安拦截器)。
6.8 应用“主动代理
”性能
到目前为止,咱们曾经思考过通过应用 ProxyFactoryBean
或相似的工厂 bean 来显式创立 AOP 代理。
Spring 还容许咱们应用“主动代理
”Bean 定义,该定义能够主动代理选定的 Bean 定义。它构建在 Spring 的 bean 后处理器基础设施上,该基础设施容许在装载容器时批改任何 bean 定义。
在这个模型中,你在 XML bean 定义文件中设置了一些非凡的 bean 定义来配置主动代理基础设施。这使你能够申明有资格进行主动代理的指标。你无需应用ProxyFactoryBean
。
有两种办法能够做到这一点:
- 通过应用在以后上下文中援用特定 bean 的主动代理创立器。
- 主动代理创立的一个非凡状况值得独自思考: 由源码级别元数据属性驱动的主动代理创立。
6.8.1 自定代理 Bean 定义
本节介绍了 org.springframework.aop.framework.autoproxy
包提供的主动代理创立器。
BeanNameAutoProxyCreator
BeanNameAutoProxyCreator
类是一个BeanPostProcessor
,能够主动为名称与文字值或通配符匹配的 bean 创立 AOP 代理。以下示例显示了如何创立BeanNameAutoProxyCreator
bean:
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>
与 ProxyFactoryBean
一样,有一个 interceptorNames
属性而不是一列拦截器,以容许原型 advisors
的正确行为。名为“拦截器”的能够是 advisors
或任何告诉类型。
一般而言,与主动代理一样,应用 BeanNameAutoProxyCreator
的要点是将雷同的配置统一地利用于多个对象,并且配置量起码。将申明式事务利用于多个对象是一种风行的抉择。
名称匹配的 Bean 定义,例如后面示例中的 jdkMyBean
和onlyJdk
,是带有指标类的一般旧 Bean 定义。BeanNameAutoProxyCreator
主动创立一个 AOP 代理。雷同的告诉实用于所有匹配的 bean。留神,如果应用了advisors
(而不是后面的示例中的拦截器),则切入点可能会不同地利用于不同的 bean。
DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator
是更通用,性能极其弱小的主动代理创立器。这将主动在以后上下文中利用合格的 advisor
,而不须要在主动代理advisor
bean 定义中蕴含特定的 bean 名称。与BeanNameAutoProxyCreator
一样,它具备统一的配置和防止反复的长处。
应用此机制波及:
- 指定
DefaultAdvisorAutoProxyCreator
bean 定义。 - 在雷同或关联的上下文中指定任何数量的
advisor
。请留神,这些必须是advisor
,而不是拦截器或其余告诉。这是必要的,因为必须有一个要评估的切入点来查看每个告诉到候选 bean 定义的资格。DefaultAdvisorAutoProxyCreator
主动评估每个advisor
中蕴含的切入点,以查看应该将什么 (如果有的话) 告诉利用到每个业务对象 (例如示例中的businessObject1
和businessObject2
)。
这意味着能够将任意数量的 advisor
主动利用于每个业务对象。如果任何 advisor
中没有切入点匹配业务对象中的任何办法,则该对象不会被代理。当为新的业务对象增加 Bean 定义时,如有必要,它们会主动被代理。
通常,主动代理的长处是使调用者或依赖者无奈取得未告诉的对象。在此 ApplicationContext
上调用 getBean(“businessObject1”)
会返回 AOP 代理,而不是指标业务对象。(后面显示的“inner
bean”也提供了这一益处。)
以下示例创立一个DefaultAdvisorAutoProxyCreator
bean 和本节中探讨的其余元素:
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>
<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>
<bean id="businessObject1" class="com.mycompany.BusinessObject1">
<!-- Properties omitted -->
</bean>
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>
如果要将雷同的告诉统一地利用于许多业务对象,则 DefaultAdvisorAutoProxyCreator
十分有用。一旦基础设施定义就位,你就能够增加新的业务对象,而不包含特定的代理配置。你还能够很容易地删除其余切面 (例如,跟踪或性能监督切面),只需对配置进行最小的更改。DefaultAdvisorAutoProxyCreator
反对过滤 (通过应用命名约定,只有特定的advisor
被评估,这容许在同一个工厂中应用多个不同配置的 AdvisorAutoProxyCreators
) 和排序。Advisor
能够实现 org.springframework.core.Ordered
接口,以确保在呈现问题时能够正确排序。后面示例中应用的 TransactionAttributeSourceAdvisor
具备可配置的程序值。默认设置为无序。
参考代码:
com.liyong.ioccontainer.starter.AdvisorAutoProxyCreatorIocContainer
6.9 应用 TargetSource
实现
Spring 提供了 TargetSource
的概念,以 org.springframework.aop.TargetSource
接口示意。该接口负责返回实现连接点的“指标对象
”。每当 AOP 代理解决办法调用时,就会向TargetSource
实现询问指标实例。
应用 Spring AOP 的开发人员通常不须要间接应用 TargetSource
实现,然而这提供了反对池、热交换和其余简单指标的弱小办法。例如,通过应用池来治理实例,TargetSource
能够为每次调用返回不同的指标实例。
如果未指定TargetSource
,则将应用默认实现包装本地对象。每次调用都返回雷同的指标(与你冀望的一样)。
本节的其余部分形容了 Spring 随附的规范指标源以及如何应用它们。
应用自定义指标源时,指标通常须要是原型而不是单例 bean 定义。这样,Spring 能够在须要时创立一个新的指标实例。
6.9.1 可热交换指标源
org.springframework.aop.target.HotSwappableTargetSource
的存在让 AOP 代理指标被切换,同时让调用者放弃对它的援用。
扭转指标源的指标立刻失效。HotSwappableTargetSource
是线程平安的。
你能够在 HotSwappableTargetSource
上通过应用 swap()
办法扭转指标,相似上面例子展现:
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
上面的例子显示所须要的 XML 定义:
<bean id="initialTarget" class="mycompany.OldTarget"/>
<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg ref="initialTarget"/>
</bean>
<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="swapper"/>
</bean>
后面的 swap()
调用更改了可替换 bean 的指标。持有对该 bean 的援用的客户端不晓得更改,但会立刻开始达到新指标。
只管这个示例没有增加任何告诉 (应用TargetSource
不须要增加告诉),然而能够将任何 TargetSource
与任意告诉联合应用。
6.9.2 池指标源
应用池指标源能够提供相似于无状态会话 ejb 的编程模型,其中保护雷同实例的池,办法调用将开释池中的对象。
Spring 池和 SLSB
池之间的要害区别在于,Spring 池能够利用于任何 POJO。与 Spring 个别状况一样,能够以非侵入性的形式利用此服务。
Spring 提供对 Commons Pool 2.2
反对,它提供一个相当地高效池实现。你须要在你的利用类门路上增加 commons-pool
jar 去应用这个个性。你也能够应用org.springframework.aop.target.AbstractPoolingTargetSource
去反对其余的池化 API。
还反对 Commons Pool 1.5+,但从 Spring Framework 4.2 开始不举荐应用。
以下清单显示了一个示例配置:
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
scope="prototype">
... properties omitted
</bean>
<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPool2TargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
<property name="maxSize" value="25"/>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="poolTargetSource"/>
<property name="interceptorNames" value="myInterceptor"/>
</bean>
请留神,指标对象 (在后面的示例中为businessObjectTarget
) 必须是原型。这使 PoolingTargetSource
实现能够创立指标的新实例,以依据须要去扩大池中对象。无关其属性的信息,请参见 AbstractPoolingTargetSource 的 javadoc 和心愿应用的具体子类 maxSize
是最根本的,并且始终保障存在。
在这种状况下,myInterceptor
是须要在同一 IoC 上下文中定义的拦截器的名称。然而,你无需指定拦截器即可应用池。如果只心愿池化而没有其余告诉,则齐全不要设置 interceptorNames
属性。
你能够将 Spring 配置为可能将任何池化对象转换到 org.springframework.aop.target.PoolingConfig
接口,该接口通过 introduction
来公开无关池的配置和以后大小的信息。
你须要定义相似于以下内容的advisor
:
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="poolTargetSource"/>
<property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>
通过在 AbstractPoolingTargetSource
类上调用便捷办法来取得此 advisor
,因而能够应用MethodInvokingFactoryBean
。该advisor
的名称(在此处为 poolConfigAdvisor
)必须位于裸露池对象的ProxyFactoryBean
中的拦截器名称列表中。
转换的定义如下:
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is" + conf.getMaxSize());
池化无状态的服务对象通常是不必要的。咱们不认为它应该是默认抉择,因为大多数无状态对象天然是线程平安的,并且如果缓存了资源,实例池会成问题。
通过应用主动代理,能够实现更简略的池化。你能够设置任何主动代理创建者应用的 TargetSource
实现。
6.9.3 原型指标源
设置“原型
”指标源相似于设置池化TargetSource
。在这种状况下,每次办法调用都会创立指标的新实例。只管在古代 JVM 中创立新对象的老本并不高,但连贯新对象(满足其 IoC 依赖项) 的老本可能会更高。因而,没有充沛的理由就不应应用此办法。
为此,你能够批改后面显示的 poolTargetSource
定义,如下所示(为分明起见,咱们也更改了名称):
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
<property name="targetBeanName" ref="businessObjectTarget"/>
</bean>
惟一的属性是指标 Bean 的名称。在 TargetSource
实现中应用继承来确保命名统一。与池化指标源一样,指标 bean 必须是原型 bean 定义。
6.9.4 ThreadLocal
指标源
如果须要为每个传入申请(每个线程)创立一个对象,则 ThreadLocal
指标源很有用。ThreadLocal
的概念提供了 JDK 范畴的性能,能够通明地将资源与线程一起存储。设置 ThreadLocalTargetSource
简直与针对其余类型的指标源所阐明的雷同,如以下示例所示:
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
</bean>
在多线程和多类加载器环境中谬误地应用
ThreadLocal
实例时,会带来重大的问题(可能导致内存透露)。你应该始终思考在其余一些类中包装threadlocal
,并且相对不要间接应用ThreadLocal
自身(包装类中除外)。另外,你应该始终记住正确设置和勾销设置线程本地资源的正确设置和勾销设置(后者仅波及对ThreadLocal.set(null)
的调用)。在任何状况下都应进行勾销设置,因为不勾销设置可能会导致呈现问题。Spring 的ThreadLocal
反对为你做到了这一点,应该始终思考应用ThreadLocal
实例,无需其余适当的解决代码。
6.10 定义新告诉类型
Spring AOP 被设计为可扩大的。尽管目前在外部应用拦挡实现策略,然而除了在盘绕告诉、前置告诉、异样告诉以及在返回告诉进行拦挡外,还能够反对任意的告诉类型。
适配器包是一个 SPI 包,它容许在不更改外围框架的状况下增加对新的自定义告诉类型的反对。对自定义 Advice
类型的惟一限度是它必须实现 org.aopalliance.aop.Advice
标记接口。
无关更多信息,请参见 org.springframework.aop.framework.adapter javadoc。
参考代码:
com.liyong.ioccontainer.starter.TargetSourceIocContainer
作者
集体从事金融行业,就任过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就任于某银行负责对立领取零碎建设。本身对金融行业有强烈的喜好。同时也实际大数据、数据存储、自动化集成和部署、散布式微服务、响应式编程、人工智能等畛域。同时也热衷于技术分享创建公众号和博客站点对常识体系进行分享。关注公众号:青年 IT 男 获取最新技术文章推送!
博客地址: http://youngitman.tech
CSDN: https://blog.csdn.net/liyong1…
微信公众号:
技术交换群: