Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
给你学习路线:html-css-js-jq-javase-数据库-jsp-servlet-Struts2-hibernate-mybatis-spring4-springmvc-ssh-ssm
现在的Java程序员赶上了好时候。在将近20年的历史中,Java的发展历经沉浮。尽管有很多为人诟病的产品,例如applets、EJB、Java Data Object(JDO)和数不清的日志框架,Java还是发展为一个庞大且丰富的开发平台,很多企业级应用都是基于JVM平台构建。Spring是JVM开发平台中的一颗明珠。
小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
Spring最开始出现的目的是替代企业级开发框架EJB,相比EJB,Spring提供更轻量和更易用的编程模型。Spring的重要特点是非侵入式增强POJO(plain old java object)的能力。
在后续的发展过程中,EJB也效仿Spring的做法提供了简单的以POJO为中心的编程模型,现在的EJB框架也拥有依赖注入(DI)和面向切面编程(AOP)能力,可以论证是受Spring成功的影响。
尽管J2EE一直在追赶Spring的发展,但是Spring本身也没有停止进步。现在,Spring在一些J2EE刚刚涉入或者完全没有涉入的领域飞速发展:移动开发、社交API整合、NoSQL数据库、云计算和大数据。就目前来看,Spring的未来一片光明。
重要的事情再强调一遍:现在的Java程序员赶上了好时候。
这篇文章会从一个比较高的层次探索Spring,介绍Spring框架解决了哪些主要问题。
1.1 简化Java开发
Spring是一种开源框架,由Rod Johnson发明,并在其著作《Expert One-on-One:J2EE设计与开发》。Spring的初衷是降低企业级开发的复杂性,并试图通过POJO对象实现之前EJB这类重型框架才能实现的功能。Spring不仅仅对服务端开发有用,任何Java应用都可受益于Spring的简洁、易测试和低耦合等特性。
Spring框架中使用beans或JavaBeans来表示应用程序中的组件,但这并不意味着该组件必须严格满足Java Bean的规范。
Spring做了很多事情,但是归根到底是一些基本的思路,而所有这些思路最终都导向Spring的使命:简化Java开发。
Spring通过下列四种策略来简化Java开发:
基于POJO的轻量级、最小侵入式开发;
通过依赖注入和面向接口编程实现松耦合;
通过面向切面编程和惯例实现声明式编程;
通过面向切面编程和模板消除样板式代码(boierplate code)
几乎Spring的每条特性都可以追溯到这四条策略之一,接下来分别对这四条策略进行阐述,并给出具体的代码说明Spring如何简化Java开发。
1.1.1 激发POJO的能力
如果你做Java开发足够久,你应该遇到过很多会束缚程序员能力的开发框架,这些框架要求程序员继承框架提供的类或者实现它提供的接口,例如EJB框架中的session beans,另外,在EJB之前的很多框架中也有类似的侵入式编程模型,如Struts、WebWork、Tapestry等等。
Spring尽量避免让自己的API污染你的应用代码。Spring几乎不会强制要求开发人员实现某个Spring提供的接口或者继承某个Spring提供的类,在Spring应用中的Java类看起来和普通类一样,不过,Spring现在经常使用注解来修饰Java类,但是这个类还是一个POJO。
举个代码例子说明,看如下的HelloWorldBean
可以看出,这就是一个简单的Java类-POJO,没有什么特殊的标志表明它是一个Spring组件。Spring这种非侵入式编程模型使得这个类在Spring和非Spring框架下具备相同的功能。
尽管形式非常简单,POJO的能力值却可能非常高,例如Spring可以通过依赖注入编织这些POJOs来激发POJO的能力。
1.1.2 依赖注入
依赖注入听起来比较吓人,貌似一种非常复杂的编程技术或者设计模式。实际上依赖注入并不复杂,通过在工程中应用依赖注入技术,可以得到更简单、更容易理解和测试的代码。
How DI works
除了Hello-world级别的程序,稍微复杂一点的Java应用都需要多个类配合实现功能。一般而言,每个类自己负责获取它要合作的类对象的引用,这会导致代码高度耦合且难以测试。
首先看如下代码:
可以看出,DamselRescuingKnight在它的构造函数中创建了自己的Quest实例——RescueDamselQuest实例,这使得DamselRescuingKnight与RescueDamselQuest紧密耦合,如果需要刺杀Damsel,则这个刀可以使用,但是如果需要刺杀恐龙,则这个刀就派不上用场了。
更糟的是,给DamselRescuingKnight写单元测试很不方便,在这个测试中,你必须确认:当调用knight的emarkOnQuest函数时,quest的embark函数也正确调用,但这并不容易。
耦合是一头双头怪:一方面,紧耦合的代码难以测试、难以复用并且难以理解,并且经常陷入“修复一个bug但引入一个新的bug”的开发怪圈中;另一方面,应用程序必须存在适当的耦合,否则该应用无法完成任何功能。总之,耦合是必要的,但是应该控制组件之间的耦合程度。
通过使用依赖注入(DI)技术,对象之间的依赖关系由Spring框架提供的容器进行管理,而不需要某个对象主动创建自己需要的引用,如下图所示:
依赖注入的作用
再看一个BraveKnight类的例子:
该对象不再局限于一种quest实例,在构造过程中利用构造函数的参数传入quest实例,这种类型的依赖注入称为构造注入。
还有一点需要注意,使用接口定义quest实例,这就是面向接口编程,使得BraveKnight不再局限于某种特定的Quest实现,这就是DI带来的最大的好处——松耦合。
实现依赖注入
在上述例子代码可以看出,Spring相当于将依赖注入的位置从BraveKnight类中剥离出来,那么具体的依赖注入代码如何写呢?开发人员如何规定给BraveKnight注入哪个Quest实现,例如SlayDragonQuest?
小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
在Spirng框架中,最通用的方法是通过写XML配置文件来定义组件之间的依赖关系,如下所示:
在这个xml配置文件中分别定义了BraveKnight和SlayDragonQuest两个bean:在BraveKnightbean的定义中,通过构造器函数传入一个SlayDragonQuest的引用;在SlayDragonQuest的定义中,通过SpEL语言将System.out传入它的构造函数。
Spring 3.0引入了JavaConfig,这种写法比xml文件的好处是具备类型安全检查,例如,上面XML配置文件可以这么写:
不论是基于XML的配置还是基于Java文件的配置,都由Spring框架负责管理beans之间的依赖关系。
启动依赖注入
在Spring应用中,由application context负责加载beans,并将这些beans根据配置文件编织在一起。Spring框架提供了几种application context的实现,如果使用XML格式的配置文件,则使用ClassPathXmlApplicationContext;如果使用Java文件形式的配置文件,则使用AnnotationConfigApplicationContext。
上述代码中,根据KnightConfig.java文件创建Spring应用上下文,可以把该应用上下文看成对象工厂,来获取idknight的bean。
1.1.3 切面编程
依赖注入(DI)实现了模块之间的松耦合,而利用面向切面编程(AOP)可以将涉及整个应用的基础功能(安全、日志)放在一个可复用的模块中。
AOP是一种在软件系统中实现关注点分离的技术。软件系统由几个模块构成,每个模块负责一种功能,不过在系统中有些需求需要涉及到所有的模块,例如日志、事务管理和安全等。如果将这些需求相关的代码都分散在各个模块中,一方面是不方便维护、另一方面是与原来每个模块的业务逻辑代码混淆在一起,不符合单一职责原则。
实现系统级别处理的代码分散在多个子模块中,这意味着如果要修改这些处理代码,则要在每个模块中都进行修改。即使将这些代码封装到一个模块中,在没给个子模块中只保留对方法的调用,这些方法调用还是在各个模块中重复出现。
业务逻辑代码与非核心功能的代码混淆在一起。例如,一个添加address book的方法应该只关心如何添加address book,而不应该关心该操作是否安全或者是否能够实现事务处理。
下面这张图可以体现这种复杂性,左边的业务逻辑模块与右边的系统服务模块沟通太过密切,每个业务模块需要自己负责调用这些系统服务模块。
业务逻辑模块与系统服务模块过度交互
AOP可以模块化这些系统服务,然后利用声明式编程定义该模块需要应用到那些业务逻辑模块上。这使得业务模块更简洁,更专注于处理业务逻辑,简而言之,切面(aspects)确保POJO仍然是普通的Java类。
可以将切面想象为覆盖在一些业务模块上的毯子,如下图所示。在系统中有一些模块负责核心的业务逻辑,利用AOP可以为所有这些模块增加额外的功能,而且核心业务模块无需知道切面模块的存在。
切面就像毯子一样覆盖在几个核心业务模块之上
AOP实践
继续上面的例子,如果需要一个人记录BraveKnight的所作所为,下面代码是该日志服务:
然后在XML文件中定义Minstrel对应的切面:
小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!
在这个配置文件中增加了aop配置名字空间。首先定义Minstrel的bean,然后利用标签定义aop相关的配置;然后在节点中引用minstrel,定义方面;aspect负责将pointcut和要执行的函数(before、after或者around)连接在一起。
还有一种更先进的写法,利用注解和Java配置文件,可以参考aop docs
Spring框架中的一些子模块也是基于AOP实现的,例如负责事务处理和负责安全的模块。