Spring in Action——Spring之旅

根本使命

Spring的目标是致力于全方位的简化java开发:

  • 基于POJO的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模版减少样板式代码

POJO
Spring竭力避免因为自身API而弄乱你的应用代码。Spring不会强迫你实现Spring规范的接口或继承Spring规范的类,相反,在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring

依赖注入DI

public class DamseRescuingKnight implements Knight{
	private RescueDamseQuest quest;
	public DamseRescuingKnight(){
		this.quest=new RescueDamseQuest;
	}
	public void embarkOnQuest(){
		quest.embark();
	}
}

可以看到,它的构造函数自己创建了一个RescueDamseQuest,这使得耦合度太高。并且使得测试异常困难,在这样也测试中,你必须保证当骑士的embarkOnQuest()方法被调用的时候,探险的embark()方法也要被调用,但是没有一个简单明了的方式能够实现这一点。很遗憾,DamseRescuingKnight将无法进行测试。

那么实现了DI之后,如下:

public class BraveKnight implements Knight{
	private Quest quest;//变成了Quest,而不是它的实现(比如RescueDamseQuest或者SlayDragonQuest)
	public BraveKnight(Quest quest){//构造方法注入
		this.quest=quest;
	}
	public void embarkOnQuest(){
		quest.embark();
	}
}

这里的要点是BraveKnight 没有和 任何 特定的 Quest发生耦合,对他来说,只要是实现了Quest接口的探险任务就可以,具体是哪种无关紧要。
对依赖进行替换的一个常用方法就是在测试的时候使用mock实现,在使用DI后,我们可以轻松的测试BraveKnight:

public class BraveKnightTest{
	@Test
	public void knightShouldEmbarkOnQuest(){
		Quest mockQuest=mock(Quest.class);
		BraveKnight knight=new BraveKnight(mockQuest);//注入mockQuest
		knight.embarkOnQuest();
		verify(mockQuest,times(1).embark();)
	}
}

使用XML装配:

<beans>
	<bean id="knight" class="..BraveKnight">
		<constructor-arg ref="quest"/>
	</bean>
	<bean id="quest" class="..SlayDragonQuest"></bean>
</beans>

如果XML配置不符合你的喜好,那么还可以使用java:

@Configuration
public class KnightConfig{
	@Bean
	public Knight knight(){
		return new BraveKnight(quest());
	}
	@Bean
	public Quest quest(){
	return new SlayDragonQuest(System.out);
	}
}

Spring通过应用上下文(Application Context)装在bean的定义并且把它们组装起来。因为knights.xml中的bean是使用XML文件进行配置的,所以选择ClassPathXmlApplicationContext作为应用上下文相对是比较核实的。该类加载位于应用程序路径下的一个或多个XML配置文件:

public class KnightMain{
	public static void main(String[] args)throws Exception{
		ClassPathXmlApplicaitionContext context=new ClassPathXmlApplicationContext("META-INF/spring/knights.xml");
		Knight knight=context.getBean(Knight.class);//获取Knight bean
		knight.embarkOnQuest();//使用kngiht
		context.close();
	}
}

AOP
AOP使得诸如事务管理、安全模块、日志模块等服务模块化(因为如果不使用AOP,他们到处存在于java代码中)。所以可以使得组件会高内聚,并且更加关注自身的业务,而不需要关注系统服务而带来复杂性。
也就是AOP是的POJO简单。
为了演示Spring中如何应用AOP,我们重新回到骑士的例子:
假设吟游诗人用诗歌记载了骑士的事迹(日志)。

public class Minstrel{
	private PrintStream stream;
	public Minstrel(PrintStream stream){
		this.stream=stream;
	}
	public void singBeforeQuest(){//探险前调用
		stream.println("Fa la la, the knight is so brave");}
	public void SingAfterQuest(){//探险后调用
		stream.println("Tee hee hee, the brave knight"+
		"did embark on a quest !");}
}

Minstrel会通过一个PrintStream类来歌颂骑士的事迹,分别在探险前后都会歌颂。下面的程序尝试了吟游诗人和骑士的第一次组合:

public class BraveKnight implements Knight{
	private Quest quest;
	private Minstrel minstrel;
	public BraveKnight(Quest quest,Minstrel minstrel){
		this.quest=quest;
		this.minstrel=minstrel;
	}
	public void embarkOnQuest() throws QuestException{
		minstrel.singBeforeQuest();
		quest.embark();
		minstrel.singAfterQuest();
	}
}

Knight应该管理它的Minstrel么?显然不是,它脱离了Knight的职责,此外骑士需要知道吟游诗人,所以必须把它注入到自己的类中,会使得自己更加复杂。
要将Minstrel抽象为一个切面,所需要做的事情就是在一个Spring的配置文件里声明它:

<bean id="knight" class="..BraveKnight">
	<constructor-arg ref="quest"/>
</bean>
<bean id="quest" class="..SlayDragonQuest">
	<constructor-arg value="#(T(System).out)"/>
</bean>
<!--声明Minstrel bean-->
<bean id="minstrel" class="..Minstrel">
	<constructor-arg value="#(T(System).out)"/>
</bean>
<aop:config>
	<aop:aspect ref="minstrel">
		<!--定义切点-->
		<aop:pointcut id="embark" 
			expression="execution(* *.embarkOnQuest(..))"/>
		<aop:before pointcut-ref="embark"
			method="singBeforeQuest"/>
		<aop:after pointcut-ref="embark"
			method="singAfterQuest"/>
	</aop:aspect>
</aop:config>

这里使用了aop配置命名空间把Minstrel bean声明为一个切面。首先,需要把Minstrel声明为一个bean,然后在<aop:aspect>元素中引用该bean。为了进一步定义切面,声明(使用<aop:before>)在embarkOnQuest()放啊执行前调用Minstrel的singBeforeQuest()方法,这种方式称为前置通知。同时也有后置通知。
这两种方式中,pointcut-ref属性都引用了名字为embark的切入点,该切入点实在前面的<pointcut>元素中定义的,并配置expression属性来选择所应用的通知。表达式的语法采用的是AspectJ的切点表达式语言(现在毋须了解AspectJ或者编写AspectJ的切点表达式语言的细节,之后会重复)。

使用模版消除样板式代码
举个例子,如果你曾经使用过jdbc,相信你写过如下类似的代码:

public Employee getEmployeeById(long id){
	Connection conn=null;
	PreparedStatement stmt=null;
	ResultSet rs=null;
	try{
		conn=dataSource.getConnection();
		...
	}catch(SQLException e){...}
	finally{
	if(rs!=null){try{...}catch(...){...}}
	...
	}
}

正如你所看到,少量的查询代码淹没在一堆JDBC样板式代码中,建立链接关闭链接。JDBC不是产生样板式代码的唯一场景,JMS/JNDI/REST服务等等都会产生大量的重复代码。
Spring旨在通过模版封装来消除样板式代码,举个例子,使用Spring的JdbcTemplate重写getEmployeeById()方法仅仅关注获取员工数据的核心逻辑,而不需要迎合JDBC API的需求:

public Employee getEmployeeById(long id){
	return jdbcTemplate.queryForObject{
		"select id,firstname,lastname,salary form emplyee where id=?",
		new RowMapper<Employee>(){
			public Employee mapRow(ResultSet rs,int rowNom)throws SQLExcepion{
				Employee employee=new Employee();
				employee.setId(rs.getLong("id"));
				employee.setFirstName(rs.getString("firstname"));
				...//各种set
				return employee;
			}
		},
		id);//指定查询参数
}

新版本的getEmployeeById()简单多了,而且仅仅关注于从数据库中查询员工。模版的queryForObject()方法需要一个SQL查询语句,一个RowMapper对象(把数据映射为一个域对象),零个躲着多个查询参数。

容纳你的bean

Spring容器并不是只有一个,Spring自带了多个容器实现,可以归为两种不同的类型:BeanFactory是最简单的容器,提供基本的DI支持;ApplicationContext基于BeanFactory构建,并提供应用框架级别的服务。
但是BeanFactory对于大多数应用来说往往太低级了,我们这里主要讨论ApplicationContext的使用上。
使用ApplicaitonContext
下面罗列几个最有可能遇到的应用上下文:
AnnotationConfigApplicationContext:从一个或者多个基于Java配置类中加载Spring应用上下文。
AnnotationConfigWebApplicationContext:从一个或者多个基于Java配置类中加载Spring Web应用上下文。
XmlWebApplicationContext:从Web应用下的一个或者多个XML配置文件中加载上下文定义。
ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
FileSystemXmlApplicationContext:从文件系统下的一个或者多个XML配置文件中加载上下文定义。

我们之后会提到前三种ApplicaitonContxt。我们现在先简单使用ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。无论是从文件系统还是类路径装在应用上下文,将bean加载到BeanFactory的过程是类似的,区别在于一个是从文件系统路径下查找xml文件,另一个是在所有的类路径下查找xml文件。

ApplicationContext context=new FileSystemXmlApplicationContext("C:/knight.xml");
//或者
ApplicationContext context=new ClassPathXmlApplicationContext("knight.xml");

而如果想从java配置中加载应用上下文:

ApplicationContext context=new  AnnotationConfigApplicationContext(com.springinaction.knights.config.KnightConfig.class);

bean的生命周期
在这里插入图片描述

Spring生态圈

spring模块
当我们下载Spring发布版本并查看其lib目录的时候,会发现里面有多个jar文件。在Spring4.0中,Spring框架的发布版本包括了20多个不同的模块。
这些模块根据功能可划分为6种。
在这里插入图片描述
总而言之,这些模块为企业级应用开发提供了所需的一切。
在这里插入图片描述
让我们逐一浏览Spring模块,看看它们是怎么构建起Spring生态圈的
Spring核心容器
容器是Spring框架最核心的部分,它掌管着Spring应用中bean的创建、配置和管理。包括了bean工厂——提供DI功能。基于bean工厂,我们还可以知道有Application Factory的存在。
Spring的AOP模块
AOP解耦合。
数据访问与集成
简化模版。
Web与远程调用
MVC模式是一种普遍被接受的构建Web应用的方法,它可以帮助用户将界面逻辑与应用逻辑分离。java从来不缺少MVC框架,SpringMVC我们将在后续学习到。
远程调用功能集成了RMI/Hessian/Burlap/JAX-WS等等,后面我们会学习到。
Instrumentation
Spring的Instrumentation模块提供了为JVM添加代理(agent)的功能。具体的来讲,它为Tomcat提供了一个织入代理,能够为Tomcat传递类文件,就像这些文件是被类加载器加载的一样。在本书中,我们不介绍该模块。
测试
监狱开发者自测的重要性,Spring提供了测试模块。通过该模块,Spring为使用JNDI、Servlet和Porlet编写单元测试提供了一系列mock对象实现。
TDD测试驱动开发。

Spring Portfolio

超出本书范围。暂时跳过。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我没有找到关于"Spring in Action5 chm"的引用内容。但是,我可以告诉你《Spring in Action》是一本关于使用Spring框架的书籍,它介绍了Spring框架的各个方面,包括核心概念、IoC容器、AOP、数据访问、Web开发等等。这本书可以帮助你深入理解和应用Spring框架。如果你需要更详细的信息,我建议你阅读这本书。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring数据绑定](https://blog.csdn.net/weixin_45968443/article/details/117466448)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Spring学习5——AOP](https://blog.csdn.net/doubleview/article/details/115872366)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [SpringInAction第九章学习笔记:Spring Security](https://blog.csdn.net/cairuojin/article/details/89520822)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值