springaop
**二、AOP的设计与实现
1、JVM的动态代理特性**
在Spring AOP实现中, 使用的核心技术时动态代理,而这种动态代理实际上是JDK的一个特性。通过JDK的动态代理特性,可以为任意java对象创建代理对象,对于具体使用来说,这个特性使通过Java Reflection API来完成的。在此之前先简要复习一下Proxy模式,其静态类图如下:
我们可以看到有一个Realsubject,这个对象是目标对象,而在代理模式的设计中,会设计一个接口和目标对象一致的代理对象Proxy,它们都实现了接口Subject的request方法。在这种情况下,对目标对象的request调用,往往就被代理对象“浑水摸鱼”给拦截了。通过这种拦截,为目标对象的方法操作做了铺垫。
在Proxy的调用过程中,如果客户调用Proxy的request方法,会在调用目标对象的request方法,会在调用目标对象的request方法的前后调用一系列的处理,而这一系列的处理相当于对目标对象来说是透明的,目标对象对这些处理可以毫不知情,这就是proxy模式。
我们知道JDK中已经实现了这个Proxy模式,在基于java虚拟机设计应用程序时,只需要直接使用这个特性就可以了。具体来说,可以再Java的Reflection包中看到proxy对象,这个对象生成后,所起的作用就类似于Proxy模式中的Proxy对象。在使用时,还需要为代理对象设计一个回调方法,这个回调方法起到的作用是,在其中假如了作为代理需要额外处理的动作。这个回调方法,如果在JDK中实现,需要实现下面所示的Invocationhandler接口:
public interface InvocationHandler{
public Object invoke(Object proxy,Method method,Object[] args) throws throwable;
}
- 1
- 2
- 3
至于invoke方法和Proxy挂上钩,熟悉proxy用法的读者都知道,只要在实现通过调用Proxy.newinstance方法生成具体的Proxy对象时,把InvocationHandler设置到参数里面就可以了,剩下的由Java虚拟机来完成。
2、spring aop的设计分析
Spring AOP以动态代理技术为基础,设计出了一系列AOP的横切实现,比如前置通知、返回通知、异常通知等。同时SpringAOP还提供了一系列的Pointcut来匹配切入点,可以使用现有的切入点来设计横切面,也可以扩展相关的Pointcut方法来切入需求。
在Spring AOP中,虽然对于AOP的使用者来说,只需要配置相关的Bean定义即可,但仔细分析Spring AOP内部设计可以看到,为了让AOP起作用,需要完成一系列过程,比如,需要为目标对象建立代理对象,这个代理对象可以通过使用JDK的Proxy来完成,也可以通过第三方的类生成器CGLIB来完成。然后,还需要启动代理对象的拦截器来完成各种横切面的织入,这一系列的织入设计是通过一系列Adapter来实现的。通过Adapter的设计,可以把AOP的横切面设计和Proxy模式有机结合起来,从而实现在AOP中定义好的各种织入方式。
3、Spring AOP的应用场景
SpringAOP把跨越应用程序多个模块的功能抽象出俩,并通过简单的AOP的使用,灵活的编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也是通过Spring AOP来实现的,比如后面将要介绍的事务处理。下面以ProxyFactoryBean的实现为例,和大家一起来了解Spring AOP的具体设计和实现
**三、建立AOPProxy代理对象
1、设计原理**
在Spring的AOP模块中,一个主要的部分是代理对象的生成,而对于Spring应用,可以看到,是通过配置和调用Spring的ProxyFactoryBean来完成这个任务的。在ProxyFactoryBean中,封装了主要代理对象的生成过程。在这个过程中,可以使用JDK的Proxy和CGLIB两种方式。
以ProxyFactoryBean的设计为中心,可以看到相关类的继承关系:
2、配置ProxyFactoryBean
我们开始进入到Spring AOP的实现部分,在分析Spring AOP的实现原理中,主要以ProxyFactoryBean的实现作为例子和实现的基本线索进行分析。这是因为ProxyFactoryBean是在Spring IOC环境中创建AOP应用的底层方法,也是最灵活的方法,Spring通过他完成了对AOP使用分封装。以ProxyFactoryBean的实现为入口,逐层深入,是一条帮助我们快速理解Spring AOP实现的学习路径。
在了解ProxyFactoryBean的实现之前,先简要介绍下ProxyFactoryBean的配置和使用,在基于XML配置Spring的Bean时,往往需要一系列的配置补助来使用ProxyFactoryBean和AOP。
1)定义使用的通知器Advisor,这个通知器应该作为一个Bean来定义。这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
2)定义ProxyFactoryBean,把他作为另一个Bean来定义,他是封装AOP功能的主要类。
3)定义target属性,作为target属性注入的Bean,是需要用AOP通知器中的切面应用来增强的对象,也就是前面提到的base对象。
有了这些配置,就可以使用ProxyFactoryBean完成AOP的基本功能了,例如:
<bean id="testAdvisor" class="com.jader.TestAdvisor" />
<bean id="testAOP" class="org.springframework.aop.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.jader.AbcInterface</value>
</property>
<property name="interceptorNames">
<list>
<value>testAdvisor</value>
</list>
</property>
</bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
掌握这些配置信息后,就可以具体看一看这些AOP是如何实现的,也就是说,切面应用是怎样通过ProxyFactoryBean对target对象起作用的,下面详细分析。
3、ProxyFactoryBean生成AOPProxy代理对象
在Spring AOP的使用中,我们已经知道,可以通过ProxyFactoryBean来配置目标对象和切面行为。这个ProxyFactoryBean是一个FactoryBean。在ProxyFactoryBean中,通过interceptorNames属性来配置已经定义好的通知器Advisor。虽然名字为interceptorNames但实际上却是供AOP应用配置通知器的地方。在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备工作。
ProxyFactoryBean的AOP实现需要依赖JDK或者CGLIB提供的Proxy特性。从FactoryBean中获取对象,是以getObject方法作为入口完成的;ProxyFactoryBean实现中的getObject方法,是FactoryBean需要实现的接口。对ProxyFactoryBean来说,把需要对target目标对象增加的增强处理都通过getObject方法进行封装了。这些增强处理是为AOP功能的实现提供服务的。getObject方法首先对通知器链进行初始化,通知器链封装了一系列的拦截器,这些拦截器从配置中读取,然后为代理对象的生成做好准备。在生成代理对象时,因为Spring中有SingleTon类型和prototype类似这两种不同的Bean,所以要对代理对象的生成做一个区分。
getObject的代码如下:
/**
* Return a proxy. Invoked when clients obtain beans from this factory bean.
* Create an instance of the AOP proxy to be returned by this factory.
* The instance will be cached for a singleton, and create on each call to
* {@code getObject()} for a proxy.
* @return a fresh AOP proxy reflecting the current state of this factory
*/
public Object getObject() throws BeansException {
// 这里初始化通知器链
initializeAdvisorChain();
// 这里对SingleTon和prototype的类型进行区分,生成对应的proxy
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
为Proxy代理对象配置Advisor链是在initializeAdvisorChain方法中实现的。这个初始化过程中有一个标志位AdvisorChainInitialized,这个标志用来表示通知器是否已经初始化。如果已经初始化,那么这里就会在初始化,而是直接返回。也就说,这个初始化的工作发生在应用第一次通过ProxyFactoryBean去获取代理对象的时候。在完成这个初始化之后,接着读取配置中出现的所有通知器,这个取得通知器的过程也比较简单,把通知器的名字交给容器的getBean方法就可以了,这是通过对IOC容器实现的一个回调完成的。然后把从IOC容器中取得的通知器加入到拦截器链中,这个动作是由addAdvisorOnChainCreation方法来实现的。
下面看看对Advisor配置链的初始化:
/**
* Create the advisor (interceptor) chain. Aadvisors that are sourced
* from a BeanFactory will be refreshed each time a new prototype instance
* is added. Interceptors added programmatically through the factory API
* are unaffected by such changes.
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
}
if (!ObjectUtils.isempty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}
// Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}
// Materialize interceptor chain from bean names.
// 这里是添加Advisor链的调用,是通过interceptorNames属性进行配置
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
// 如果程序在这里被调用,那么需要加入命名的拦截器advice,并且需要检查这个Bean是SingleTon还是prototype类型
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypeplaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
未完待续……
相关阅读
Spring整合HIbernate和springmvc 1.导入jar包 <properties> <org.springframework.version>4.2.0.RELEASE</org.springfram
之前断断续续的学过springcloud,但没能很好的掌握知识。这次决定改变方式,利用输出倒逼输入,同时也将学到的知识经验分享出来。 本文
作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域。在设计和构建 Java Web
目录 1、给普通字段注入值 2、通过构造方法给参数赋值以及调用指定的构造方法 3、给对象类型的属性注入值 4、给list集合注
史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册
转载请标明出处:http://blog.csdn.net/forezp/article/details/81040925本文出自方志朋的博客 一、spring cloud简介 鉴于《史上最