java bean 属性 比对 获取差异_Spring 中用 XML 装配 Bean,竟然有五种姿势!

44464c43ac8a195912ba4a9955a8d529.png
原创:微信公众号“江南一点雨 ”
作者:江南一点雨

Spring Boot 系列还在不断的更新,有小伙伴和松哥抱怨对 Spring 还不太懂,其实我 2016 年的时候写过一点点 Spring 的教程,但是不够详细,因此,最近决定再挖一个坑,和大家聊聊 Spring 的一些常见用法,也顺便聊聊源码。
Spring 和 Spring Boot 强相关,因此,相信这个系列不会烂尾。
本想写一些高大上的架构方面的,可是考虑到有很多读者是刚入行的状态,因此还是决定先把 Spring 也和大家过一遍,当然这些东西会穿插着来,尽量满足每一个小伙伴的需求。
今天就先来和大家聊一聊 Spring 中 XML 装配 Bean 的一些经常被人忽略的细节。
使用 XML 配置 Spring ,很多人都用过,可能有的小伙伴没认真总结过,今天我们就来稍微总结下,算是开启我们的 Spring 之旅。
基本配置
XML 配置是最原始最古老的 Bean 的装配方案,曾经我们的项目离不开它,而如今,我们却在慢慢的抛弃它,没办法,时代在进步,我们也要进步呀。为了能看懂前辈们写的代码,我们还是有必要来看一下如何通过 XML 来装配 Bean。
首先我们来创建一个普通的 Maven 工程(不用创建成 web 工程),创建成功之后,引入 Spring 相关的依赖,这里只要引入 spring-context 即可,如下:

<dependencies>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>5.1.5.RELEASE</version>
 </dependency>
</dependencies>


创建成功之后,我们再来创建一个 Book 类,如下:

public class Book {
 private Integer id;
 private String name;
 private Double price;
 //省略 getter/setter
}


然后再在 resources 目录下创建一个 beans.xml 文件,作为 Spring 的配置文件,然后在里边配置一个 Book bean,如下:

<?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 的名称,大部分场景下两者无任何差别,会有一些特殊场景下(例如用,隔开多个实例名,两者的处理方案不同),两者有区别。
然后我们在 Java 代码中加载这个配置文件:

ClassPathXmlApplicationContext ctx = new 
ClassPathXmlApplicationContext("classpath:beans.xml");

题外话:
ClassPathXmlApplicationContext 是配置文件众多的加载方式之一,表示从 classpath 下加载配置文件,这也是较常用的加载方式之一。其他常见的加载方式如下:

31a6bd0519ed567e0ac5e0290c19f8a8.png


从这些不同的实现类中可以看到,我们也可以直接从文件系统中加载 Spring 的 XML 配置文件,使用 FileSystemXmlApplicationContext 类即可。
配置文件加载完成后,我们就可以从 Spring 容器中去获取这些 Bean 了,方式如下:

Book book = (Book) ctx.getBean("book");


这个表示根据 id 获取相应的 Bean ,我们也可以通过类型来获取,方式如下:

Book b1 = ctx.getBean(Book.class);


两种方式推荐第一种。
第二种通过类型获取 Bean 的方式存在一个问题,就是容器中同一个类如果存在多个实例,通过类型获取肯定会出错。
但是此时我们获取到的 Bean 中的属性全部为 null,没有值,这是因为我们在配置的时候没有给属性指定值。
在配置 Bean 时,给 Bean 指定相关的属性值,我们有几种不同的方式:
1.构造方法指定
首先我们可以通过构造方法指定 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 的时候,就可以看到这些属性了。
2.通过属性注入
当然也可以通过属性注入,这是一种更为常见的方式:

<bean class="org.javaboy.spring.Book" id="book4">
 <property name="id" value="99"/>
 <property name="name" value="水浒传"/>
 <property name="price" value="99"/>
</bean>


3.p名称空间注入
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
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
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 的一个简单介绍,比较简单,但是为了知识的完整性,我还是稍微写了下,能看到这里的都是真爱啊!

Spring Boot 系列还在不断的更新,有小伙伴和松哥抱怨对 Spring 还不太懂,其实我 2016 年的时候写过一点点 Spring 的教程,但是不够详细,因此,最近决定再挖一个坑,和大家聊聊 Spring 的一些常见用法,也顺便聊聊源码。
Spring 和 Spring Boot 强相关,因此,相信这个系列不会烂尾。
本想写一些高大上的架构方面的,可是考虑到有很多读者是刚入行的状态,因此还是决定先把 Spring 也和大家过一遍,当然这些东西会穿插着来,尽量满足每一个小伙伴的需求。
今天就先来和大家聊一聊 Spring 中 XML 装配 Bean 的一些经常被人忽略的细节。
使用 XML 配置 Spring ,很多人都用过,可能有的小伙伴没认真总结过,今天我们就来稍微总结下,算是开启我们的 Spring 之旅。
基本配置
XML 配置是最原始最古老的 Bean 的装配方案,曾经我们的项目离不开它,而如今,我们却在慢慢的抛弃它,没办法,时代在进步,我们也要进步呀。为了能看懂前辈们写的代码,我们还是有必要来看一下如何通过 XML 来装配 Bean。
首先我们来创建一个普通的 Maven 工程(不用创建成 web 工程),创建成功之后,引入 Spring 相关的依赖,这里只要引入 spring-context 即可,如下:

<dependencies>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>5.1.5.RELEASE</version>
 </dependency>
</dependencies>


创建成功之后,我们再来创建一个 Book 类,如下:

public class Book {
 private Integer id;
 private String name;
 private Double price;
 //省略 getter/setter
}


然后再在 resources 目录下创建一个 beans.xml 文件,作为 Spring 的配置文件,然后在里边配置一个 Book bean,如下:

<?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 的名称,大部分场景下两者无任何差别,会有一些特殊场景下(例如用,隔开多个实例名,两者的处理方案不同),两者有区别。
然后我们在 Java 代码中加载这个配置文件:

ClassPathXmlApplicationContext ctx = new 
ClassPathXmlApplicationContext("classpath:beans.xml");

题外话:
ClassPathXmlApplicationContext 是配置文件众多的加载方式之一,表示从 classpath 下加载配置文件,这也是较常用的加载方式之一。其他常见的加载方式如下:

31a6bd0519ed567e0ac5e0290c19f8a8.png


从这些不同的实现类中可以看到,我们也可以直接从文件系统中加载 Spring 的 XML 配置文件,使用 FileSystemXmlApplicationContext 类即可。
配置文件加载完成后,我们就可以从 Spring 容器中去获取这些 Bean 了,方式如下:

Book book = (Book) ctx.getBean("book");


这个表示根据 id 获取相应的 Bean ,我们也可以通过类型来获取,方式如下:

Book b1 = ctx.getBean(Book.class);


两种方式推荐第一种。
第二种通过类型获取 Bean 的方式存在一个问题,就是容器中同一个类如果存在多个实例,通过类型获取肯定会出错。
但是此时我们获取到的 Bean 中的属性全部为 null,没有值,这是因为我们在配置的时候没有给属性指定值。
在配置 Bean 时,给 Bean 指定相关的属性值,我们有几种不同的方式:
1.构造方法指定
首先我们可以通过构造方法指定 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 的时候,就可以看到这些属性了。
2.通过属性注入
当然也可以通过属性注入,这是一种更为常见的方式:

<bean class="org.javaboy.spring.Book" id="book4">
 <property name="id" value="99"/>
 <property name="name" value="水浒传"/>
 <property name="price" value="99"/>
</bean>


3.p名称空间注入
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
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
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 的一个简单介绍,比较简单,但是为了知识的完整性,我还是稍微写了下,能看到这里的都是真爱啊!


Spring系列视频,包含springboot、springmvc……

2915d1fc090f2c2a994d9c2a8206855c.png

a14cbd6426d9144461f65c76e95d14c6.png

98e6b19ca69e5d564b8bd01f103b606a.png
Java基础、入门、精通、架构师全套资源​shimo.im
cbca8b2072fc3705e290e2d7443c411e.png

推荐阅读:

java钢铁侠-马克51号:Github上有哪些Java面试/学习相关的仓库推荐?​zhuanlan.zhihu.com
e07e2b7c9b4f6fc82bae2fb5a99b1f5b.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值