Spring为了简化Java开发而产生,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情,在简单性,可测试性和松耦合等方面从Spring获益。
一、Spring是怎么简化java开发的??
1、 基于POJO的轻量级和最小侵入性变成
2、通过依赖注入和面向接口实现松耦合
3、基于切面和惯例进行声明式编程
4、通过切面和模板减少样板式代码
1、Spring不会在HelloWorld上导入乱糟糟的方法。
2、依赖注入Dependency Injection(DI)
- 为什么要用DI
因为应用之间是通过很多组件和类的相互协作来完成的。通常,每个对象负责管理与自己相互协作的对象的引用,这样会导致高度耦合和难以测试的代码。
也就是相互调用太多,代码看起来太复杂。当一个组件需要修改的时候,所有涉及到它的其他组件都要进行修改。
这里讲了依赖注入的方式之一,构造器注入。
public class DamselRescuingKnight implements Knight{
private RescueDamselQuest quest;
public DamselRescuingKnight(){
quest = new RescueDamselQuest();
}
public void embarkOnQuest() throws QuestException{
quest.embark();
}
}
- DamselRescuingKnight在他的构造函数中自行创建了RescueDamselQuest。所以骑士就和这个任务紧密的结合在一起了。而且难以测试embark()是否在embarkQuest()方法被调用的时候执行。所以这种写法很不好。
- 所以我们采用依赖注入。对象的依赖关系将由负责协调系统中各个对象的第三方组件在创建对象时设定。对象无需自行创建或者管理它们的依赖关系。依赖关系将被自动注入到需要它们的对象中去。
public class BraveKnight implements Knight{
private Quest quest;
public BraveKnight(Quest quest){
this.quest = quest; -->Quest被注入进来
}
public void embarkOnQuest() throws QuestException{
quest.embark();
}
}
- 这里没有自行创建任务。而是在构造时把任务当做构造器参数传入,即构造注入
- 而 且Quest是所有任务的接口。可以实现任意一种任务。重要的是骑士没有与任何一个特定的任务发生耦合。
- 这样就实现了松耦合。
- 如果一个对象只通过接口(而不是具体化实现或者初始化的过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。也就是改实现不用换接口。
- 可以采用mock测试
然后可以用Spring将SlayDragonQuest通过XML文件注入到BraveKnight当中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://www.springframework.org/schema/cache
default-lazy-init="true">
<bean id="knight" class = "com.springinaction.knights.BraveKnight">
<constructor-arg ref="quest"></constructor-arg> -->通过Constructor-arg来告诉Spring额外的信息
</bean>
<bean id="quest"
class="com.springinaction.knight.SlayDragonQusest">
</bean>
</beans>
Spring通过Application Context应用上下文装载Bean
Spring应用上下文全权负责对象的创建和组装。
-
Spring自带了几种应用上下文的实现,它们之间的主要区别是如何加载它们的配置
public class KnightMain{ public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("knight.xml"); -->加载Spring上下文 Knight knight = (Knight) context.getBean("knight"); -->获取knight Bean knight.embarkOnQuest(); -->使用knight } }
这里的main()方法创建了一个Spring上下文,该上下文加载了knight.xml文件。随后它调用该上下文获取一个ID为knight的Bean。得到Knight的对象之后,只需要调用embarkOnQuest()方法就可以执行所赋予的任务。
3、应用切面 (AOP Aspect Oriented Programming)
允许你把遍布应用程序各处的功能分离出来形成可重用的组件。
- 为什么要用AOP?
因为系统由许多不同的组件组成,每个组件负责一块特定的职责。有些系统服务跨越多个组件,被称为横切关注点。
如果将这些服务分散到各个组件中,代码将会变得重复而混乱。每个模块都要调用这些服务
所以AOP使这些服务模块化,并以声明的方式将他们应用到需要影响的组件中去。将他们想象成一个切面,覆盖了多个组件。使这些组件更关注自身业务而不需要了解系统服务
- 如何实现?
在Spring配置文件中声明它。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://www.springframework.org/schema/cache
default-lazy-init="true">
<bean id="knight" class = "com.springinaction.knights.BraveKnight">
<constructor-arg ref="quest"></constructor-arg>
</bean>
<bean id="quest"
class="com.springinaction.knight.SlayDragonQusest">
</bean>
<bean id="minstrel" class="com.springinaction.knights.Minstrel"> -->声明Minstrel Bean
</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>
</beans>
使用AOP配置的命名空间把Minstrel Bean声明为一个切面:
- 先声明Minstrel为一个Bean,然后在<aop:aspect>元素中引用该bean
这样就不用在knight中每次调用Minstrel了
4、使用模板消除样板式代码:封装
比如使用JDBC的时候,样板式代码是核心业务代码的好几倍,这时候用Spring就简洁的多
Spring通过模板封装来消除样板式代码。比如JDBCTemplate就可以简化原来的代码,因为都被封装了。
二、Bean的容器和生命周期
在基于Spring的应用中,应用对象生存于Spring的容器中。
容器是Spring框架的核心。有两种类型
- Bean工厂(Bean Factories)
- 应用上下文(Application Context)基于BeanFactory之上构建,并提供面向应用的服务。最常用
三种最常见的应用上下文
1、ClassPathXmlApplicationContext
从类路径下的XML配置文件中加载上下文定义,把应用上下文定义文件当做资源
2、FileSystemXmlApplicationContext
读取文件系统下的XML配置文件并加载上下文定义
3、XmlWebApplicationContext
读取web应用下的XML配置文件并加载上下文定义
通过现有的应用上下文引用,可以调用应用上下文的getBean()方法从Spring容器中获取Bean