在阅读此文之前请你熟悉一些IOC的知识,同时了解AOP的概念。
在 Spring 中所有的通知都是以 Java 类的形式编写的。 Spring 是采用运行期的方式来将切面织入到系统中的。
代理 Bean 只有在第一次被应用系统需要的时候才被创建。 Spring 有两种方式创建代理: Proxy 类创建代理 ( 实现过接口的目标类 ) 和运用 CGLIB 库创建代理 ( 没有实现过任何接口的目标类 ) 。需要注意两点: 1 、对接口创建代理优于对类创建代理,因为这样会产生更加松耦合的系统, 2 、标记为 final 的方法是不能被通知的,因为 Spring 在实现的时候是为目标类产生子类。
Spring 只支持方法联接点。
Spring 中通知的类型:
1 、 Around org.aopalliance.intercept.MethodInterceptor 栏截对目标对象方法的调用
2 、 Before org.springframework.aop.MethodBeforAdvice 在目标方法被调用之前调用
3 、 After org.springframework.aop.AfterReturningAdvice 当目标方法被调用之后调用
4 、 Thorws org.springframework.aop.ThrowsAdvice 当目标方法抛出异常时调用
前置通知 实现 MethodBeforeAdvice 接口,该接口有以下方法:
void befor(Method method,Object []args,Object target) throws Throwable;
例:
public class WelcomeAdvice implements MethodBeforeAdvice{
public void before(Method method,Object []args,Object target){
System.out.println("Hello,"+((Customer)args[0]).getName());
}
}
配置文件如下:
<beans>
<bean id="aaaTargetObject" class="AAA"/> // 目标类 , 这是一个实现某个接口的类
<bean id="welcomeAdvice" class="WelcomeAdvice"/> // 通知类
<bean id="aaa" class="org.springframework.aop.framework.proxyFactoryBean">
<property name="interceptorNames">
<list>
<value>welcomAdvice</value>
</list>
</property>
<property name="target">
<ref bean="aaaTargetObject"/>
</property>
</bean>
</beans>
后置通知 实现 AfterReturningAdvice 接口,该接口有以下方法:
void afterReturning(Object returnValue,Method method,Object []args,Object target) throws Throwable;
例:
public class ThankYouAdvice implements AfterReturningAdvice{
public void afterReturning(Object returnValue,Method method,Object []args,Object target){
System.out.println("Thank you, come again.");
}
}
在前置通知和后置通知中都不能改变参数中传进来的值,改变执行流程的惟一方法就是抛出异常。
环绕通知 实现 MethodInterceptor 接口,该接口有以下方法:
Object invoke(MethodInvocation invocation) throws Throwable;
环绕通知能够控制目标方法是否真的被调用,通过调用 MethodInvocation.proceed() 方法来调用目标方法。它还可以让你控制返回的对象。
例:
public class ExampleAroundInterceptor implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Throwable{
// 调用之前可以做一些处理
Object returnObject=invocation.proceed();// 调用目标方法
// 调用之后还可以做一些处理
return returnObject;
}
}
异常通知 实现 ThrowsAdvice 接口,此接口是一个标示接口,没有定义必须实现的方法,但是下面两个方法中必须实现一个:
void afterThrowing(Throwable throwable);
void afterThrowing(Method method,Object []args,Object target,Throwable throwable);
ThrowsAdvice 要处理的异常取决于你的方法定义的异常类型,在一个实现类中,也可以实现多个 afterThrowing 方法,根据抛出的异常类型调用适当的方法。
引入通知 给目标对象添加新的方法 ( 以及属性 ) 。
定义切入点
切入点 就是应用通知的地方,切入点决定了一个特定的类的特定方法是否满足一条特定的规则,如果一个方法符合的话,通知就应用到该方法上。 Spring 的切入点可以让我们以一种灵活的方式定义在什么地方将通知织入到我们的类中。
切入点的核心接口是 Pointcut 接口,同时它也需要两个接口支持,如下:
public interface Pointcut{
ClassFilter getClassFilter(); // 决定一个类是否符合要求
MethodMatcher getMethodMatcher(); // 决定一个方法是否符合要求
}
public interface ClassFilter{
boolean matches(Class clazz);
}
此接口总是包含一个简单的 ClassFilter 接口的实现 --ClassFilter.TRUE ,它是规范的适合任何类的 ClassFilter 实例,它适合于只根据方法决定什么时候符合要求的切入点。
public interface MethodMatcher{
boolean matches(Method m,class targetClass);// 静态的决定一个方法是否被通知织入
public boolean isRuntime();// 决定是静态的还是动态的织入
public boolean mathes(Method m,Class targetClass,Object []args);// 动态的决定一个方法是否被通知织入,系统开销会增加,不推荐使用
}
大多数的切面是由定义切面行为的通知和定义切面在什么地方执行的切入点给合而成的, Spring 为此提供了 Advisor ,它把通知和切入点组合到一个对象中。接口 PointcutAdvisor 提供了这些功能,接口如下:
public interface PointcutAdvisor{
Pointcut getPointcut();
Advice getAdvice();
}
大多数 Spring 自带的切入点都有一个对应的 PointcutAdvisor 。这样方便你在一个地方定义通知和切入点。
使用 Spring 的静态切入点 :
Spring 为创建静态切入点提供了方便的父类: StaticMethodMatcherPointcut 。如果想自定义静态切入点的话,继承这个类,并实现 isMatch 方法就 OK 了。
Spring 提供了一个静态切入点的实现类: NameMatchMethodPointcut 。它有如下两个方法:
Public void setMappedName(String);
Public void setMappedNames(String []);
这个类通过名字映射,上面两个方法的参数中均可以使用通配符 * 。
规则表达式切入点, Spring 提供了 RegexpMethodPointcut 让你利用正则表达式的力量来定义切入点。
符号
|
描述
|
示例
|
.
|
匹配任何单个字符
|
setFoo. 匹配 setFooB, 但不匹配 setFoo 或 setFooBar
|
+
|
匹配前一个字符一次或多次
|
setFoo.+ 匹配 setFooBar 和 setFooB, 但不匹配 setFoo
|
*
|
匹配前一个字符 0 次或多次
|
setFoo.* 匹配 setFoo,setFooB 和 setFooBar
|
\
|
匹配任何正则表达式符号
|
\.setFoo. 匹配 bar.setFoo, 但不匹配 setFoo
|
如果你想匹配所有 setXxx 方法,我们需要使用 .*set*. 模版 ( 第一个通配符匹配任何类名。笔者认为此处《 Spring in action 》一书中有误,我认为此处应为 .*set.*) 。如果使用 RegexpMethodPointcut ,你需要在你的应用系统中引入 ORO 类库。
<bean id=”queryPointcutAdvisor”
Class=”org.springframework.aop.support.RegExPointcutAdvisor”>
<property name=”pattern”>
<value>.*get.+By.+</value>
</property>
<property name=”advice”>
<ref bean=”queryInterceptor”/>
</property>
</bean>
使用动态切入点
Spring 提供了一种内置的动态切入点: ControlFlowPointcut 。这个切入点根据线程调用堆栈的信息来匹配方法。也就是说,它可以配置成只有当指定方法或类能在当前线程执行堆栈中找到时,返回 true 。
<bean id=”servletPointcut” class=”org.springframework.aop.support.ControlFlowPointcut”>
<construct-arg>
<value>javax.servlet.http.HttpServlet</value>
</construct-arg>
</bean>
<bean id=”servletAdvisor” class=”org.springframework.aop.support.DefaultPointcutAdvisor”>
<property name=”advice”>
<ref bean=”servletInterceptor”/>
</property>
<property name=”pointcut”>
<ref bean=”servletPointcut”/>
</property>
</bean>
注: ControlFlowPointcut 明显比其他的动态切入点慢。
切入点实施
Spring 支持在切入点上进行操作 — 合并与产叉 — 来创建新的切入点。只有当切入点都匹配时交叉集合才匹配,任何一个切入点匹配都会使合并集合匹配。 Spring 为创建这两种切入点提供了两个类:第一个类是 ComposablePointcut 。通过将已有的 ComposablePointcut 、切入点、 MethodMatcher 以及 ClassFilter 对象进行合并或交叉,组装成一个新的 ComposablePointcut 对象。这可以通过调用 ComposablePointcut 实例的 intersection () 或 union () 方法实现。
ComposablePointcut cp=new ComposablePoint()
cp=p.intersection(myPointcut).union(myMethodmatcher);
为了对两个 Pointcut 对象进行合并,必须使用 Pointcuts 类。这是一个工具类,它拥有很多操作 Pointcut 对象的静态方法。如:
Pointcut union=Pointcuts.union(pointcut1,pointcut2);
创建引入
引入与其他类型的 Spring 通知有所不同。其它类型通知是在方法调用的周围织入到不同的连接点,而引入则是影响整个类,他们通过给需要消息的类型添加方法和属性来实现。也就是说,你可以用一个已存在的类让它实现另外的接口,维持另外的状态 ( 这也叫混合 ) 。换句话说,引入让你能够动态地建立复合对象,提供了多态继承的好处。
实现 IntroductionInterceptor
Spring 通过一个特殊的方法拦截器接口 IntroductionMethodInterceptor 来实现引入。这个接口有一个方法:
Boolean implementsInterface(Class intf);
如果 IntroductionMethodInterceptor 是为了实现指定接口,那么方法 implementsInterface 应该返回 true 。就是说,对用这个接口声明的方法的任何调用将被委托给 IntroductionMethodInterceptor 的 invoke() 方法。 Invoke() 方法负责实现这个方法,不能调用 MethodInvocation.proceed() 。它引入了新的接口,调用目标对象是没有用的。
使用 ProxyBeanFactory
BeanFactory 对象是一个负责创建其他 JavaBean 的 JavaBean 。属性列表如下:
属性
|
使 用
|
target
|
代理的目标对象
|
proxyInterfaces
|
代理应该实现的接口列表
|
interceptorNames
|
需要应用到目标对象上的通知 Bean 的名字。可以是拦截器, Advisor 或其他通知类型的名字。这个属性必须按照在 BeanFactory 中使用的顺序设置。
|
singleton
|
是否返回的是同一个代理实例。如果使用的是状态通知,应该设置为 false
|
aopProxyFactory
|
使用的 ProxyFactoryBean 实现
|
exposeProxy
|
目标对象是否需要得到当前的代理。通过调用 AopContext.getCurrentProxy 实现。记住这样做会在你的代码中引入 Spring 专有的 AOP 代码,所以,尽量避免使用。
|
frozen
|
一旦工厂被创建,是否可以修改代理的通知。
|
optimize
|
是否对创建的代理进行优化 ( 仅适用于 CGLIB) 。
|
ProxyTargetClass
|
是否代理目标类,而不是实现接口。 ( 需要 CGLIB 支持 )
|
大多数情况下我们只用到前三个属性。
如果想避免将目标对象暴露给系统中其他 Bean 的话,可以将它声明为一个内部 Bean 。
proxyInterfaces 属性指定了从工厂中创建的 Bean 需要实现的接口。如:
<property name=”proxyInterfaces”>
<value>com.springinaction.service.CourseService</value>
</property>
这样就让 ProxyBeanFactory 知道它创建的所有 Bean 都要实现 CourseService 接口。可以像这样只提供一个接口,也可以用 <list> 提供多个接口。
interceptorNames 属性定义了一个应用到目标对象上的 Advisor 或通知 Bean 的列表。目标 Bean 也可以放在此属性的 <list> 列表的最后,但是最好还是用 Target 属性设置目标 Bean 。
自动代理
Spring 有一个自动代理机制,它可以让容器为我们产生代理。 Spring 有两个类提供这种服务: BeanNameAutoProxyCreator 和 DefaultAdvisorAutoProxyCreator 。
BeanNameAutoProxyCreator 为匹配一系列名字的 Bean 自动创建代理。它允许在名字的两端进行通配符匹配。通常用于为符合相同命名规则的 Bean 应用一个或一组切面 。 ( 主要是解决 target 属性配置时目标类过多的问题。 ) 如:
<bean id=”preformanceThresholdProxyCreator
Class=”org.springframework.aop.framework.autoproxy.BeanNameAutoProxyProlyCreator”>
<property name=”beanNames”>
<list>
<value>*Service</value>
</list>
</property>
<propery name=”interceptorNames”>
<value>performaceThresholdInterceptor</value>
</property>
</bean>
如果 Bean 是一个 Advisor 或拦截器,它将应用到代理对象的所有方法上。如果是通知的话, Advisor 切入点会根据不同 Bean 将通知应用到不同的地方。
自动代理框架对于代理需要暴露哪些接口作了一些假设。目标对象实现的任何接口代理对象都要暴露出来。如果目标类没有实现任何接口,那么应用于前面讨论过的 ProxyFactoryBean 一样的规则 — 动态生成一个子类。
DefaultAdvisorAutoProxyCreator
这个类的奇妙之处在于它实现了 BeanPostProcessor 接口。当 ApplicationContext 读入所有 Bean 的配置信息后, DefaultAdvisorAutoProxyCreator 将扫描上下文,寻找所有的 Advisor 。它将这些 Advisor 应用到所有符合 Advisor 切入点的 Bean 中。这个代理创建器只能与 Advisor 配合使用 。 ( 我们知道,一个 Advisor 是一个切入点和一个通知的结合体。 DefaultAdvisorAutoProxyCreator 需要 Advisor 来知道哪些 Bean 需要通知。 )
<bean id=”advisor” class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>
<property name=”advice”>
<bean class=”performaceThresholdInterceptor”/>
</property>
<property name=”pattern”>
<value>.+Service\..+</value>
</property>
</bean>
<bean id=”autoProxyCreator”
class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”/>
在读入所有 Bean 的定义信息后, BeanFactory 中的所有 Advisor 将被释放出来。它们会将它们的通知配置到任何匹配它们的切入点的 Bean 上 ( 这就是它真正的威力 ) 。
不用显示地将 Advisor 与其他东西结合,现在只要简单地定义它们,然后让它们自动地应用到它们匹配的地方。这样松耦合 Bean 以及它们的通知就实现了。
Spring 也支持通过元数据 (Metadata) 驱动自动代理。这种类型自动代理,代理配置是通过源代码属性而不是外部配置文件获得的。最常见的元数据自动配置是声明式事务支持。
我觉得,动态代理是AOP的主要实现手段之一,AOP是动态代理的一种应用深化
AOP 与动态代理的关系
AOP是一种思想,或者是方法论,类似OOP,是OOP的有力补充;
OOP侧重于主要业务功能设计(一般关注点);
AOP侧重于横切关注点(事务、日志、安全等,贯穿于多个模块的功能)
小分析:
OOP在横切关注点问题的不足是:1)代码混乱 2)代码分散
AOP很好的解决了这个问题,并且可以很好的解决“侵入问题”。
AOP的实现,必然要求对截获的对象进行代理,这个地方无疑要用的是动态代理机制。
如果非要说谁是谁的实现手段,我更倾向于AOP主要应用了动态代理,动态代理是AOP的实现手段。
附记:
记得一个文章提及:
AOP是设计模式的延续;
GoF侧重于接口、抽象函数来解除耦合;却对对象的内部无能为力。
AOP却恰恰从对象的内部着手,实现调用者和被调用者的分离。
分享到:
相关推荐
本文主要承接上文,向您展示 Spring Framework 组件的典型应用场景和基于这个场景设计出的简单案例,并以此引出 Spring 的核心要点,比如 IOC 和 AOP 等。在此基础上还引入了不同的配置方式,如 XML、Java 配置和...
- **Spring AOP基础概念**:Spring框架中的AOP实现细节。 **3.2 AspectJ 6初探** - **AspectJ介绍**:了解AspectJ 6的新特性和改进。 **3.3 老式Spring AOP** - **传统Spring AOP**:回顾早期Spring AOP的实现...
【Spring 框架要点详解】 Spring 是一个广泛使用的 Java 应用开发框架,它提供了丰富的功能,包括依赖注入(DI)、面向切面编程(AOP)、事务管理等。以下将详细介绍 Spring 中的 AOP 相关知识点。 1. **AOP(面向切面...
### Java之Hibernate和Spring技术难点及其要点总结 #### Hibernate与Spring技术概述 在Java开发领域,Hibernate和Spring作为两个非常重要的框架,对于提高应用程序的开发效率、降低维护成本具有不可替代的作用。...
下面我们将详细讲解整合Spring、Hibernate与JPA的要点。 1. **依赖包** 在整合Spring和Hibernate的JPA之前,首先需要确保引入了正确的依赖包。这包括Spring的核心库、Hibernate的Core库以及JPA相关的库。如`spring...
- **AOP(面向切面编程)**:使用Spring AOP实现横切关注点的分离,比如日志记录、安全性检查等。 - **性能优化**:针对高并发场景进行性能调优,确保系统的稳定运行。 #### 六、总结 IoC 和 AOP 是现代软件开发中...
3. **Spring AOP**:可能使用AOP来处理事务管理和日志记录,确保Web服务调用的原子性和可跟踪性。 4. **Spring MVC**:如果涉及RESTful服务,Spring MVC框架将用于处理HTTP请求,将请求映射到特定的控制器方法,...
2. AOP:AOP是Spring提供的一种面向切面编程的能力,允许开发者定义“切面”,并在适当的时间自动执行相应的代码,如日志记录、事务管理等。`org.springframework.aop` 包是AOP的核心,包含了代理、通知、切点等关键...
通过接口实现增强处理是较低版本Spring AOP的做法,如果在一个使用低版本Spring AOP开发的项目上进行升级,可以考虑使用<aop:advisor>复用已经存在的增强类;如果项目采用JDK 5.0以上版本,可以考虑使用@AspectJ注解...
通过邹波老师的Spring视频教程和笔记,我们可以系统地学习到Spring框架的核心知识和技术要点。无论是初学者还是有一定经验的开发者,都能够从中获益匪浅。Spring框架作为目前最流行的Java企业级开发框架之一,掌握其...
- **AOP(面向切面编程)**:Spring AOP允许开发者定义“切面”来封装那些跨多模块的功能,如日志记录、安全控制等,从而提高代码的复用性和可维护性。 - **事务管理**:Spring提供了一套强大的事务管理机制,能够...
虽然给定的部分内容并未包含实际的技术细节,但从标题、描述以及标签来看,我们可以推断出这份文档会涵盖Spring框架的核心概念和技术要点。 ### Spring框架简介 Spring框架是一个开源的应用程序框架,主要用于Java...
1. **Spring的事务管理**:通过AOP(面向切面编程)实现声明式事务管理,简化了事务控制。 2. **IoC(Inversion of Control)容器**:Spring管理Hibernate Session,提供SessionFactory和Transaction的生命周期管理...
3. **AOP(面向切面编程)**:Spring的AOP支持可以帮助开发者定义拦截器,实现在不修改原有代码的情况下,对特定操作进行增强,如日志记录、事务管理等。 4. **模型驱动**:Spring的ModelAndView对象可以用来封装...
- 该手册详细介绍了Spring框架的核心概念和技术要点,帮助读者快速掌握Spring的基本用法和最佳实践。 #### 知识点二:无EJB架构的体验 - 手册强调了“no EJB”的架构理念,意味着Spring框架可以独立于EJB...
### Spring 2.5版本更新要点 Spring 2.5版本相比之前的版本,在功能上有了显著增强,包括但不限于: 1. **支持新的注解**:增加了对JSR-303验证注解的支持,这使得开发人员能够更轻松地进行数据验证。 2. **更好的...
### Spring框架概述与核心特性 #### 一、Spring框架简介 Spring框架是一个开源的轻量级Java开发框架,主要用于简化企业级应用的开发工作。...以上是对Spring框架的一些基础知识和技术要点的总结,希望对你有所帮助。