又到年关了,还有几天就是春节。趁最后还有些时间,复习一下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)的更多相关文章

  1. 依赖注入(DI)和控制反转(IOC)

    依赖注入(DI)和控制反转(IOC) 0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只 ...

  2. PHP依赖注入(DI)和控制反转(IoC)详解

    这篇文章主要介绍了PHP依赖注入(DI)和控制反转(IoC)的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程 ...

  3. 轻松理解 Java开发中的依赖注入(DI)和控制反转(IOC)

    前言 关于这个话题, 网上有很多文章,这里, 我希望通过最简单的话语与大家分享. 依赖注入和控制反转两个概念让很多初学这迷惑, 觉得玄之又玄,高深莫测. 这里想先说明两点: 依赖注入和控制反转不是高级 ...

  4. spring学习之依赖注入DI与控制反转IOC

    一 Ioc基础 1.什么是Ioc? Ioc(Inversion of Control)既控制反转,Ioc不是一种技术,而是一种思想,在Java开发中意味着将设计好的对象交给容器来进行控制,并不是像传统 ...

  5. 依赖注入(DI)和控制反转(IOC)的理解,写的太好了。

    学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  6. 【串线篇】依赖注入DI与控制反转IOC

    DI&IOC 在spring框架中DI与IOC说的其实是一回事 一句话:本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象. 也就是说我对对象的『依赖』是注入进来的,而和 ...

  7. 话说 依赖注入(DI) or 控制反转(IoC)

    科普:首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些 ...

  8. ASP.NET MVC进阶之路:深入理解依赖注入(DI)和控制反转(IOC)

    0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户 ...

  9. PHP 依赖注入(DI) 和 控制反转(IoC)

    要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题: DI —— Dependency Injection 依赖注入 IoC —— Inversion of Control ...

  10. 聊一聊PHP的依赖注入(DI) 和 控制反转(IoC)

    简介 IoC Inversion of Control 控制反转DI Dependency Injection 依赖注入 依赖注入和控制反转说的实际上是同一种东西,它们是一种设计模式,这种设计模式用来 ...

随机推荐

  1. HDU 5965 枚举模拟 + dp(?)

    ccpc合肥站的重现...一看就觉得是dp 然后强行搞出来一个转移方程 即 根据第i-1列的需求和i-1 i-2列的枚举摆放 可以得出i列摆放的种类..加了n多if语句...最后感觉怎么都能过了..然 ...

  2. ajax下载多文件,并且打包 C#中 ,文件批下载zip

    //提交要下载的文件 $.ajax({ url:"/sub/ZipFile.aspx", data:"paras="+datas, type: 'HEAD', ...

  3. 一起来做webgame,《Javascript蜘蛛纸牌》

    不得不说,做游戏是会上瘾的,这次带来的是win系统上的经典游戏<蜘蛛纸牌>,不能完美,但求一玩 移牌 0 次 Javascript game_蜘蛛纸牌 正在努力加载... // " ...

  4. 【java学习笔记】字符串和Date的转换

    String dateVal = "1992-12-06 18:34:23"; SimpleDateFormat sdf = new SimpleDateFormat(" ...

  5. 【转】Mecanim Animator使用详解

    http://blog.csdn.net/myarrow/article/details/45242403 1. 简介 Mecanim把游戏中的角色设计提高到了一个新的层次,使用Mecanim可以通过 ...

  6. sprint2总结

    在sprint第二阶段的学习过程中,前期和后期在学校网络的支持下我们成功的延迟了把代码上传github的时间,在我们自己感觉上看,完成项目的质量比较理想,在经过sprint第一阶段的学习后,部分上的问 ...

  7. kaggle实战记录 =>Digit Recognizer

    date:2016-09-13 今天开始注册了kaggle,从digit recognizer开始学习, 由于是第一个案例对于整个流程目前我还不够了解,首先了解大神是怎么运行怎么构思,然后模仿.这样的 ...

  8. 【原】使用Xfermode正确的绘制出遮罩效果

    以前写as3的时候,遮罩效果一个mask属性就搞定了,真是方便. 转到android上以后,发现要实现类似的效果,可以使用Xfermode,android一共提供了三种: AvoidXfermode; ...

  9. Mongodb基本操作说明

    Mongodb基本操作说明 1.首先cmd(管理员方式运行)下启动mongo服务(类似初始化工具): Mongod.exe 默认文件夹为 :c:\data\db 如果没有创建该文件夹的话,需要先创建该 ...

  10. web前端开发和后端开发有什么区别?

    web前端分为网页设计师.网页美工.web前端开发工程师 首先网页设计师是对网页的架构.色彩以及网站的整体页面代码负责 网页美工只针对UI这块儿的东西,比如网站是否做的漂亮 web前端开发工程师是负责 ...