Spring Boot 系列还在不断的更新,有小伙伴和松哥抱怨对 Spring 还不太懂,其实我 2016 年的时候写过一点点 Spring 的教程,可是不够详细,所以,最近决定再挖一个坑,和你们聊聊 Spring 的一些常见用法,也顺便聊聊源码。java
Spring 和 Spring Boot 强相关,所以,相信这个系列不会烂尾。web
本想写一些高大上的架构方面的,但是考虑到有不少读者是刚入行的状态,所以仍是决定先把 Spring 也和你们过一遍,固然这些东西会穿插着来,尽可能知足每个小伙伴的需求。spring
今天就先来和你们聊一聊 Spring 中 XML 装配 Bean 的一些常常被人忽略的细节。数组
使用 XML 配置 Spring ,不少人都用过,可能有的小伙伴没认真总结过,今天咱们就来稍微总结下,算是开启咱们的 Spring 之旅。bash
XML 配置是最原始最古老的 Bean 的装配方案,曾经咱们的项目离不开它,而现在,咱们却在慢慢的抛弃它,没办法,时代在进步,咱们也要进步呀。为了能看懂前辈们写的代码,咱们仍是有必要来看一下如何经过 XML 来装配 Bean。架构
首先咱们来建立一个普通的 Maven 工程(不用建立成 web 工程),建立成功以后,引入 Spring 相关的依赖,这里只要引入 spring-context 便可,以下:ui
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency></dependencies>复制代码
建立成功以后,咱们再来建立一个 Book 类,以下:this
public class Book { private Integer id; private String name; private Double price; //省略 getter/setter}复制代码
而后再在 resources 目录下建立一个 beans.xml 文件,做为 Spring 的配置文件,而后在里边配置一个 Book bean,以下:spa
<?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 class="org.javaboy.spring.Book" id="book"/></beans>复制代码
在这里,咱们在 class 属性中配置类的全路径,id 则表示 bean 的名称,也能够经过 name 属性来指定 bean 的名称,大部分场景下二者无任何差异,会有一些特殊场景下(例如用,隔开多个实例名,二者的处理方案不一样),二者有区别。code
而后咱们在 Java 代码中加载这个配置文件:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:beans.xml");复制代码
题外话:
ClassPathXmlApplicationContext 是配置文件众多的加载方式之一,表示从 classpath 下加载配置文件,这也是较经常使用的加载方式之一。其余常见的加载方式以下:
从这些不一样的实现类中能够看到,咱们也能够直接从文件系统中加载 Spring 的 XML 配置文件,使用 FileSystemXmlApplicationContext 类便可。
配置文件加载完成后,咱们就能够从 Spring 容器中去获取这些 Bean 了,方式以下:
Book book = (Book) ctx.getBean("book");复制代码
这个表示根据 id 获取相应的 Bean ,咱们也能够经过类型来获取,方式以下:
Book b1 = ctx.getBean(Book.class);复制代码
两种方式推荐第一种。
第二种经过类型获取 Bean 的方式存在一个问题,就是容器中同一个类若是存在多个实例,经过类型获取确定会出错。
可是此时咱们获取到的 Bean 中的属性所有为 null,没有值,这是由于咱们在配置的时候没有给属性指定值。
在配置 Bean 时,给 Bean 指定相关的属性值,咱们有几种不一样的方式:
首先咱们能够经过构造方法指定 bean 的属性值,前提是咱们为 Book 类提供一个有参构造方法(你们在建立有参构造方法时,必定记得再顺手加一个无参构造方法):
public class Book { private Integer id; private String name; private Double price; public Book() { } public Book(Integer id, String name, Double price) { this.id = id; this.name = name; this.price = price; } //省略 getter/setter}复制代码
而后在 XML 文件中,咱们就能够经过构造方法注入相关值了:
<bean class="org.javaboy.spring.Book" id="book2"> <constructor-arg name="id" value="99"/> <constructor-arg name="name" value="三国演义"/> <constructor-arg name="price" value="99"/></bean>复制代码
使用构造方法注入相关值的时候,也可使用下标来描述参数的顺序,注意若是使用下标,参数顺序不能错:
<bean class="org.javaboy.spring.Book" id="book3"> <constructor-arg index="0" value="99"/> <constructor-arg index="1" value="红楼梦"/> <constructor-arg index="2" value="100"/></bean>复制代码
注入成功以后,当咱们再次去获取 Bean 的时候,就能够看到这些属性了。
固然也能够经过属性注入,这是一种更为常见的方式:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/></bean>复制代码
p 名称空间本质上仍是经过属性注入的,只不过写法有些差别,p 名称空间注入方式以下:
<bean class="org.javaboy.spring.Book" id="book5" p:id="100" p:name="西游记" p:price="40"/>复制代码
以上三种不一样的属性注入方式,我给你们演示的都是注入基本数据类型,若是注入的是一个对象的话,只须要经过 ref 属性来指明对象的引用便可。
除了这些基本属性以外,还有一些特殊属性,例如集合、数组、map 等。咱们分别来看。
集合/数组的注入方式基本一致,首先咱们给项目添加一个集合属性,以下:
public class Book { private Integer id; private String name; private Double price; private List<String> authors; //省略 getter/setter}复制代码
属性注入时,能够经过 array 节点注入值:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors"> <array> <value>zhangsan</value> <value>lisi</value> <value>javaboy</value> </array> </property></bean>复制代码
也能够经过 list 节点注入值:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors"> <list> <value>zhangsan</value> <value>lisi</value> <value>javaboy</value> </list> </property></bean>复制代码
还有一个可能你们使用比较少的方式,就是经过 utils:list 来建立集合属性,而后配置到 Book 属性中去,便可:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors" ref="authors" /></bean><utils:list id="authors"> <value>javaboy</value> <value>zhangsan</value></utils:list>复制代码
这种方式比较少见。
map 的注入也有几种不一样的方式,能够经过属性指定,也能够经过 utils 来搞定,先来看第一种:
public class Book { private Integer id; private String name; private Double price; private List<String> authors; private Map<String, Object> info; //省略 getter/setter}复制代码
在 xml 文件中经过以下方式指定属性值:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors" ref="authors" /> <property name="info"> <map> <entry key="name" value="zhangsan"/> <entry key="age" value="99"/> </map> </property></bean>复制代码
也能够经过 utils 来指定 map 的值,以下:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors" ref="authors" /> <property name="info" ref="info"/></bean><utils:map id="info"> <entry key="name" value="lisi"/> <entry key="age" value="98"/></utils:map>复制代码
properties 属性也是同样的配置方案。既能够经过 props 属性指定,也能够经过 utils 来指定,例如:
public class Book { private Integer id; private String name; private Double price; private List<String> authors; private Map<String, Object> info; private Properties info2; //省略 getter/setter}复制代码
经过 props 属性指定方式以下:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors" ref="authors" /> <property name="info" ref="info"/> <property name="info2"> <props> <prop key="name">zhangsan</prop> <prop key="age">99</prop> </props> </property></bean>复制代码
经过 utils 指定方式以下:
<bean class="org.javaboy.spring.Book" id="book4"> <property name="id" value="99"/> <property name="name" value="水浒传"/> <property name="price" value="99"/> <property name="authors" ref="authors"/> <property name="info" ref="info"/> <property name="info2" ref="info2"/></bean><utils:properties id="info2"> <prop key="name">zhangsan</prop> <prop key="age">99</prop></utils:properties>复制代码
除了这几种装配方式以外,咱们也能够经过工厂方法装配。
工厂方法装配能够分为静态工厂和实例工厂两种方式,咱们分别来看。
静态工厂方法装配须要咱们先建立一个静态工厂方法,像下面这样:
public class BookFactory { public static Book getInstance() { return new Book(); }}复制代码
而后在 XML 文件中装配:
<bean class="org.javaboy.spring.BookFactory" id="book6" factory-method="getInstance"/>复制代码
此时咱们去容器中获取 book6 这个实例,拿到的就是你在静态工厂中返回的 Book 实例。
实例工厂方法则是指工厂方法是一个普通方法,不是静态的,像下面这样:
public class BookFactory2 { public Book getInstance() { return new Book(); }}复制代码
而后在 XML 文件中,咱们须要首先配置 BookFactory2 的实例,而后才能调用实例中的方法获取 Book 对象,以下:
<bean class="org.javaboy.spring.BookFactory2" id="bookFactory2"></bean><bean class="org.javaboy.spring.Book" id="book7" factory-bean="bookFactory2" factory-method="getInstance"/>复制代码
工厂方法装配的价值在哪里呢?
例如 Druid 中的 DataSource 对象,经过 DruidDataSourceBuilder.create().build()
方法来构建,若是咱们想在 XML 中作这个配置,显然不太容易,此时就可使用工厂方法装配了。
好了,这就是 XML 装配 Bean 的一个简单介绍,比较简单,可是为了知识的完整性,我仍是稍微写了下,能看到这里的都是真爱啊!