作为Spring新手,边学《Spring in Action》边总结相关知识。

什么是DI

DI,Dependency Injection,即依赖注入,不是去依赖“注入”这个东东,而是将“依赖”这个东东给注入。

那么什么是依赖?我们都知道,一个稍微大一点的应用程序,它都是由若干个对象组成的,这些对象如果各干各的谁也不理谁,那工作怎么可能完成呢!所以这些对象肯定都是意识到了一些其它对象的存在,并且要和它们交流通信,朝着共同的目标去努力,这样才可能达成目标,正所谓众志成城也。从编程的角度来说,这些对象之间存在着依赖关系。

传统的建立这些依赖关系的方法,是让对象自己去记录、维护自己所依赖的对象,这本是一些本不属于它们自己工作范围的事情。这样也会增加对象之间的耦合度,使得它们难以复用、难以测试。

而在Spring里面,对象自己不需要负责去寻找或是创建它们所依赖的对象,而是由容器(container)来维护对象之间的引用关系。举个栗子,订单管理模块可能会需要一个信用卡授权模块,但是它不需要去创建这个信用卡授权模块——它只需要两手空空地现身,自然会有人给它一个信用卡授权模块。

这种创建应用程序对象之间的依赖关系的行为,就是DI的本质,也经常被称作装配(wiring),被装配到一起的对象,称为bean。有很多装配的方法,首先可以来感受一下配置Spring容器的三种最常见的方法。

配置Spring容器

虽然容器要负责创建beans,并且通过DI来协调这些对象之间的关系,但当然也得靠我们程序员来告诉Spring,要创建哪些beans,怎样把它们装配到一起等等。Spring提供三种机制来让我们做这件事:

通过XML显式配置

通过Java显式配置

隐式进行bean搜索并自动装配

上述三种方法该如何选择呢?书作者Walls的建议是,尽量使用自动配置,需要的显式说明越少就越好。如果必须显式配置(比如当你没有你要配置的beans的源代码时),通过Java配置更理想,因为它类型安全且功能更强。只有当存在方便的XML命名空间可用,而Java配置中又没有可替代者时,才考虑用XML配置。

接下来依次学习这三种机制的使用方法。

一、自动装配beans

Spring从两个方面来实现自动的装配:

组件扫描(Component scanning)——Spring自动找寻需要在应用程序上下文中创建的beans。

自动装配(Autowiring)——Spring自动满足bean的依赖。

以上两者组合在一起,就可以实现强大的自动装配的功能,将显式的配置说明控制到最少。具体来说,可以通过@Component、@ComponentScan、@Autowired注解来实现自动装配,下面分别介绍它们的作用。

@Component

被@Componet修饰的类,Spring为会其创建一个bean。Spring的应用程序上下文里,所有的beans都有一个ID。当@Component不加参数时,为该类生成的ID就是其类名(首字母小写);也可以加参数,如

@Component("someCoolName")

public class SomeClass {}

生成的bean的ID就是someCoolName了。

但是组件扫描并不是默认开启的,所以还需要写一点显式的配置说明,告诉Spring去找寻被@Component修饰的类,为它们创建beans。

@ComponentScan

如果存在被@ComponentScan修饰的类,那么Spring就会去扫描找寻组件来生成bean(也可以通过XML文件的方式来配置组件扫描)。

当@ComponentScan不加参数时,扫描范围就是该类所在的包。可以加字符串参数,如

@ComponentScan(basePackages = "somepackage")

public class ConfigurationClass {}

这样扫描范围就是somepackage包;参数还可以是字符串数组,如

@ComponentScan(basePackages = {"somepackage", "anotherpackage"})

public class ConfigurationClass {}

扫描范围就变成了多个包。除了字符串,参数还可以是类或接口,如

@ComponentScan(basePackageClasses = {Class1.class, Class2.class})

public class ConfigurationClass {}

这样扫描范围就是这些类所在的包。相比于传字符串形式的参数,传Java类类型的参数更加类型安全。另外,虽然这里用的是一个类来作为@ComponentScan的标记类,但更推荐用一个空的接口来做标记,这样可以更加“重构友好”(refactor-friendly)地引用接口,而不用引用任何实际的程序代码(它们以后可能会被重构到你想要进行组件扫描的包之外)。

如果应用程序中所有对象都没有依赖,那么靠组件扫描就够了。但很多对象都是依赖其它对象的,因此在装配beans的时候,也需要把它们具有的所有依赖都一同装配进来。

@Autowired

简单地说,自动装配就是让Spring自动去满足bean的需求,也就是在应用程序上下文里去寻找这个bean所需要的其它的beans。@Autowired注解就是用来告诉Spring,需要进行自动装配(也可以用Java自带的@Inject注解,两者存在细微区别,但基本可以互换)。如下述的CDPlayer类:

@Component

public class CDPlayer implements MediaPlayer {

private CompactDisc cd;

@Autowired

public CDPlayer(CompactDisc cd) {

this.cd = cd;

}

public void play() {

cd.play();

}

}

它的构造方法有@Autowired注解,表示当Spring创建CDPlayer的bean时,应该用该构造方法来实例化,并且传入一个CompactDisc的bean。

其实不光是构造方法,任何方法都可以用@Autowired注解,Spring就会去尝试满足该方法的参数所表达的依赖。如果没有bean满足匹配,Spring就会在应用程序上下文被创建的时候扔一个异常。可以通过设置@Autowired的required参数来避免异常:

@Autowired(required=false)

public CDPlayer(CompactDisc cd) {

this.cd = cd;

}

当required为false时,Spring仍然会尝试去自动装配,但是如果没有匹配的bean,它就不会装配这个等待被装配的bean。要小心这样设置,因为未装配的属性可以导致空指针异常。

如果存在不只一个bean满足匹配,Spring就会扔一个表示歧义的异常,当然有办法可以管理并避免歧义,这里就不讨论了。

二、通过Java装配beans

尽管大多数情况下,通过组件扫描和自动装配是更好的方法,但有些情况下无法使用自动配置,你将不得不显式地进行配置。比如你想要将第三方库中的组件装配进程序中,但是没有它们的源代码,也就无法给它们打上@Component这些注解,于是自动配置不可行。

通过JavaConfig配置比通过XML配置更为推荐,前者功能更强,更加类型安全且重构友好,因为它就是Java代码。

但同时也要意识到,这些Java代码和程序中其它的Java代码又不一样,因为在概念上,它和程序中的业务逻辑、领域模型这些是分离的,它属于配置代码,因此不应该包含任何业务逻辑,也不应该侵入任何包含业务逻辑的代码。实际上,经常把这些配置代码放置于一个单独的包,这样就不会跟程序的其它逻辑混在一起。

接下来看看具体怎么用JavaConfig进行装配。

@Configuration

要创建一个JavaConfig类,就用@Configuration注解这个类,该注解将它标识为一个配置类,它应该包含需要在Spring应用程序上下文中创建的beans的详细信息。如:

@Configuration

public class CDPlayerConfig {}

它将CDPlayerConfig类标记为配置类。

@Bean

如果某方法被@Bean注解,那么Spring就知道该方法将会返回一个对象,该对象应该在Spring应用程序上下文中被注册为一个bean,方法体内包含着最终生成bean实例的代码逻辑。例如下面的代码声明了CompactDisc的bean:

@Bean

public CompactDisc sgtPeppers() {

return new SgtPeppers();

}

方法体返回了一个新的SgtPeppers实例(SgtPeppers继承自CompactDisc),事实上方法里面可以写任何Java代码,只要最后能返回一个CompactDisc实例。

默认情况下,这个bean会被赋予一个与@Bean注解的方法名相同的ID,上述样例中就是sgtPeppers。可以通过name属性来赋一个不同的名字:

@Bean(name="lonelyHeartsClubBand")

public CompactDisc sgtPeppers() {

return new SgtPeppers();

}

CompactDisc的bean比较简单,它自己没有依赖的对象。现在如果要声明一个CDPlayer的bean,它是依赖一个CompactDisc对象的,应该怎样来装配呢?

JavaConfig中最简单的做法就是调用所需bean的@Bean方法,还是举例说明,你可以这样声明CDPlayer的bean:

@Bean

public CDPlayer cdPlayer() {

return new CDPlayer(sgtPeppers());

}

cdPlayer()方法像sgtPeppers()方法一样,也有@Bean注解,以此来表示它将会产生一个要在Spring应用程序上下文中注册的bean实例,这个bean的ID是cdPlayer,与方法名相同。

cdPlayer()的方法体与sgtPeppers()的有着细微的不同,前者并没有通过默认构造方法来创建实例,而是调用了有一个CompactDisc参数的构造方法来创建CDPlayer的实例。

看上去CompactDisc的实例是通过调用sgtPeppers()方法来提供的,但并不是这样。因为sgtPeppers()方法有@Bean注解,Spring就会拦截任何对它的调用,并确保该方法提供的bean被返回,而不是让它再被调用一次。

继续举栗子,假设你又引进了另一个CDPlayer的bean,和第一个一模一样:

@Bean

public CDPlayer cdPlayer() {

return new CDPlayer(sgtPeppers());

}

@Bean

public CDPlayer anotherCDPlayer() {

return new CDPlayer(sgtPeppers());

}

如果对sgtPeppers的调用被当成与其它普通Java方法的调用一样,那么每一个CDPlayer都会被给予一个它自己的SgtPeppers实例。如果我们谈论的是真实的CD播放机和压缩碟片,这倒是有意义的,因为当你有两个CD播放机时,不可能将一张碟片同时插入到两个播放器中。

但在软件中,你可以随意将同一个SgtPeppers的实例注入到任意多个其它的beans里面去。默认情况下,Spring中的所有beans都是单例,也没有什么原因需要你为第二个CDPlayer的bean再创建一个重复的实例,所以Spring会阻止对sgtPeppers()的调用,并确保返回的bean是当Spring自己调用sgtPeppers()时所创建的CompactDisc bean。因此,两个CDPlayer的beans都会被给予同一个SgtPeppers的实例。

如果对通过调用其方法来引用一个bean感到迷惑,另一种方式也许更容易让人理解:

@Bean

public CDPlayer cdPlayer(CompactDisc compactDisc) {

return new CDPlayer(compactDisc);

}

这里,cdPlayer()方法需要一个CompactDisc作为参数,当Spring调用cdPlayer()来创造CDPlayer bean的时候,它将一个CompactDisc自动装配进配置方法,然后方法体内可以在任何适当的时候使用该bean。利用这个机制,cdPlayer()方法仍然可以将CompactDisc注入CDPlayer的构造方法中去,而无需显式地引用CompactDisc的@Bean方法。

这一引用其它beans的方法通常是最好的选择,因为它不依赖在同一个配置类中声明的CompactDisc bean。事实上,CompactDisc bean也完成可以不用通过JavaConfig来声明,它可以被组件扫描所发现,也可以在XML中声明。你还可以将你的配置拆成一个健壮的混合体,将配置类、XML文件、自动扫描及装配的beans这三者融合起来。不管CompactDisc是怎样创建的,Spring都乐于将其交给这个配置方法,用来创建CDPlayer的bean。

任何情况下都有必要意识到,尽管你是在通过CDPlayer的构造方法进行DI,在这里你也不是不可以应用其它形式的DI。例如,当你想要通过setter方法来注入一个CompactDisc,cdPlayer()也许就是下面这样了:

@Bean

public CDPlayer cdPlayer(CompactDisc compactDisc) {

CDPlayer cdPlayer = new CDPlayer(compactDisc);

cdPlayer.setCompactDisc(compactDisc);

return cdPlayer;

}

现在又要重复提醒一下,一个@Bean方法的主体部分可以使用任何需要的Java代码来生成bean实例。构造方法和setter方法的注入只是其中两个简单的例子,你能在一个@Bean注解的方法里做的事情多了去了,唯一的限制也就只有Java语言本身的能力了。

三、通过XML装配beans

(暂略)

java中di,初识Spring的DI及其基本用法相关推荐

  1. java 获取spring对象数组_解析Java中如何获取Spring中配置的bean

    解析Java中如何获取Spring中配置的bean Java中如何获取Spring中配置的bean?下面是由百分网小编为大家整理的解析Java中如何获取Spring中配置的bean,喜欢的可以收藏一下 ...

  2. stopwatch_在Java中衡量执行时间– Spring StopWatch示例

    stopwatch 有两种方法可以通过使用System.currentTimeinMillis()或使用System.nanoTime() 来测量Java中经过的执行时间 . 这两个方法可用于测量 J ...

  3. 在Java中衡量执行时间– Spring StopWatch示例

    有两种方法可以通过使用System.currentTimeinMillis()或通过使用System.nanoTime() 来测量Java中经过的执行时间 . 这两个方法可用于测量 Java中两个方法 ...

  4. Java中static、this、super、final用法

    一.static 请先看下面这段程序: public class Hello{ public static void main(String[] args){ //(1) System.out.pri ...

  5. 在Java中什么是IoC和DI?DI是如何实现的?

    IoC叫控制反转,是Inversion of Control的缩写,DI(Dependency Injection)叫依赖注入,是对IoC更简单的诠释. 控制反转是把传统上由程序代码直接操控的对象的调 ...

  6. java中di,在Java中什么是IoC和DI?DI是如何实现的?

    IoC叫控制反转,是Inversion of Control的缩写,DI(Dependency Injection)叫依赖注入,是对IoC更简单的诠释. 控制反转是把传统上由程序代码直接操控的对象的调 ...

  7. DI 之Spring更多DI的知识

    转载自  [第三章] DI 之 3.3 更多DI的知识 --跟我学spring3 3.3.1  延迟初始化Bean 延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始 ...

  8. Java中12 种 Spring 常用注解,必须记住!

    1.声明bean的注解 @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller ...

  9. java中jooq,在Spring中使用jOOQ源码案例

    Spring专题 在Spring中使用jOOQ源码案例 虽然ORM大部分性能问题是由开发者自己引起的,以只读方式使用ORM是不值得的,现在有一种其他可选方式,使用JOOQ,jOOQ从您的数据库生成Ja ...

最新文章

  1. Android 文件操作
  2. Android数据适配器Adapter简介
  3. 关于Exception类
  4. MySQL Schema设计(三)利用Python操作Schema
  5. html算术计算,js实现计算器 提供算术表达式求值
  6. GraphQL:面对复杂类型
  7. mysql外键关联查询_MySQL外键约束和多表联查
  8. PSIM软件BUCK转换数字控制官方例程
  9. Spring Boot Lombok配置
  10. android fragment传递参数_fragment之间传值的两种方法
  11. CocoaPods详解之----制作篇
  12. 相同java代码,编译生成class文件不同的原因-JDK版本不同(大版本相同,小版本不同)
  13. 并操作、差操作、广义积操作
  14. mysql中join操作_Mysql中的join操作
  15. 如果计算机黑屏请分析原因是什么原因,电脑黑屏是什么原因 电脑黑屏原因分析【解决方法】...
  16. 计算机主板虚拟化,启用Lenovo计算机BIOS虚拟化的方法
  17. 网站备案 服务器变更,网站变更服务器备案
  18. android生成透明图片格式,Android编程实现图片透明的方法
  19. 跳跃表的原理以及实现
  20. bt5重启网卡命令_BT5之网络配置

热门文章

  1. Java实现Websocket客户端
  2. qtabbar设置不同宽度_前端之css(宽高)设置小技巧
  3. eter测试软件,AcCellerator高通量单细胞力学荧光测试分析系统
  4. 一文读懂BloomFilter
  5. Unity 仿 了不起修仙模拟器画符
  6. opc-da协议机器数据采集-python
  7. RedHat7.0 实验指南之firewall的应用
  8. CSS3属性justify-content
  9. 你所不知道的ndJSON:序列化与管道流
  10. 程序设计基础c语言上机考试,程序设计基础C语言上机考试题第一套.DOC