读Spring实战(第四版)概括—装配Bean

2024-09-08 09:58

本文主要是介绍读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对集合的装配。构造函数如下所示(需要装配StringList<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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1147798

相关文章

Spring MVC如何设置响应

《SpringMVC如何设置响应》本文介绍了如何在Spring框架中设置响应,并通过不同的注解返回静态页面、HTML片段和JSON数据,此外,还讲解了如何设置响应的状态码和Header... 目录1. 返回静态页面1.1 Spring 默认扫描路径1.2 @RestController2. 返回 html2

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

SpringBoot 整合 Grizzly的过程

《SpringBoot整合Grizzly的过程》Grizzly是一个高性能的、异步的、非阻塞的HTTP服务器框架,它可以与SpringBoot一起提供比传统的Tomcat或Jet... 目录为什么选择 Grizzly?Spring Boot + Grizzly 整合的优势添加依赖自定义 Grizzly 作为

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

java Stream操作转换方法

《javaStream操作转换方法》文章总结了Java8中流(Stream)API的多种常用方法,包括创建流、过滤、遍历、分组、排序、去重、查找、匹配、转换、归约、打印日志、最大最小值、统计、连接、... 目录流创建1、list 转 map2、filter()过滤3、foreach遍历4、groupingB

SpringBoot如何使用TraceId日志链路追踪

《SpringBoot如何使用TraceId日志链路追踪》文章介绍了如何使用TraceId进行日志链路追踪,通过在日志中添加TraceId关键字,可以将同一次业务调用链上的日志串起来,本文通过实例代码... 目录项目场景:实现步骤1、pom.XML 依赖2、整合logback,打印日志,logback-sp