Spring发展历程

        目前不少公司的架构,从Struts2迁移到了SpringMVC。你有想过为何不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?javascript

        既然这样,咱们从源头提及。Struts2的源头其实也是Servlet。Servlet的做用是接收浏览器传给服务端的请求(request),并将服务端处理完的响应(response)返回给用户的浏览器,浏览器和服务端之间经过http协议进行沟通,其过程是浏览器根据用户的选择将相关信息按http协议报文的规范组装请求的http报文,报文经过网络传输到指定的服务器,服务器经过特定的web容器接收这个报文信息,例如:tomcat,jetty,jboss这样的web容器,web容器会将http报文解析出来,若是是用户请求,最终解析出来的报文信息会用一个request对象存储起来,服务端使用这个request作完相应的处理后,服务端程序将结果信息封装到response对象里,而后将response对象交给web容器,web容器则把这个response对象转变为http协议的报文,并将报文回传给浏览器,浏览器最后解析这个响应报文,将最终结果展现给用户。css

        Web容器创造了servlet接口,servlet接口就是开发人员本身实现业务逻辑的地方,程序员开发servlet就比如作填空题,而填空题的语境或者说上下文提示就是由request和response对象,可是javaEE规范里的servlet接口很简单,就三个方法init,service和destory,可是这个接口太笼统,因此规范里还提供了一个HttpServlet类,这个类根据http请求类型提供了doGet,doPost等方法,servlet接口最大的特色就是根据http协议的特色进行定义,所以作servlet开发时候若是使用者对http协议特色不是特别熟悉,都会碰到或多或少使人迷惑的问题,特别是碰到一些复杂特殊的请求时候:例如文件上传,返回特殊的文件格式到浏览器,这时候使用servlet开发就不是很方便了,servlet开发还有个问题可能你们经常被忽视,就是请求的数据的类型转化,http协议传输都是文本形式,到了web容器解析后也是文本类型,若是碰到货币,数字,日期这样的类型须要咱们根据实际状况进行转化,若是页面传送的信息很是多,咱们就不得不作大量类型转化,这种工做没有什么技术含量,是个体力活并且很容易致使程序错误。同时java的企业开发都是围绕javabean进行,类型转化好的数据还要封装到对应的javabean里,这种转来转去的事情对于项目开发绝对不是什么好事情,因此古老的struts1为这种问题找到了一种解决方案,就是定义了一个DTO对象(数据传输对象),专门负责作这样的事情,不过到了struts2,整个替代servlet的action自己就是一个javabean。html

        Java的企业开发一个技术特色就是使用javabean进行的,struts2的特色之一就是它替代servlet的操做类就是一个典型的javabean,首先struts2框架将页面传输的数据进行类型转化和封装后将请求信息封装到了这个javabean的属性里,这样咱们开发web程序时候就省去了烦心的类型转化和封装的问题,前面我讲到传统的servlet是根据http协议进行定义的,它会按你请求方式(post仍是get方式)来处理用户的请求,可是对于一名程序开发人员而言,一个请求,具体到一个url,其实对于服务端而言就是服务端对外提供的一个功能,或者说是服务端对外的一个动做,若是咱们使用servlet开发程序咱们就得把http的动做转化为具体的业务动做,这就让程序开发变得繁琐,加强了开发的难度,因此struts2替代servlet的javabean就屏蔽了servlet里http的请求方式和具体业务动做转化的问题,javabean里的每个方法均可以和每个url请求一一对应,这必然减轻了开发的难度问题。前端

        Servlet另外一个做用就是构造response对象,让页面得到正确的响应,其实现代的浏览器是一个多媒体工具,文字,图片,视屏等等东西均可以在浏览器里显示,资源的不一样就会致使http响应报文的差异,若是咱们使用servlet开发就要根据资源的不一样在java程序里用硬编码的形式处理,这样的程序很难复用,并且若是程序员对某种资源的处理理解不到位,就会致使问题的出现,struts2经过配置文件的形式将这样的逻辑从java程序里剥离出来,使用配置的方式进行统一管理,这个作法和spring的AOP方式相似,这样就让结果处理方式更加统一,更加利于管理,同时也提高了程序的健壮性以及下降了开发的难度。java

        Servlet在MVC开发模式里就是其中C层即控制层,控制层就像俄罗斯的双头鹰(一个头向东看一个头向西看)同样,一个头向M层模型层看,一个头向V层视图层看,模型层也是用java编写的,控制层也属于服务端语言开发,因此M层和C层的沟通没有自然的障碍,可是和V层视图层就不同了,这是一个跨语言的沟通,对于浏览器,它只懂得html,javascript和css,浏览器是理解不了java这种语言的东西,可是要让服务端的东西能被浏览器理解接受,咱们就必须得把服务端的响应信息放到页面里,所以就须要一个技术把java的信息转化到html页面里,这就是javaEE规范里提供了jsp技术,jsp实际上是一种服务端技术而非客户端技术,不过它看起来彷佛更像html技术,最先的jsp开发里都是直接将java代码写到页面里,这种坏处谁都知道,以后javaEE规范提供了自定义标签技术,使用一种相似html标签的方式来解析java代码,struts2框架提供了一整套完整的自定义标签技术,这彷佛听起来不算啥,可是它的做用非凡,由于自定义标签之因此叫自定义就是每一个人均可以本身来定义,若是没有一个规范必然产生混乱,并且一套完善的自定义标签是个系统工程,一套完整的自定义标签至关于咱们在本身定义一套新的开发语言,作程序的人听到这个必定就会明白开发一套完整的自定义标签的工做量和开发难度都是不可思议的,并且自定义标签都是和控制层紧密相连,其难度又会增长一个维度,因此struts2提供的自定义标签对于业务开发带来的将是质的飞越。程序员

        Servlet里还有两个重要的技术:监听器和过滤器,对于监听器在web开发里使用的场景比较少,都是一些十分特别的状况才会使用,大部分web开发里能够忽略它的使用,咱们用的最多的监听器可能就是对ServletContext建立和销毁的监听器,ServletContext是整个web应用的全局对象,它和Web应用的生命周期绑定在一块儿,所以使用这个监听器对Web应用的全局信息进行初始化和销毁操做,例如spring容器的初始化操做。比较有意思的是过滤器,在struts2里有个拦截器,它们的做用相同都是用来拦截请求的,由于拦截器是struts2的特有功能,在struts2里使用拦截器天然比使用过滤器更顺手,其实拦截器所用的技术比过滤器更加先进,由于拦截器使用了反射技术,所以拦截器拦截的面更大,控制请求的能力更强,它能完成的任务也会更加的丰富多彩。web

        在我第一次接触struts2时候,有人告诉我struts设计的一个目的就是想屏蔽在控制层里操做request和response对象,由于这两个http协议的儿子会形成web开发里思路的混乱,可是我在实际开发里却常常不自觉的使用这两个对象。并且本人作前端开发很是喜欢使用ajax,使用ajax技术时候我就很讨厌struts2的自定义标签,我更加喜欢在页面里用javascript技术处理各类信息,最终struts2在我眼里就是一个servlet的变体,所以曾经有段时间我经常在想是否是能够抛弃struts2,直接用servlet,由于struts2里用到了太多反射机制,特别是使用注解作配置(注解是用反射实现的),在java里反射的执行效率是很是低的,直接使用servlet必定能提高web应用的执行效率。其实这个倒很难作到,由于当时我无法在servlet里灵活的运用spring技术。ajax

        说完Servlet+jsp技术到Struts2技术的过渡。接下来谈谈Spring。spring

        spring技术能够说是java企业开发里最重要的技术,不过真的理解spring的做用和意义还真是一件麻烦的事情,不少人对spring理解其实都是停留在使用阶段(例如:声明式事务很好用等等),当今的spring技术生态环境里可谓是蔚为壮观,spring已经一应俱全,它的内容之多彻底不亚于它的本源java语言了,而spring这么大的框都是创建在ioc和aop技术之上,只有深刻理解了这两个技术咱们才能明白为何spring这个框能装的下那么多东西了。sql

        首先是ioc,ioc技术第一个解释叫作控制反转,它还有个解释就是依赖注入,这两个名字很难从字面理解,可是当你理解它的原理后就会发现它们的描述是何等准确。Ioc技术的本质就是构建对象的技术换句话说就是将一个类实例化成对象的技术,在java里实例化类经过new关键字进行的,每次new一个类都会产生一个新的实例对象,这么作视乎很浪费,有时这种浪费还挺危险,由于在程序开发时候咱们经常只须要某个类永远只能产生一个的实例对象这个时候就得使用单例模式,此外在设计模式里还能够经过工厂方式产生对象,使用过spring的人看到上面的文字就知道了,spring里bean的定义就和上面的内容一一对应,scope属性single产生单例对象,prototype产生新对象,bean还能够经过工厂方式产生对象,能够说spring的bean就是制造对象的工具。面向对象编程里对象至关于显示生活中的一个实体,例如咱们有个对象做用是完成打猎的操做,那么打猎这个对象内部包含两个辅助对象:人和枪,只有人和枪赋予了打猎这个对象,那么打猎对象才能完成打猎的操做,可是构建一我的和枪的对象并非看起来那么简单,这里以枪为例,要创造一把枪咱们须要金属,须要机床,须要子弹,而机床和子弹又是两个新对象,这些对象一个个相互嵌套相互关联,大伙试想下若是咱们在java代码里构建一个枪的对象那是何其的复杂,假如咱们要构造的不是简单的枪对象而是更加复杂的航空母舰,那么构造这个对象的成本之高是让人不可思议的,怎么来消除这种对象相互嵌套相互依赖的关系了?spring提供了一种方式,这种方式就是spring提供一个容器,咱们在xml文件里定义各个对象的依赖关系,由容器完成对象的构建,当咱们java代码里须要使用某个实例的时候就能够从容器里获取,那么对象的构建操做就被spring容器接管,因此它被称为控制反转,控制反转的意思就是原本属于java程序里构建对象的功能交由容器接管,依赖注入就是当程序要使用某个对象时候,容器会把它注入到程序里,这就叫作依赖注入。在java开发里咱们想使用某个类提供的功能,有两种方式,一种就是构造一个新的类,新的类继承该类,另外一种方式则是将某个类定义在新类里,那么两个类之间就创建一种关联关系,spring的ioc容器就是实现了这种关联关系(记住不是继承关系哦),那么某个类要被赋予到新类有哪些办法了?通常只有两种:一种就是经过构造函数,一种就是经过setXXX方式,这也是spring容器使用到了两种标准的注入方式。

        无论是上面说的继承方式,仍是关联方式其实都是加强目标对象能力的开发手段,在设计模式里有一种代理模式,代理模式将继承模式和关联模式结合在一块儿使用,代理模式就是继承模式和关联模式的综合体,不过这个综合体的做用倒不是解决对象注入的问题,而是为具体操做对象找到一个保姆或者是秘书,这就和小说里的二号首长同样,这个二号首长对外表明了具体的实例对象,实例对象的入口和出口都是经过这个二号首长,由于具体的实例对象是一号首长,一号首长是要干大事的,因此一些事务性,重复性的工做例如泡茶,安排车子,这样的工做是不用劳烦一号首长的大驾,而是二号首长帮忙解决的,这就是aop的思想,aop解决程序开发里事务性,和核心业务无关的问题,但这些问题对于业务场景的实现是颇有必要的,在实际开发里aop也是节省代码的一种方式。

        Spring的核心技术的做用本质就是一个沟通机制,spring老是尽全力的让沟通的双方信息畅通,同时下降双方的沟通成本,在现实机构里一个善于沟通的人确定是该公司的领导,很会沟通的领导能调动起各类资源的积极性,善于沟通的领导就会作到海纳百川,让各类不一样人追随他,因此当今的spring就是一个大框,什么均可以往里装。Spring很像银行,它不能直接创造物质财富,可是一切资源都要经过它进行流通,它能控制经济发展的走向,回到程序的世界,spring的做用是被标榜为程序之间的解耦,spring能下降不一样模块之间的耦合度,缘由就是在程序开发里不一样模块之间信息的沟通是经过对象传递完成的,而对象可否顺利传递就是要合理的构建好对象,而管理好对象的构建方式就能管理好对象传递,这就是spring给系统架构设计带来的好处。

        说到Spring, Spring的事务你懂吗?

        什么是事务?为何事务要管理?什么是Spring事务?事务就是对一系列的数据库操做(好比插入多条数据)进行统一的提交或回滚操做,若是插入成功,那么一块儿成功,若是中间有一条出现异常,那么回滚以前的全部操做。这样能够防止出现脏数据,防止数据库数据出现问题。开发中为了不这种状况通常都会进行事务管理。在JDBC中,是经过Connection对象进行事务管理的,默认是自动提交事务,能够手工将自动提交关闭,经过commit方法进行提交,rollback方法进行回滚,若是不提交,则数据不会真正的插入到数据库中。Hibernate中则是经过Transaction进行事务管理,处理方法与JDBC中相似。Spring中也有本身的事务管理机制,通常是使用TransactionMananger进行管理,能够经过Spring的注入完成此功能。

        我通俗的理解以下:spring只是控制数据库的事务提交和回滚,借助于java的反射机制,在事务控制的方法(一般是service层的方法)先后获取事务开启session,而后执行你的数据操做,若是你的方法内有异常被抛出,spring会捕获异常并回滚你在这个方法内全部的数据操做,若是成功则提交全部的数据,最后spring会帮你关闭须要关闭的东西。因此spring想要作的是,要程序员专一于写逻辑,不须要关心数据库什么时候开启和关闭链接。

        再说的通俗点儿:事务,对于一件事,对了就提交,错了就回滚,何时回滚,都是事务要作的事情。具体的操做由spring 配置来管理(同时你也能够脱离框架,本身写事务管理方法)。

        使用Spring事务的优势?

        在SSH框假中Spring充当了管理容器的角色。咱们都知道Hibernate用来作持久层,由于它将JDBC作了一个良好的封装,程序员在与数据库进行交互时能够不用书写大量的SQL语句。Struts是用来作应用层的,他它负责调用业务逻辑serivce层。因此SSH框架的流程大体是:Jsp页面----Struts------Service(业务逻辑处理类)---Hibernate(左到右)。struts负责控制Service(业务逻辑处理类),从而控制了Service的生命周期,这样层与层之间的依赖很强,属于耦合。这时,使用spring框架就起到了控制Action对象(Strus中的)和Service类的做用,二者之间的关系就松散了,Spring的Ioc机制(控制反转和依赖注入)正是用在此处。   Spring的Ioc(控制反转和依赖注入) 控制反转:就是由容器控制程序之间的(依赖)关系,而非传统实现中,由程序代码直接操控 依赖注入:组件之间的依赖关系由容器在运行期决定 ,由容器动态的将某种依赖关系注入到组件之中。 从上面咱们不难看出:从头至尾Action仅仅是充当了Service的控制工具,这些具体的业务方法是怎样实现的,他根本就不会管,也不会问,他只要知道这些业务实现类所提供的方法接口就能够了。而在以往单独使用Struts框架的时候,全部的业务方法类的生命周期,甚至是一些业务流程都是由Action来控制的。层与层之间耦合性太紧密了,既下降了数据访问的效率又使业务逻辑看起来很复杂,代码量也不少。Spring容器控制全部Action对象和业务逻辑类的生命周期,因为上层再也不控制下层的生命周期,层与层之间实现了彻底脱耦,使程序运行起来效率更高,维护起来也方便。   使用Spring的第二个好处(AOP应用): 事务的处理: 在以往的JDBCTemplate中事务提交成功,异常处理都是经过Try/Catch 来完成,而在Spring中。Spring容器集成了TransactionTemplate,她封装了全部对事务处理的功能,包括异常时事务回滚,操做成功时数据提交等复杂业务功能。这都是由Spring容器来管理,大大减小了程序员的代码量,也对事务有了很好的管理控制。Hibernate中也有对事务的管理,hibernate中事务管理是经过SessionFactory建立和维护Session来完成。而Spring对SessionFactory配置也进行了整合,不须要在经过hibernate.cfg.xml来对SessionaFactory进行设定。这样的话就能够很好的利用Sping对事务管理强大功能。避免了每次对数据操做都要现得到Session实例来启动事务/提交/回滚事务还有繁琐的Try/Catch操做。这些也就是Spring中的AOP(面向切面编程)机制很好的应用。一方面使开发业务逻辑更清晰、专业分工更加容易进行。另外一方面就是应用Spirng AOP隔离下降了程序的耦合性使咱们能够在不一样的应用中将各个切面结合起来使用大大提升了代码重用度。有利于代码重用,特别是Dao代码的重用。事务每每和业务规则紧密关联。当业务逻辑发生改变,意味着dao的大幅度改动。系统规模达到必定程度,修改风险至关大。Spring的好处是不更改现有的dao,仅需对现有的service bean进行配置就达到事务效果了。同时,把事务统一在service层,系统结构更清晰。

        为何说风险风大? Spring对于事务的配置有两种方式:第一种,使用xml形式,第二种,使用注解的形式。 基于XMl方式: 优势:能够在后期维护的时候适当的调整事务管理模式,而且只要遵循必定的命名规范,可让程序员没必要关心事务。        缺点:系统越庞大,xml配置就越大。 基于注解方式:优势:配置比较方便,程序员只要在service层代码设置便可以实现。不须要知道系统须要多少个bean,交给容器来注入就行了。        缺点:当你要修改或删除一个bean的时候,你没法肯定到底有多少个其余的bean依赖于这个bean。(解决方法:须要有严格的开发文档,在修改实现时尽量继续遵照相应的接口避免使其余依赖于此的bean不可用)

        在咱们用SSH开发项目的时候,咱们通常都是将事务设置在Service层 那么当咱们调用Service层的一个方法的时候它可以保证咱们的这个方法中执行的全部的对数据库的更新操做保持在一个事务中,在事务层里面调用的这些方法要么所有成功,要么所有失败。那么事务的传播特性也是从这里提及的。 若是你在你的Service层的这个方法中,除了调用了Dao层的方法以外,还调用了本类的其余的Service方法,那么在调用其余的 Service方法的时候,这个事务是怎么规定的呢,我必须保证我在我方法里掉用的这个方法与我自己的方法处在同一个事务中,不然如何保证事物的一致性。事务的传播特性就是解决这个问题的,“事务是会传播的”在Spring中有针对传播特性的多种配置咱们大多数状况下只用其中的一种:PROPGATION_REQUIRED:这个配置项的意思是说当我调用service层的方法的时候开启一个事务(具体调用那一层的方法开始建立事务,要看你的aop的配置),那么在调用这个service层里面的其余的方法的时候,若是当前方法产生了事务就用当前方法产生的事务,不然就建立一个新的事务。这个工做是由Spring来帮助咱们完成的。 之前没有Spring帮助咱们完成事务的时候咱们必须本身手动的控制事务,例如当咱们项目中仅仅使用hibernate,而没有集成进 spring的时候,咱们在一个service层中调用其余的业务逻辑方法,为了保证事物必须也要把当前的hibernate session传递到下一个方法中,或者采用ThreadLocal的方法,将session传递给下一个方法,其实都是一个目的。如今这个工做由 spring来帮助咱们完成,就可让咱们更加的专一于咱们的业务逻辑。而不用去关心事务的问题。默认状况下当发生RuntimeException的状况下,事务才会回滚,因此要注意一下。若是你在程序发生错误的状况下,有本身的异常处理机制定义本身的Exception,必须从RuntimeException类继承,这样事务才会回滚!

        事务的特性:原子性、一致性、隔离性、持久性。

        Spring事务传播特性总结:

        1.只要定义为spring的bean就能够对里面的方法使用@Transactional注解。

        2.Spring的事务传播是Spring特有的。不是对底层jdbc的代理。

        3.使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在[方法调用以前决定是否开启一个事务],并在[方法执行以后]决定事务提交或回滚事务。

  4.Spring支持的PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:PROPAGATION_REQUIRES_NEW:二个事务没有信赖关系,不会存在A事务的成功取决于B事务的状况。有可能存在A提交B失败。A失败(好比执行到doSomeThingB的时候抛出异常)B提交,AB都提交,AB都失败的可能。PROPAGATION_NESTED:与PROPAGATION_REQUIRES_NEW不一样的是,内嵌事务B会信赖A。即存在A失败B失败。A成功,B失败。A成功,B成功。而不存在A失败,B成功。

    5.特别注意PROPAGATION_NESTED的使用条件:使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager做为事务管理器。须要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了一样的功能。使用PROPAGATION_NESTED,还须要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而 nestedTransactionAllowed属性值默认为false;

        6.特别注意PROPAGATION_REQUIRES_NEW的使用条件:JtaTransactionManager做为事务管理器

        补充文字来讲一下上面的关于Spring的一个问题。基于xml和基于注解,固然了它们都有优缺点。咱们通俗的说,是这样的。先来回顾一下传统上是如何配置 Bean 并完成 Bean 之间依赖关系的创建。下面是 3 个类,它们分别是 Office、Car 和 Boss,这 3 个类须要在 Spring 容器中配置为 Bean。

//  Office.java
public class Office {
	private String officeNo = "001"; // 省略 get/setter
	@Override
	public String toString() {
		return "officeNo:" + officeNo;
	}
}
//  Car.java
public class Car {
	private String brand;
	private double price; // 省略 get/setter
	@Override
	public String toString() {
		return "brand:" + brand + "," + "price:" + price;
	}
}
//  Boss.java
public class Boss {
	private Car car;
	private Office office; // 省略 get/setter
	@Override
	public String toString() {
		return "car:" + car + "/n" + "office:" + office;
	}
}
相关文章
相关标签/搜索