Spring学习笔记(一):Bean(xml版)

前言:上篇文章谈到了bean的简介,这篇文章将带你深入了解Spring中bean并通过实现Bean来更好的认识Spring中Bean的概念,同时掌握Spring中Bean的操作。毕竟IOC、DI是Spring的核心思想,而Bean就是这个思想的载体,Spring就是围绕着Bean的创建注入以及Bean的使用管理进行项目管理的。

ps1:本文是一个学习笔记,是根据我的理解汇总的博客,可能趣味性少一点,如果你想看趣味性强一点,那种娓娓道来的文章。可以看一下《Spring实战第四版》

创建学习Spring的项目


“工欲善其事必先利其器”,因此实践第一步 创建Spring学习项目,创建项目了才能够写Bean的代码。创建过程看我的另一篇文章: Spring学习项目的创建配置

创建完成,开始正式进入Bean的实践阶段

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中显式配置
  • 在java代码中进行显式配置(其实就是创建个配置类)
  • 隐式的bean发现机制和自动装配

其实我觉得只有两种,一种xml配置,一种注解开发,后面两种都算是注解开发

在这三种方案中,各有各的好处,但是我还是推荐尽可能地使用自动配置的机制,显示配置越少也好,当你必须使用显示配置时,优先使用javaConfig,最后只有当你想要使用便利的xml命名空间,并且在javaConfig没有同样的实现时,才应该使用xml。

由于写起来才知道Bean要讲的东西太多了,因此这篇文章先讲xml显式配置情况下的Bean。

Bean的实例化


简单来说只需要在配置文件中添加标签就可以了
格式:

 <bean id="bean" class="cn.svllen.Spring.demo01.Bean1" >

< bean >标签:

  • id:唯一标识符,不能包含特殊符号
  • name:与id的作用差不多,但是可以包含特殊符号(基本不用,只是用来解决Struts1的遗留问题)
  • class: Bean的全路径
  • scope:Bean的作用范围(详情请参考上面提到的Bean的作用范围)
  • init-method:Bean组件里指定初始化方法
  • destory-mthod:Bean组件里的指定销毁方法
  • autowire:表示bean的自动装配
    • no:默认值,不进行装配
    • byName:通过属性的名称自动装配(注入)。Spring会在容器中查找名称与bean属性名称一致的bean,并自动注入到bean属性中。当然bean的属性需要有setter方法。例如:bean A有个属性master,master的setter方法就是setMaster,A设置了autowire=“byName”,那么Spring就会在容器中查找名为master的bean通过setMaster方法注入到A中。
    • byType:通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用Seter方法为其注入。如果Spring的容器中包含多个这个类型的bean,Spring将抛出异常。如果没有找到这个类型的bean,那么注入动作将不会执行。
    • constructor:类似于byType,但是是通过构造函数的参数类型来匹配。假设bean A有构造函数A(B b, C c),那么Spring会在容器中查找类型为B和C的bean通过构造函数A(B b, C c)注入到A中。与byType一样,如果存在多个bean类型为B或者C,则会抛出异常。但是与byType不同的是,如果在容器中找不到匹配的类的bean,将抛出异常,因为Spring无法调用构造函数实例化这个bean。
    • autodetect :在byType和constructor之间自动的选择注入方式。
    • default:采用父级标签(即beans的default-autowire属性)的配置。
  • factory-method:
  • factory-bean:

在普通的应用程序中,对象是通过反射进行创建的,在Bean工厂类(Beanfactory或者是applicationContext)会根据Bean的关联方式不同也有不同的实例化方法。主要分为三类,构造器实例化,静态工厂实例化,实例工厂实例化。构造器方式的实例化是最常用的,后面两种很少用。所以后面两种静态工厂方法我勉强写了,但是还是觉得不是很好,所以实例工程实例化的方式我就直接不写了,附上一篇博客链接(打哪里都行 不要打脸~~)

构造器实例化的方式

构造器实例化也分为有参和无参

  1. 无参构造器

创建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 -->
    <bean id="bean" class="cn.svllen.Spring.demo01.Bean1" />
```
  1. 有参构造器

其实有参构造也简单,在< 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的属性注入


前面我们提到了如何把Bean实例化到Spring的applicationContext中,一般来说 我们的Bean或多或少都有属性,这时候我们就需要考虑属性注入的方式了。

spring中Bean有两种注入方式,一种是构造器注入,另一种是set方法注入。

这里要说明的是,我把配置文件分成好几个,然后在总配置文件applicationContext中导入这些子配置文件。这些子配置文件分为

  • applicationContext01.xml(以构造器方式实现属性注入)
  • applicationContext02.xml (以setter方式实现属性注入)

那如何导入呢,很简单,在总配置文件中使用< import > 标签就好了
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"/>

    <!-- 导入以setter方法实现属性注入的配置文件-->
    <import resource="applicationContext02.xml"/>

</beans>

华丽的分割线


构造器属性注入

在xml的构造器注入中,有两种方案可以选择

  • < constructor-arg>标签(< bean>的字标签)
  • c-命名空间(Spring3.0引入)

具体的作用请看下面的讲解

注入基本属性

光说不练假把式,下面拿一个例子举例。现在汽车有很多品牌,因此把汽车抽象出来,定义一个车的接口类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配置文件中

<?xml version="1.0" encoding="UTF-8"?>
<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">
        
    <!-- 使用构造器方式的constructor-arg标签注入基本属性(包括int String float等)-->
    <bean id="BMWX3" class="cn.svllen.Spring.demo03.SUVCar" >
        <constructor-arg name="name" value="宝马X3"/>
        <constructor-arg name="price" value="38万" />
    </bean> 
    
    <!--使用构造器方式的c-命名空间注入基本属性(包括int String float等) -->
    <bean id="BMWX3" class="cn.svllen.Spring.demo03.SUVCar" c:name="宝马x3" c:price="38万"/>
    
    <!-- c-命名空间还有另一种用法,使用属性的下标作为标记 -->
    <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顶部声明其模式

<?xml version="1.0" encoding="UTF-8"?>
<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(对象)

先说如何注入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>标签的 -ref模式 -->
        <constructor-arg  name="suvCar" ref="BMWX3"/>
    </bean>
    
    <!-- 使用构造器方式的c-命名空间注入复杂属性之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">
 
    <!-- 装配String[]类型的array -->
    <constructor-arg name="array">
        <array>
            <value>数组值1</value>
            <value>数组值2</value>
            <value>数组值3</value>
        </array>
    </constructor-arg>
    
    <!-- 装配List类型的list -->
    <constructor-arg name="list">
        <list>
            <value>张三</value>
            <value>李四</value>
            <value>王五</value>
        </list>
    </constructor-arg>

    <!-- 装配Set类型的set -->
    <constructor-arg name="set">
        <set>
            <value>宝马</value>
            <value>奔驰</value>
            <value>奥迪</value>
        </set>
    </constructor-arg>
 
    <!-- 装配Map类型的map -->
    <constructor-arg name="map">
        <map>
            <entry key="BMW" value="宝马"/>
            <entry key="BC" value="奔驰"/>
            <entry key="AD" value="奥迪"/>
        </map>
    </constructor-arg>
 
    <!-- 装配Properties类型的properties -->
    <constructor-arg name="properties">
        <props>
            <prop key="prop1">value-prop-1</prop>
            <prop key="prop2">value-prop-2</prop>
            <prop key="prop3">value-prop-3</prop>
        </props>
    </constructor-arg>
   
</bean>

其实也挺简单,就是在< constructor-arg>标签中添加各种子标签

  • < list >标签:设置list类型的集合,< value>标签里放置的是注入的值,除了 < value>之外,其实也可以添加 < ref >标签引入其他Bean对象
  • < set >标签:设置set集合,上同
  • < map >标签:设置map集合
  • < prop >标签:设置Properties属性类,不知道Properties的可以参考这篇文章
setter方法属性注入

当我们初始化一个类时,除了构造器设置属性,一般也会用到setter方法进行设置。因此在spring中也支持setter方法。

对于xml方式的,spring提供了两种方案,也是一个标签一个命名空间

  • < peoperty >标签(< bean >的子标签)
  • p-命名空间(Spring 2.0引入)

< 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配置文件中

 <!--使用setter方式的 < property>标签注入基本属性(int String float)等 -->
    <bean id="ADa6" class="cn.svllen.Spring.demo03.SUVCar">
        <property name="name" value="奥迪a6"/>
        <property name="price" value="38万"/>
    </bean>

    <!-- 使用setter方式的p-命名空间注入基本属性(int float String)-->
    <bean id="ADa6p" class="cn.svllen.Spring.demo03.SUVCar" p:name="奥迪A6" p:price="38万" />

跟之前构造器方式注入基本属性很相似对吧,因为这些本来就不难。
 

注入复杂属性之Bean(对象)引用

对于复杂类型Bean对象引用,setter方式又以怎样的方式实现呢。

applicationContext02.xml配置文件中

<!-- 使用setter方式的<property> 标签注入复杂属性之Bean(对象)引用-->
    <bean id="driver" class="cn.svllen.Spring.demo03.Driver">
        <property name="suvCar" ref="ADa6"/>
    </bean>
    
    <!--使用setter方式的p- 命名空间注入复杂属性之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配置文件中

<!--使用setter方式的p- 命名空间注入复杂属性之集合(list set map) -->
    <bean id="collection" class="cn.svllen.Spring.demo03.CollectionAssembly">

        <!-- 装配String[]类型的array -->
        <property name="array">
            <array>
                <value>数组值1</value>
                <value>数组值2</value>
                <value>数组值3</value>
            </array>
        </property>

        <!-- 装配List类型的list -->
        <property name="list">
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
            </list>
        </property>

        <!-- 装配Set类型的set -->
        <property name="set">
            <set>
                <value>宝马</value>
                <value>奔驰</value>
                <value>奥迪</value>
            </set>
        </property>

        <!-- 装配Map类型的map -->
        <property name="map">
            <map>
                <entry key="BMW" value="宝马"/>
                <entry key="BC" value="奔驰"/>
                <entry key="AD" value="奥迪"/>
            </map>
        </property>

        <!-- 装配Properties类型的properties -->
        <property name="properties">
            <props>
                <prop key="prop1">value-prop-1</prop>
                <prop key="prop2">value-prop-2</prop>
                <prop key="prop3">value-prop-3</prop>
            </props>
        </property>

    </bean>

对于p-命名空间来说,严格意义上我们是不能实现注入集合属性的,但是通过util-命名空间与p-命名空间的混合使用实现这一功能

使用util-命名空间也得在xml顶部声明:xmlns:util=“http://www.springframework.org/schema/util”

applicationContext02.xml配置文件中

<?xml version="1.0" encoding="UTF-8"?>
<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">
     <!-- 使用setter方式的p-命名空间与util-命名空间混合的方式注入集合类型的属性(int float String -->
    
    <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"/>

    <!-- 装配List类型的list -->
    <util:list id="list">
        <value>张三</value>
        <value>李四</value>
        <value>王五</value>
    </util:list>
    
    <!-- 装配Set类型的set -->
    <util:set id="set">
        <value>宝马</value>
        <value>奔驰</value>
        <value>奥迪</value>
    </util:set>


    <!-- 装配Map类型的map -->
    <util:map id="map">
        <entry key="BMW" value="宝马"/>
        <entry key="BC" value="奔驰"/>
        <entry key="AD" value="奥迪"/>
    </util:map>


    <!-- 装配Properties类型的properties -->
    <util:properties id="properties">
        <prop key="prop1">value-prop-1</prop>
        <prop key="prop2">value-prop-2</prop>
        <prop key="prop3">value-prop-3</prop>
    </util:properties>

</beans>

总结:当你看到这里时,是不是感觉xml配置怎么那么麻烦,虽然不再需要我自己在java代码中手动捆绑类与类之间的关系,但是你在xml都写了一遍啊。其实在实际开发中,xml是作为最后的实现方式的,现在大部分项目只有配置文件是需要xml实现,其他的bean注入都交给注解了(不过其实也可以使用注解来设置配置文件)。下一篇文章介绍的注解方式你就会看到更简单的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值