Hello Spring Framework——依赖注入(DI)与控制翻转(IoC)
又到年关了,还有几天就是春节。趁最后还有些时间,复习一下Spring的官方文档。
写在前面的话:
Spring是我首次开始尝试通过官方文档来学习的框架(以前学习Struts和Hibernate都大多是视频或书籍为主,文档一带而过)。除了语言上的障碍以外更困难的地方是对新概念的理解,这些都是过了很久才逐渐体会。要说有什么经验的话,那就是对于不同的文档都似乎有它们自己的上下文语境。虽然不可否认外国人在文档统一性方面已经做的非常出色了,但只要还有那么一点点差异在语言的“阻拦”下依旧会让我觉得深奥。仅这一点来说看中文的书籍或视频讲解确实学的更快。这次跟着创作博客的节奏又大致浏览一遍,希望能有新的发现。
***************************以下是正文的部分***************************
一、Ioc和DI
(1)概念
This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) [1] principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.
Introduction to the Spring IoC container and beans
以上摘自Spring官方文档,大意是:Spring Framework实现了控制翻转功能,而控制翻转又需要依赖注入。在这个过程中,框架可以针对构造器、工厂模式以及Setter方法实现注入。由于整个过程完全自动,因此被称为控制翻转(IoC)。
(2)The Spring IoC container

Spring容器是元数据和配置文件的消费者,通过用户定义最终的产品由容器来生成。也就是说,作为Spring的使用者,你必须告诉容器如何使用Object,以及处理它们之间的相互依赖。
(3)初始化容器
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
ApplicationContext
(4)使用容器
// 创建applicationContext
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"}); // 实例化一个对象
PetStoreService service = context.getBean("petStore", PetStoreService.class); // 另一种更常用的实例化方式
PetStoreService service = (PetStoreService)context.getBean("petStore"); // 使用对象
List<String> userList = service.getUsernameList();
Using the container
二、通过XML的方式
(1)Bean overview
<?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.xsd"> <bean id="beanID" class="className" scope="singleton/prototype" lazy-init="true/false">
<!-- 通过构造函数注入 -->
<constructor-arg ref="args"/>
<!-- 通过Setter方法注入 -->
<property name="methodName" ref="anotherBeanID"/>
</bean>
<!-- 依赖关系的产生也需要通过Spring -->
<bean id="anotherBeanID" class="..."/>
<!-- more bean definitions go here --> </beans>
常见XML配置
通常情况下,Spring生成的是单例对象并且会在容器初始化时就完成。可以配置scope="singleton/prototype"和lazy-init="true/false"来做调整。
(2)Collections
<bean id="beanName" class="className">
<!-- 等同于java.util.Properties -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- 等同于java.util.List -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- 等同于java.util.Map -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- 等同于java.util.Set -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
Java容器配置
三、通过Annotation的方式
(1)官方推荐使用annotation的方式,但是到底哪一种更适合你呢?
Are annotations better than XML for configuring Spring? The introduction of annotation-based configurations raised the question of whether this approach is 'better' than XML. The short answer is it depends. The long answer is that each approach has its pros and cons, and usually it is up to the developer to decide which strategy suits them better. Due to the way they are defined, annotations provide a lot of context in their declaration, leading to shorter and more concise configuration. However, XML excels at wiring up components without touching their source code or recompiling them. Some developers prefer having the wiring close to the source while others argue that annotated classes are no longer POJOs and, furthermore, that the configuration becomes decentralized and harder to control. No matter the choice, Spring can accommodate both styles and even mix them together. It’s worth pointing out that through its JavaConfig option, Spring allows annotations to be used in a non-invasive way, without touching the target components source code and that in terms of tooling, all configuration styles are supported by the Spring Tool Suite.
Are annotations better than XML for configuring Spring?
以上摘自Spring官方文档,大意是:annotation的方式相较XML的方式更加简练,而且分开配置可以更加自由。但是XML依然也是一种不错的选择,因为当你修改XML配置的时候,源代码不需要重新参与编译。总之就是萝卜青菜各有所爱咯!
(2)声明
<?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: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.xsd"> <context:annotation-config/> </beans>
annotation-config
实际上,通过上述方式声明的annotation源码依然需要在XML文件中配置<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"
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.xsd"> <context:component-scan base-package="packageName"/> </beans>
component-scan
注意:使用<context:component-scan>隐含了<context:annotation-config>。在有的视频上似乎是教大家两条需要同时配置,实际上在官方文档中写的很清楚。
(3)常用注解
根据Spring官方文档的描述,Spring4.x版本支持自己定义的annotation以及JSR-250和JSR-330的annotation。
i.@Required
在Setter方法上使用,初始化时如果没有匹配的对象注入Spring会抛出NullPointerExceptions。
ii.@Autowired
可以在方法和属相上设置,另外@Autowired(required=false)注解也可以规定注入不是必须的实现。
iii.@Configuration和@Bean
通过Java源码提供配置,@Configuration设置在类名上,@Bean设置在方法名上。
package test.primary; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class MovieConfiguration {
@Bean
public MovieCatalog firstMovieCatalog() {
return new FirstMovieCatalog();
} @Bean
public MovieCatalog secondMovieCatalog() {
return new SecondMovieCatalog();
}
}
MovieConfiguration
iv.@Primary和@Qualifier
@Primary和@Bean配合使用,@Qualifier和@Autowired配合使用。使得实现了相同接口的对象能够准确注入。
v.@Resource
功能上等同于@Autowired,它是由JSR-250提供的注解。主要是依照对象名称注入。
vi.@Component
配置在类名上,提供给Spring扫描包使用。
vii.@ComponentScan
配置在类名上,提供给依赖注入的对象直接扫描包使用(不重要一般不会使用)。
viii.@Component,@Repository,@Service和@Controller
其它的可以配置在类名上,提供给Spring在不同的环境中通过包扫描机制区分各种类型。
(4)包扫描过滤器
根据Spring官方文档的定义,包扫描过滤器配置有5种类型。分别为annotation、assignable、aspectj、regex和custom。迄今为止,我只接触过annotation和regex两种方式,它们同时支持annotation与XML。由于@ComponentScan注解本身并不常用,因此一般是配合XML来用。
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
//...
}
appConfig
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
xmlConfig
(5)对JSR-330注解的支持
JSR-330提供的注解相对比较简单,主要是@Inject和@Named。
import javax.inject.Inject;
import javax.inject.Named; @Named
public class SimpleMovieLister {
private MovieFinder movieFinder; @Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ... }
SimpleMovieLister
此外@Named也可以被放置在方法的参数前面,语义类似@Qualifier。应该说JSR-330提供的注解更方便,只是由于需要导入新的包依赖关系,所以在很多开发场景中并不被推荐。
四、基于Java源码的容器配置
这一块好像绝大部分的中文教材上都没有特别讲解,我也是在第二次读文档的时候才注意到的(应该是很少被使用)。不过作为文档学习的一部分,我还是打算将这个部分大致的整理一下。
(1)@Comfiguration和@Bean
以下两种配置是相同的
@Configuration
public class AppConfig { @Bean
public MyService myService() {
return new MyServiceImpl();
} }
AppConfig
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
appConfig.xml
(2)使用AnnotationConfigApplicationContext
基于Java源码的容器配置,必须使用AnnotationConfigApplicationContext类来初始化容器。文档中提供了两种初始化的方式,分别对应XML中的<bean/>和<context:component-scan/>配置。实际上如果去读API文档就会发现,使用AnnotationConfigApplicationContext来初始化容器也可以在构造函数中直接完成。
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
注册方式
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
包扫描方式
public class SpringTest {
@Test
public void test() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
for (String s : context.getBeanDefinitionNames()) {
System.out.println(s);
}
}
}
构造函数方式
提醒:配置了@Configuration和@Bean的类,只能通过构造函数或注册方式来解析内部的对象,包扫描方式不起作用。
(3)有关@Bean的其他方面
@Configuration
public class AppConfig { @Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
} }
AppConfig
上面的例子是Spring官方文档提供的,表示如果AccountRepository对象已经通过Spring容器生成,那么@Bean注解可以直接注入进需要的方法参数里。换句话说,同@Autowired等价。
public class Foo {
public void init() {
// initialization logic
}
}
public class Bar {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
}
lifecycle callbacks
上面的例子用来说明可以在@Bean注解后面指定初始化方法和清理方法,不过好像用的极少。
另外同XML一样,@Bean提供的对象默认也是单例的。你也可以专门指定生成方式...
@Configuration
public class MyConfiguration { @Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
} }
原型模式
(4)有关@Configuration的其他方面
XML文档提供了一个<import/>标签,使得一篇文档可以包含其他文档。同样在基于Java源码的容器配置中也有一个类似的注解:@Import
@Configuration
public class ConfigA { @Bean
public A a() {
return new A();
} } @Configuration
@Import(ConfigA.class)
public class ConfigB { @Bean
public B b() {
return new B();
} }
Import
不过要吐槽一下,这个东西我就没见有人用过。忽略吧骚年,我连例子都是复制文档的。
Spring既然已经支持了基于XML和annotation两种方式的容器配置方法,为什么还要提供基于Java源码的配置方法呢?首先要澄清一个概念,annotation配置和Java源码配置是完全不同方案,使用annotation配置的对象依然需要在XML文档里提供包扫描机制,以及通过ClassPathXmlApplicationContext来初始化容器。而基于Java源码配置则是用过AnnotationConfigApplicationContext初始化容器。至于如何选择,官方文档描述了一个理由:
Combining Java and XML configuration Spring’s @Configuration class support does not aim to be a 100% complete replacement for Spring XML. Some facilities such as Spring XML namespaces remain an ideal way to configure the container. In cases where XML is convenient or necessary, you have a choice: either instantiate the container in an "XML-centric" way using, for example, ClassPathXmlApplicationContext, or in a "Java-centric" fashion using AnnotationConfigApplicationContext and the @ImportResource annotation to import XML as needed.
Combining Java and XML configuration
很简单不翻译了,反正看你喜欢咯 :-P
五、两个重要的描述
文档中有两个比较重要的描述,我单列在最后。这对于初学Spring框架的用户来说可以忽略。但是如果你想对Spring了解的更深一些还是建议大家去读官方文档的相关部分。
(1)通过XML载入外部资源文件有两种方法
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean> <bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
通过Spring提供的对象载入外部资源文件
这也是比较传统的方式。在Spring2.5以后增加了<context:property-placeholder/>标签
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>
使用Spring提供的XML标签载入外部资源
显然,第二种方式更加推荐。
(2)BeanFactory or ApplicationContext?
BeanFactory是从容器中获取对象的最上层接口,ApplicationContext扩展了BeanFactory的功能。对此文档中有清楚的描述:BeanFactory主要提供给第三方框架使用,而对于普通用户来说,除非有特殊的理由。否则,你应该直接使用ApplicationContext。
***************************以下是个人感想***************************
有关SpringIoC本来没打算写这么多。只是一点点读文档发现还是应该总结下来,也方便以后自己查询。文档描述的内容基本都在上面了,看不懂英文的同学完全可以对照着上面的内容来编写代码。
Hello Spring Framework——依赖注入(DI)与控制翻转(IoC)的更多相关文章
- 依赖注入(DI)和控制反转(IOC)
依赖注入(DI)和控制反转(IOC) 0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只 ...
- PHP依赖注入(DI)和控制反转(IoC)详解
这篇文章主要介绍了PHP依赖注入(DI)和控制反转(IoC)的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程 ...
- 轻松理解 Java开发中的依赖注入(DI)和控制反转(IOC)
前言 关于这个话题, 网上有很多文章,这里, 我希望通过最简单的话语与大家分享. 依赖注入和控制反转两个概念让很多初学这迷惑, 觉得玄之又玄,高深莫测. 这里想先说明两点: 依赖注入和控制反转不是高级 ...
- spring学习之依赖注入DI与控制反转IOC
一 Ioc基础 1.什么是Ioc? Ioc(Inversion of Control)既控制反转,Ioc不是一种技术,而是一种思想,在Java开发中意味着将设计好的对象交给容器来进行控制,并不是像传统 ...
- 依赖注入(DI)和控制反转(IOC)的理解,写的太好了。
学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- 【串线篇】依赖注入DI与控制反转IOC
DI&IOC 在spring框架中DI与IOC说的其实是一回事 一句话:本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象. 也就是说我对对象的『依赖』是注入进来的,而和 ...
- 话说 依赖注入(DI) or 控制反转(IoC)
科普:首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些 ...
- ASP.NET MVC进阶之路:深入理解依赖注入(DI)和控制反转(IOC)
0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户 ...
- PHP 依赖注入(DI) 和 控制反转(IoC)
要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题: DI —— Dependency Injection 依赖注入 IoC —— Inversion of Control ...
- 聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)
简介 IoC Inversion of Control 控制反转DI Dependency Injection 依赖注入 依赖注入和控制反转说的实际上是同一种东西,它们是一种设计模式,这种设计模式用来 ...
随机推荐
- 百度地图 获取两点坐标之间的驾车距离(非直线距离) c#
百度接口了解: http://lbsyun.baidu.com/index.php?title=webapi/route-matrix-api-v2 起点与终点为多对多关系,如果你只想取两个坐标,那就 ...
- MULTITHREADING AND CHIP MULTIPROCESSORS
COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION The most important me ...
- jquery的$().each,$.each的区别
在jquery中,遍历对象和数组,经常会用到$().each和$.each(),两个方法.两个方法是有区别的,从而这两个方法在针对不同的操作上,显示了各自的特点. $().each,对于这个方法,在d ...
- 学霸数据处理项目之数据处理网页以及后台以及C#代码部分开发者手册
写在前面,本文将详细介绍学霸数据处理项目中的数据处理网页与后台函数,以及c#代码中每一个方法的意义及其一些在运行方面需要注意的细节,供开发人员使用,开发人员在阅读相关方法说明时请参照相关代码,对于本文 ...
- 17+个ASP.NET MVC扩展点【附源码】
1.自定义一个HttpModule,并将其中的方法添加到HttpApplication相应的事件中!即:创建一个实现了IHttpmodule接口的类,并将配置WebConfig. 在自定义的Http ...
- 2003-can't connect to mysql server on 'localhost'(10061) MySQL错误
开始遇到这个问题,我以为是服务没链接,在网上查了下错误,但没解决,后来链接了下端口,结果发现我原来我 端口不对,MySQL端口默认是3306我安装时端口是设的3307.希望能帮到遇到这种问题的人
- Java语言程序设计(基础篇) 第四章 数学函数、字符和字符串
第四章 数学函数.字符和字符串 4.2 常用数学函数 方法分三类:三角函数方法(trigonometric method).指数函数方法(exponent method)和服务方法(service m ...
- 1.初识Shell脚本语言
PS:在做Linux下STM8固件升级项目中,需要让CPU通过I2C总线给STM8传输数据,刚开始一个一个的敲,很浪费时间,用shell脚本大大提高了数据传输效率,它是用户与内核进行交互操作的一种接口 ...
- 树状sql--采用递归方式获取节点
创建数据库 create table City(id varchar(3) primary key , pid varchar(3) , name varchar(10)) 插入数据 insert i ...
- 基于spring的aop实现读写分离与事务配置
项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换. 一.Spring事务开启流程 Spring中通常通过@Transactional来 ...