Spring实现IOC
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
一、使用XML配置的方式实现IOC
假设项目中需要完成对图书的数据访问服务,我们定义好了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.0</modelVersion> <groupId>com.zhangguo</groupId>
<artifactId>Spring051</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>Spring051</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</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权威指南 第一版》");
}
}

运行结果:
二、使用Spring注解配置IOC
上一个示例是使用传统的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命名空间与模式约束文件位置。增加了注解扫描的范围,指定了一个包,可以通过属性设置更加精确的范围如:
<context>标记常用属性配置:
resource-pattern:对指定的基包下面的子包进行选取
<context>子标记:
include-filter:指定需要包含的包
exclude-filter:指定需要排除的包
<!-- 自动扫描com.zhangguo.anno.bo中的类进行扫描 -->
<context:component-scan base-package="com.zhangguo.anno" resource-pattern="bo/*.class" />
<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>
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表示过滤的表达式。
<!-- 1、如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类 -->
<context:component-scan base-package="com.zhangguo.Spring051"
resource-pattern="ioc04/A*.class">
</context:component-scan>
只扫描com.zhangguo.Spring051.ioc04下所有名称以A开始的类。

<!--2、扫描注解了org.springframework.stereotype.Repository的类
exclude-filter表示排除,include-filter表示包含,可以有多个-->
<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>


<!--3、aspectj类型,扫描dao下所有的类,排除entity下所有的类-->
<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权威指南 第三版》");
}
}

运行结果:
四、零配置实现IOC
所谓的零配置就是不再使用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相当于配置文件中的<beans/>,ComponentScan相当于配置文件中的context:component-scan,属性也一样设置
,@Bean相当于<bean/>,只能注解在方法和注解上,一般在方法上使用,源码中描述:@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),方法名相当于id。中间使用到了User,User类的代码如下:

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文件中定义了一个<bean id="getUser" class="..."/>,在User类上注解@Component("user1")相当于另一个<bean id="user1" class="..."/>。
运行结果:
小结:使用零配置和注解虽然方便,不需要编写麻烦的xml文件,但并非为了取代xml,应该根据实例需要选择,或二者结合使用,毕竟使用一个类作为容器的配置信息是硬编码的,不好在发布后修改。
Spring实现IOC的更多相关文章
- Spring的IOC和AOP之深剖
今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...
- Spring框架IOC容器和AOP解析
主要分析点: 一.Spring开源框架的简介 二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置 一.S ...
- Spring总结—— IOC 和 Bean 的总结
一.Spring 官方文档中给出的 Spring 的整体结构. 二.我自己所理解的 Spring 整体结构图. 三.本次总结 Spring 核心部分 1.从上面图中可以看出,Beans 和 Conte ...
- spring的IOC和AOP
spring的IOC和AOP 1.解释spring的ioc? 几种注入依赖的方式?spring的优点? IOC你就认为他是一个生产和管理bean的容器就行了,原来需要在调用类中new的东西,现在都是 ...
- spring容器IOC创建对象<二>
问题?spring是如何创建对象的?什么时候创建对象?有几种创建方式?测试对象是单例的还是多例的 ?对象的初始化和销毁? 下面的四大模块IOC的内容了!需要深刻理解 SpringIOC定义:把对象的创 ...
- Spring中IoC的入门实例
Spring中IoC的入门实例 Spring的模块化是很强的,各个功能模块都是独立的,我们可以选择的使用.这一章先从Spring的IoC开始.所谓IoC就是一个用XML来定义生成对象的模式,我们看看如 ...
- Spring中IOC和AOP的详细解释
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC就是典型的工厂模式,通过s ...
- Spring的IoC应用
IoC(Inversion of Control,控制反转) Spring的IoC应用是其框架的最大的特点,通过依赖注入可以大大降低代码之间的耦合度,从而实现代码和功能之间的分离.在代码中可以不直接和 ...
- Spring 实践 -IoC
Spring 实践 标签: Java与设计模式 Spring简介 Spring是分层的JavaSE/EE Full-Stack轻量级开源框架.以IoC(Inverse of Control 控制反转) ...
- 挖坟之Spring.NET IOC容器初始化
因查找ht项目中一个久未解决spring内部异常,翻了一段时间源码.以此文总结springIOC,容器初始化过程. 语言背景是C#.网上有一些基于java的spring源码分析文档,大而乱,乱而不全, ...
随机推荐
- [转自itilxf论坛]iTop百问百答
iTop是什么,和其他itsm软件相比有什么优势?iTop,是一个开源web应用程序,用于IT环境的日常运营.它基于ITIL最佳实践,而又不拘泥于任何具体流程. 优势: 1. 开源,扩展性强容易开发. ...
- Sprint会议记录(第五组)
会议时间:12/8 下午14:00 会议地点:宿舍 会议进程: *首先我们讨论了实验第一个Sprint1要实现的功能:排球规则分析.比赛详细过程.比赛人物分析, *之后对是任务的认领, *最后每个人对 ...
- PHP 使用 curl_* 系列函数和 curl_multi_* 系列函数进行多接口调用时的性能对比
在页面中调用的服务较多时,使用并行方式,即使用 curl_multi_* 系列函数耗时要小于 curl_* 系列函数. 测试环境 操作系统:Windows x64 Server:Apache PHP: ...
- PHP 模拟 HTTP 基本认证(Basic Authentication)
当某个页面需要认证才能进行访问时,接到请求后服务器端会在响应头中发送一个 WWW-Authenticate 首部(用来标识认证安全域),语法为 WWW-Authenticate:Basic relam ...
- asp.net core项目发布网站时的选项
发布网站时的选项 Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序. Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的, ...
- 【php】命名空间 和 自动加载的关系
目的 本文的目的主要是说明 命名空间的 use 关键词 和 new ClassName 这两个步骤,哪个步骤才会执行自动加载,这是逻辑有点混乱的表现,这种想法也是很正常的,让我们来解密吧 命名空间(n ...
- responsive tables
以上内容原本是整理为ppt格式的,贴过来格式有点乱,请见谅. 其他responsive tables参考: http://gergeo.se/RWD-Table-Patterns/ 3种类型的代码参考 ...
- cloudera learning2:HDFS
存入HDFS的文件会按块(block)划分,默认每块128MB.默认1个block还有2个备份.备份增加了数据的可靠性和提高计算效率(数据本地化). HDFS部署可选择不支持HA,也可选择支持HA. ...
- C/C++源代码从写完到运行发生了什么
有时候经常听到一些不明觉厉的词语,什么编译啊链接啊语义分析啊的,就找书来看看,把笔记画成了图: 编译器干了些啥呢,如下图: 参考书:<程序员的自我修养——链接.装载与库>,<深入理解 ...
- Java语言程序设计(基础篇)第二章
第二章 基本程序设计 2.2 编写简单的程序 1.变量名尽量选择描述性的名字(descriptive name). 2.实数(即带小数点的数字)在计算机中使用一种浮点的方法来表示.因此,实数也称为浮点 ...