1. Spring配置的可选方案
- 在XML中进行显式配置;
- 在Java中进行显式配置;
- 隐式的bean发现机制和自动装配。
2. 自动化装配bean
Spring从两个角度来实现自动化装配:
组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring):Spring自动满足bean之间的依赖。2.1 创建可被发现的bean
使用了@Component注解。这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。
启用组件扫描
- 基于Java @ComponentScan注解启用了组件扫描
@Configuration@ComponentScan(basePackages="soundsystem","video")public class CDPlayerConfig { }
如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。(通过一个空标记接口(remark interface)来这这个工作,是一个很好的实践。)
2.2 设置组件扫描的基础包
上面的例子中,所设置的基础包是以String类型表示的。我认为这是可以的,但这种方法是类型不安全(not type-safe)的。除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:
@Configuration@ComponentScan(basePackageClasses = {CDPlayer.class})public class CDPlayerConfig { }
basePackages属性被替换成了basePackageClasses。同时,我们不是再使用String类型的名称来指定包,为basePackageClasses属性所设置的数组中包含了类。这些类所在的包将会作为组件扫描的基础包。
- 通过XML启用组件扫描
2.3 通过为bean添加注解实现自动装配
为了声明要进行自动装配,我们可以借助Spring的@Autowired注解.
@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。@Autowiredpublic void setCompactDisc(CompactDisc cd) { this.cd=cd;}
如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,你可以将@Autowired的required属
性设置为false:@Autowired(required = false)public CDPlayer(CompactDisc cd){ this.cd=cd;}
3. 通过Java代码装配bean
你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。 在这种情况下,你必须要采用显式装配的方式。在进行显式配置的时候,有两种可选方案:Java和XML。
JavaConfig是配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了。
- 声明简单的bean
@Beanpublic CompactDisc sgtPeppers() { return new SgtPeppers();}
- 借助JavaConfig实现注入
@Beanpublic CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc);}
通过这种方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。
- 使用方法替代构造器的注入 需要提醒的是,我们在这里使用CDPlayer的构造器实现了DI功能,但是我们完全可以采用其他风格的DI配置。比如说,如果你想通过Setter方法注入CompactDisc的话,那么代码看起来应该是这样的:
@Beanpublic CDPlayer cdPlayer(CompactDisc compactDisc) { CDPlayer cdPlayer = new CDPlayer(compactDisc); cdPlayer.setCompactDisc(compactDisc); return cdPlayer;}
4. 通过XML装配bean
- 声明一个简单的
<bean>
- 借助构造器注入初始化bean
- 使用方法替代构造器的注入
<property>
元素为属性的Setter方法所提供的功能与<constructor-arg>
元素为构造器所提供的功能是一样的。
- 将字面量注入到属性中 有Java对象如下:
public class BlankDisc implements CompactDisc { private String title; private String artist; private Listtracks; public BlankDisc(String title, String artist, List tracks) { this.title = title; this.artist = artist; this.tracks = tracks; } public void play() { System.out.println("Playing " + title + " by " + artist); for (String track : tracks) { System.out.println("-Track: " + track); } }}
可用以下xml将字面值(String
、List<String>
)注入到属性中:
Sgt. Pepper's Lonely Hearts Club Band With a Little Help from My Friends Lucy in the Sky with Diamonds Getting Better Fixing a Hole She's Leaving Home Being for the Benefit of Mr. Kite! Within You Without You When I'm Sixty-Four Lovely Rita Good Morning Good Morning Sgt. Pepper's Lonely Hearts Club Band (Reprise) A Day in the Life
5. 导入和混合配置
5.1 在JavaConfig中引用XML配置
- Java配置
@Configurationpublic class CDPlayerConfig { @Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); }}
- XML配置
Sgt. Pepper's Lonely Hearts Club Band With a Little Help from My Friends Lucy in the Sky with Diamonds Getting Better Fixing a Hole
以上xml配置中使用了
c命名空间
,这里_0,_1相当与第一个参数和第二个参数。
- 在Java配置中引入XML配置 两个bean——配置在JavaConfig中的CDPlayer以及配置在XML中BlankDisc——都会被加载到Spring容器之中。代码如下:
@Configuration@Import(CDPlayerConfig.class)@ImportResource("classpath:cd-config.xml")public class SoundSystemConfig {}
@Import将两个配置类组合在一起;
@ImportResource可在JavaConfig中引入XML配置; 以上代码是利用一个新的Java配置类,引入了已有的Java配置类以及XML配置文件。当然也可以在已有的Java配置类中直接引入XML配置文件。5.2 在XML配置中引用JavaConfig
- Java配置
@Configurationpublic class CDConfig { @Bean public CompactDisc compactDisc() { return new SgtPeppers(); }}
- XML配置
- 在XML配置中引入Java配置
以上代码是在已有的XML配置文件中引入Java配置类,当然可以建立一个新的XML引入已有的XML配置文件和已有的Java配置类,如:
XML配置文件应用已有XML配置文件使用<import resource>
标签。
注意
不管使用JavaConfig还是使用XML进行装配,我通常都会创建一个根配置(root configuration),也就是这里展现的这样,这个配置会将两个或更多的装配类和/或XML文件组合起来。我也会在根配置中启用组件扫描(通过<context:component-scan>
或@ComponentScan)。
Spring 3的一些内容
1、装配wiring,即创建应用对象之间的协作关系的行为,者也是依赖注入的本质。
2、创建Spring配置 从Sring3.0开始,Spring容器提供了两种配置Bean的方式:3、典型的xml配置文件:
beans命名空间不是唯一的Spring命名空间,Spring核心框架自带了10个命名空间配置,如下:
4、当Spring容器加载Bean时,Spring使用反射来创建Bean。5、覆盖Spring默认的单例配置:Spring Bean默认都是单例,但有时我们每次都需要获得唯一的Bean实例,比如每个人的门票要唯一:
我们只需要配置Bean的scope属性为prototype即可: 1 Spring提供的作用域选项:6、初始化和销毁Bean
Auditorium(舞台)要保证做的最先和最后两件事情: 开灯,关灯。 需要在Bean中使用init-method和destory-method属性: 当有很多上下文定义的Bean有相同名字的初始化方法和销毁方法时,可以直接在上层beans元素中声明default-init-method和default-destory-method属性,从而避免每一个都要设置一遍的问题。最小化Spring XML配置——
Bean的自动装配(autowiring); Bean的自动检测(autodiscovery);一、自动装配Bean属性
1、 4种类型的自动装配 byName——把与Bean的属性具有相同名字(或ID)的其他Bean自动装配到Bean的对应属性中,名字不匹配则不装配; byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中,类型不匹配则不装配; constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中; autodetect——首先尝试使用constructor自动装配,若失败,再尝试使用byType进行自动装配。2、byName
原来的装配是这样的: 当bean的id属性和property属性的name一致时,通过配置autowire属性皆可以自动装配instrument了,修改后的文件如下:3、byType自动装配
byType自动装配的工作方式类似于byName,只不过不再是匹配属性的名字而是检查属性的类型。 若Instrument类型有多个Bean,Spring就会抛出异常,为解决这个为,需要标识首选的Bean或者取消某个Bean的自动装配候选资格:首选Bean
设置Bean的primary属性为true。但是不幸的是所有的bean的默认primary都是true,因此只能将非首选的Bean的primary属性设置为false: 取消Bean候选资格 排除某些Bean是,可设置这些Bean的autowire-candidate属性为false: 4、constructor自动装配 通过构造器注入来配置Bean,我们可以移除元素,由Spring在应用上下文中自动选择Bean注入到构造器入参中: 与byType一样,当发现多个Bean匹配某个构造器入参时,Spring依旧报出异常。
5、最佳自动装配
首选constructor自动装配,若没有发现与构造器相匹配的Bean时,Spring将尝试使用byType自动装配。6、默认自动装配
当需要为上下文中的每一个Bean配置相同的上述自动装配autowire属性,可以在根目录增加一个default-autowire属性: 该属性默认none,标示所有Bean都不使用自动装配,除非Bean自己配置了autowire属性。 另外,种配置的autowire属性可以覆盖根元素的默认自动装配策略——混合策略。 混合策略对构造器有所限制:不能混合使用constructor自动装配策略和元素。二、使用注解装配
注解方式允许更细粒度的自动装配,可以选择性地标注某一个属性来对其应用自动装配。 Spring容器默认禁止注解装配,最简单的启用方式是使用Spring的context命名空间配置中的元素。 注解,可以为Spring属性、方法和构造器进行自动装配,因此要写在这些的上方一行。 1、@Autowired 对属性的setter方法进行标注: Spring会尝试对该方法执行byType自动装配。 除了setter方法,@Autowired可以标注需要自动装配Bean引用的任何方法、构造器。 甚至可以使用@Autowired直接标注属性,并删除setter方法: 但是,简单的@Autowired标注有两种麻烦——没有bean或者多个bean:没有bean
当属性是可选的,而bean不存在时,可以使用required属性: @Autowired(required=false) 多个bean【限定歧义性的依赖】 为帮助@Autowired鉴别哪一个Bean才是我们想要的,可配合使用Spring的@Qualifier注解,该注解可以包含一个字符串作为Bean的标识(标识不一定是id)。 如将一个id为guitar的乐器装配到instrument属性中: 注意,上述的guitar可以在xml中对的id属性设置; 另外,可以不用id属性,而是在中以qualifier子元素(子元素vs属性)中写明,或者在Guitar类上标注: 若是Qualifier含义不是那么明确或者范围过大,可以自定义Qualifier接口,以代替Qualifier:
2、借助@Inject实现基于标准的自动装配
JCP(Java Community Process)发布Java依赖注入规范——JSR-330,@Inject注解是JSR-330的核心部件。 》note: JDK中并没有JSR-330的实现,需要下载响应的jar依赖包@Inject
和@Autowired一样,@Inject可以自动装配属性、方法和构造器,但是没有required属性,即@Inject注解所标注的依赖关系必须存在,否则会报出异常。 与@Autowired对应的@Qualifier类似,@Inject对应@Name注解。 @Inject本身也有@Qualifier注解,但是不推荐使用,而是推荐自定义限定器注解。3、在注解中使用表达式
String类型以及基本类型的属性,可以直接通过@Value的方式填入值。但是这相当于硬编码,与直接写在代码中没多大区别,因此不推荐使用,如一首歌名Eruption: 但是@Value可以使用SpEL表达式,获取系统属性,功能强大:三、自动检测Bean
= +自动检测和定义Bean。 自动检测可以免去的定义,是的Spring应用中的大多数甚至所有Bean实现定义【厉害啊】和装配。 元素即负责Spring的自动检测Bean和定义Bean。 【vs ,该元素有助于消除Spring配置中的和元素,但是依旧需要显示定义】 使用代替 元素: 那么,哪些类需要注册为Spring的Bean呢? 1、为自动检测标注Bean 默认地,查找使用“构造型(stereotype)”注解所标注的类,这些特殊的注解如下: 如,对一个类注解@Component: 那么Spring扫描到Guitar,会自动将它注册为Spring Bean,该Bean的ID默认为无限定类名(何为无限定?)——guitar。2、过滤组件扫描
通过为配置和子元素,我们可以随意调整扫描行为。如自动注册所有的Instrument实现类: 的type和expression属性一起协作来定义组件扫描策略。 不过,默认的基于注解的过滤策略是最经常用的。四、使用Spring基于Java的配置
1、创建基于Java的配置 Spring的Java配置可以不用XML就可以编写大多数的Spring配置,但是还是需要极少量的XML来启动Java配置。就是说,XML总要有的。Spring在base-package指定的包内查找使用@Configuration注解所标注的所有类。
2、定义一个配置类
使用@Configuration注解的Java类,就等价于XML配置中的元素,可以在改类内部定义@Bean方法了。 声明的方法示例如下,将得到ID为方法名的Bean: 优点:Spring的基于Java的配置中,并没有String属性,Bean的ID和类型都被是否方法签名的一部分,所以可以进行编译期检查来确保Bean的类型是合法类型,并且Bean的ID是唯一的。 vs XML配置:Bean的类型和ID都是由String属性标示的,String标识符的缺点是它们无法进行编译期检查,如果重命名了Juggler类,或许会忘记修改相应的XML配置! 智能:其他Bean的声明调用另一个Bean时,Spring都会拦截该方法的调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。源码
自动配置:
Java配置: XML配置: 混合配置: