本文主要是介绍读Spring实战(第四版)概括—装配Bean,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
很久很久以前读过Spring实战(第三版),因为第三版和第四部差异还是特别明显的,在整体思想上有了比较重大的改变,比如用注解和JavaConfig替换Xml以及现在非常火热的Springboot在书的最后也有提到。OK,开始看书,书本的第一章讲了一下Spring存在的目的(简化Java开发)和Spring的功能,以及Spring3->Spring4增加了哪些功能,那我就从第二章开始概括本书,以给我自己后面快速回忆使用。
在Spring中,对象无需自己查找或者创建与其所关联的其他对象。相反容器负责把需要相互协作的对象引用赋予各个对象。创建应用对象之间协作关系的行为通常被称为装配(wiring),这也是依赖注入(DI)的本质。
1.Spring配置的可选方案
当描述bean如何装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:
- 在XML中进行显示配置
- 在Java中进行显示配置
- 隐式的Bean发现机制和自动装配
我建议尽可能的使用自动配置的机制。显示配置越少越好。如果必须要使用显示配置,我推荐使用类型安全并且比XML更加强大的JavaConfig,最后在使用XML配置。
2.自动化装配Bean
Spring从两个角度来实现自动化装配:
- 组件扫描(component scanning):Spring会发现应用上下文中所创建的Bean
- 组件装配(autowiring):Spring自动满足Bean之间的依赖
组件扫描和自动装配组合一起就能发挥强大的威力,他们能够将你的显示配置降低到最小。
实现自动化装配有两种方式:(组件扫描默认是不启动的)
1)通过XML启用组件扫描
首先在mvc-config.xml中启动组件扫描功能,并用base-package属性指定扫描范围
<context:component-scan base-package="com.wxh.controller"/>
再通过在需要注解的class上添加@Controller、@Service、@Repository、@Component等注解,比如:
@Controller
public class UserController {// ......}
2)通过@ComponentScan注解启用组件扫描
首先在class上使用@ComponentScan启用组件扫描,例如:
@ComponentScan
public class AppConfig {// ......}
此外:@ComponentScan(basePackages="conf")等同于@ComponentScan("conf"),然后通过在需要注解的class上添加@Controller、@Service、@Repository、@Component等注解,例如:
@Controller
public class UserController {// ......}
对于@ComponentScan可以通过basePackages或者basePackageClasses指定扫描范围,等同于XML注解中的base-package属性;如果不指定扫描范围,则默认扫描当前类所在包以及子包的所有类。当然Craig Walls建议使用basePackageClasses,因为如果代码重构的话这种方式会立马发现错误,下面是basePackageClasses的使用方式(指定基础扫描类):
@ComponentScan(basePackageClasses={UserController.class// ......
})
public class AppConfig {}
使用@Autowired可以为bean实现自动装配,@Autowired可以使用在构造函数、Setter方法、普通方法和成员变量上。比如下面的用法:
// 用法一:
@Autowired
MessageSend messageSend;
// 用法二:(构造函数也一样,主要是函数参数的依赖)
@Autowired
public void setMessageSend(MessageSend messageSend) {this.messageSend = messageSend;
}
设置@Autowired(required=false)时,Spring尝试执行自动装配,但是如果没有匹配的bean则忽略,但是这种情况故意出现空指针异常NullPointerException。@Autowired注解可以使用@Inject替换,@component可以使用@Named注解替换,后者是源于Java依赖注入规范。
3.通过Java代码装配
大部分的场景自动化装配Bean是满足要求的,但是在一些特殊场景下自动化装配Bean是无法满足要求的,比如说要将第三方的组件装配到自己的应用中,因为没有方法将@component或者@Autowired注解放置在它们的类上。但是你仍然可以采用显示装配方式:Java代码装配和XML配置。
首先需要创建配置类,创建配置类的关键是使用@Configuration注解来表明这个类是一个配置类,该类包涵Spring上下文中如何创建Bean的细节。
声明一个简单Bean:在Java的配置类中编写一个带有@Bean注解的方法,比如下面:
@Bean
public UserServiceImpl userService(){return new UserServiceImpl();
}
默认情况下Bean的ID是方法名,也可以指定Bean的ID:@Bean(name="userService"),如果有依赖可以使用下面这些的方式来实现:
@Bean
public UserServiceImpl userService(){return new UserServiceImpl(userDao());
}
@Bean
public UserDaoImpl userDao(){return new UserDaoImpl();
}
上面看起来是调用UserDaoImpl(),其实在配置类中Spring会拦截对这个方法的引用,并返回该方法所创建的bean,而不是每次都对其进行实际调用。当然下面这种方式也是可以的,userService()方法需要userDao作为参数,Spring创建Bean的时候会自动装配一个UserDaoImpl到方法中(我猜测应该和@Autowired意思差不多,当Spring Context下只有一个UserDaoImpl就可以通过匹配原则进行装配),这种方式是被推荐的,如果UserDaoImpl不是在本配置类下配置,任然可以正常使用(比如XML默认的组件扫描等)。
@Bean
public UserServiceImpl userService(UserDaoImpl userDao){return new UserServiceImpl(userDao);
}
上面我们通过构造函数的方式实现依赖注入(DI),当然我们也可以用一种更好的方式来实现依赖注入,就是用Setter方法注入,如下所示:
@Bean
public UserServiceImpl userService(UserDaoImpl userDao){UserServiceImpl userService = new UserServiceImpl();userService.setDao(userDao);return userService;
}
上面两种,通过JavaConfig或者自动化装配Bean在SpringBoot会被大量推荐使用,下面的XML模式的装配Bean已经不被推荐使用了。
4.通过XML装配Bean
XML装配Bean的方式,虽然已经不推荐了,但是还是我们最早使用的一种Bean装配的方式,还是需要学习的(很多古老的项目还在用,我目前的公司也是)。下面是一个简单Bean的声明:
<bean id="myFile" class="com.wxh.config.MyFile"></bean>
<Bean>元素类似于配置类中的@Bean,如果没有声明id="myFile",则这个bean的ID则为myFile#0,以此类推是myFile#1、myFile#2......
借助构造器(使用构造函数)注入初始化Bean的方式有两个:
- <constructor-arg>元素
- 使用Spring3中引入的c命名空间
如下所示:
<!-- 下面是使用<constructor-arg>元素方式注入Bean -->
<bean id="myFile" class="com.wxh.config.MyFile"><constructor-arg ref="fileProperty"></constructor-arg>
</bean>
<!-- 下面是使用Spring3.0中引入的c-命名空间方式注入Bean -->
<bean id="myFile-c" class="com.wxh.config.MyFile"
c:fileProperty-ref="fileProperty" c:count="0"></bean>
上面的两种方式第二张会比较简单,配置代码短,但是风格比较诡异,它的一个解读如下所示:
当然不论是<constructor-arg>元素还是Spring3.0 c-命名空间还可以使用构造器参数位置来注入相应的bean:
<bean id="myFile" class="com.wxh.config.MyFile"><constructor-arg index="0" ref="fileProperty"></constructor-arg><constructor-arg index="1" value="0"></constructor-arg>
</bean>
<bean id="myFile-c" class="com.wxh.config.MyFile" c:_0-ref="fileProperty" c:_1-ref="0"></bean>
上面说的都是将对象的引用注入到依赖于它们的对象之中,有时候我们也需要一个字面量来配置对象。如下所示(构造函数参数分别是id(int)和name(String)):
<constructor-arg value="101"></constructor-arg>
<constructor-arg value="wuxinhui"></constructor-arg>
对于c标签的使用就不介绍了,个人觉得不如普通的<constructor-arg>元素好用。
有时候,我们需要使用XML对集合的装配。构造函数如下所示(需要装配String和List<String>):
public MyFile(String name, List<String> type) {this.name = name;this.type = type;
}
对于XMl配置,我们可以使用<null/>来传递一个空值给构造器
<bean id="myfile" class="com.wxh.config.MyFile"><constructor-arg value="my.txt"></constructor-arg><constructor-arg><null/></constructor-arg>
</bean>
不过这种方式在使用的时候容易出现空指针异常,所以用下面的方式更好,使用<list>或者<set>元素来传入初始化值:
<bean id="myfile" class="com.wxh.config.MyFile"><constructor-arg value="my.txt"></constructor-arg><constructor-arg><list><value>txt</value><value>doc</value><value>docx</value><!--... 其他格式--></list></constructor-arg>
</bean>
<bean id="myfile" class="com.wxh.config.MyFile"><constructor-arg value="my.txt"></constructor-arg><constructor-arg><set><value>txt</value><value>doc</value><value>docx</value><!--... 其他格式 --></set></constructor-arg>
</bean>
list(java.util.List)和set(java.util.Set)的区别在于set是没有重复值,且无序的。其实就是Java中set和list的区别。
对于类属性的装配:有构造器注入和Setter注入两种;一般强依赖属性使用构造器,可选依赖属性使用Setter注入。对于使用注解来装配属性上面已经阐述过,下面主要说一下Spring XML方式进行属性的注入,如下所示:
<bean id="f" class="com.wxh.config.MyFile"><!-- 属性注入 --><property name="name" value="ceshi.docx"></property><!-- 其他属性 -->
</bean>
或者我们也可以使用p标签来简化属性的注入配置(个人比较推荐,语法比c标签清晰很多),如下:
<bean id="f" class="com.wxh.config.MyFile" p:name="wxk.txt"></bean>
使用util-命令空间中的元素来注入字面量(数组、集合等),下面是Spring util-命名空间中的元素:
元 素 | 描 述 |
<util:constant> | 引用某个类型的public static域,并将其暴露为bean |
<util:list> | 创建一个java.util.List类型的bean,其中包含值和引用 |
<util:map> | 创建一个java.util.Map类型的bean,其中包含值和引用 |
<util:properties> | 创建一个java.util.Properties类型的bean |
<util:property-path> | 引用一个bean的属性(或内嵌属性),并将其暴露为bean |
<util:set> | 创建一个java.utilSet类型的bean,其中包含值和引用 |
使用事例如下所示:
<!-- 静态变量 -->
<util:constant static-field="com.wxh.config.MyFile.MAX_SIZE" />
<!-- list -->
<util:list id="list" list-class="java.util.LinkedList"><value>D-1</value><value>D-2</value><value>D-3</value>
</util:list>
<!-- map -->
<util:map id="map"><entry key="数学" value="44" /><entry key="语文" value="77" />
</util:map>
<!-- 加载资源文件 -->
<util:properties id="prop" location="classpath:application.property">
</util:properties>
<!-- 暴露指定bean的属性 -->
<util:property-path path="myfile.name" />
<util:set id="school"><value>小学</value><value>中学</value><value>大学</value>
</util:set>
5.导入和混合配置
在混合配置中,@Autowired自动装配是考虑全局的所有Bean不管XML配置还是JavaConfig注解配置。一般混合配置的使用方式就是:JavaConfig拆分配置、XML拆分配置、JavaConfig配置中引用XML配置、XML中引用JavaConfig配置
5.1.JavaConfig拆分配置
一般如果Bean比较复杂或者逻辑上比较独立,我们为了逻辑上更加清晰,可以将一个JavaConfig拆分成多个。使用@Import注解进行导入,如下所示:
/** AppConfiguration.java */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.wxh.service" })
@Import(InitJavaConfig.class)
public class AppConfiguration {// 声明的Bean 。。。。。。
}
/** InitJavaConfig.java */
@Configuration
@ComponentScan(basePackages = { "com.wxh.service" })
public class InitJavaConfig {// 声明的Bean 。。。。。。
}
5.2.XML拆分配置
当如果我们使用XML进行Bean的配置,我们有时候为了逻辑更加的清晰,会对XML配置进行拆分。我们可以使用<import>元素来引用其他的XML配置,如下所示可以对拆分的XML进行相互引用:(假如我有一个init-config.xml Bean配置文件)
<import resource="init-config.xml"/>
5.3.JavaConfig配置中引用XML配置
对于使用这种模式的还是比较怪异的,早期我在使用Springboot时,由于不太懂使用注解对Bean配置,用过这种奇特的方式。对于JavaConfig配置中引用XML配置,我们可以使用@ImportResource注解实现,如下使用:
@ContextConfiguration
@Component
@ImportResource("classpath:init-config.xml")
public class AppConfig {// .....
}
5.4.XML中引用JavaConfig配置
对于也是比较奇葩,我目前还没遇到过。不过这种引用JavaConfig配置却更加简单了,只需要使用<bean>标签就可以了。使用如下所示:(AppConfig是使用注释配置的Bean的类)
<bean class="com.test.config.AppConfig"></bean>
总结:只要是拆分的模式,我习惯性的还是会创建一个根配置,这个配置会展现多个装配类或者XML文件或者混合模式。大致的使用方式如下所示(混合模式),Root XML中配置:
<bean class="com.test.config.AppConfig"></bean>
<import resource="init-config.xml"/>
这篇关于读Spring实战(第四版)概括—装配Bean的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!