前言:上篇文章谈到了bean的简介,这篇文章将带你深入了解Spring中bean并通过实现Bean来更好的认识Spring中Bean的概念,同时掌握Spring中Bean的操作。毕竟IOC、DI是Spring的核心思想,而Bean就是这个思想的载体,Spring就是围绕着Bean的创建注入以及Bean的使用管理进行项目管理的。
ps1:本文是一个学习笔记,是根据我的理解汇总的博客,可能趣味性少一点,如果你想看趣味性强一点,那种娓娓道来的文章。可以看一下《Spring实战第四版》
“工欲善其事必先利其器”,因此实践第一步 创建Spring学习项目,创建项目了才能够写Bean的代码。创建过程看我的另一篇文章: Spring学习项目的创建配置
创建完成,开始正式进入Bean的实践阶段
在Spring学习之提纲(我暂时还没发出来。。。因为整个系列并没有写完)中,我们谈到了Spring,IOC(DI),Bean的概念。知道了Bean只是加入Spring管理的组件的统称,Spring会生成一个工厂类,这个工厂类管理这些Bean,我们只负责创建这些Bean,并在xml文件上写下清单(或者是用在Bean类上添加注解),工厂类会根据上面的清单创建各个Bean的实例,然后调用Bean里指定的方法,组装起来。举个例子吧
从前厨师svllen做菜都是一个铁锅打天下,自己买原材料(原材料就是Bean)自己做菜(做菜就是new的比喻),做菜的步骤都得记清楚,什么时候该放这个什么时候放 放哪个都有讲究。有一天店里引进了新设备。svllen发现他再也不用记这些步骤了,新设备都给你提供了
说白了Bean就是一些组件(各种类 接口),既然要创建注入,我们肯定得指定类路径吧 类名 还有你要注入的属性名 属性类型等信息。Spring就是通过这些信息管理Bean的。
因此Bean的实现分为两步 一步是实例化(这个实例化指得是在Spring容器(applicationContext或者BeanFactory)中实例化,提供给Bean工厂,而不是创建这个Bean类,Bean类是你自己创建的),另一步就是属性注入
在Spring中,提供了3种装配Bean的方案:
其实我觉得只有两种,一种xml配置,一种注解开发,后面两种都算是注解开发
在这三种方案中,各有各的好处,但是我还是推荐尽可能地使用自动配置的机制,显示配置越少也好,当你必须使用显示配置时,优先使用javaConfig,最后只有当你想要使用便利的xml命名空间,并且在javaConfig没有同样的实现时,才应该使用xml。
由于写起来才知道Bean要讲的东西太多了,因此这篇文章先讲xml显式配置情况下的Bean。
简单来说只需要在配置文件中添加标签就可以了
格式:
<bean id="bean" class="cn.svllen.Spring.demo01.Bean1" >
< bean >标签:
在普通的应用程序中,对象是通过反射进行创建的,在Bean工厂类(Beanfactory或者是applicationContext)会根据Bean的关联方式不同也有不同的实例化方法。主要分为三类,构造器实例化,静态工厂实例化,实例工厂实例化。构造器方式的实例化是最常用的,后面两种很少用。所以后面两种静态工厂方法我勉强写了,但是还是觉得不是很好,所以实例工程实例化的方式我就直接不写了,附上一篇博客链接(打哪里都行 不要打脸~~)
构造器实例化也分为有参和无参
创建Bean类:bean1
public class Bean1 {
private String msg;
public Bean1(){
super();
System.out.println("Bean1的无参构造方法执行了。。。。。。");
}
public Bean(String msg){
this.msg = msg;
System.out.println("Bean1的有参构造方法执行了");
}
}
配置文件中配置
```
<bean id="bean" class="cn.svllen.Spring.demo01.Bean1" />
```
其实有参构造也简单,在< bean>标签中使用< constructor-arg/>子标签就好了。
java类看上面就好了。
xml文件
<bean id="bean" class="cn.svllen.Spring.demo01.Bean1" >
<constructor-arg index="0" value="Hello Java"/>
bean>
在实际开发中,我们可能会遇到jar包的类,这时候我们不能确定其是否有默认的构造方法,也不能用注解。这时候就可以使用静态工厂的方式(但是其实这种情况也很少见的,所以一般静态工厂实例化的方式并不多见)
比如说我们要生成的bean对象的类为
public class Bean2 {
public void init() {
System.out.println("使用静态工厂方式实例化");
}
}
以前没用spring的话我们利用静态工厂方法生成是这样的,创建一个静态工厂方法创建,当然这个工厂方法并不是很正规(应该有参数,里面还应该有多个判断条件生成不一样的类的,但是这里从简)
public class BeanFactory {
public static Bean2 getInstance() {
return new Bean2();
}
}
使用spring的xml方式就是这样
<bean id="bean2" class="cn.svllen.BeanFactory" factory-method="getInstance"/>
其实就是在bean中使用了factory-method属性,里面的值就是方法名
博客链接:https://blog.csdn.net/u012702547/article/details/56021922#commentBox
前面我们提到了如何把Bean实例化到Spring的applicationContext中,一般来说 我们的Bean或多或少都有属性,这时候我们就需要考虑属性注入的方式了。
spring中Bean有两种注入方式,一种是构造器注入,另一种是set方法注入。
这里要说明的是,我把配置文件分成好几个,然后在总配置文件applicationContext中导入这些子配置文件。这些子配置文件分为
那如何导入呢,很简单,在总配置文件中使用< import > 标签就好了
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
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">
<import resource="applicationContext01.xml"/>
<import resource="applicationContext02.xml"/>
beans>
华丽的分割线
在xml的构造器注入中,有两种方案可以选择
具体的作用请看下面的讲解
光说不练假把式,下面拿一个例子举例。现在汽车有很多品牌,因此把汽车抽象出来,定义一个车的接口类Car,车有很多种类型,我这里拿SUV举例,因此创建Car接口的实现类SUV。现在的一般来说都得人控制(排除自动驾驶),因此还得创建一个Driver类,Driver类中肯定得与Car的实现类绑定。接下来看代码
Car接口
public interface Car {
//汽车都可以跑
void run();
}
SUV
public class SUVCar implements Car {
private String name; //车名
private String price; //车是烧油还是电动
public SUVCar(String name, String price) {
this.name = name;
this.price = price;
}
@Override
public void run() {
System.out.println("这辆价格为:" + price +"的" + name + "汽车在行驶" );
}
}
applicationContext01.xml配置文件中
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
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 id="BMWX3" class="cn.svllen.Spring.demo03.SUVCar" >
<constructor-arg name="name" value="宝马X3"/>
<constructor-arg name="price" value="38万" />
bean>
<bean id="BMWX3" class="cn.svllen.Spring.demo03.SUVCar" c:name="宝马x3" c:price="38万"/>
<bean id="BMWX3" class="cn.svllen.Spring.demo03.SUVCar" c:_0="宝马x3" c:_1="38万"/>
beans>
我们通过< constructor-arg>和c-命名空间完成对SUVCar中name和price两个属性的注入,对于< constructor-arg>标签来说,name属性 为要注入的属性名,value属性 为要注入的属性的值,没啥好说的
c-命名空间的的用法是 c:属性名=“属性值” 或者c:_属性的index(下标)=“属性值”,推荐使用前者,后者不能看出属性的名称。与< constructor-arg>相比,c-命名空间更加简洁,这里有一个点需要注意,要使用c-命名空间还得在xml顶部声明其模式
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
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">
其中name和price都是基本类型,如果我们要注入的是复杂类型呢,比如其他的Bean(对象),集合,这又该怎么办呢
先说如何注入Bean(对象)类型的属性吧,比如在Driver类的构造器方法中我们需要把suvCar这个属性给实例化了,通过属性注入的形式。
Driver类
public class Driver {
private SUVCar suvCar;
public Driver(SUVCar suvCar){
this.suvCar = suvCar;
}
...
}
applicationContext01.xml配置文件中
<bean id="BMWX3" class="cn.svllen.Spring.demo03.SUVCar" >
<constructor-arg name="name" value="宝马X3"/>
<constructor-arg name="price" value="38万" />
bean> -->
<bean id="driver" class="cn.svllen.Spring.demo03.Driver" >
<constructor-arg name="suvCar" ref="BMWX3"/>
bean>
<bean id="driverC-" class="cn.svllen.Spring.demo03.Driver" c:suvCar-ref="BMWX3"/>
不管是< constructor-arg>标签还是c-命名空间,都是使用了ref这个关键字,值是要注入bean的id。
对于< constructor-arg>标签来说,它引用了ID为BMWX3的Bean(通过ref属性),并将其注入到属性名为suvCar中(即name属性)。
对于c-命名空间来说,构成如下图所示。
在以构造器方式注入集合类型属性中,我们只能选择< constructor-arg>标签,目前c-命名空间并没有支持。
集合有list set和map,为了讲解如何注入,我们创建了一个没有实际意义的演示CollectionAssembly
CollectionAssembly类
public class CollectionAssembly {
private List<String> list;
private Map<String, String> map;
private Properties properties;
private Set<String> set;
private String[] array;
public CollectionAssembly(List<String> list, Map<String, String> map, Properties properties, Set<String> set, String[] array) {
this.list = list;
this.map = map;
this.properties = properties;
this.set = set;
this.array = array;
}
}
applicationContext01.xml配置文件
<bean id="collection" class="cn.svllen.Spring.demo03.CollectionAssembly">
<constructor-arg name="array">
<array>
<value>数组值1value>
<value>数组值2value>
<value>数组值3value>
array>
constructor-arg>
<constructor-arg name="list">
<list>
<value>张三value>
<value>李四value>
<value>王五value>
list>
constructor-arg>
<constructor-arg name="set">
<set>
<value>宝马value>
<value>奔驰value>
<value>奥迪value>
set>
constructor-arg>
<constructor-arg name="map">
<map>
<entry key="BMW" value="宝马"/>
<entry key="BC" value="奔驰"/>
<entry key="AD" value="奥迪"/>
map>
constructor-arg>
<constructor-arg name="properties">
<props>
<prop key="prop1">value-prop-1prop>
<prop key="prop2">value-prop-2prop>
<prop key="prop3">value-prop-3prop>
props>
constructor-arg>
bean>
其实也挺简单,就是在< constructor-arg>标签中添加各种子标签
当我们初始化一个类时,除了构造器设置属性,一般也会用到setter方法进行设置。因此在spring中也支持setter方法。
对于xml方式的,spring提供了两种方案,也是一个标签一个命名空间
< property >标签与之前的< constructor-arg>很相似,属性都一样,都有name,ref,value。name为注入属性的属性名,ref是引用的bean对象id,value是属性的值
p-命名空间的基本用法 p:属性名="属性值" 如果是引用Bean对象 p:属性名-ref="属性值",与c-命名空间相比,少了以参数下标为属性名的用法。同理,使用p-命名空间也得在xml顶部进行声明。
同样以之前构造器方法属性注入的这几个类(Car SUVCar Driver)来演示,只不过得把其中的构造器方法换成setter方法。(代码我就不贴了,这自己改就好了),实在觉得需要的话我上传了个txt的文档。
applicationContext02.xml配置文件中
<bean id="ADa6" class="cn.svllen.Spring.demo03.SUVCar">
<property name="name" value="奥迪a6"/>
<property name="price" value="38万"/>
bean>
<bean id="ADa6p" class="cn.svllen.Spring.demo03.SUVCar" p:name="奥迪A6" p:price="38万" />
跟之前构造器方式注入基本属性很相似对吧,因为这些本来就不难。
对于复杂类型Bean对象引用,setter方式又以怎样的方式实现呢。
applicationContext02.xml配置文件中
<bean id="driver" class="cn.svllen.Spring.demo03.Driver">
<property name="suvCar" ref="ADa6"/>
bean>
<bean id="driverp" class="cn.svllen.Spring.demo03.Driver" p:suvCar-ref="ADa6"/>
< property>标签为属性的setter方法所提供的功能与< constructor-arg>标签为构造器所提供的功能是一样的。在上面的例子中,< peoperty>通过ref属性引用了ID为ADa6的bean,并将其注入到name=suvCar的属性中
除了< property>标签,我们也可以选择p-命名空间来注入Bean(对象)引用类型的属性。格式直接上图
由于< property >标签与 < constructor-arg>标签存在高度的相似性,因此在注入集合类型属性时,我们应该可以想到 < property >标签也可以实现这个功能。
applicationContext02.xml配置文件中
<bean id="collection" class="cn.svllen.Spring.demo03.CollectionAssembly">
<property name="array">
<array>
<value>数组值1value>
<value>数组值2value>
<value>数组值3value>
array>
property>
<property name="list">
<list>
<value>张三value>
<value>李四value>
<value>王五value>
list>
property>
<property name="set">
<set>
<value>宝马value>
<value>奔驰value>
<value>奥迪value>
set>
property>
<property name="map">
<map>
<entry key="BMW" value="宝马"/>
<entry key="BC" value="奔驰"/>
<entry key="AD" value="奥迪"/>
map>
property>
<property name="properties">
<props>
<prop key="prop1">value-prop-1prop>
<prop key="prop2">value-prop-2prop>
<prop key="prop3">value-prop-3prop>
props>
property>
bean>
对于p-命名空间来说,严格意义上我们是不能实现注入集合属性的,但是通过util-命名空间与p-命名空间的混合使用实现这一功能
使用util-命名空间也得在xml顶部声明:xmlns:util=“http://www.springframework.org/schema/util”
applicationContext02.xml配置文件中
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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 id="Collectionp-" class="cn.svllen.Spring.demo03.CollectionAssembly"
p:list-ref="list" p:set-ref="set" p:map-ref="map" p:properties-ref="properties"/>
<util:list id="list">
<value>张三value>
<value>李四value>
<value>王五value>
util:list>
<util:set id="set">
<value>宝马value>
<value>奔驰value>
<value>奥迪value>
util:set>
<util:map id="map">
<entry key="BMW" value="宝马"/>
<entry key="BC" value="奔驰"/>
<entry key="AD" value="奥迪"/>
util:map>
<util:properties id="properties">
<prop key="prop1">value-prop-1prop>
<prop key="prop2">value-prop-2prop>
<prop key="prop3">value-prop-3prop>
util:properties>
beans>
总结:当你看到这里时,是不是感觉xml配置怎么那么麻烦,虽然不再需要我自己在java代码中手动捆绑类与类之间的关系,但是你在xml都写了一遍啊。其实在实际开发中,xml是作为最后的实现方式的,现在大部分项目只有配置文件是需要xml实现,其他的bean注入都交给注解了(不过其实也可以使用注解来设置配置文件)。下一篇文章介绍的注解方式你就会看到更简单的方法。