Spring最根本的使命----简化Java开发。
什么是spring框架
spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,ibatis框架等组合使用。
Spring存在原因即它简化和java开发,为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
1、基于POJO的轻量级和最小侵入性编程。
2、通过依赖注入和面向接口实现松耦合。
3、基于切面和惯例进行声明式编程。
4、通过切面和模板减少样板式代码。
1、轻量级和最小侵入性的POJO
很多框架通过强迫应用继承它们的类工实现它们的接口从而让应用跟框架绑死。在Spring普及之前,常用的框架有EJB。典型的例子即,EJB2的无状态会话Bean,Java bean实现SessionBean接口,它强迫你参与EJB的生命周期。使得JavaBean很大一部分多余代码为使用EJB而编写。
使用Spring,JavaBean不需要实现、继承或者导入与Spring API相关的任何东西。Spring通过依赖注入来装配POJO,使应用对象彼此之间保持松散耦合。
2、通过依赖注入和面向接口实现松耦合
任何一个有实现意义的应用都是由两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。通常,每个对象负责管理与自己相互协作的对象的引用,这将会导致
高度耦合和难以测试的代码。
我们以骑士(Knight)执行探险任务(Quest)为例,如拯救少女,击杀恶龙.....等。
依照通常方式进行开发,代码将为如下:
/* * 拯救少女 */ public class DamselRescuingKinght { private RescuingDamselQuest quest ; //拯救少女任务 public DamselRescuingKinght() { this.quest = new RescuingDamselQuest(); } public void embarkOnQuest(){ quest.embark(); } } class RescuingDamselQuest{ public void embark(){ System.out.println("执行任务!"); } }
如上所见,DamselRescuingKinght在它的构造函数中自行创建了RescuingDamselQuest。这使得RescuingDamselQuest和DamselRescuingKinght紧密的耦合到了一起,因此极大的限制了骑士执行探险的能力。使得该骑士只能执行救援少女任务,如果要执行击杀恶龙,该骑士将无法执行。另一方面DamselRescuingKinght该类的 单元测试的代码将难以编写,并且该代码将难以复用。
使用Spring依赖注入以及面向接口使用应用对象之间依赖关系实现松耦合。对象的依赖关系将由负责协调系统中各个对象的第三方组件在创建对象时设定。无需自行创建或管理它们的依赖关系即---- 依赖关系将被自动注入到需要它们的对象中去。
使用Spring的构造器注入
所必须包: spring-beans-3.2.0.RELEASE spring-context-3.2.0.RELEASE.jar spring-core-3.2.0.RELEASE.jar spring-expression-3.2.0.RELEASE.jar commons-logging-1.1.1.jar knight.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd "> <bean id="knight" class="com.demo.BraveKnight"> <constructor-arg ref="quest1"></constructor-arg> </bean> <bean id="quest1" class="com.demo.RescuingDamselQuest" /> </beans> public interface Knight { public void embarkOnQuest(); } public interface Quest { public void embark(); } /* * 执行各种任务的骑士 */ public class BraveKnight implements Knight{ private Quest quest ; public BraveKnight(Quest quest){ this.quest = quest; } public void embarkOnQuest(){ quest.embark(); } } /* * 拯救少女任务 */ public class RescuingDamselQuest implements Quest{ public void embark(){ System.out.println("RescuingDamsel Go...."); } } /* 测试 */ public class KnightMain { public static void main(String[] args) { ApplicationContext apx = new ClassPathXmlApplicationContext("knight.xml"); //加载spring上下文 Knight knight1 = (Knight) apx.getBean("knight"); //获取bean knight1.embarkOnQuest(); //使用bean } }
根据以上代码:Spring通过应用上下文装载Bean的定义并把它们组装起来。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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd " default-autowire="byName"> <bean id="knight" class="com.demo.BraveKnight" /> <bean id="quest" class="com.demo.SlayDragonQuest"/> </beans> /* * 执行各种任务的骑士 */ public class BraveKnight implements Knight{ @Autowired private Quest quest ; public void embarkOnQuest(){ quest.embark(); } public void setQuest(Quest quest) { this.quest = quest; } } /* * 击杀恶龙任务 */ public class SlayDragonQuest implements Quest{ public void embark() { System.out.println("SlayDragon Go...."); } }
3、基于切面和惯例进行声明式编程。
AOP编程允许把遍布应用各处的功能分离出来形成可重用的组件。
面向切面编程往往被定义为促使应用程序分离关注点的一项技术。系统由许多不同组件组成,每一个组件各负责一块特定功能,除了实现自身核心的功能之外,这些组件还经常承担着额外的职责。诸如日志、事务管理、安全此类的系统服务经常融入到有自身核心业务逻辑的组件中去,这些系统服务通常被称为 横切关注点,因为它们总是跨越系统的多个组件。
如果将这些关注点分散到多个组件中去,代码将会:
(1)实现关注点代码将会重复出现在多个组件中。
(2)使得组件本身的核心业务因无关代码而变得混乱。
AOP使用得这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。结果使用这些组件具有 高内聚以及更加关注自身业务,总之,AOP确保POJO保持简单。
继承以骑士执行探险任务为例,添加一个吟游诗人歌唱骑士的事迹
/* 吟游诗人 */ public class Minstrel { public void singBeforeQuest(){ System.out.println("before ---knight is so brave!"); //探险任务之前执行 } public void singAfterQuest(){ System.out.println("after ----knight did embark on a quest!"); //探险任务之后执行 } }
如上所示,Minstrel有两个方法一个在执行任务之前执行,一个在执行任务之后执行。
将它加入骑士类中:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd " default-autowire="byName"> <bean id="knight" class="com.demo.BraveKnight" /> <bean id="quest" class="com.demo.SlayDragonQuest"/> <bean id="minstrel" class="com.demo.Minstrel" /> </beans> /* * 执行各种任务的骑士 */ public class BraveKnight implements Knight{ @Autowired private Quest quest ; @Autowired private Minstrel minstrel; public void embarkOnQuest(){ minstrel.singBeforeQuest(); quest.embark(); minstrel.singAfterQuest(); } public void setQuest(Quest quest) { this.quest = quest; } public void setMinstrel(Minstrel minstrel){ this.minstrel = minstrel; } }
如上代码可以实现需要的效果,但是管理他的吟游诗人是骑士的职责吗?这应该是吟游诗人自己的事, 不应该要骑士提醒吟游诗人去做他的事。
另外这样使得骑士必须知道吟游诗人,使得骑士BraveKnight 的 代码复杂化了。
使用AOP实现声明编程
将Minstrel抽象为一个切面,你所做的事情只是在一个Spring配置文件中声明它。
aop必须包 aopalliance-1.0.jar aspectjweaver-1.8.7.jar spring-aop-3.2.0.RELEASE.jar spring-aspects-3.2.0.RELEASE.jar spring-beans-3.2.0.RELEASE spring-context-3.2.0.RELEASE.jar spring-core-3.2.0.RELEASE.jar spring-expression-3.2.0.RELEASE.jar commons-logging-1.1.1.jar <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd " default-autowire="byName"> <bean id="knight" class="com.demo.BraveKnight" /> <bean id="quest" class="com.demo.SlayDragonQuest"/> <bean id="minstrel" class="com.demo.Minstrel" /> <!-- 声明 --> <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> import org.springframework.beans.factory.annotation.Autowired; /* * 执行各种任务的骑士 */ public class BraveKnight implements Knight{ @Autowired private Quest quest ; public void embarkOnQuest(){ quest.embark(); } public void setQuest(Quest quest) { this.quest = quest; } } //...其它不变
4、通过切面和模板减少样板式代码
如常见的样板式代码范例JDBC访问数据库查询,再如JMS、JNDI、使用REST服务通常也会涉及大量重复代码。
Spring旨在通过模板封装来消除样板式代码。