如何用Spring 3.1的spring4 environmentt和Profile简化工作

核心提示:Spring Framework(简称Spring)是根据Rod Johnson著名的《Expert One-on-One J2EE Design and Development》而开发的J2EE应用程序框架。
1.什么是Spring Framework?  Spring Framework(简称Spring)是根据Rod Johnson著名的《Expert One-on-One J2EE Design and Development》而开发的J2EE应用程序框架。目前主要根据Rod Johnson和Juergen Hoeller而进行开发的,目前发布的最新版为1.1.4。 Spring是J2EE应用程序框架,不过,更严格地讲它是针对Bean的生命周期进行管理的轻量级容器(Lightweight container),可以单独利用Spring构筑应用程序,也可以和Struts,Webwork,Tapestry等众多Web应用程序框架组合使用,并且可以与Swing等桌面应用程序API组合。所以Spring并不仅仅只能应用在J2EE中,也可以应用在桌面应用及小应用程序中。针对Spring开发的组件不需要任何外部库。2.使用Spring有什么好处?(1)Spring能有效地组织你的中间层对象。(2)Spring能消除在许多工程中常见的对Singleton的过多使用。(3)Spring能消除各种各样自定义格式的属性文件的需要,使配置信息一元化。(4)Spring能够帮助我们真正意义上实现针对接口编程。(5)在Spring应用中的大多数业务对象没有依赖于Spring。(6)使用Spring构建的应用程序易于单元测试。(7)Spring支持JDBC和O/R Mapping产品(Hibernate)(8)MVC Web框架,提供一种清晰,无侵略性的MVC实现方式。(9)JNDI抽象层,便于改变实现细节,可以方便地在远程服务和本地服务间切换。(10)简化访问数据库时的例外处理。(11)Spring能使用AOP提供声明性事务管理,可以不直接操作JTA也能够对事务进行管理。(12)提供了JavaMail或其他邮件系统的支持。3.什么是轻量(Lightweight)级容器?Spring的开发者可以避免使用重量级容器开发EJB时的缺点:(1)带有侵略性的API。(代码依赖于EJB)(2)对容器的依赖。(代码不能在EJB容器之外工作)(3)提供固定的一组机能,不具有配置能力。(4)不同的产品,部署过程不同,不易通用。(5)启动时间长。  针对以上问题,Spring采用了IoC使代码对Spring的依赖减少,根据Web应用,小应用程序,桌面应用程的不同,对容器的依赖程度也不同。Spring将管理的Bean作为POJO(Plain Old Java Object)进行控制,通过AOP Interceptor能够增加其它的功能。除了Spring以外的轻量级容器还有PicoContainer,(不是轻量级容器)对Bean的生命周期进行管理的还有Apache Avalon Project的Avalon等。  总结:Spring的核心思想便是IoC和AOP,Spring本身是一个轻量级容器,和EJB容器不同,Spring的组件就是普通的Java Bean,这使得单元测试可以不再依赖容器,编写更加容易。Spring负责管理所有的Java Bean组件,同样支持声明式的事务管理。我们只需要编写好Java Bean组件,然后将它们"装配"起来就可以了,组件的初始化和管理均由Spring完成,只需在配置文件中声明即可。这种方式最大的优点是各组件的耦合极为松散,并且无需我们自己实现Singleton模式。
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隔离降低了程序的耦合性使我们可以在不同的应用中将各个切面结合起来使用大大提高了代码重用度
当模块化到一定地步了,spring就有很大的做了,这个时候基本上新的业务就是配置spring就可以搞定了,基本上不需要写代码了。反过来如果基本上没有模块化,就非常累,一边写代码一边还要去写配置。最简单的就是如果你把做菜的每个步骤都分清楚了,洗菜,配料,切菜,烧火,整个流程每一个细节都实现了,那么要回锅肉就写一个回锅肉的配置,要青椒肉丝就写一个青椒肉丝的配置,当然这个是建立在你流程中每一个节点(模块)都全部实现了,想切菜,你要有切丝,切片,切块等诸多实现模式,这个时候你用spring的配置优势才会体现。
如果是那种典型单一炒菜方式,为一道菜而炒菜,spring反而会多此一举。spring就是依赖注入的一个解决方案,依赖注入我觉得主要是面向接口的,想要达到的效果就跟U盘是的,插到电脑上就能用,拔掉就不性用。减小了耦合度
发表评论:
TA的最新馆藏[转]&[转]&[转]&微信扫一扫,分享给好友吧。Spring(12)
java(19)
我编写或设计的软件通常需要部署在不同的环境,也需要使用不同的部署配置。这些部署配置大致可以分为以下几类:
本文暂不对GUI应用程序进行讨论,留到后续再说。对于其他的类型(容器、独立应用程序和测试框架)来说,代码往往是相同的。因此,在设计或者编码这类软件的时候,这一点非常关键。对于每种场景来说,我所写的代码需要完美运行在每一种场景中。
这是程序合格并且健壮的关键!这里的问题是,有一些资源的配置是与代码的运行平台息息相关的。在我编写单元测试的时候,我无法(其实我能,这么说是为了让你领会文章的主题)获得绑定到JNDI树上的数据源。而在容器中,我只需要遍历JNDI树,询问是否可以获取数据源即可。
另外,想Spring这样的框架也是鼓励这样的开发模式的,至少控制反转挺受欢迎。不同于直接用代码配置数据库和队列,Spring框架是在运行时注入这些对象的,生活依然美好。
你以为我的博客会把网上到处都是控制反转的示例代码再罗列一遍么?当然不是。看下面的代码:
public class BusinessClazz implements SomethingReallyImportant {
private DataSource dataS
public void setDataSource(DataSource dataSource) {
this.dataSource = dataS
}数据源是通过运行时注入获得的,BusinessClazz对数据源本身一无所知。我不是世上最聪明的人,但也肯定不是最笨的人。我是说,我曾经读过像《J2ee Development Without Ejb》和《Expert
one-on-one J2EE design and development》这类的书,并且自以为已经理解了其中的内容。但这些都是给妹子们看的,不是么?开个玩笑。我在Spring中间了一个数据源实例,并注入到BusinessClazz实例中。
&bean name=&myBusinessClazz&&
&property name=&dataSource& ref=&dataSource&/&
现在,我的可以完全不考虑数据源的具体实现,仍能在所有的部署环境中运行。但对于数据源呢,应该如何配置才能保证“一次配置,到处运行”?我们将注重对数据源的配置做说明,这个例子也是用于那些因运行环境或运行时不同而需要做修改的组件。
一般情况下,数据源需要两部分配置信息。第一部分是关于数据库位于何处,以及如何连接该数据库的信息。这里需要的信息包括主机名、款口端口号、服务名等。
第二部分是关于如何展示描述配置信息的。下面是一些可选方案。在其中,我建立了一个到数据库的连接,
&bean id=&dataSource& destroy-method=&close&
class=&mons.dbcp.BasicDataSource&&
&property name=&driverClassName& value=&org.hsqldb.jdbcDriver&/&
&property name=&url& value=&jdbc:hsqldb:hsql://localhost&/&
&property name=&username& value=&sa&/&
&property name=&password& value=&&/&
&/bean&也可以使用Apache的commons-dbcp库创建一个连接池,
&bean id=&dataSource&
class=&mons.dbcp.BasicDataSource& destroy-method=&close&&
&property name=&driverClassName& value=&net.sourceforge.jtds.jdbc.Driver&/&
&property name=&url& value=&jdbc:myserver&/&
&property name=&username& value=&username&/&
&property name=&password& value=&password&/&
&property name=&initialSize& value=&2&/&
&property name=&maxActive& value=&5&/&
&property name=&maxIdle& value=&2&/&
&/bean&还可以使用容器来管理连接,再从容器中获取连接,
&jee:jndi-lookup id=&dataSource& jndi-name=&java:mydatasource&/&
当然,还有其他许多方法可以配置数据源,但为了说明问题,上面的示例已经够用了。Spring框架的类可以用来处理随运行环境而变化的属性,使用简单,功能强大。
现在的问题是,如何在不同的部署环境中无缝切换,并选择不同类型的数据源呢?
本文所说的问题并不起最近才有的,在过去的6、7年中,许多部署团队想出了各种不同的方法来解决它。每个团队都针对其特定的问题,采取了适合自身条件的方法来解决,他们会在代码中加入一些自适应的判断来简化问题。
这里,希望代码可以“一次编写,到处运行”的最大驱动力之一,就是可以开发人员编写用于测试生还 生产环境代码的单元测试。
对这个问题的一个典型的示例方案是将数据源定义在不同上下文文件中,这些文件需要按照统一的命名约定来命名,以免造成混乱。例如,假设我们有3个操作模式,所以需要建3个不同的上下文文件,
接下来就是平台相关的代码了,会在你的应用程序初始化Spring的时候执行。它通过Ant式的通配符跨越部署的类路径来查找上下文文件。
ClassPathXmlApplicationContext containerContext =
new ClassPathXmlApplicationContext(&**/**-containerContext.xml&);
ClassPathXmlApplicationContext nonContainerContext =
new ClassPathXmlApplicationContext(&**/**-pooledContext.xml&);
ClassPathXmlApplicationContext testingContextContext =
new ClassPathXmlApplicationContext(&**/**-singleContext.xml&);
好了,在刚刚过去的几个小时里,你已经建立一个可以在多种环境中运行的系统,所编写的代码也充分利用这个特点。这个办法你已经用了好几年了,但是,这个办法的主要缺点是,每个团队、每个项目都会有自己的方式来解决前面提到的问题。
使用Spring Profile
Spring 3.1为这个问题提供了一个解决方案(如果你还没有为自己的项目升级Spring版本,嗯,你麻烦大了)。
Spring在容器中引入Environment和Profile的概念。每个应用程序上下文都有一个都可以访Environment对象。
ClassPathXmlApplicationContext classPathXmlApplicationContext =
new ClassPathXmlApplicationContext();
ConfigurableEnvironment configurableEnvironment =
classPathXmlApplicationContext.getEnvironment();
每种运行环境都有很多活动Profile类可供使用。大多数讲解Spring Profile的例子都是在开发模式或生产模式下。对于不同运行环境问题来说,我的解决方案是使用使用多个Profile来适应不同运行时。这个解决方案的优势是你可以自行决定如何使用Profile。
默认星空情况下,你所创建的Bean在载入容器中后是没有Profile对象的。下面看一个例子。假设下面是我的应用程序中,数据源实例的定义。
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&…&&
&bean id=&dataSource& destroy-method=&close&
class=&mons.dbcp.BasicDataSource&&
&property name=&driverClassName& value=&org.hsqldb.jdbcDriver&/&
&property name=&url& value=&jdbc:hsqldb:hsql://localhost&/&
&property name=&username& value=&sa&/&
&property name=&password& value=&&/&
在Spring 3.0中,增加了一个新的容器类&,可以作为ClassPathXmlApplicationContext和FileSystemXmlApplicationContext之外的另一个选择。
GenericXmlApplicationContext类的特点是可以通过Setter方法完成所有的配置,而无需依靠笨重的构造器去完成配置。记住,在初始化容器的准备工作完成后,需要调用refresh()方法完成实际的初始化工作。
下面的代码展示了如何使用GenericXmlApplicationContext类初始化容器:
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.getEnvironment().setActiveProfiles(&standalone&);
ctx.load(&*Context.xml&);
ctx.refresh();这里,我将活动Profile设置为“standalone”。在这个工程里,我希望代码既可以作为“standalone”运行在应用程序容器之外,还可以作为“container”运行在容器中。这里,我可以设置多个Profile,例如,下面的代码设置了Profile为“standalone”与“activemq”。
ctx.getEnvironment().setActiveProfiles(&standalone&, &activemq&);虽然做了上面的配置,实际上并不会对当前的配置上下文产生影响,因为还没有配置Profile实例。所以,修改配置上下文为:
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&…& profile=&standalone&&
&bean id=&dataSource& destroy-method=&close&
class=&mons.dbcp.BasicDataSource&&
&property name=&driverClassName& value=&org.hsqldb.jdbcDriver&/&
&property name=&url& value=&jdbc:hsqldb:hsql://localhost&/&
&property name=&username& value=&sa&/&
&property name=&password& value=&&/&
只有当活动Profile设置为“standalone”时,才会实例化这个Bean。Profile是Bean的属性,而不是一个实例对象,因此,你无法配置单独的Bean来选择Profile。在较早的Spring版本中,这会导致产生多个文件,而Ant的通配符无法在运行时找到正确的配置文件。
在Spring 3.1中,&beans/&标签可以嵌套在&beans/&标签内。现在,我们重新编写一下数据源配置文件:
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&…&&
&beans profile=&standalone&&
&bean id=&dataSource&&
&property name=&driverClassName& value=&org.hsqldb.jdbcDriver&/&
&property name=&url& value=&jdbc:hsqldb:hsql://localhost&/&
&property name=&username& value=&sa&/&
&property name=&password& value=&&/&
&beans profile=&container&&
&jee:jndi-lookup id=&dataSource& jndi-name=&java:mydatasource&/&
&/beans&这样,就可以通过下面的代码快速切换Profile:
ctx.getEnvironment().setActiveProfiles(&container&);另一种切换Profile的方法是在运行时作为系统参数传入:
-Dspring.profiles.active=&standalone&此外,也可以作为Ear/War的初始化参数传入:
&servlet-name&dispatcher&/servlet-name&
&servlet-class&
org.springframework.web.servlet.DispatcherServlet
&/servlet-class&
&init-param&
&param-name&spring.profiles.active&/param-name&
&param-value&production&/param-value&
&/init-param&
&/servlet&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:53882次
排名:千里之外
原创:35篇
转载:22篇
(1)(1)(1)(1)(1)(1)(1)(14)(2)(3)(27)(1)(2)(1)(1)<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&1.1 简化Java 开发
1.1 简化Java开发
Spring是一个开源框架,最早由Rod Johnson创建,并在《Expert One-on-One:J2EE Design and Development》这本著作中进行了介绍。Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但Spring不仅仅局限于服务器端开发,任何Java应用都能在简单性、可测试性和松耦合等方面从Spring中获益。
任何名称的Bean
虽然Spring使用Bean或者JavaBean来表示应用组件,但这并不意味着Spring组件必须遵循JavaBean规范。一个Spring组件可以是任何形式的POJO。在本书中,我采用JavaBean的广泛定义,即POJO的同义词。
纵览全书,你会发现Spring 可以做很多事情。但归根结底,支撑Spring的仅仅是少许的基本理念,所有的理念都可以追溯到Spring最根本的使命上:简化Java开发。
这是一个郑重的承诺。许多框架都声称在某些方面做了简化,但Spring的目标致力于全方位的简化Java开发。这势必引出更多的解释,Spring是如何简化Java开发的呢?
为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
基于POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码。
几乎Spring所做的任何事情都可以追溯到上述的一条或多条核心策略。在本章的其他部分,我将通过具体的案例进一步阐述这些理念,以此来证明Spring是如何完美兑现它的承诺的:也就是简化Java开发。让我们先从基于POJO的最小侵入性编程开始。
1.1.1 激发POJO的潜能
如果你从事Java编程有一段时间了,或许你会发现很多框架通过强迫应用继承它们的类或实现它们的接口从而让应用跟框架绑死。一个典型的例子是EJB 2的无状态会话Bean。即使像程序清单1.1中的一个简单的HelloWorldBean类,EJB 2规范都可以把它变得相当臃肿。
程序清单1.1 EJB 2.1强迫实现你根本不需要的方法
SessionBean接口允许你实现若干个生命周期回调方法以便参与到EJB的生命周期内。或许我应该换种说法,SessionBean接口强迫你参与EJB的生命周期,即使你根本不想这么做。HelloWorldBean的大部分代码仅仅是为使用EJB而编写的。这引发出一个问题:到底是谁为谁服务?
EJB 2并不是特例,其他流行框架也是如此,例如早期版本的Struts、WebWork和Tapestry。这些重量级框架都存在如下问题:强迫开发者编写大量冗余代码、应用与框架绑定,并且通常难以编写测试代码。
Spring竭力避免因自身的API而弄乱你的应用代码。Spring不会强迫你实现Spring规范的接口或继承Spring规范的类,相反,在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring。最坏的场景是,一个类或许会使用Spring注解,但它依旧是POJO。
不妨举个例子,我们采用Spring技术把程序清单1.1的HelloWorldBean类进行重写,它看起来可能是这样的。
程序清单1.2 Spring不会在HelloWorldBean上有不合理的要求
是不是看起来好多了?所有乱糟糟的生命周期方法全消失了。HelloWorldBean类没有实现、继承或者导入与Spring API相关的任何东西。HelloWorldBean只是一个普通的Java对象。
尽管形式简单,但POJO一样可以具有魔力。Spring赋予POJO魔力的方式之一就是通过依赖注入来装配它们。让我们看看依赖注入是如何帮助应用对象彼此之间保持松散耦合的。
1.1.2 依赖注入
依赖注入这个词让人望而生畏,现在已经演变成一项复杂编程技巧或设计模式理念。但事实证明,依赖注入并不像它听上去那么复杂。在项目中应用依赖注入,你会发现代码会变得异常简单、更容易理解和更易于测试。
任何一个有实际意义的应用(肯定比HelloWorld示例更复杂)都是由两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。通常,每个对象负责管理与自己相互协作的对象(即它所依赖的对象)的引用,这将会导致高度耦合和难以测试的代码。
例如,考虑程序清单1.3所展现的Knight类。
程序清单1.3 DamselRescuingKnight只能执行RescueDamselQuest探险任务
正如你所见,DamselRescuingKnight在它的构造函数中自行创建了RescueDamselQuest。这使得DamselRescuingKnight紧密地与RescueDamselQuest耦合到了一起,因此极大地限制了这个骑士执行探险的能力。如果一个少女需要救援,这个骑士能够召之即来。但是如果一条恶龙需要杀掉,或者一个圆桌……呃……需要滚起来,那么这个骑士只能爱莫能助了。
更糟糕的是,为这个DamselRescuingKnight编写单元测试将出奇地困难。在这样的一个测试中,你必须保证当骑士的embarkOnQuest()方法被调用的时候,探险的embark()方法也要被调用。但是没有一个简单明了的方式能够实现这一点。遗憾的是,DamselRescuingKnight将无法测试。
耦合具有两面性(two-headed beast)。一方面,紧密耦合的代码难以测试,难以复用,难以理解,并且典型地表现出“打地鼠”式的bug特性(修复一个bug,导致出现一个新的或者甚至更多的bug)。另一方面,一定程度的耦合又是必须的——完全没有耦合的代码什么也做不了。为了完成有实际意义的功能,不同的类必须以适当的方式进行交互。总而言之,耦合是必须的,但应当小心谨慎地管理它。
另一种方式,通过依赖注入(DI),对象的依赖关系将由负责协调系统中各个对象的第三方组件在创建对象时设定。对象无需自行创建或管理它们的依赖关系——依赖关系将被自动注入到需要它们的对象中去。
为了展示这一点,让我们看一看程序清单1.4中的BraveKnight,这个骑士不仅勇敢,而且能挑战任何形式的探险。
程序清单1.4 BraveKnight足够灵活可以接受任何赋予他的探险
正像你看到的那样,不同于之前的DamselRescuingKnight,BraveKnight没有自行创建探险任务,而是在构造时把探险任务作为构造器参数传入。这是依赖注入的方式之一,即构造器注入。
更重要的是,它被传入的探险类型是Quest,也就是一个所有探险任务都必须实现的接口。所以,BraveKnight能够响应RescueDamselQuest、SlayDragonQuest、MakeRoundTableRounderQuest等任意一种Quest实现。
这里的要点是BraveKnight没有与任何特定的Quest实现发生耦合。对它来说,被要求挑战的探险任务只要实现了Quest接口,那么具体是哪一类型的探险就无关紧要了。这就是依赖注入最大的好处——松耦合。如果一个对象只通过接口(而不是具体实现或初始化的过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。
对依赖进行替换的最常用的方法之一,就是在测试的时候使用mock实现。你无法充分测试DamselRescuingKnight,因为它是紧耦合的;但是你可以轻松地测试BraveKnight,只需给它一个Quest的mock实现,如程序清单1.5所示。
程序清单1.5 为了测试BraveKnight,需要注入一个mock Quest
你可以使用mock对象框架Mockito去创建一个Quest接口的mock实现。通过现有的mock对象,你创建一个新的BraveKnight实例,通过构造器注入这个mock Quest。当调用embarkOnQuest()方法时,你可以要求Mockito框架验证Quest的mock实现的embark()方法仅仅被调用了一次。
图1.1 依赖注入
注入一个Quest到Knight
现在BraveKnight类可以接受你传递给它的任意一种Quest的实现,但你如何把特定的Query实现传给它呢?
创建应用组件之间协作的行为通常称为装配。Spring有多种装配Bean的方式,采用XML配置通常是最常见的装配方式。程序清单1.6展现了一个简单的Spring配置文件:knights.xml,该配置文件让BraveKnight接受了一个SlayDragonQuest探险任务。
程序清单1.6 使用Spring将SlayDragonQuest注入到BraveKnight中
这是Spring装配Bean的一种简单方式。谨记现在不要过多关注细节。第2章我们会深入了解Spring配置文件并一探究竟,同时我们还会了解Spring装配Bean的其他方式。
现在已经声明了BraveKnight和Quest的关系,你只需要装载XML配置文件,并把应用启动起来。
观察它如何工作
Spring通过应用上下文(Application Context)装载Bean的定义并把它们组装起来。Spring应用上下文全权负责对象的创建和组装。Spring自带了几种应用上下文的实现,它们之间主要的区别仅仅是如何加载它们的配置。
因为knights.xml中的Bean是在XML文件中声明的,所以选择ClassPathXmlApplicationContext作为应用上下文是相对比较适合的。该类加载位于应用系统classpatch下的一个或多个XML文件。程序清单1.7中的main()方法调用ClassPathXmlApplicationContext加载knights.xml,并获得Knight对象的引用。
程序清单1.7 KnightMain.java加载包含Knight的Spring上下文
这里的main()方法创建了一个Spring应用上下文,该应用上下文加载了knights.xml文件。随后它调用该应用上下文获取一个ID为knight的Bean。得到Knight对象的引用后,只需简单调用embarkOnQuest()方法就可以执行所赋予的探险任务了。注意这个类完全不知道我们的英雄骑士接受哪种探险任务,而且完全没有意识到这是由BraveKnight来执行的。只有knights.xml文件知道哪个骑士执行哪种探险任务。
通过示例我们对依赖注入进行了一个快速介绍。纵览全书,你将对依赖注入有更多的认识。如果你想了解更多关于依赖注入的信息,我推荐阅读Dhanji R. Prasanna的《Dependency Injection》,该著作涵盖了依赖注入的所有内容。
现在我们来关注Spring简化Java开发的下一个理念:基于切面进行声明式编程。
1.1.3 应用切面
依赖注入让相互协作的软件组件保持松散耦合,而AOP编程允许你把遍布应用各处的功能分离出来形成可重用的组件。
面向切面编程往往被定义为促使应用程序分离关注点的一项技术。系统由许多不同组件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外的职责。诸如日志、事务管理和安全此类的系统服务经常融入到有自身核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们总是跨越系统的多个组件。
如果将这些关注点分散到多个组件中去,你的代码将引入双重复杂性:
遍布系统的关注点实现代码将会重复出现在多个组件中。这意味着如果你要改变这些关注点的逻辑,你必须修改各个模块的相关实现。即使你把这些关注点抽象为一个独立的模块,其他模块只是调用它的方法,但方法的调用还是重复出现在各个模块中;
你的组件会因为那些与自身核心业务无关的代码而变得混乱。一个向地址簿增加地址条目的方法应该只关注如何添加地址,而不应该关注它是不是安全的或者是否需要支持事务。
图1.2展示了这种复杂性。左边的业务对象与系统级服务结合的过于紧密。每个对象不但要知道它需要记日志、进行安全控制和参与事务,还要亲自执行这些服务。
图1.2 对遍布系统的关注点服务(例如日志和安全)的调用经常散布到各个组件中,而这些关注点并不是组件的核心业务
AOP使这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。结果是这些组件具有更高内聚性以及更加关注自身业务,完全不需要了解可能涉及的系统服务的复杂性。总之,AOP确保POJO保持简单。
如图1.3所示,我们可以把切面想象为覆盖在很多组件之上的一个外壳。应用是由那些实现各自业务功能的模块组成。利用AOP,你可以使用各种功能层去包裹核心业务层。这些层以声明的方式灵活应用到你的系统中,甚至你的核心应用根本不知道它们的存在。这是一个非常强大的理念,可以将安全、事务和日志关注点与你的核心业务逻辑相分离。
图1.3 利用AOP,遍布系统的关注点覆盖在它们所影响的组件之上,促使应用组件只需关注它们的核心业务功能
为了示范在Spring中如何应用切面,让我们重新回到骑士的例子,并为它添加一个切面。
每一个人都熟知骑士的任何事情,这是因为吟游诗人用诗歌记载了骑士的事迹并将其进行传诵。假设你需要使用吟游诗人这个服务类来记载骑士的所有事迹。程序清单1.8展示了Minstrel(吟游诗人)这个类。
程序清单1.8 Minstrel是中世纪的音乐记录器
正如你所看到的那样,Minstrel是一个只有两个方法的简单的类。在骑士执行每一个探险任务之前,singBeforeQuest()方法被调用;在骑士完成探险任务之后,singAfterQuest()方法被调用。把它加入你的代码并能正常工作,这对你来说是小事一桩。让我们做适当的调整来让BraveKnight可以使用Minstrel。程序清单1.9展示了第一次尝试。
程序清单1.9 BraveKnight必须调用Minstrel方法
这应该可以达到预期效果,但总感觉有些东西不太对。管理他的吟游诗人真的是骑士职责内的工作吗?在我看来,吟游诗人应该做他份内的事,根本不需要骑士命令他这么做。毕竟,用诗歌记载骑士的探险事迹,这是吟游诗人的职责。为什么骑士还需要提醒吟游诗人去做他份内的事情呢?
此外,因为骑士需要知道吟游诗人,你就必须把吟游诗人注入到BarveKnight类中。这不仅使BraveKnight的代码复杂化了,而且还让我疑惑你是否还需要一个不需要吟游诗人的骑士呢!如果Minstrel为null会发生什么呢?我是否应该引入一个空值校验逻辑来覆盖该场景?
简单的BraveKnight类开始变的复杂,如果你还需要应对不需要吟游诗人的场景,那代码会变得更复杂。但利用AOP,可以声明吟游诗人必须歌颂骑士的探险事迹,而骑士就不再直接访问吟游诗人的方法了。
把Minstrel抽象为一个切面,你所做的事情只是在一个Spring配置文件中声明它。程序清单1.10是更新后的knights.xml文件,Minstrel被声明为一个切面。
程序清单1.10 Minstrel被声明为一个切面
这里使用了Spring的AOP配置的命名空间把Minstrel Bean声明为一个切面。首先,必须把Minstrel声明为一个Bean,然后在&aop:aspect&元素中引用该Bean。为了进一步定义切面,必须使用&aop:before&来声明在embarkOnQuest()方法执行前调用Minstrel的singBeforeQuest()方法。这种方式被称为前置通知。同时还必须使用&aop:after&声明在embarkOnQuest()方法执行后调用singAfterQuest()方法。这种方式被称为后置通知。
在这两种方式中,pointcut-ref属性都引用了名为embank的切入点。该切入点是在前边的&pointcut&元素中定义的,并配置expression属性来选择所应用的通知。表达式的语法采用了AspectJ的切点表达式语言。
你无需担心你不了解AspectJ或者编写AspectJ切点表达式语言的细节,我们稍后会在第4章详细探讨Spring AOP的内容。现在你已经可以理解如何让Spring在骑士执行探险任务前后来调用Minstrel的singBeforeQuest()和singAfterQuest()方法。
这就是所做的一切!通过少量的XML配置,你就可以把Minstrel声明为一个Spring切面。如果你现在还没有完全理解,不必担心,在第4章你会看到更多的Spring AOP示例,那将会帮助你弄清楚。现在我们可以从这个示例中获得两个重要的观点。
首先,Minstrel仍然是一个POJO,没有任何代码表明它要被作为一个切面使用。当我们按照上面那样配置后,在Spring的上下文中,Minstrel实际上已经变成一个切面了。
其次,也是最重要的,Minstrel可以被应用到BraveKnight中,而BraveKnight不需要显式地调用它。实际上,BraveKnight完全不知道Minstrel的存在。
我还必须指出的是,尽管你使用了Spring的魔法把Minstrel转变为一个切面,但你首先要把它声明为一个Spring&bean&。我的观点是可以利用Spring AOP为Spring Bean做任何事情,例如为Spring Bean注入依赖。
应用切面来歌颂骑士可能有点好笑,但是Spring AOP可以做很多有实际意义的事情。在后续章节你还会了解基于Spring AOP实现声明式事务(第6章)和安全(第9章)。
但是现在,让我们再看看 Spring简化Java开发的其他思想。
1.1.4 使用模板消除样板式代码
你是否写过这样的代码,编写时总会感觉以前写过相同的代码?我的朋友,这不是似曾相识。这是样板式的代码。你经常不得不重复编写这样的代码,只是为了实现通用的和简单的任务。
遗憾的是,它们中的很多是因为使用Java API而导致的样板式代码。一个常见的样板式代码范例是使用JDBC访问数据库查询数据。例如,如果你曾经用过JDBC,你或许会写出类似程序清单1.1所示的代码。
程序清单1.11 许多Java API,例如JDBC,会涉及编写大量的样板式代码
正如你所看到的,这段JDBC代码查询数据库获得员工姓名和薪水。我打赌你很难把上面的代码逐行看完,这是因为少量的核心查询代码淹没在一堆JDBC的样板式代码中。首先你不得不创建一个数据库连接,然后再创建一个语句(Statement)对象,最后才能进行查询。为了预防资源泄漏,你必须捕捉SQL异常——一个检查型异常,即使它抛出后你也做不了太多事情。
最后,毕竟该说的也说了,该做的也做了,你不得不收拾残局,关闭数据库连接、语句和结果集。同样为了解决JDBC的问题,你还要捕捉SQLException。
程序清单1.11中的代码和你实现其他JDBC操作时所写的代码几乎是相同。只有少量的代码与查询员工逻辑有关系,其他的代码都是JDBC的样板式代码。
并不是只有JDBC才会产生样板式代码。在许多编程中往往都会导致类似的样板式代码,JMS、JNDI和使用REST服务通常也会涉及大量的重复代码。
Spring旨在通过模板封装来消除样板式代码。Spring的JdbcTemplate使得在执行数据库操作时,避免传统的JDBC样板式代码成为了可能。
例如,使用Spring的SimpleJdbcTemplate(利用了 Java 5特性的JdbcTemplate的实现),重写的getEmployeeById()方法仅仅关注于获取员工数据的核心逻辑,而不需要迎合JDBC API的需求。下面的程序清单1.12展示了修订后的getEmployeeById()方法。
程序清单1.12 模板让你的代码关注于自身职责
正如你所看到的,新版本的getEmployeeById()简单多了,而且仅仅关注于从数据库中查询员工。模板的queryForObject()方法需要一个SQL查询语句,一个RowMapper对象(把数据映射为一个域对象),零个或多个查询参数。getEmployeeById()方法再也看不到以前的JDBC样板式代码了,它们全部被封装到了模板中。
这里已经向你展示了Spring通过面向POJO编程、依赖注入、AOP和模板技术来简化Java开发的复杂性。我向你展示了在基于XML的配置文件中配置Bean和切面,但这些文件是如何加载的呢?它们被加载到哪里去了?让我们再了解下Spring容器,应用中的所有Bean所驻留的地方。
第一部分 Spring 的核心
第3章 最小化Spring XML 配置
第4章 面向切面的Spring
第二部分 Spring 应用程序的核心组件
第5章 征服数据库
第6章 事务管理
第7章 使用Spring MVC 构建
第8章 使用Spring Web Flow
第9章 保护Spring 应用
第三部分 Spring 集成
第10章 使用远程服务
第11章 为Spring 添加REST 功能
第12章 Spring 消息
第13章 使用JMX 管理 Spring Bean
第14章 其他Spring 技巧
微信公众号:异步社区
京ICP备号-6 ·

我要回帖

更多关于 maven spring profile 的文章

 

随机推荐