java spring aop实现beans怎么用java代码实现

spring-beans的源码分析之依赖jar包分析 - 简书
spring-beans的源码分析之依赖jar包分析
spring-beans module依赖了一些jar包
Paste_Image.png
第一个jar包
Paste_Image.png
javax开头的都是javaee的包
java分Java SE、Java EE、Java ME Java SE(Java Platform,Standard Edition)。Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为 Java Platform,Enterprise Edition(Java EE)提供基础。Java EE(Java Platform,Enterprise Edition)。这个版本以前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端 Java 应用程序。Java EE 是在 Java SE 的基础上构建的,它提供 Web 服务、组件模型、管理和通信 API,可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和 Web 2.0 应用程序。 Java ME(Java Platform,Micro Edition)。这个版本以前称为 J2ME。Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次,就可以用于许多设备,而且可以利用每个设备的本机功能。
这个jar包主要提供了几个注解和一个接口 比较简单oracle的api地址:目前只能看到7版本的
是从版本5开始加入的其实spring自带的注解@Autowired等价于@Inject、 @Qualifier等价 @Named、Spring自带的@Qualifier的扩展@Qualifier限定描述符注解情况等价于JSR-330的@Qualifier注解以前的spring注解@Autowired注解需要写到set方法上 不过现在改进已不需要而 @Inject 只需要写到对应的属性定义上即可所以会出现
javax.inject.jar而spring的框架是支持这个的所以引入了
javax.inject.jar
为什么有了@Inject还需要@Qualifier,大家想啊如果一个接口有很多实现类 你注入接口的时候是不是得指定是哪个实现类啊,还有@Autowired为什么有时候不需要指定呢,因为只有一个实现类 且spring默认按name注入的 所以就没问题
java.el-api-2.25.jar虽然在beans module中没有使用到(我没找到)但是其实是Java EE对el表达式的一种支持。oracle文档地址:文档第一句Provides the API for the Unified Expression Language 3.0 el表达式3.0的支持。el表达式主要用在jsp页面中,获取对象属性之类的操作,其实就是一种语法 而这个jar包就是为该语法提供支持的。snakeyaml-1.15.jar
是"YAML Ain't a Markup Language"(YAML不是一种置标语言)的递归缩写,早先YAML的意思其实是:"Yet Another Markup Language"(另外一种置标语言),但为了强调这种语言以数据做为中心,而不是以置标语言为重点,而用返璞词重新命名,YAML的官方定义很简单,即一种人性化的数据格式定义语言,其主要功能用途类似于XML或JSON,YAML使用空白字符和分行来分隔数据,且巧妙避开各种封闭符号,如:引号、括号等,以避免这些符号在复杂层次结构中变得难以辨认。YAML的语法与高阶语言类似,可以很简单地表述序列(java中的list)、杂凑表(java中的map)、标量(java中的基本类型等)数据结构,它重点强调可阅读性,其实JSON语法是YAML的子集,大部分的JSON文件都可以被YAML的剖析起剖析。虽然大部分的数据分层形式也可以使用类似JSON的格式,不过YAML并不建议这样使用,除非这样编写能让文件可读性增加,更重要的是,YAML的许多扩展在JSON是找不到的,如:进阶资料形态、关系锚点、字串不需要引号、映射资料形态会储存键值的顺序等。java对此语法文件的解析实现即为此jar包,.yaml文件解析。SnakeYAML 网站 : 支持Unicode,包括UTF-8/UTF-16的输入/输出;为序列化和反序列化本地的Java对象提供了高级API;支持中的所有类型;比较理性的错误信息。
SnakeYA要求Java 5或者更高版本。目前的稳定版本是: 1.5。示例代码:Yaml yaml = new Yaml();String document = "\n- Hesperiidae\n- Papilionidae\n- Apatelodidae\n- Epiplemidae";List&String& list = (List&String&) yaml.load(document);System.out.println(list);输出结果:['Hesperiidae', 'Papilionidae', 'Apatelodidae', 'Epiplemidae']
junit-4.12.jarjunit 比较熟悉是做单元测试用的,断言、测试异常,捕获异常,测试方法的性能的测试。mockito-core.jarmock也是做测试目的用的,主要解决比较难以构建的对象,是简单轻量级能够替代EasyMock的框架。使用简单,测试代码可读性高。源码见github:hamcrest-all-1.1.jarhamcrest是辅助测试工具 ,junit中的断言 就是使用的它,但是二者是不同的框架地址:log4j.jar这个是记录日志用的tomcat-embed-core.jar这个jar包很神奇的可以不将项目copy到tomcat目录下而实现基于tomcat环境的开发
//设置工作目录
String catalina_home = "C:/";
Tomcat tomcat = new Tomcat();
tomcat.setHostname("localhost");
tomcat.setPort(startPort);
//设置工作目录,其实没什么用,tomcat需要使用这个目录进行写一些东西
tomcat.setBaseDir(catalina_home);
//设置程序的目录信息
tomcat.getHost().setAppBase("e:/");
// Add AprLifecycleListener
StandardServer server = (StandardServer) tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
server.addLifecycleListener(listener);
//注册关闭端口以进行关闭
tomcat.getServer().setPort(shutdownPort);
//加载上下文
StandardContext standardContext = new StandardContext();
standardContext.setPath("/aa");//contextPath
standardContext.setDocBase("aa");//文件目录位置
standardContext.addLifecycleListener(new
Tomcat.DefaultWebXmlListener()) ;
//保证已经配置好了。
standardContext.addLifecycleListener(new Tomcat.FixContextListener());
standardContext.setSessionCookieName("t-session");
tomcat.getHost().addChild(standardContext);
tomcat.start();
tomcat.getServer().await();private static void shutdown() throws Exception {
Socket socket = new Socket("localhost", shutdownPort);
OutputStream stream = socket.getOutputStream();
for(int i = 0;i & shutdown.length();i++)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
}objenesis.jar和spring-cglib-repack.jar
spring-cglib-repack是spring cglib的具体实现 objenesis是序列化用的objenesis 介绍地址:
spring-cglib-repack 在spring4中内联了objenesis类库基于CGLIB的类代理不再要求类必须有空参构造器了:这是一个很好的特性,使用构造器注入有很多好处,比如可以只在创建Bean时注入依赖,然后就不变了,如果使用setter注入,是允许别人改的。当然我们可以使用spring的字段级别注入。如果大家使用过如Shiro,我们可能要对Controller加代理。如果是类级别代理,此时要求Controller必须有空参构造器,有时候挺烦人的。spring如何实现的呢?其内联了objenesis类库,通过它来实现,可以去其官网看看介绍
jar包终于大概说了一遍 好饿去吃饭。
IT爱好者及从业者编写一个工具类
&实现spring 的ApplicationContextAware接口
代码如下:
1 package com.xnh.web.
3 import org.springframework.beans.BeansE
4 import org.springframework.context.ApplicationC
5 import org.springframework.context.ApplicationContextA
* @author Kylin
11 public class ApplicationUtil implements ApplicationContextAware{
private static ApplicationContext applicationC
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationUtil.applicationContext = applicationC
public static Object getBean(String name){
return applicationContext.getBean(name);
然后需要在application中把把org.springframework.context.ApplicationContext作为属性注入给类
其实只需要这样写就可以如下:
1 &bean id="app" class="com.xnh.web.utils.ApplicationUtil"&
这样子就是被注入进去了
运行的时候只需要加载你的applicationContext.xml
*普通java类main方法这样调用
new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationUtil.getBean("rssService");
*web工程直接调用
ApplicationUtil.getBean("rssService");
就大功告成了,如果按我的方法不成功,请看看是不是没有加载applicationContext.xml,或者你想getbean的方法没有在xml里面配置
据我测试没有在xml配置而仅仅使用注解貌似是不成功的
有问题留言哦
阅读(...) 评论()Spring在代码中获取bean的方法小结
投稿:daisy
字体:[ ] 类型:转载 时间:
在工作中有时候我们需要在非spring依赖注入或管理的类中获取service、dao等bean对象,这时候用@Autowired和@Resource显然是不行的,那么下面这篇文章就给大家了整理几种获取bean的方式,对大家的学习和工作具有一定的参考借鉴,下面来一起看看吧。
一、通过Spring提供的ContextLoader
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean(beanID);
这种方式不依赖于servlet,不需要注入的方式。但是需要注意一点,在服务器启动时,Spring容器初始化时,不能通过这种方法获取Spring容器
二、实现接口ApplicationContextAware
定义工具类
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationC
//Spring应用上下文环境
* 实现ApplicationContextAware接口的回调方法,设置上下文环境
* @param applicationContext
* @throws BeansException
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationC
* @return ApplicationContext
public static ApplicationContext getApplicationContext() {
return applicationC
* 获取对象
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
* 获取类型为requiredType的对象
* 如果bean不能被类型转换,相应的异常将会被抛出(BeanNotOfRequiredTypeException)
* @param name
bean注册名
* @param requiredType 返回对象类型
* @return Object 返回requiredType类型对象
* @throws BeansException
public static Object getBean(String name, Class requiredType) throws BeansException {
return applicationContext.getBean(name, requiredType);
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
* @param name
* @return boolean
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
* 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
* @param name
* @return boolean
* @throws NoSuchBeanDefinitionException
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return applicationContext.isSingleton(name);
* @param name
* @return Class 注册对象的类型
* @throws NoSuchBeanDefinitionException
public static Class getType(String name) throws NoSuchBeanDefinitionException {
return applicationContext.getType(name);
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
* @param name
* @throws NoSuchBeanDefinitionException
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return applicationContext.getAliases(name);
三、配置bean
&!-- SpringContextUtil 通过代码获取bean --&
&bean id="SpringContextUtil " class="org.shaofan.demo.utils.SpringContextUtil"/&
以上就是Spring在代码中获取bean的几种方式,希望本文的内容对大家的学习或者工作能有所帮助,如果有疑问大家可以留言交流。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具Java中Spring获取bean方法小结
字体:[ ] 类型:转载 时间:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?
Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IoC的配置机制。BeanFactory使管理不同类型的Java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文。但有时为了行文方便,我们也将ApplicationContext称为Spring容器。
对于两者的用途,我们可以进行简单划分:BeanFactory是Spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合我们都直接使用ApplicationContext而非底层的BeanFactory。
ApplicationContext的初始化和BeanFactory有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因此ApplicationContext的初始化时间会比BeanFactory稍长一些
本文不涉及通过 @Resource 、 @Autowired 自动注入,仅仅通过 ApplicationContext 获取 Sping 配置文件中的 Bean 。
要获取XML中配置的Bean,最关键的是获取org.springframework.context.ApplicationContext
第一种获取 ApplicationContext 的方法:
import org.springframework.context.ApplicationC
import org.springframework.context.support.FileSystemXmlApplicationC
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("applicationContext.xml");
import org.springframework.context.ApplicationC
import org.springframework.context.support.ClassPathXmlApplicationC
private ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
这种方式实例化applicationContext是非常耗时的,这种方式适用于采用Spring框架的独立应用程序,仅仅推荐使用在程序需要通过配置文件手工初始化Spring的情况。ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件
public class BeanManager {
private static ApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml") ;
public static Object getBean(String beanId) {
return context.getBean(beanId);
在 web.xml 中写一个 servlet ,自动启动, init 方法中调用一下 BeanManager
init() throws ServletException {
BeanManager bm = new BeanManager();//可选的,为的是在 web 应用启动的时候就让 spring 加载 bean 配置。
// 否则会在第一次调用 BeanManager 的时候加载,影响一次速度。
在 java 代码中使用 BeanManager.getBean(String beanId); 来获得 bean 实例。
第二种获取 ApplicationContext 的方法: 通过Spring提供的工具类获取ApplicationContext对象,专为web工程定制的方法,推荐Web项目中使用。例如:
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext ac1 = WebApplicationContextUtils .getRequiredWebApplicationContext(ServletContext sc)
ApplicationContext ac2 = WebApplicationContextUtils .getWebApplicationContext(ServletContext sc)
ac1.getBean("beanId");
ac2.getBean("beanId");
通过javax.servlet.ServletContext 获取到ApplicationContext实例对象,这意味着,必须使用到request、session等等。
这样,就不能把ApplicationContext对象设置为成员变量。需要在每个具体的方法中通过request、session等获取到ServletContext再获取ApplicationContext实例。
因此,此方法仅仅推荐使用在可以获取到ServletContext对象的Web项目中,并且不需要将ApplicationContext对象定义为成员变量的情况下。
注意:当使用WebApplicationContextUtils获取ApplicationContext实例时,需要在web.xml配置文件中添加org.springframework.web.context.ContextLoaderListener监听器,否则获取不到ApplicationContext对象,返回Null。
配置文件:web.xml
&!--ContextLoaderListener自动注入 applicationContext,通过
WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext())获取 --&
&!--Spring配置文件加载位置 --&
&context-param&
&param-name&contextConfigLocation&/param-name&
&param-value&/WEB-INF/spring/appContext.xml,/WEB-INF/spring/appInterceptor.xml&/param-value&
&/context-param&
&listener&
&listener-class&org.springframework.web.context.ContextLoaderListener&/listener-class&
&/listener&
3. 继承自抽象类ApplicationObjectSupport
抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。
4. 继承自抽象类WebApplicationObjectSupport
通过继承org.springframework.web.context.support.WebApplicationObjectSupport使用getWebApplicationContext() 获取到org.springframework.web.context.WebApplicationContext由于Web应用比一般的应用拥有更多的特性,因此WebApplicationContext扩展了ApplicationContext。WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_ CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中,因此我们可以直接通过以下语句从Web容器中获取WebApplicationContext:
WebApplicationContext wac = (WebApplicationContext)servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
5. 实现接口ApplicationContextAware
实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。Spring初始化时,会通过该方法将ApplicationContext 对象注入。
第三、四、五种方法都需要将类配置在 Spring 配置文件中:
&!--假定ApplicationContextTool为继承或者实现了第三、四、五种方法的具体实现类--&
&bean class="com.twovv.utils.ApplicationContextTool"&&/bean&
否则将获取不到 ApplicationContext ,返回 Null 。
以上内容给大家介绍了Java中Spring获取bean方法小结,希望大家喜欢。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具Java开源笔记:Spring源代码解析
夜半待客客不至,闲敲棋子落灯花;一个好的开源软件代码就像一卷优雅的棋谱,让我们好好享受一下吧!
日 星期六
我们看看Spring中的事务处理的代码,使用Spring管理事务有声明式和编程式两种方式,声明式事务处理通过AOP的实现把事物管理代码作为方面封装来横向插入到业务代码中,使得事务管理 代码和业务代码解藕。在这种方式我们结合IoC容器和Spirng已有的FactoryBean来对事务管理进行属性配置,比如传播行为,隔离级别等。其 中最简单的方式就是通过配置TransactionProxyFactoryBean来实现声明式事物;
在整个源代码分析中,我们可以大致可以看到Spring实现声明式事物管理有这么几个部分:
对在上下文中配置的属性的处理,这里涉及的类是TransactionAttributeSourceAdvisor,这是一个通知器,用它来对属性值进行处理,属性信息放在TransactionAttribute中来使用,而这些属性的处理往往是和对切入点的处理是结合起来的。对属性的处理放在类TransactionAttributeSource中完成。
创建事物的过程,这个过程是委托给具体的事物管理器来创建的,但Spring通过TransactionStatus来传递相关的信息。
对事物的处理通过对相关信息的判断来委托给具体的事物管理器完成。
我们下面看看具体的实现,在TransactionFactoryBean中:
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements FactoryBean, BeanFactoryAware {
//这里是Spring事务处理而使用的AOP拦截器,中间封装了Spring对事务处理的代码来支持声明式事务处理的实现
private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
//这里Spring把TransactionManager注入到TransactionInterceptor中去
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionInterceptor.setTransactionManager(transactionManager);
}
//这里把在bean配置文件中读到的事务管理的属性信息注入到TransactionInterceptor中去
public void setTransactionAttributes(Properties transactionAttributes) {
this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
}
.........中间省略了其他一些方法.......
//这里创建Spring AOP对事务处理的Advisor
protected Object createMainInterceptor() {
this.transactionInterceptor.afterPropertiesSet();
if (this.pointcut != null) {
//这里使用默认的通知器
return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
}
else {
// 使用上面定义好的TransactionInterceptor作为拦截器,同时使用TransactionAttributeSourceAdvisor
return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
}
}
}
那什么时候Spring的TransactionInterceptor被注入到Spring AOP中成为Advisor中的一部分呢?我们看到在TransactionProxyFactoryBean中,这个方法在IOC初始化bean的时候被执行:
public void afterPropertiesSet() {
.......
//TransactionProxyFactoryBean实际上使用ProxyFactory完成AOP的基本功能。
ProxyFactory proxyFactory = new ProxyFactory();
if (this.preInterceptors != null) {
for (int i = 0; i & this.preInterceptors. i++) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));
}
}
//这里是Spring加入通知器的地方
//有两种通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor
//这里把Spring处理声明式事务处理的AOP代码都放到ProxyFactory中去,怎样加入advisor我们可以参考ProxyFactory的父类AdvisedSupport()
//由它来维护一个advice的链表,通过这个链表的增删改来抽象我们对整个通知器配置的增删改操作。
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
if (this.postInterceptors != null) {
for (int i = 0; i & this.postInterceptors. i++) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));
}
}
proxyFactory.copyFrom(this);
//这里创建AOP的目标源
TargetSource targetSource = createTargetSource(this.target);
proxyFactory.setTargetSource(targetSource);
if (this.proxyInterfaces != null) {
proxyFactory.setInterfaces(this.proxyInterfaces);
}
else if (!isProxyTargetClass()) {
proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));
}
this.proxy = getProxy(proxyFactory);
}
Spring已经定义了一个transctionInterceptor作为拦截器或者AOP advice的实现,在IOC容器中定义的其他属性比如transactionManager和事务管理的属性都会传到已经定义好的TransactionInterceptor那里去进行处理。以上反映了基本的Spring AOP的定义过程,其中pointcut和advice都已经定义好,同时也通过通知器配置到ProxyFactory中去了。
下面让我们回到TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎样定义的,这样我们可以理解具体的属性是怎样起作用,这里我们分析一下类TransactionAttributeSourceAdvisor:
public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
//和其他Advisor一样,同样需要定义AOP中的用到的Interceptor和Pointcut
//Interceptor使用传进来的TransactionInterceptor
//而对于pointcut,这里定义了一个内部类,参见下面的代码
private TransactionInterceptor transactionI
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();
.........
//定义的PointCut内部类
private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
.......
//方法匹配的实现,使用了TransactionAttributeSource类
public boolean matches(Method method, Class targetClass) {
TransactionAttributeSource tas = getTransactionAttributeSource();
//这里使用TransactionAttributeSource来对配置属性进行处理
return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);
}
........省略了equal,hashcode,tostring的代码
}
这里我们看看属性值是怎样被读入的:AbstractFallbackTransactionAttributeSource负责具体的属性读入任务,我们可以有两种读入方式,比如annotation和直接配置.我们下面看看直接配置的读入方式,在Spring中同时对读入的属性值进行了缓存处理,这是一个decorator模式:
public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
//这里先查一下缓存里有没有事务管理的属性配置,如果有从缓存中取得TransactionAttribute
Object cacheKey = getCacheKey(method, targetClass);
Object cached = this.cache.get(cacheKey);
if (cached != null) {
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
}
else {
return (TransactionAttribute)
}
}
else {
// 这里通过对方法和目标对象的信息来计算事务缓存属性
TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
//把得到的事务缓存属性存到缓存中,下次可以直接从缓存中取得。
if (txAtt == null) {
this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
...........
this.cache.put(cacheKey, txAtt);
}
return txA
}
}
别急,基本的处理在computeTransactionAttribute()中:
private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {
//这里检测是不是public方法
if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));
if (txAtt != null) {
return txA
}
// Second try is the transaction attribute on the target class.
txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));
if (txAtt != null) {
return txA
}
if (specificMethod != method) {
// Fallback is to look at the original method.
txAtt = findTransactionAttribute(findAllAttributes(method));
if (txAtt != null) {
return txA
}
// Last fallback is the class of the original method.
return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));
}
}
经过一系列的尝试我们可以通过findTransactionAttribute()通过调用findAllAttribute()得到TransactionAttribute的对象,如果返回的是null,这说明该方法不是我们需要事务处理的方法。
在完成把需要的通知器加到ProxyFactory中去的基础上,我们看看具体的看事务处理代码怎样起作用,在TransactionInterceptor中:
public Object invoke(final MethodInvocation invocation) throws Throwable {
//这里得到目标对象
Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);
//这里同样的通过判断是否能够得到TransactionAttribute来决定是否对当前方法进行事务处理,有可能该属性已经被缓存,
//具体可以参考上面对getTransactionAttribute的分析,同样是通过TransactionAttributeSource
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final String joinpointIdentification = methodIdentification(invocation.getMethod());
//这里判断我们使用了什么TransactionManager
if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
// 这里创建事务,同时把创建事务过程中得到的信息放到TransactionInfo中去
TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
Object retVal =
try {
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retV
}
else {
// 使用的是Spring定义的PlatformTransactionManager同时实现了回调接口,我们通过其回调函数完成事务处理,就像我们使用编程式事务处理一样。
try {
Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//同样的需要一个TransactonInfo
TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
try {
return invocation.proceed();
}
.....这里省去了异常处理和事务信息的清理代码
});
...........
}
}
这里面涉及到事务的创建,我们可以在TransactionAspectSupport实现的事务管理代码:
protected TransactionInfo createTransactionIfNecessary(
TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
public String getName() {
return joinpointI
}
};
}
TransactionStatus status =
if (txAttr != null) {
//这里使用了我们定义好的事务配置信息,有事务管理器来创建事务,同时返回TransactionInfo
status = getTransactionManager().getTransaction(txAttr);
}
return prepareTransactionInfo(txAttr, joinpointIdentification, status);
}
首先通过TransactionManager得到需要的事务,事务的创建根据我们定义的事务配置决定,在AbstractTransactionManager中给出一个标准的创建过程,当然创建什么样的事务还是需要具体的PlatformTransactionManager来决定,但这里给出了创建事务的模板:
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
......
if (definition == null) {
//如果事务信息没有被配置,我们使用Spring默认的配置方式
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction)) {
// Existing transaction found -& check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
//下面就是使用配置信息来创建我们需要的事务;比如传播属性和同步属性等
//最后把创建过程中的信息收集起来放到TransactionStatus中返回;
if (definition.getTimeout() & TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -& check propagation behavior to find out how to behave.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"Transaction propagation 'mandatory' but no existing transaction found");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//这里是事务管理器创建事务的地方,并将创建过程中得到的信息放到TransactionStatus中去,包括创建出来的事务
doBegin(transaction, definition);
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
}
else {
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return newTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null);
}
}
接着通过调用prepareTransactionInfo完成事务创建的准备,创建过程中得到的信息存储在TransactionInfo对象中进行传递同时把信息和当前线程绑定;
protected TransactionInfo prepareTransactionInfo(
TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);
if (txAttr != null) {
.....
// 同样的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中来。
txInfo.newTransactionStatus(status);
}
else {
.......
}
// 绑定事务创建信息到当前线程
txInfo.bindToThread();
return txI
}
将创建事务的信息返回,然后看到其他的事务管理代码:
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());
}
mit(txInfo.getTransactionStatus());
}
}
通过transactionManager对事务进行处理,包括异常抛出和正常的提交事务,具体的事务管理器由用户程序设定。
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (txInfo.transactionAttribute.rollbackOn(ex)) {
......
try {
this.transactionManager.rollback(txInfo.getTransactionStatus());
}
..........
}
else {
.........
try {
mit(txInfo.getTransactionStatus());
}
...........
}
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
......
mit(txInfo.getTransactionStatus());
}
}
Spring通过以上代码对transactionManager进行事务处理的过程进行了AOP包装,到这里我们看到为了方便客户实现声明式的事务处理,Spring还是做了许多工作的。如果说使用编程式事务处理,过程其实比较清楚,我们可以参考书中的例子:
TransactionDefinition td = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(td);
try{
......//这里是我们的业务方法
}catch (ApplicationException e) {
transactionManager.rollback(status);
throw e
}
mit(status);
........
我们看到这里选取了默认的事务配置DefaultTransactionDefinition,同时在创建事物的过程中得到TransactionStatus,然后通过直接调用事务管理器的相关方法就能完成事务处理。
声明式事务处理也同样实现了类似的过程,只是因为采用了声明的方法,需要增加对属性的读取处理,并且需要把整个过程整合到Spring AOP框架中和IoC容器中去的过程。
下面我们选取一个具体的transactionManager - DataSourceTransactionManager来看看其中事务处理的实现:
同样的通过使用AbstractPlatformTransactionManager使用模板方法,这些都体现了对具体平台相关的事务管理器操作的封装,比如commit:
public final void commit(TransactionStatus status) throws TransactionException {
......
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)
if (defStatus.isLocalRollbackOnly()) {
......
processRollback(defStatus);
}
.......
processRollback(defStatus);
......
}
processCommit(defStatus);
}
通过对TransactionStatus的具体状态的判断,来决定具体的事务处理:
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked =
try {
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked =
boolean globalRollbackOnly =
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
........
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
......
doCommit(status);
}
.........
}
这些模板方法的实现由具体的transactionManager来实现,比如在DataSourceTransactionManager:
protected void doCommit(DefaultTransactionStatus status) {
//这里得到存在TransactionInfo中已经创建好的事务
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
//这里得到和事务绑定的数据库连接
Connection con = txObject.getConnectionHolder().getConnection();
........
try {
//这里通过数据库连接来提交事务
mit();
}
.......
}
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
//这里通过数据库连接来回滚事务
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
我们看到在DataSourceTransactionManager中最后还是交给connection来实现事务的提交和rollback。整个声明式事务处理是事务处理在Spring AOP中的应用,我们看到了一个很好的使用Spring AOP的例子,在Spring声明式事务处理的源代码中我们可以看到:
1.怎样封装各种不同平台下的事务处理代码
2.怎样读取属性值和结合事务处理代码来完成既定的事务处理策略
3.怎样灵活的使用SpringAOP框架。
如果能够结合前面的Spring AOP的源代码来学习,理解可能会更深刻些。
发表者 jiwenke 位置在: 下午9:44 0 评论 指向此文章的链接&
日 星期五
下面我们来看看Spring的AOP的一些相关代码是怎么得到Proxy的,让我们我们先看看AOP和Spring AOP的一些基本概念:
Advice:
通知,制定在连接点做什么,在Sping中,他主要描述Spring围绕方法调用注入的额外的行为,Spring提供的通知类型有:
before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice,这些都是Spring AOP定义的接口类,具体的动作实现需要用户程序来完成。
Pointcut:
切点,其决定一个advice应该应用于哪个连接点,也就是需要插入额外处理的地方的集合,例如,被某个advice作为目标的一组方法。Spring pointcut通常意味着标示方法,可以选择一组方法调用作为pointcut,Spring提供了具体的切点来给用户使用,比如正则表达式切点 JdkRegexpMethodPointcut通过正则表达式对方法名进行匹配,其通过使用 AbstractJdkRegexpMethodPointcut中的对MethodMatcher接口的实现来完成pointcut功能:
public final boolean matches(Method method, Class targetClass) {
// TODO use target class here?
String patt = method.getDeclaringClass().getName() + "." + method.getName();
for (int i = 0; i & this.patterns. i++) {
// 这里是判断是否和方法名匹配的代码,当然知道true或者false
boolean matched = matches(patt, i);
if (matched) {
for (int j = 0; j & this.excludedPatterns. j++) {
boolean excluded = matchesExclusion(patt, j);
if(excluded) {
}
在JDKRegexpMethodPointcut中通过JDK中的正则表达式匹配来完成pointcut的最终锁定
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = piledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
Advisor:
当 我们完成额外完成的动作设计(advice)和动作插入点的设计(pointcut)以后,我们需要一个对象把他们结合起来,这就是通知器 - advisor,定义应该在哪里应用哪个通知。Advisor的实现有:DefaultPointcutAdvisor他有两个属性advice和 pointcut来让我们配置advice和pointcut。
接着我们就可以通过ProxyFactoryBean来配置我们的代理对象和方面 行为,在ProxyFactoryBean中有interceptorNames来配置已经定义好的通知器-advisor,具体的代理实现通过JDK 的Proxy或者CGLIB的技术来完成。我们可以看看具体的代码实现,在ProxyFactoryBean中我们看看怎样得到Proxy:
public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
//根据定义需要生成单件的Proxy
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.");
}
//根据定义需要生成Prototype的Proxy
return newPrototypeInstance();
}
}
我们看看怎样生成单件的Proxy:
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// 这里设置Proxy的接口
setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
}
// Eagerly initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
// 注意这里的方法会使用ProxyFactory来生成我们需要的Proxy
this.singletonInstance = getProxy(createAopProxy());
// We must listen to superclass advice change events to recache the singleton
// instance if necessary.
addListener(this);
}
return this.singletonI
}
ProxyFactoryBean的父类是AdvisedSupport,Spring使用AopProxyFactory接口把AOP代理的实现与框架的其他部分分离开来;在AdvisedSupport中通过这样的方式来得到AopProxy,这里还需要AopProxyFactory的帮助 - 下面我们看到Spring为我们提供了默认的实现可以帮助我们方便的从JDK或者cglib中得到我们想要的:
protected synchronized AopProxy createAopProxy() {
if (!this.isActive) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
而在ProxyConfig中对使用的AopProxyFactory做了定义:
//这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,
//当然了它包含JDK和Cglib两种实现方式。
private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
其中在DefaultAopProxyFactory中是这样生成AopProxy的:
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
advisedSupport.getProxiedInterfaces().length == 0) {
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
// 这里使用Cglib来生成Proxy,如果target不是接口的实现的话
return CglibProxyFactory.createCglibProxy(advisedSupport);
}
else {
// 这里使用JDK来生成Proxy
return new JdkDynamicAopProxy(advisedSupport);
}
}
于是我们就可以看到其中的Proxy可以有JDK或者Cglib来生成,我们看到JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,在JDK实现中我们可以看到Proxy是怎样生成的:
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
Class targetClass = this.advised.getTargetSource().getTargetClass();
logger.debug("Creating JDK dynamic proxy" +
(targetClass != null ? " for [" + targetClass.getName() + "]" : ""));
}
Class[] proxiedInterfaces = pleteProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//这里我们调用JDK Proxy来生成需要的Proxy实例
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
这样用Proxy包装target之后,对其的调用就被Proxy拦截了,ProxyFactoryBean的getObject()方法得到的实际上已经是Proxy了。
发表者 jiwenke 位置在: 上午2:51 0 评论 指向此文章的链接&
日 星期四
下面我们对Spring MVC框架代码进行分析,对于webApplicationContext的相关分析可以参见以前的文档,我们这里着重分析Spring Web MVC框架的实现.我们从分析DispatcherServlet入手:
//这里是对DispatcherServlet的初始化方法,根据名字我们很方面的看到对各个Spring MVC主要元素的初始化
protected void initFrameworkServlet() throws ServletException, BeansException {
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
}
看 到注解我们知道,这是DispatcherSerlvet的初始化过程,它是在WebApplicationContext已经存在的情况下进行的,也就 意味着在初始化它的时候,IOC容器应该已经工作了,这也是我们在web.xml中配置Spring的时候,需要把DispatcherServlet的 load-on-startup的属性配置为2的原因。
对于具体的初始化过程,很容易理解,我们拿initHandlerMappings()来看看:
private void initHandlerMappings() throws BeansException {
if (this.detectAllHandlerMappings) {
// 这里找到所有在上下文中定义的HandlerMapping,同时把他们排序
// 因为在同一个上下文中可以有不止一个handlerMapping,所以我们把他们都载入到一个链里进行维护和管理
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
getWebApplicationContext(), HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// 这里通过order属性来对handlerMapping来在list中排序
Collections.sort(this.handlerMappings, new OrderComparator());
}
}
else {
try {
Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
//如果在上下文中没有定义的话,那么我们使用默认的BeanNameUrlHandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
........
}
}
怎样获得上下文环境,可以参见我们前面的对IOC容器在web环境中加载的分析。 DispatcherServlet把定义了的所有HandlerMapping都加载了放在一个List里待以后进行使用,这个链的每一个元素都是一个handlerMapping的配置,而一般每一个handlerMapping可以持有一系列从URL请求到 Spring Controller的映射,比如SimpleUrl
HandlerMaaping中就定义了一个map来持有这一系列的映射关系。
DisptcherServlet通过HandlerMapping使得Web应用程序确定一个执行路径,就像我们在HanderMapping中看到的那样,HandlerMapping只是一个借口:
public interface HandlerMapping {
public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
//实际上维护一个HandlerExecutionChain,这是典型的Command的模式的使用,这个执行链里面维护handler和拦截器
HandlerExecutionChain getHandler(HttpServletRequest request) throws E
}
他的具体实现只需要实现一个接口方法,而这个接口方法返回的是一个HandlerExecutionChain,实际上就是一个执行链,就像在Command模式描述的那样,这个类很简单,就是一个持有一个Interceptor链和一个Controller:
public class HandlerExecutionChain {
private HandlerInterceptor[]
........
}
而这些Handler和Interceptor需要我们定义HandlerMapping的时候配置好,比如对具体的SimpleURLHandlerMapping,他要做的就是根据URL映射的方式注册Handler和Interceptor,自己维护一个放映映射的handlerMap,当需要匹配Http请求的时候需要使用这个表里的信息来得到执行链。这个注册的过程在IOC容器初始化SimpleUrlHandlerMapping的时候就被完成了,这样以后的解析才可以用到map里的映射信息,这里的信息和bean文件的信息是等价的,下面是具体的注册过程:
protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
//这里迭代在SimpleUrlHandlerMapping中定义的所有映射元素
Iterator it = urlMap.keySet().iterator();
while (it.hasNext()) {
//这里取得配置的url
String url = (String) it.next();
//这里根据url在bean定义中取得对应的handler
Object handler = urlMap.get(url);
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" +
}
//这里调用AbstractHandlerMapping中的注册过程
registerHandler(url, handler);
}
}
}
在AbstractMappingHandler中的注册代码:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
//试图从handlerMap中取handler,看看是否已经存在同样的Url映射关系
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
........
}
//如果是直接用bean名做映射那就直接从容器中取handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String)
if (getApplicationContext().isSingleton(handlerName)) {
handler = getApplicationContext().getBean(handlerName);
}
}
//或者使用默认的handler.
if (urlPath.equals("/*")) {
setDefaultHandler(handler);
}
else {
//把url和handler的对应关系放到handlerMap中去
this.handlerMap.put(urlPath, handler);
........
}
}
handlerMap是持有的一个HashMap,里面就保存了具体的映射信息:
private final Map handlerMap = new HashMap();
而SimpleUrlHandlerMapping对接口HandlerMapping的实现是这样的,这个getHandler根据在初始化的时候就得到的映射表来生成DispatcherServlet需要的执行链
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//这里根据request中的参数得到其对应的handler,具体处理在AbstractUrlHandlerMapping中
Object handler = getHandlerInternal(request);
//如果找不到对应的,就使用缺省的handler
if (handler == null) {
handler = this.defaultH
}
//如果缺省的也没有,那就没办法了
if (handler == null) {
}
// 如果handler不是一个具体的handler,那我们还要到上下文中取
if (handler instanceof String) {
String handlerName = (String)
handler = getApplicationContext().getBean(handlerName);
}
//生成一个HandlerExecutionChain,其中放了我们匹配上的handler和定义好的拦截器,就像我们在HandlerExecutionChain中看到的那样,它持有一个handler和一个拦截器组。
return new HandlerExecutionChain(handler, this.adaptedInterceptors);
}
我们看看具体的handler查找过程:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//这里的HTTP Request传进来的参数进行分析,得到具体的路径信息。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
.......//下面是根据请求信息的查找
return lookupHandler(lookupPath, request);
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) {
// 如果能够直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
Object handler = this.handlerMap.get(urlPath);
if (handler == null) {
// 这里使用模式来对map中的所有handler进行匹配,调用了Jre中的Matcher类来完成匹配处理。
String bestPathMatch =
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() &= registeredPath.length())) {
//这里根据匹配路径找到最象的一个
handler = this.handlerMap.get(registeredPath);
bestPathMatch = registeredP
}
}
if (handler != null) {
exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
}
}
else {
exposePathWithinMapping(urlPath, request);
}
//
}
我 们可以看到,总是在handlerMap这个HashMap中找,当然如果直接找到最好,如果找不到,就看看是不是能通过Match Pattern的模式找,我们一定还记得在配置HnaderMapping的时候是可以通过ANT语法进行配置的,其中的处理就在这里。
这样可以清楚地看到整个HandlerMapping的初始化过程 - 同时,我们也看到了一个具体的handler映射是怎样被存储和查找的 - 这里生成一个ExecutionChain来储存我们找到的handler和在定义bean的时候定义的Interceptors.
让我们回到DispatcherServlet,初始化完成以后,实际的对web请求是在doService()方法中处理的,我们知道DispatcherServlet只是一个普通的Servlet:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.......
//这里把属性信息进行保存
Map attributesSnapshot =
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
//这里使实际的处理入口
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
我们看到,对于请求的处理实际上是让doDispatch()来完成的 - 这个方法很长,但是过程很简单明了:
protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest =
//这是从handlerMapping中得到的执行链
HandlerExecutionChain mappedHandler =
int interceptorIndex = -1;
........
try {
//我们熟悉的ModelAndView开始出现了。
ModelAndView mv =
try {
processedRequest = checkMultipart(request);
// 这是我们得到handler的过程
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
// 这里取出执行链中的Interceptor进行前处理
if (mappedHandler.getInterceptors() != null) {
for (int i = 0; i & mappedHandler.getInterceptors(). i++) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
interceptorIndex =
}
}
//在执行handler之前,用HandlerAdapter先检查一下handler的合法性:是不是按Spring的要求编写的。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 这里取出执行链中的Interceptor进行后处理
if (mappedHandler.getInterceptors() != null) {
for (int i = mappedHandler.getInterceptors().length - 1; i &= 0; i--) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
// Did the handler return a view to render?
//这里对视图生成进行处理
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
.......
}
我们很清楚的看到和MVC框架紧密相关的代码,比如如何得到和http请求相对应的执行链,怎样执行执行链和怎样把模型数据展现到视图中去。
先看怎样取得Command对象,对我们来说就是Handler - 下面是getHandler的代码:
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
//在ServletContext取得执行链 - 实际上第一次得到它的时候,我们把它放在ServletContext进行了缓存。
HandlerExecutionChain handler =
(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if (handler != null) {
if (!cache) {
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
}
//这里的迭代器迭代的时在initHandlerMapping中载入的上下文所有的HandlerMapping
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
.......
//这里是实际取得handler的过程,在每个HandlerMapping中建立的映射表进行检索得到请求对应的handler
handler = hm.getHandler(request);
//然后把handler存到ServletContext中去进行缓存
if (handler != null) {
if (cache) {
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
}
}
如果在ServletContext中可以取得handler则直接返回,实际上这个handler是缓冲了上次处理的结果 - 总要有第一次把这个handler放到ServletContext中去:
如 果在ServletContext中找不到handler,那就通过持有的handlerMapping生成一个,我们看到它会迭代当前持有的所有的 handlerMapping,因为可以定义不止一个,他们在定义的时候也可以指定顺序,直到找到第一个,然后返回。先找到一个 handlerMapping,然后通过这个handlerMapping返回一个执行链,里面包含了最终的Handler和我们定义的一连串的 Interceptor。具体的我们可以参考上面的SimpleUrlHandlerMapping的代码分析知道getHandler是怎样得到一个 HandlerExecutionChain的。
得到HandlerExecutionChain以后,我们通过HandlerAdapter对这个Handler的合法性进行判断:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
//同样对持有的所有adapter进行匹配
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
}
}
........
}
通过判断,我们知道这个handler是不是一个Controller接口的实现,比如对于具体的HandlerAdapter - SimpleControllerHandlerAdapter:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
.......
}
简单的判断一下handler是不是实现了Controller接口。这也体现了一种对配置文件进行验证的机制。
让我们再回到DispatcherServlet看到代码:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这个就是对handle的具体调用!相当于Command模式里的Command.execute();理所当然的返回一个ModelAndView,下面就是一个对View进行处理的过程:
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
调用的是render方法:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {response.setLocale(locale);
View view =
//这里把默认的视图放到ModelAndView中去。
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
// 这里对视图名字进行解析
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
.......
}
else {
// 有可能在ModelAndView里已经直接包含了View对象,那我们就直接使用。
view = mv.getView();
........
}
//得到具体的View对象以后,我们用它来生成视图。
view.render(mv.getModelInternal(), request, response);
}
从整个过程我们看到先在ModelAndView中寻找视图的逻辑名,如果找不到那就使用缺省的视图,如果能够找到视图的名字,那就对他进行解析得到 实际的需要使用的视图对象。还有一种可能就是在ModelAndView中已经包含了实际的视图对象,这个视图对象是可以直接使用的。
不管怎样,得到一个视图对象以后,通过调用视图对象的render来完成数据的显示过程,我们可以看看具体的JstlView是怎样实现的,我们在JstlView的抽象父类 AbstractView中找到render方法:
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
......
// 这里把所有的相关信息都收集到一个Map里
Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
mergedModel.putAll(this.staticAttributes);
if (model != null) {
mergedModel.putAll(model);
}
// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
}
//这是实际的展现模型数据到视图的调用。
renderMergedOutputModel(mergedModel, request, response);
}
注 解写的很清楚了,先把所有的数据模型进行整合放到一个Map - mergedModel里,然后调用renderMergedOutputModel();这个renderMergedOutputModel是一个模 板方法,他的实现在InternalResourceView也就是JstlView的父类:
protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// 这里得到InternalResource定义的内部资源路径。
String dispatcherPath = prepareForRendering(request, response);
//这里把请求转发到前面得到的内部资源路径中去。
RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);
if (rd == null) {
throw new ServletException(
"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
}
.......
}
首 先对模型数据进行处理,exposeModelAsRequestAttributes是在AbstractView中实现的,这个方法把 ModelAndView中的模型数据和其他request数据统统放到ServletContext当中去,这样整个模型数据就通过 ServletContext暴露并得到共享使用了:
protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
Iterator it = model.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
..........
String modelName = (String) entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
...........
}
else {
request.removeAttribute(modelName);
.......
}
}
}
让我们回到数据处理部分的exposeHelper();这是一个模板方法,其实现在JstlView中实现:
public class JstlView extends InternalResourceView {
private MessageSource jstlAwareMessageS
protected void initApplicationContext() {
super.initApplicationContext();
this.jstlAwareMessageSource =
JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
}
protected void exposeHelpers(HttpServletRequest request) throws Exception {
JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
}
}
在JstlUtils中包含了对于其他而言jstl特殊的数据处理和设置。
过程是不是很长?我们现在在哪里了?呵呵,我们刚刚完成的事MVC中View的render,对于InternalResourceView的render过程比较简单只是完成一个资源的重定向处理。需要做的就是得到实际view的internalResource路径,然后转发到那个资源中去。怎样得到资源的路径呢通过调用:
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return getUrl();
}
那这个url在哪里生成呢?我们在View相关的代码中没有找到,实际上,他在ViewRosolve的时候就生成了,在UrlBasedViewResolver中:
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
}
这里是生成View的地方,自然也把生成的url和其他一些和view相关的属性也配置好了。
那这个ViewResolve是什么时候被调用的呢?哈哈,我们这样又要回到DispatcherServlet中去看看究竟,在DispatcherServlet中:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
........
View view =
// 这里设置视图名为默认的名字
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
//这里对视图名进行解析,在解析的过程中根据需要生成实际需要的视图对象。
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
..........
}
......
}
下面是对视图名进行解析的具体过程:
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {
//我们有可能不止一个视图解析器
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
//这里是视图解析器进行解析并生成视图的过程。
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
}
这 里调用具体的ViewResolver对视图的名字进行解析 - 除了单纯的解析之外,它还根据我们的要求生成了我们实际需要的视图对象。具体的viewResolver在bean定义文件中进行定义同时在initViewResolver()方法中被初始化到viewResolver变量中,我们看看具体的 InternalResourceViewResolver是怎样对视图名进行处理的并生成V视图对象的:对resolveViewName的调用模板在AbstractCachingViewResolver中,
public View resolveViewName(String viewName, Locale locale) throws Exception {
//如果没有打开缓存设置,那创建需要的视图
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
// No synchronization, as we can live with occasional double caching.
synchronized (this.viewCache) {
//这里查找缓存里的视图对象
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
//如果在缓存中没有找到,创建一个并把创建的放到缓存中去
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
........
}
}
}
}
关于这些createView(),loadView(),buildView()的关系,我们看看Eclipse里的call hiearchy
然后我们回到view.render中完成数据的最终对httpResponse的写入,比如在AbstractExcelView中的实现:
protected final void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
.........
// response.setContentLength(workbook.getBytes().length);
response.setContentType(getContentType());
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
out.flush();
}
这样就和我们前面的分析一致起来了:DispatcherServlet在解析视图名的时候就根据要求生成了视图对象,包括在InternalResourceView中需要使用的url和其他各种和HTTP response相关的属性都会写保持在生成的视图对象中,然后就直接调用视图对象的render来完成数据的展示。
这就是整个Spring Web MVC框架的大致流程,整个MVC流程由DispatcherServlet来控制。MVC的关键过程包括:
配置到handler的映射关系和怎样根据请求参数得到对应的handler,在Spring中,这是由handlerMapping通过执行链来完成的,而具体的映射关系我们在bean定义文件中定义并在HandlerMapping载入上下文的时候就被配置好了。然后DispatcherServlet调用HandlerMapping来得到对应的执行链,最后通过视图来展现模型数据,但我们要注意的是视图对象是在解析视图名的时候生成配置好的。这些作为核心类的HanderMapping,ViewResolver,View,Handler的紧密协作实现了MVC的功能。
发表者 jiwenke 位置在: 上午1:39 0 评论 指向此文章的链接&
日 星期二
下面我们看看Spring JDBC相关的实现,
在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数据库,在JdbcTemplate为用户程序提供了许多便利的数据库操作方法,比如查询,更新等,而且在Spring中,也许多类似JdbcTemplate的Template,比如HibernateTemplate等等 - 看来这是Rod.Johnson的惯用手法,一般而言这种Template中都是通过回调函数CallBack类的使用来重新对需要客户订制的行为进行定制,比如使用客户想要用的SQL语句(Spring又不是神仙,它只是做好模板来减少用户程序的工作量,至于具体要做什么要查询删除什么,那还得劳您大驾),一般来说回调函数的用法采用一下这种一个匿名类的方式,比如:
JdbcTemplate = new JdbcTemplate(datasource);
jdbcTemplate.execute(new CallBack(){
public CallbackInterface(){
......
//用户定义的代码或者说Spring替我们实现的代码
}
}
实际在Template的过程中,用户定义的代码就是模板代码的实现,在模板中嵌入客户代码把模板客户化事项客户程序要求的功能。下面让我们具体看看在JdbcTemplate中的代码是怎样具体完成自己的使命的,我们举出JdbcTemplate.execute()为例,这个方法是在JdbcTemplate中被其他方法调用的基本方法之一来执行基本的SQL语句:
public Object execute(ConnectionCallback action) throws DataAccessException {
//这里得到数据库联接
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse =
//有些特殊的数据库需要我们使用特别的方法取得datasource
if (this.nativeJdbcExtractor != null) {
// Extract native JDBC Connection, castable to OracleConnection or the like.
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
else {
// Create close-suppressing Connection proxy, also preparing returned Statements.
conToUse = createConnectionProxy(con);
}
//这里调用的是传递进来的匿名类的方法,也就是用户程序需要实现CallBack接口的地方。
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
//如果捕捉到数据库异常,把数据库联接释放,同时抛出一个经过Spring转换过的Spring数据库异常,
//我们知道,Spring做了一个有意义的工作是把这些数据库异常统一到自己的异常体系里了。
DataSourceUtils.releaseConnection(con, getDataSource());
con =
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
}
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
对于JdbcTemplate中给出的其他方法,比如query,update,execute,我们看看query的基本处理:
public Object query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
throws DataAccessException {
..........
//这里调用了我们上面看到的execute()基本方法;这里基本上是Spring为我们代劳
return execute(psc, new PreparedStatementCallback() {
public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs =
try {
if (pss != null) {
pss.setValues(ps);
}
//这里执行的SQL查询
rs = ps.executeQuery();
ResultSet rsToUse =
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
//返回需要的记录集合
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}
其中同过辅助类DataSourceUtils来对数据库连接进行管理,比如打开和关闭等操作。
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
//把对数据库连接放到事务管理里面进行管理
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conH
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
}
那我们实际的DataSource对象是怎样得到的?我们对JdbcTemplate配置DataSource,它是在JdbcTemplate的父类中被定义的:
public abstract class JdbcAccessor implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
/** Used to obtain connections throughout the lifecycle of this object */
private DataSource dataS
/** Helper to translate SQL exceptions to DataAccessExceptions */
private SQLExceptionTranslator exceptionT
private boolean lazyInit =
........
}
而对于DataSource, 我们可以通过定义Apache Jakarta Commons DBCP或者C3P0提供的DataSource实现来完成,然后只要让JdbcTemplate保持对它的引用就可以直接使用了。 JdbcTemplate提供了简单查询和更新功能,但是可能需要更高层次的抽象,以及更面向对象的方法来访问数据库。这种功能是由 org.springframework.jdbc.object来提供的,包含了SqlQuery,SqlMappingQuery, SqlUpdate和StoredProcedure类,这些类都是Spring JDBC应用程序的主要类,使用这些类,用户需要为他们配置好一个JdbcTemplate,因为他们在内部使用JdbcTemplate用于数据库访 问。
比如说我们使用MappingSqlQuery来将表数据直接映射到一个对象集合:
1.我们需要建立DataSource和sql语句并建立持有这}

我要回帖

更多关于 spring beans 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信