Spring是一个开源框架,他是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许选择使用那一个组件。Spring框架的分层架构,由7个定义良好的模块构成。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理Bean的方式。。。
7个模块分别是:1、AOP(spring-aop.jar);2、ORM(spring-hibernate2.jar spring-hibernate3.jar spring-toplink.jar spring-ibatis.jar spring-jdo.jar) ;
3、Web(WebApplicationContext)[spring-webjar] 4、MVC(spring-support.jar spring-webmvc.jar spring-portlet.jar spring-struts.jar);
5、DAO(spring-dao.jar spring-jdbc.jar) 6、Context (ApplicationContext)[spring-context.jar] 7、Core(BeanFactory)[spring-core.jar spring-beans.jar]
组成Spring框架的每个模块都可以单独存在,或者与其他一个或者多个模块联合使用。这七个模块的功能如下:
Spring核心模块(Core):提供Spring框架的基本功能。核心容器的主要组建市BeanFactory,它是佛纳甘长模式的实现。BeanFactory使用控制反转模式(IOC)将应用成学配置和依赖性规范与实际的应用程序代码分开。
Spring上下文(Context):Spring上下文是一个配置文件,想Spring框架提供上下文信息。Spring上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP:同股票配置管理特性,Spring AOP模块直接将面向切面的编程功能集成到了Spring框架中。所以很容易的使Spring框架管理的任何对象支持AOP。Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用Spring AOP,不再依赖EJB组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO:JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理同数据库供应商抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了西药变得异常代码数量(例如打开和关闭连接)。Spring DAO的面向JDBC的异常遵从通用的DAO异常层次结构
Spring ORM:Spring框架插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括了JDO、Hibernate、iBatis SQL Map 和Toplink。所有的这些都遵从Spring的通用事务和DAO异常层次结构
Spring Web:Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文,所以,Spring框架支持与jakarta Struts的继承。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作
Spring MVC框架:Spring的MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的MVC容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText 和POI.
关于Spring的两大核心技术,IOC和AOP,接下来的两篇文章做了比较全面详细的总结,如下:首先是IOC(转自Iteye的开涛对Spring框架的IOC的理解,写得非常通俗易懂,以下内容全部来自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846)
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
用图例说明一下,传统程序设计如图2-1,都是主动去创建相关对象然后再组合起来:
图1 传统应用程序示意图
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2所示:
图2 有IoC/DI容器后程序结构示意图
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
那么Sping 是怎样实现IOC的呢,(转自 http://www.cnblogs.com/best/p/5727935.html 以下内容全部来自原文)
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
假设项目中需要完成对图书的数据访问服务,我们定义好了IBookDAO接口与BookDAO实现类
IBookDAO接口如下:
package com.zhangguo.Spring051.ioc01; /** * 图书数据访问接口 */ public interface IBookDAO { /** * 添加图书 */ public String addBook(String bookname); }
BookDAO实现类如下:
package com.zhangguo.Spring051.ioc01; /** * 图书数据访问实现类 */ public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加图书"+bookname+"成功!"; } }
Maven项目的pom.xml如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0modelVersion> <groupId>com.zhangguogroupId> <artifactId>Spring051artifactId> <version>0.0.1-SNAPSHOTversion> <packaging>jarpackaging> <name>Spring051name> <url>http://maven.apache.orgurl> <properties> <project.build.sourceEncoding>UTF-8project.build.sourceEncoding> <spring.version>4.3.0.RELEASEspring.version> properties> <dependencies> <dependency> <groupId>junitgroupId> <artifactId>junitartifactId> <scope>testscope> <version>4.10version> dependency> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-contextartifactId> <version>${spring.version}version> dependency> <dependency> <groupId>org.aspectjgroupId> <artifactId>aspectjweaverartifactId> <version>1.8.9version> dependency> <dependency> <groupId>cglibgroupId> <artifactId>cglibartifactId> <version>3.2.4version> dependency> dependencies> project>
业务类BookService如下:
package com.zhangguo.Spring051.ioc01; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 图书业务类 */ public class BookService { IBookDAO bookDAO; public BookService() { //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans01.xml"); //从容器中获得id为bookdao的bean bookDAO=(IBookDAO)ctx.getBean("bookdao"); } public void storeBook(String bookname){ System.out.println("图书上货"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
容器的配置文件IOCBeans01.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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bookdao" class="com.zhangguo.Spring051.ioc01.BookDAO">bean> beans>
测试类Test如下:
package com.zhangguo.Spring051.ioc01; public class Test { @org.junit.Test public void testStoreBook() { BookService bookservice=new BookService(); bookservice.storeBook("《Spring MVC权威指南 第一版》"); } }
运行结果:
上一个示例是使用传统的xml配置完成IOC的,如果内容比较多则配置需花费很多时间,通过注解可以减轻工作量,但注解后修改要麻烦一些,偶合度会增加,应该根据需要选择合适的方法。
2.1、修改BookDAO
package com.zhangguo.Spring051.ioc02; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 图书数据访问实现类 */ @Component("bookdaoObj") public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加图书"+bookname+"成功!"; } }
在类上增加了一个注解Component,在类的开头使用了@Component注解,它可以被Spring容器识别,启动Spring后,会自动把它转成容器管理的Bean。
除了@Component外,Spring提供了3个功能基本和@Component等效的注解,分别对应于用于对DAO,Service,和Controller进行注解。
1:@Repository 用于对DAO实现类进行注解。
2:@Service 用于对业务层注解,但是目前该功能与 @Component 相同。
3:@Constroller用于对控制层注解,但是目前该功能与 @Component 相同。
2.2、修改BookService
package com.zhangguo.Spring051.ioc02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; /** * 图书业务类 */ @Component public class BookService { IBookDAO bookDAO; public void storeBook(String bookname){ //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans02.xml"); //从容器中获得id为bookdao的bean bookDAO=(IBookDAO)ctx.getBean("bookdaoObj"); System.out.println("图书上货"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
将构造方法中的代码直接写在了storeBook方法中,避免循环加载的问题。
2.3、修改IOC配置文件IOCBeans02.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" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.zhangguo.Spring051.ioc02">context:component-scan> beans>
粗体字是新增的xml命名空间与模式约束文件位置。增加了注解扫描的范围,指定了一个包,可以通过属性设置更加精确的范围如:
resource-pattern:对指定的基包下面的子包进行选取
include-filter:指定需要包含的包
exclude-filter:指定需要排除的包
include-filter表示需要包含的目标类型,exclude-filter表示需要排除的目标类型,type表示采的过滤类型,共有如下5种类型:
Filter Type | Examples Expression | Description |
annotation | org.example.SomeAnnotation | 注解了SomeAnnotation的类 |
assignable | org.example.SomeClass | 所有扩展或者实现SomeClass的类 |
aspectj | org.example..*Service+ | AspectJ语法表示org.example包下所有包含Service的类及其子类 |
regex | org\.example\.Default.* | Regelar Expression,正则表达式 |
custom | org.example.MyTypeFilter | 通过代码过滤,实现org.springframework.core.type.TypeFilter接口 |
expression表示过滤的表达式。
<context:component-scan base-package="com.zhangguo.Spring051" resource-pattern="ioc04/A*.class"> context:component-scan>
只扫描com.zhangguo.Spring051.ioc04下所有名称以A开始的类。
<context:component-scan base-package="com.zhangguo.Spring051.ioc04"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" /> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> context:component-scan>
<context:component-scan base-package="com.zhangguo.anno" > <context:include-filter type="aspectj" expression="com.zhangguo.anno.dao.*.*"/> <context:exclude-filter type="aspectj" expression="com.zhangguo.anno.entity.*.*"/> context:component-scan>
2.4、测试类
package com.zhangguo.Spring051.ioc02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans02.xml"); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC权威指南 第二版》"); } }
运行结果:
2.5、小结
从配置文件中我们可以看出我们并没有声明bookdaoObj与BookService类型的对象,但还是从容器中获得了实例并成功运行了,原因是:在类的开头使用了@Component注解,它可以被Spring容器识别,启动Spring后,会自动把它转成容器管理的Bean。
从上一个示例中可以看出有两个位置都使用了ApplicationContext初始化容器后获得需要的Bean,可以通过自动装配简化。
3.1、修改BookDAO
package com.zhangguo.Spring051.ioc03; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 图书数据访问实现类 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加图书"+bookname+"成功!"; } }
把注解修改成了Repository,比Component更贴切一些,非必要。
3.2、修改BookService
package com.zhangguo.Spring051.ioc03; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; /** * 图书业务类 */ @Service public class BookService { @Autowired IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println("图书上货"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
将类BookService上的注解替换成了Service;在bookDao成员变量上增加了一个注解@Autowired,该注解的作用是:可以对成员变量、方法和构造函数进行注解,来完成自动装配的工作,通俗来说就是会根据类型从容器中自动查到到一个Bean给bookDAO字段。@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier。另外可以使用其它注解,@ Resource :等同于@Qualifier,@Inject:等同于@ Autowired。
@Service用于注解业务层组件(我们通常定义的service层就用这个)
@Controller用于注解控制层组件(如struts中的action)
@Repository用于注解数据访问组件,即DAO组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行注解。
装配注解主要有:@Autowired、@Qualifier、@Resource,它们的特点是:
1、@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;
2、@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;
3、@Resource注解是又J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用@Resource的方式;如果Maven项目是1.5的JRE则需换成更高版本的。
4、@Resource和@Autowired都可以书写注解在字段或者该字段的setter方法之上
5、@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的注解对象是成员变量、方法入参、构造函数入参。
6、@Qualifier("XXX") 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。
7、@Autowired 注释进行自动注入时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个,通过属性required可以设置非必要。
8、@Resource装配顺序
8.1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
8.2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
8.3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
8.4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
package com.zhangguo.Spring051.ioc05; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; /** * 图书业务类 */ @Service public class BookService { public IBookDAO getDaoofbook() { return daoofbook; } /* @Autowired @Qualifier("bookdao02") public void setDaoofbook(IBookDAO daoofbook) { this.daoofbook = daoofbook; }*/ @Resource(name="bookdao02") public void setDaoofbook(IBookDAO daoofbook) { this.daoofbook = daoofbook; } /* @Autowired @Qualifier("bookdao02") */ IBookDAO daoofbook; /* public BookService(@Qualifier("bookdao02") IBookDAO daoofbook) { this.daoofbook=daoofbook; }*/ public void storeBook(String bookname){ System.out.println("图书上货"); String result=daoofbook.addBook(bookname); System.out.println(result); } }
3.3、测试运行
package com.zhangguo.Spring051.ioc03; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器 ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans03.xml"); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC权威指南 第三版》"); } }
运行结果:
所谓的零配置就是不再使用xml文件来初始化容器,使用一个类型来替代,
IBookDAO代码如下:
package com.zhangguo.Spring051.ioc06; /** * 图书数据访问接口 */ public interface IBookDAO { /** * 添加图书 */ public String addBook(String bookname); }
IBookDAO的实现类BookDAO代码如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 图书数据访问实现类 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加图书"+bookname+"成功!"; } }
在BookDAO类上注解了@Repository当初始化时该类将被容器管理会生成一个Bean,可以通过构造方法测试。
业务层BookService代码如下:
package com.zhangguo.Spring051.ioc06; import javax.annotation.Resource; import org.springframework.stereotype.Service; /** * 图书业务类 */ @Service public class BookService { @Resource IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println("图书上货"); String result=bookDAO.addBook(bookname); System.out.println(result); } }
类BookService将对容器管理因为注解了@Service,初始化时会生成一个单例的Bean,类型为BookService。在字段bookDAO上注解了@Resource,用于自动装配,Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
新增一个用于替代原xml配置文件的ApplicationCfg类,代码如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 容器的配置类 */ @Configuration @ComponentScan(basePackages="com.zhangguo.Spring051.ioc06") public class ApplicationCfg { @Bean public User getUser(){ return new User("成功"); } }
@Configuration相当于配置文件中的
,@Bean相当于
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; @Component("user1") public class User { public User() { System.out.println("创建User对象"); } public User(String msg) { System.out.println("创建User对象"+msg); } public void show(){ System.out.println("一个学生对象!"); } }
初始化容器的代码与以前有一些不一样,具体如下:
package com.zhangguo.Spring051.ioc06; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test { @org.junit.Test public void testStoreBook() { //容器,注解配置应用程序容器,Spring通过反射ApplicationCfg.class初始化容器 ApplicationContext ctx=new AnnotationConfigApplicationContext(ApplicationCfg.class); BookService bookservice=ctx.getBean(BookService.class); bookservice.storeBook("《Spring MVC权威指南 第四版》"); User user1=ctx.getBean("user1",User.class); user1.show(); User getUser=ctx.getBean("getUser",User.class); getUser.show(); } }
容器的初始化通过一个类型完成,Spring通过反射ApplicationCfg.class初始化容器,中间user1与getUser是否为相同的Bean呢?
答案是否定的,因为在ApplicationCfg中声明的方法getUser当相于在xml文件中定义了一个
运行结果:
小结:使用零配置和注解虽然方便,不需要编写麻烦的xml文件,但并非为了取代xml,应该根据实例需要选择,或二者结合使用,毕竟使用一个类作为容器的配置信息是硬编码的,不好在发布后修改。