Spring学习笔记
版本:5.3.5
该文档主要介绍了Spring的面向切面的编程(AOP)技术。
AOP是面向切面编程。也是Spring的关键你能组件之一,提供强大的中间件解决方案。
使用AspectJ切入点的SpringAOP Spring可通过 基于Schema的方法 或者 @AspectJ注解方式,自定义编写切面的方法。
AOP在Spring框架中的用途:
Spring AOP模型的基本概念:
@Aspect注解的类就是切面around、before和after等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。Spring AOP包含的通知类型:
Spring AOP是用纯Java实现的。不需要特殊的编译过程。Spring AOP不需要控制类加载器的层次结构,因此适合在Servlet容器或应用程序服务器中使用。
Spring AOP当前仅支持方法调用的连接点(通知在Spring Bean上执行方法)。
Spring AOP的AOP方法不同于大多数其他AOP框架。目的不是提供最完整的AOP实现(尽管Spring AOP相当强大)。相反,其目的是在AOP实现和Spring IoC之间提供紧密的集成,以帮助解决企业应用程序中的常见问题。
Spring AOP默认将标准JDK动态代理用于AOP代理。这使得可以代理任何接口(或一组接口)。
Spring AOP也可以使用CGLIB代理。它是代理类的,而不是接口。默认情况下,如果业务对象未实现接口,则使用CGLIB。由于对接口而不是对类进行编程是一种好习惯,因此业务类通常实现一个或多个业务接口。在那些需要通知在接口上未声明的方法或需要将代理对象作为具体类型传递给方法的情况下(在极少数情况下),可以强制使用CGLIB。
@AspectJ是使用注解方式声明切面。@AspectJ注解是AspectJ项目在AspectJ 5发行版中引入的。Spring使用AspectJ提供的用于切入点解析和匹配的相同注解。但是,AOP运行时仍然是纯Spring AOP,并且不依赖于AspectJ编译器或编织器。
spring-aop项目中将AspectJ的注解
需要添加aspectjweaver.jar依赖。
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
</beans>
参考:io.github.wesleyone.spring.core.c5.c4.MyAspect
启用@AspectJ支持后,Spring会自动检测在应用程序上下文中使用@AspectJ注解的bean,并用于配置Spring AOP。
import org.aspectj.lang.annotation.Aspect;
@Component
@Aspect
public class NotVeryUsefulAspect {
}
在Spring AOP中,切面本身不能做其他切面的目标对象。
参考:io.github.wesleyone.spring.core.c5.c4.MyAspect
切入点用于确认感兴趣的连接点。Spring AOP仅支持Spring Bean的方法执行连接点,因此您可以将切入点视为与Spring Bean上的方法执行相匹配的切入点。切入点声明有两个部分:一个切入点方法声明,以及一个切入点表达式,该切入点表达式精确地确定我们感兴趣的方法。
import org.aspectj.lang.annotation.Aspect;
@Component
@Aspect
public class NotVeryUsefulAspect {
@Pointcut("execution(* transfer(..))") // 切入点表达式
private void anyOldTransfer() {} // 切入点方法声明
}
AspectJ支持的其他切入点指示符,SpringAop不支持,会抛异常。
SpringAOP特有的指示符,用于匹配beanName符合的bean的方法执行:
bean(idOrNameOfBean)
可以使用&&、||、!表达式
@Pointcut("execution(public * *(..))") // 定义匹配的方法
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.myapp.trading..*)") // 定义匹配的类型中所有方法
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()") // 定义以上两种规则同时匹配的所有方法
private void tradingOperation() {}
定义通用切面类来定义通用切入点
所有公共方法:
execution(public * *(..))
方法名称以set开头的所有方法
execution(* set*(..))
AccountService接口定义的所有方法:
execution(* com.xyz.service.AccountService.*(..))
该包路径下的所有方法:
execution(* com.xyz.service.*.*(..))
该包路径及下级路径下的的所有方法:
execution(* com.xyz.service..*.*(..))
该包路径下的所有切入点(在SpringAOP中仅是方法执行)
within(com.xyz.service.*)
该包路径及下级路径的所有切入点(在SpringAOP中仅是方法执行)
within(com.xyz.service..*)
实现接口的代理对象的所有切入点(在SpringAOP中仅是方法执行),通常是以绑定形式使用。
this(com.xyz.service.AccountService)
实现接口的目标对象的所有切入点(在SpringAOP中仅是方法执行),通常是以绑定形式使用。
target(com.xyz.service.AccountService)
匹配指定运行时方法参数数量和类型的所有切入点(在SpringAOP中仅是方法执行),通常是以绑定形式使用。
args(java.io.Serializable)
包含注解的目标对象的所有切入点(在SpringAOP中仅是方法执行)
@target(org.springframework.transaction.annotation.Transactional)
目标对象的类型有指定注解的所有切入点(在SpringAOP中仅是方法执行)
@within(org.springframework.transaction.annotation.Transactional)
执行难方法上有指定注解的所有切入点(在SpringAOP中仅是方法执行)
@annotation(org.springframework.transaction.annotation.Transactional)
匹配指定运行时方法参数数量和带有指定注解的类型的所有切入点(在SpringAOP中仅是方法执行),通常是以绑定形式使用。
@args(com.xyz.security.Classified)
匹配Spring BeanName的对象的所有切入点(在SpringAOP中仅是方法执行)
bean(tradeService)
通配符匹配Spring BeanName的对象的所有切入点(在SpringAOP中仅是方法执行)
bean(*Service)
尽量缩小切入点匹配搜索范围。
优先使用范围搜索within,范围匹配比较快。
参考:io.github.wesleyone.spring.core.c5.c4.MyAspect
通知与切入点表达式关联,并且在切入点匹配的方法执行之前,之后或环绕运行。切入点表达式可以是对命名切入点的简单引用,也可以是直接的切入点表达式。
切面通过org.springframework.core.Ordered接口和@Order注解,当处理相同切入点时,可以确定优先级。
相同切面内的存在多个相同通知时,顺序不能控制,建议写到同个通知,或者分开多个切面。
# Spring5.x
@Around
@Before
执行目标对象方法
@AfterReturning/@AfterThrowing
@After
@Before // @Order(1)
@Before // @Order(2)
执行目标对象方法
@After // @Order(2)
@After // @Order(1)
@Aspect
public class UsageTracking {
@DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
@Before("com.xyz.myapp.CommonPointcuts.businessService() && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}
Spring支持AspectJ的perthis和pertarget实例化模型。 切面实例是在服务对象上首次调用方法时创建的。当服务对象超出范围时,切面将超出范围。在创建切面实例之前,其中的任何通知都不会运行。 创建切面实例后,在其中声明的通知将在匹配的连接点处运行,但仅当服务对象是与此切面相关联的对象时才运行。 perthis子句的作用是为执行业务服务的每个唯一服务对象(每个this与切入点表达式匹配的连接点绑定到的唯一对象)创建一个切面实例。 pertarget在匹配的连接点时,每个独立目标对象创建一个切面实例。
@Aspect("perthis(com.xyz.myapp.CommonPointcuts.businessService())")
public class MyAspect {
private int someState;
@Before("com.xyz.myapp.CommonPointcuts.businessService()")
public void recordServiceUsage() {
// ...
}
}
略
XML方式的缺点:1.没有完全解决需求的实现封装在一个地方;2.切入点表达式受限制,无法组合XML声明的命名切入点。
通过使用自动代理支持,XML-schema定义的
Spring AOP使用JDK动态代理或CGLIB创建给定目标对象的代理。JDK动态代理内置在JDK中, 而CGLIB是常见的开源类定义库(重新打包到spring-core)。
如果要代理的目标对象实现至少一个接口,则使用JDK动态代理,代理了目标类型的所有接口实现。 如果目标对象未实现任何接口,则将创建CGLIB代理。
可以强制使用CGLIB代理(代理为目标对象定义的每个方法,而不仅仅是接口方法实现),但是要考虑以下问题:
final方法无法通知,因为无法在运行时生成的子类中重写方法;@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true) // 强制使用CGLIB代理
public class AopConfig {
}
目标对象方法执行时,存在调用对象内其他方法时(自调用),调用其他方法是目标对象的调用,而不是代理对象的调用,从而导致其他方法不被代理处理。可通过以下开启为代理对象的调用:
@Configuration
@EnableAspectJAutoProxy(exposeProxy=true)
public class AopConfig {
}
// 创建一个针对目标对象生成代理对象的工厂
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// 添加切面,这个类必须有@AspectJ注解
// 可以多次调用该方法来添加多个切面
factory.addAspect(SecurityManager.class);
// 可以添加已存在的切面实例,这个对象类型必须有@AspectJ注解
factory.addAspect(usageTracker);
// 获取代理对象
MyInterfaceType proxy = factory.getProxy();
上一章使用基于@AspectJ注解和基于XML的切面定义描述了Spring对AOP的支持,适合应用开发使用。 在本章中,我们讨论底层的Spring AOP API。
Spring如何处理切入点概念。
Spring的切入点模型使切入点重复使用不受通知类型的影响。您可以使用相同的切入点来定位到不同的通知。
org.springframework.aop.Pointcut接口是核心接口,用来将通知关联到指定的类和方法。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
ClassFilter接口用于将切入点限制为给定的一组目标类。如果该matches()方法始终返回true,则匹配所有目标类。
public interface ClassFilter {
boolean matches(Class clazz);
}
MethodMatcher接口的matches(Method, Class)方法用于测试此切入点是否与目标类上的给定方法匹配。创建AOP代理时可以执行此评估,以避免需要对每个方法调用进行测试。
当两个参数的matches方法返回true,且isRuntime方法返回true,则每次方法调用时都会执行三个参数的matches方法,切入点就可以在目标通知开始之前立即查看传递的方法参数。
public interface MethodMatcher {
boolean matches(Method m, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method m, Class<?> targetClass, Object... args);
}
大多数MethodMatcher实现都是静态的,这意味着它们的isRuntime()方法将返回false。在这种情况下,matches永远不会调用三参数方法。
Spring支持切入点上的操作(特别是联合和相交)。
联合表示两个切入点其中一个匹配的方法。交集是指两个切入点都匹配的方法。联合通常更有用。您可以通过使用类中的静态方法org.springframework.aop.support.Pointcuts或使用同个包中的类 ComposablePointcut来编写切入点。但是,使用AspectJ切入点表达式通常是一种更简单的方法。
从2.0开始,Spring使用的最重要的切入点类型是org.springframework.aop.aspectj.AspectJExpressionPointcut。这是一个切入点,该切入点使用AspectJ提供的库来解析AspectJ切入点表达式字符串。
org.springframework.aop.support.JdkRegexpMethodPointcut通过JDK中的正则表达式支持实现切入点。或者使用RegexpMethodPointcutAdvisor指定正则表达式切面同时指定通知bean。org.springframework.aop.support.ControlFlowPointcutSpring提供了有用的切入点父类,以帮助您实现自定义的切入点。
因为静态切入点最有用,所以可以继承StaticMethodMatcherPointcut,仅需要实现一个抽象方法(也可以覆盖其他方法来自定义行为)。以下示例展示:
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}
也有动态切入点的父类可用于自定义。
由于SpringAop是Java类实现的,而不是语言特征,用户可以声明自定义切入点,无论是静态的还是动态的。但是推荐直接使用AspectJ切入点表达式。
每种通知都是Spring Bean。一个通知实例可以分享给所有被通知的对象或者对于每个被通知的对象都不同,分别对应与per-class或per-instance通知。
per-class通知较常用。不依赖代理对象的状态或添加新状态,仅作用与方法和参数。
per-instance通知可以使用引用(introductions),来支持mixins,可以将状态添加到代理对象中。
在同一AOP代理中允许混合使用基于类通知和基于实例通知。
Spring提供了几种通知类型,可以通过实现扩展以支持任意通知类型。
Spring使用拦截器接口MethodInterceptor实现环绕通知功能:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()方法的MethodInvocation参数封装了被调用的方法、目标连接点、AOP代理以及方法的参数。该invoke()方法的返回结果是连接点的返回值。
使用案例如下:
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()方法,沿着拦截器链向下,直到连接点(目标对象方法)。
前置通知不需要 MethodInvocation对象,因为仅在进入方法前才调用它。
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
返回类型是void,不能更改返回值。如果执行出现异常,则停止进一步执行拦截器,异常会传播回拦截器链。
使用案例:
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)
最后一个异常类型参数必须有,其他参数根据需要设置。
案例:
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}
下面案例展示一个异常处理类中可以包含多个异常通知:
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方法抛出一个已检查的异常,则它必须与目标方法的已声明异常匹配。不要抛出与目标方法签名不兼容的未声明检查异常!
异常通知能和所有切入点一起使用
实现该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;
}
}
如果通知中抛出异常,则会将异常抛出,而不是返回值。
异常通知能和所有切入点一起使用
暂略。
Advisor是只包含一个通知对象和一个切入点表达式的切面。
org.springframework.aop.support.DefaultPointcutAdvisor是常用的Advisor类。他可以使用MethodInterceptor,BeforeAdvice或ThrowsAdvice。
ProxyFactoryBean创建AOP代理在Spring中创建AOP代理的基本方法是使用 org.springframework.aop.framework.ProxyFactoryBean。这样可以完全控制切入点、任何适用的有序的通知。
ProxyFactoryBean是一个FactoryBean,通过getObject()方法获取目标对象的AOP代理对象。
使用一个ProxyFactoryBean或其他IoC-aware类创建AOP代理的最重要好处之一是,通知和切入点也可以由IoC管理。这是一项强大的功能,可以实现其他AOP框架难以实现的方法。例如,受益于依赖注入提供的所有可插入性,通知本身可以引用应用程序对象。
和大多数FactoryBean实现相同,ProxyFactoryBean本身是JavaBean,其属性用于:
一些属性继承自org.springframework.aop.framework.ProxyConfig(SpringAOP代理工厂的父类)。这些关键属性包括:
ThreadLocal,以便目标对象可以访问它。将exposeProxy属性设置为true,目标对象使用该AopContext.currentProxy()方法获取代理。其他ProxyFactoryBean特定的属性:
ProxyFactoryBean忽略通知的单例设置。
您可以在拦截器名称后加上星号(),这样做会使得名字以号前开头的所有advisor Bean被使用。getObject()调用该方法的频率如何,工厂是否应返回单个对象。默认值为true。如果要使用有状态的通知(例如,有状态的mixins),设置false来使用原型通知。ProxyFactoryBean的配置。proxyTargetClass=true,即使proxyInterfaces设置一个或多个完全限定的接口名称,也是使用CGLIB代理。proxyInterfaces设置一个或多个完全限定的接口名称,则创建基于JDK的代理。创建的代理实现proxyInterfaces中指定的所有接口。ProxyFactoryBean的proxyInterfaces属性,但是目标类确实实现了一个(或多个)接口,则ProxyFactoryBean自动检测到目标类确实实现了至少一个接口,并创建基于JDK的代理。实际代理的接口是目标类实现的所有接口。实际上,这与将目标类的所有接口的提供给该proxyInterfaces属性的效果相同。但是,它的工作量大大减少,而且不容易出现书写错误。CGLIB代理通过在运行时生成目标类的子类来工作。Spring委托该代理子类来调用原始对象。这个子类通过装饰者模式,将所有通知编织到代理中。
final方法不能被代理,因为该方法不能被子类重写;通过星号模糊匹配拦截器名称
<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"/>
为代理创建父模板(abstract=”true”不会被实例化):
<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 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
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
// 添加切面、Advisor
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
// 获取代理对象
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
创建的AOP代理对象,可以通过org.springframework.aop.framework.Advised接口控制。任何AOP代理可以转换成这个接口类型。
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + " advisors");
// 添加一个通知,比如没有切入点的拦截器,将会匹配所有代理方法
// 可添加拦截器,前置、正常后置、异常后置通知
advised.addAdvice(new DebugInterceptor());
// 添加指定切入点的通知
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
以上通过ProxyFactoryBean显示创建AOP代理
Spring基于BeanPostProcessor的功能,可以自动生成代理。
这章节的类主要来自org.springframework.aop.framework.autoproxy包。
通过BeanPostProcessor机制,自动创建名称规则匹配的Bean的AOP代理。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>
在上下文中自动应用符合的advisors实现。
需要评估切入点是否有匹配的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"/>
org.springframework.aop.TargetSource接口负责返回实现连接点的”目标对象”。AOP代理每处理一个方法调用时就会创建一个TargetSource。
org.springframework.aop.target.HotSwappableTargetSource让AOP代理的目标切换立即生效。HotSwappableTargetSource是线程安全的。
<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>
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
// 调用swap方法,切换目标对象
Object oldTarget = swapper.swap(newTarget);
org.springframework.aop.target.AbstractPoolingTargetSource
<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实现可以创建目标的新实例,以根据需要扩展池。
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
<property name="targetBeanName" ref="businessObjectTarget"/>
</bean>
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
</bean>
自定义类实现org.aopalliance.aop.Advice接口。编写org.springframework.aop.framework.adapter的SPI配置。