📄 java开源笔记:spring源代码解析.htm
字号:
AOP的例子,在Spring声明式事务处理的源代码中我们可以看到:<BR>1.怎样封装各种不同平台下的事务处理代码<BR>2.怎样读取属性值和结合事务处理代码来完成既定的事务处理策略<BR>3.怎样灵活的使用SpringAOP框架。<BR>如果能够结合前面的Spring
AOP的源代码来学习,理解可能会更深刻些。<BR>
<P></P>
<DIV style="CLEAR: both"></DIV></DIV>
<DIV class=post-footer>
<P class="post-footer-line post-footer-line-1"><SPAN
class="post-author vcard">发表者 <SPAN class=fn>jiwenke</SPAN> </SPAN><SPAN
class=post-timestamp>位置在: <A class=timestamp-link title="permanent link"
href="http://jiwenke-spring.blogspot.com/" rel=bookmark><ABBR class=published
title=2007-06-09T21:44:00-07:00>下午9:44</ABBR></A> </SPAN><SPAN
class=post-comment-link><A class=comment-link onclick=""
href="http://www.blogger.com/comment.g?blogID=4147982534777444487&postID=5535501562609619884">0
评论</A> </SPAN><SPAN class="post-backlinks post-comment-link"><A
class=comment-link href="http://jiwenke-spring.blogspot.com/#links">指向此文章的链接</A>
</SPAN><SPAN class=post-icons><SPAN
class="item-control blog-admin pid-357757119"><A title=修改文章
href="http://www.blogger.com/post-edit.g?blogID=4147982534777444487&postID=5535501562609619884"><SPAN
class=quick-edit-icon> </SPAN> </A></SPAN></SPAN></P>
<P class="post-footer-line post-footer-line-2"><SPAN
class=post-labels></SPAN></P>
<P class="post-footer-line post-footer-line-3"></P></DIV></DIV>
<H2 class=date-header>2007年6月8日 星期五</H2>
<DIV class="post hentry uncustomized-post-template"><A
name=5820869262907769498></A>
<DIV class=post-header-line-1></DIV>
<DIV class="post-body entry-content">
<P>下面我们来看看Spring的AOP的一些相关代码是怎么得到Proxy的,让我们我们先看看AOP和Spring AOP的一些基本概念:<BR><SPAN
style="FONT-WEIGHT: bold; FONT-STYLE: italic; TEXT-DECORATION: underline">Advice:</SPAN><BR>通知,制定在连接点<SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">做什么</SPAN>,在Sping中,他主要描述Spring围绕方法调用注入的额外的行为,Spring提供的通知类型有:<BR>before
advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice,这些都是Spring
AOP定义的接口类,具体的动作实现需要用户程序来完成。<BR><SPAN
style="FONT-WEIGHT: bold; FONT-STYLE: italic; TEXT-DECORATION: underline">Pointcut:</SPAN><BR>切点,其决定一个advice应该应用于哪个连接点,也就是需要插入额外处理的地方的集合,例如,被某个advice作为目标的一组方法。Spring
pointcut通常意味着标示方法,可以选择一组方法调用作为pointcut,Spring提供了具体的切点来给用户使用,比如正则表达式切点
JdkRegexpMethodPointcut通过正则表达式对方法名进行匹配,其通过使用
AbstractJdkRegexpMethodPointcut中的对MethodMatcher接口的实现来完成pointcut功能:<BR>public
final boolean matches(Method method, Class targetClass) {<BR>// TODO use target
class here?<BR>String patt = method.getDeclaringClass().getName() + "." +
method.getName();<BR><SPAN style="BACKGROUND-COLOR: rgb(255,255,102)">for (int i
= 0; i < this.patterns.length; i++) {<BR>//
这里是判断是否和方法名匹配的代码,当然知道true或者false<BR
style="BACKGROUND-COLOR: rgb(255,255,102)"></SPAN><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">boolean matched = matches(patt,
i);</SPAN><BR style="BACKGROUND-COLOR: rgb(255,255,102)">if (matched) {<BR>for
(int j = 0; j < this.excludedPatterns.length; j++) {<BR>boolean excluded =
matchesExclusion(patt, j);<BR>if(excluded) {<BR>return
false;<BR>}<BR>}<BR>return true;<BR>}<BR>}<BR>return
false;<BR>}<BR>在JDKRegexpMethodPointcut中通过JDK中的正则表达式匹配来完成pointcut的最终锁定<BR>protected
boolean matches(String pattern, int patternIndex) {<BR>Matcher matcher =
this.compiledPatterns[patternIndex].matcher(pattern);<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">return
matcher.matches();</SPAN><BR>}<BR><SPAN
style="FONT-WEIGHT: bold; FONT-STYLE: italic; TEXT-DECORATION: underline">Advisor:</SPAN><BR>当
我们完成额外完成的动作设计(advice)和动作插入点的设计(pointcut)以后,我们需要一个对象把他们结合起来,这就是通知器 -
advisor,定义应该在哪里应用哪个通知。Advisor的实现有:DefaultPointcutAdvisor他有两个属性advice和
pointcut来让我们配置advice和pointcut。<BR>接着我们就可以通过ProxyFactoryBean来配置我们的代理对象和方面
行为,在ProxyFactoryBean中有interceptorNames来配置已经定义好的通知器-advisor,具体的代理实现通过JDK
的Proxy或者CGLIB的技术来完成。我们可以看看具体的代码实现,在ProxyFactoryBean中我们看看怎样得到Proxy:<BR>public
Object getObject() throws BeansException {<BR>initializeAdvisorChain();<BR>if
(isSingleton()) {<BR>//根据定义需要生成单件的Proxy<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">return
getSingletonInstance();</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">}<BR>else {<BR>if (this.targetName ==
null) {<BR>logger.warn("Using non-singleton proxies with singleton targets is
often undesirable." +<BR>"Enable prototype proxies by setting the 'targetName'
property.");<BR>}<BR>//根据定义需要生成Prototype的Proxy<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">return
newPrototypeInstance();</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">}<BR>}<BR>我们看看怎样生成单件的Proxy:<BR>private
synchronized Object getSingletonInstance() {<BR>if (this.singletonInstance ==
null) {<BR>this.targetSource = freshTargetSource();<BR>if
(this.autodetectInterfaces && getProxiedInterfaces().length == 0
&& !isProxyTargetClass()) {<BR>//
这里设置Proxy的接口<BR>setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));<BR>}<BR>//
Eagerly initialize the shared singleton
instance.<BR>super.setFrozen(this.freezeProxy);<BR>//
注意这里的方法会使用ProxyFactory来生成我们需要的Proxy<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">this.singletonInstance =
getProxy(createAopProxy());</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">// We must listen to superclass
advice change events to recache the singleton<BR>// instance if
necessary.<BR>addListener(this);<BR>}<BR>return
this.singletonInstance;<BR>}<BR><BR>ProxyFactoryBean的父类是AdvisedSupport,Spring使用AopProxyFactory接口把AOP代理的实现与框架的其他部分分离开来;在AdvisedSupport中通过这样的方式来得到AopProxy,这里还需要AopProxyFactory的帮助
- 下面我们看到Spring为我们提供了默认的实现可以帮助我们方便的从JDK或者cglib中得到我们想要的:<BR>protected synchronized
AopProxy createAopProxy() {<BR>if (!this.isActive)
{<BR>activate();<BR>}<BR><SPAN style="BACKGROUND-COLOR: rgb(255,255,102)">return
getAopProxyFactory().createAopProxy(this);</SPAN><BR>}<BR>而在ProxyConfig中对使用的AopProxyFactory做了定义:<BR>//这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,<BR>//当然了它包含JDK和Cglib两种实现方式。<BR>private
transient AopProxyFactory aopProxyFactory = new
DefaultAopProxyFactory();<BR>其中在DefaultAopProxyFactory中是这样生成AopProxy的:<BR>public
AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException
{<BR>if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()
||<BR>advisedSupport.getProxiedInterfaces().length == 0) {<BR>if
(!cglibAvailable) {<BR>throw new AopConfigException(<BR>"Cannot proxy target
class because CGLIB2 is not available. " +<BR>"Add CGLIB to the class path or
specify proxy interfaces.");<BR>}<BR>//
这里使用Cglib来生成Proxy,如果target不是接口的实现的话<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">return
CglibProxyFactory.createCglibProxy(advisedSupport);</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">}<BR>else {<BR>//
这里使用JDK来生成Proxy<BR><SPAN style="BACKGROUND-COLOR: rgb(255,255,102)">return new
JdkDynamicAopProxy(advisedSupport);</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">}<BR>}<BR>于是我们就可以看到其中的Proxy可以有JDK或者Cglib来生成,我们看到JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,在JDK实现中我们可以看到Proxy是怎样生成的:<BR>public
Object getProxy(ClassLoader classLoader) {<BR>if (logger.isDebugEnabled())
{<BR>Class targetClass =
this.advised.getTargetSource().getTargetClass();<BR>logger.debug("Creating JDK
dynamic proxy" +<BR>(targetClass != null ? " for [" + targetClass.getName() +
"]" : ""));<BR>}<BR>Class[] proxiedInterfaces =
AopProxyUtils.completeProxiedInterfaces(this.advised);<BR>findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);<BR>//这里我们调用JDK
Proxy来生成需要的Proxy实例<BR><SPAN style="BACKGROUND-COLOR: rgb(255,255,102)">return
Proxy.newProxyInstance(classLoader, proxiedInterfaces,
this);</SPAN><BR>}<BR>这样用Proxy包装target之后,对其的调用就被Proxy拦截了,ProxyFactoryBean的getObject()方法得到的实际上已经是Proxy了。<SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)"></SPAN> </P>
<DIV style="CLEAR: both"></DIV></DIV>
<DIV class=post-footer>
<P class="post-footer-line post-footer-line-1"><SPAN
class="post-author vcard">发表者 <SPAN class=fn>jiwenke</SPAN> </SPAN><SPAN
class=post-timestamp>位置在: <A class=timestamp-link title="permanent link"
href="http://jiwenke-spring.blogspot.com/2007/06/springaopproxyaopspring-aop-advice.html"
rel=bookmark><ABBR class=published
title=2007-06-08T02:51:00-07:00>上午2:51</ABBR></A> </SPAN><SPAN
class=post-comment-link><A class=comment-link onclick=""
href="http://www.blogger.com/comment.g?blogID=4147982534777444487&postID=5820869262907769498">0
评论</A> </SPAN><SPAN class="post-backlinks post-comment-link"><A
class=comment-link
href="http://jiwenke-spring.blogspot.com/2007/06/springaopproxyaopspring-aop-advice.html#links">指向此文章的链接</A>
</SPAN><SPAN class=post-icons><SPAN
class="item-control blog-admin pid-357757119"><A title=修改文章
href="http://www.blogger.com/post-edit.g?blogID=4147982534777444487&postID=5820869262907769498"><SPAN
class=quick-edit-icon> </SPAN> </A></SPAN></SPAN></P>
<P class="post-footer-line post-footer-line-2"><SPAN
class=post-labels></SPAN></P>
<P class="post-footer-line post-footer-line-3"></P></DIV></DIV>
<H2 class=date-header>2007年6月7日 星期四</H2>
<DIV class="post hentry uncustomized-post-template"><A
name=7659085064913675164></A>
<DIV class=post-header-line-1></DIV>
<DIV class="post-body entry-content">
<P>下面我们对Spring MVC框架代码进行分析,对于webApplicationContext的相关分析可以参见以前的文档,我们这里着重分析Spring
Web
MVC框架的实现.我们从分析DispatcherServlet入手:<BR>//这里是对DispatcherServlet的初始化方法,根据名字我们很方面的看到对各个Spring
MVC主要元素的初始化<BR>protected void initFrameworkServlet() throws ServletException,
BeansException
{<BR>initMultipartResolver();<BR>initLocaleResolver();<BR>initThemeResolver();<BR>initHandlerMappings();<BR>initHandlerAdapters();<BR>initHandlerExceptionResolvers();<BR>initRequestToViewNameTranslator();<BR>initViewResolvers();<BR>}<BR>看
到注解我们知道,这是DispatcherSerlvet的初始化过程,它是在WebApplicationContext已经存在的情况下进行的,也就
意味着在初始化它的时候,IOC容器应该已经工作了,这也是我们在web.xml中配置Spring的时候,需要把DispatcherServlet的
load-on-startup的属性配置为2的原因。<BR>对于具体的初始化过程,很容易理解,我们拿initHandlerMappings()来看看:<BR>private
void initHandlerMappings() throws BeansException {<BR>if
(this.detectAllHandlerMappings) {<BR>//
这里找到所有在上下文中定义的HandlerMapping,同时把他们排序<BR>//
因为在同一个上下文中可以有不止一个handlerMapping,所以我们把他们都载入到一个链里进行维护和管理<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">Map matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)"><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">getWebApplicationContext(),
HandlerMapping.class, true, false);</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">if (!matchingBeans.isEmpty())
{<BR><SPAN style="BACKGROUND-COLOR: rgb(255,255,102)">this.handlerMappings = new
ArrayList(matchingBeans.values());</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)"><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">//
这里通过order属性来对handlerMapping来在list中排序</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)"><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">Collections.sort(this.handlerMappings,
new OrderComparator());</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)">}<BR>}<BR>else { <BR>try {<BR>Object
hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME,
HandlerMapping.class);<BR>this.handlerMappings =
Collections.singletonList(hm);<BR>}<BR>catch (NoSuchBeanDefinitionException ex)
{<BR>// Ignore, we'll add a default HandlerMapping
later.<BR>}<BR>}<BR><BR>//如果在上下文中没有定义的话,那么我们使用默认的BeanNameUrlHandlerMapping<BR>if
(this.handlerMappings == null) {<BR><SPAN
style="BACKGROUND-COLOR: rgb(255,255,102)">this.handlerMappings =
getDefaultStrategies(HandlerMapping.class);</SPAN><BR
style="BACKGROUND-COLOR: rgb(255,255,102)"><SPAN
style="BACKGROUND-COLOR: rgb(255,255,255)"></SPAN>........<BR>}<BR>}<BR>怎样获得上下文环境,可以参见我们前面的对IOC容器在web环境中加载的分析。
DispatcherServlet把定义了的所有HandlerMapping都加载了放在一个List里待以后进行使用,这个链的每一个元素都是一个handlerMapping的配置,而一般每一个handlerMapping可以持有一系列从URL请求到
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -