美文网首页Java&JAVA EE我爱编程
[spring]applicationContext.xml详解

[spring]applicationContext.xml详解

作者: 骑着乌龟去看海 | 来源:发表于2018-03-17 17:04 被阅读79次

    一、概述

    今天来学习一下Spring的核心配置文件applicationContext.xml,主要来看一下其中经常使用的标签,当然由于Spring早就支持注解的方式,所以我们以后的侧重点将也是注解方式,但对于applicationContext.xml中的各项配置,其实与注解是很像的,等我们理解了xml配置之后,很容易就理解注解配置了。

    注:学习的Spring版本是4.3.14。

    二、各种标签

    1. beans标签

    Spring配置文件的根元素是beans节点,在该节点内,我们可以配置Spring内置的各种命名空间以及bean默认的几项配置,通过配置各种命名空间,然后使用各命名空间的元素来完成对Spring的配置。

    <?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">
    </beans>
    

    前面我们说过命名空间,这里就不多说了。Spring内置了大约10种左右的命名空间供我们选择配置:

    命名空间 用途
    aop 为声明切面及将@AspectJ注解的类代理为Spring切面提供了配置元素
    beans 声明及配置Bean,是Spring最核心也是最原始的命名空间
    context 为配置Spring应用上下文提供了配置元素,包括自动检测,自动装配bean等
    jee 提供了于Java EE API的集成,比如JNDI与EJB
    jms Java消息相关的配置
    lang 支持配置由Groovy、JRuby或BeanShell等脚本实现的Bean
    mvc 提供Spring MVC相关的配置,比如控制器,拦截器,视图管理器等相关
    oxm 支持Spring的对象到XML的映射配置
    tx 声明式的事务配置
    util 提供各种各样的工具类元素配置,包括把集合配置为Bean,支持属性占位符元素

    当然,Spring的命名空间不止如此,只是展示了我们常用的而已,其他的等我们用到的时候再了解不迟。而我们如果需要某个命名空间下的元素时,引入相应的命名空间即可。比如说我们使用到了context命名空间:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
        <context:property-placeholder ignore-resource-not-found="false" ignore-unresolvable="false"/>
    </beans>
    

    接下来,我们来看下beans标签中除了命名空间外的其他默认元素。

    1.1 default-init-method元素

    如果在上下文中定义的很多bean都有相同名字的初始化方法,我们没有必要为每一个bean声明init-method属性,这时候我们就可以使用beans元素的default-init-method属性。该属性为应用上下文中所有的bean设置了共同的初始化方法。

    1.2 default-destory-method元素

    这个元素于上述default-init-method类似,该属性是用于方法的销毁。

    1.3 default-lazy-init元素

    用于延迟加载,我们都知道,一般情况下,Spring在启动的时候会初始化所有相关的bean,这样就会出现启动的时候会特别慢的问题。这时候可以配置该属性设置为延迟加载,用到的时候再去加载。当然如果该配置文件配置了bean标签或者引入了其他的配置文件,以具体的bean中的配置或其他配置文件的配置优先;

    • true,设置为true,则所有相关的bean的初始化被延迟加载;
    • false,默认为false,即不延迟加载;
    • 假如设置为延迟加载后,记得考虑比如定时任务等相关的操作,所以是否使用该属性,按需选择;
    1.4 default-autowire元素

    Spring中的bean默认注入的方式。一般情况下,当我们需要往一个bean里注入另一个bean的时候,如果没有配置该属性,就需要手动通过ref标签引入。我们以一个简单的例子来看下:
    (1). 两个对象:Student,Score

    public class Student {
        private String id;
        private String name;
        private Score score;
    }
    
    public class Score {
        private Integer math;
    }
    

    (2). bean配置:

    <bean id="student" class="entity.Student">
        <property name="id" value="id"/>
        <property name="name" value="name"/>
    </bean>
    
    <bean id="score" class="entity.Score">
        <property name="math" value="100"></property>
    </bean>
    

    (3). main方法

    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
        Student student= ac.getBean("student", Student.class);
        System.out.println(student.getScore().getMath());
    }
    

    首先,我们配置default-autowire属性,也就是默认不自动注入,这时候,我们如果直接获取的话,就获取不到Score对象,提示空指针异常:

    Exception in thread "main" java.lang.NullPointerException
        at test.Main.main(Main.java:15)
    

    我们配置default-autowire属性为byName之后,再看下结果:

    100
    

    这时候打印成功,也就是说,如果我们不配置该属性的话,需要手动使用ref标签来引用:

    <bean id="student" class="entity.Student">
            <property name="id" value="id"/>
            <property name="name" value="name"/>
            <property name="score" ref="score"/>
        </bean>
    

    当然,如果我们的对象是单个对象的话,那无论配置是否配置,都没什么影响。

    下面我们来看下该属性的几个可选值:

    • no,不使用自动注入,所以该Bean对象中其他Bean的引用必须通过ref标签来引用;
    • byName,把与beanA的属性具有相同名字的beanB自动注入到beanA的对应属性中,如果没有,则不注入。这里的相同指的是beanB配置的id或name其中一个相同即可;
    • byType,与byName相对,这里指的是相同类型,也就是相同的class,这种情况下,由于根据类型来注入,所以beanB只能有一个存在,否则会编译不通过;
    • constructor,与byType类似,通过构造方法的参数来匹配,如果没有构造方法,或没有与构造方法参数类型一致的bean,则会提示异常;

    这里判断是否存在类型一致先通过byType判断,如果只有一个类型的bean存在,则成功;如果有多个类型一致的,再根据byName,如果只有一个name相同的,则成功,否则失败,提示异常;

    • default,默认配置,由上级标签<beans>的default-autowire属性确定使用哪种装配方式,该属性一般配置于具体的bean标签中,而配置在beans标签中和配置为no类似。

    注意:在以前版本中,default-autowire还支持一个可选值:autodetect,不过在Spring4之后已经废弃掉。

    还有两点简单说下:

    1. default-lazy-init类似,如果bean中配置了autowire属性,则具体bean中的注入类型的优先级将高于beans里的配置;
    2. 当然,配置了该属性后可能会导致我们对其中的细节不够了解,也会弱化对象间的关系,出现问题的时候可能不太好定位,所以我们可以根据需要选择配置或不配置default-autowire
    1.5 default-autowire-candidates元素

    自动注入bean的候选者,比如说,default-autowire中我们使用byType的时候通常只能有一个类型为ClassA的bean。但是我们想在配置文件中有多个类型为ClassA的bean,就可以通过配置default-autowire-candidates来作为自动注入bean的候选者;

    <beans ...   
       default-autowire-candidates="*score" default-autowire="byType">
        <bean id="student" class="entity.Student">
            <property name="id" value="id"/>
            <property name="name" value="name"/>
        </bean>
    
        <bean id="score2" class="entity.Score" >
            <property name="math" value="100"></property>
        </bean>
    
        <bean id="score" class="entity.Score" >
            <property name="math" value="200"></property>
        </bean>
    </beans>
    

    在上面这个小例子中,我们通过配置该属性为*socre,让容器在进行自动注入的时候选择name或id以score结尾的bean来进行注入。

    1. default-autowire-candidates的值可以是某个bean的id,也可以是匹配字符串,比如*score,则会将以score结尾的作为候选注入的bean,也可以指定多个字符串,通过逗号进行分割;
    2. A default bean name pattern for identifying autowire candidates:
      e.g. "*Service", "data*", "*Service*", "data*Service".
      Also accepts a comma-separated list of patterns: e.g. "*Service,*Dao".
    3. 有关候选bean的细节,我们可以查看官方文档中bean的属性autowire-candidate的文档来更深一步学习。
    1.6 default-merge元素

    这个属性用的可能不太多,这是用于集合的继承相关的操作。比如说,如果父类Bean包含list,map,set等一些集合元素,那么继承父类的子类将自动继承父类Bean的集合元素;当然,子类Bean可以选择合并父类的集合元素,也可以选择替换父类的的集合元素,Spring通过beans标签的default-merge和bean标签内的list标签的merge来支持这种操作;

    我们通过简单的例子来看一下:定义对象Tutorial:

    public class Tutorial {
        private String name;
        private List topicsList = new ArrayList<>();
    }
    

    配置两个bean,先不配置default-merge属性:

    <bean id="parent" class="entity.Tutorial">
        <property name="name" value="Java parent"/>
        <property name="topicsList">
            <list>
                <value>Java Core</value>
                <value>Java Concurrency</value>
            </list>
        </property>
    </bean>
    
    <bean id="children" parent="parent">
        <property name="name" value="Java children"/>
        <property name="topicsList">
            <list>
                <value>EJB</value>
                <value>Servlet</value>
            </list>
        </property>
    </bean>
    

    指定parent的Bean集合数据为[Java Code, Java Concurrency],而children中的集合数据为[EJB, Servlet],这时候我们测试下chidren是否合并了父类的集合元素:

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
        try {
            Tutorial parent = context.getBean("parent", Tutorial.class);
            Tutorial children = context.getBean("children", Tutorial.class);
            // 获取子类数组
            List childrenList = children.getTopicsList();
            // 获取父类数组
            List parentList = parent.getTopicsList();
            System.out.println(children.getName() + " contain " + parent.getName() + "? " +
                    childrenList.containsAll(parentList));
            System.out.println(children.getName() + ": " + childrenList);
        } finally {
            context.close();
        }
    }
    

    打印结果为:

    Java children contain Java parent? false
    Java children: [EJB, Servlet]
    

    可以看到,children并没有合并父类的集合元素,只包含本身的元素。我们设置default-merge=true,再来看一下结果:

    Java children contain Java parent? true
    Java children: [Java Core, Java Concurrency, EJB, Servlet]
    

    此时,我们可以看到,子类已经合并了父类的集合元素了。

    从上面这个简单的小例子我们大概了解了该标签的用处,该标签有truefalse两个可选值,默认情况下是false,就是不合并父类的集合元素。同理,如果 list 标签配置了merge属性,则会覆盖掉beans标签里的该项配置。如果要查看更多实例,可以参考:Spring Collection Merging Example

    2. bean标签

    bean元素是Spring中最基本的配置单元,用来管理对象。通过该元素,Spring将创建一个对象,并在容器加载的时候实例化该对象。我们来看一下它的一些属性:

    2.1 id属性

    bean的唯一标识,命名格式必须符合XML中ID属性的命名规范,不能有特殊字符;

    在Spring3.1版本之前,该id属性被定义为xsd:ID,通过XML的id规范限制了它的唯一性,而在Spring3.1中,该id属性被定义为xsd:string类型,尽管不再使用XML解析器,但id的唯一性仍由容器强制执行。

    2.1 name属性

    bean的名称,命名可以比较随意,可以有特殊字符,并且一个bean可以有多个名称:name="name1,name2,name3",用逗号或分号或空格隔开(当然,在同一个name配置中,只能有一种符号分割,不能同时有多种)。name可以算作是 id 的别名,我们在获取bean的时候,可以通过id,也可以通过name来获取。在没有id的情况下,则name的第一个名称默认为id;

    1. 同一个Spring配置文件中,id,name是不能重复的;但如果一个Spring从多个配置文件中加载,则多个配置文件中是允许id或者name重复的,但后面加载的bean会覆盖掉前面加载的bean;
    2. 如果一个bean标签未指定id,name属性,则Spring会给一个默认的id,值为类的全名,比如类的路径是com.company.Score,则默认的id值是com.company.Score
    3. 如果多个同类型的bean未指定id,name属性,则Spring会按照其出现的次序,分别给其指定 id 为类全名#1类全名#2

    举个小例子看一下:

    <bean class="entity.Score">
        <property name="math" value="100"/>
    </bean>
    
    <bean class="entity.Score">
        <property name="math" value="200"/>
    </bean>
    
    <bean class="entity.Score">
        <property name="math" value="300"/>
    </bean>
    

    获取bean的方式:

    Score score = ac.getBean("entity.Score", Score.class);
    Score score1 = ac.getBean("entity.Score#1", Score.class);
    Score score2 = ac.getBean("entity.Score#2", Score.class);
    System.out.println(score.getMath());
    System.out.println(score1.getMath());
    System.out.println(score2.getMath());
    

    打印结果:

    100
    200
    300
    

    id和name这段解释可以参考官网:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-beanname

    2.3 class属性

    顾名思义,该属性就是当前对象所引用的类的全路径。

     <bean name="student2,student3,student4" class="entity.Student"/>
    
    2.4 autowire属性

    在上面beans标签的学习中,我们已经了解过default-autowire属性,这两个属性是一样的,用于注入配置,只不过一个是全局默认的,一个是针对某一个bean的。在bean中配置完autowire属性后,该bean的注入方式会覆盖掉beans标签所配置的默认注入方式。该属性的配置可选值和default-autowire一样,就不多说了。

    2.5 autowire-candidate属性

      同样,该属性和beans标签中的default-autowire-candidates元素类似,意思就注入bean的候选者。再多说一句,前面我们说到配置有autowire属性的bean,Spring在实例化某个bean的时候会在容器中查找匹配的bean,然后通过autowire的配置对bean进行属性注入,这些被查找的bean我们称为候选bean;
      该属性的配置有三个值:truefalsedefault。配置为false的时候意味着将该bean从候选bean中排除掉,也就是注入的时候不注入该bean;

    另外再说两点:

    1. 该属性只影响到基于类型的注入,而不会影响到名称的引用。也就是如果是基于name的autowire,该属性将不会有影响。
    2. 同样,bean标签中的autowire-candidate属性的值将优先于beans标签的default-autowire-candidates配置。我们以一个例子来说一下:
    <beans ...
        default-autowire="byType" default-autowire-candidates="*score1">
        
        <bean id="score" class="entity.Score" autowire-candidate="true">
            <property name="math" value="100"/>
        </bean>
        
        <bean class="entity.Score" >
            <property name="math" value="200"/>
        </bean>
        
        <bean id="student" class="entity.Student" >
            <property name="id" value="id"/>
            <property name="name" value="name"/>
        </bean>
    </beans>
    

    通过beans标签的default-autowire-candidates属性设置同类型的候选bean是id以socre1结尾的bean,而我们的bean的id是score,但我们配置了该bean的autowire-candidate为true,由于bean的优先级高,所以能注入成功。

    2.6 destroy-method属性和init-method属性

    同样,在上文beans中,我们已经介绍过default-init-method了,这两个方法用于该bean的初始化方法和销毁方法。也就是,当Spring容器加载该bean的时候,会调用init-method执行初始化操作,然后当Spring容器销毁该bean的时候(比如调用close方法),会调用destroy-method来执行销毁操作。

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
        Student student= ac.getBean("student", Student.class);
        System.out.println(student.getScore().getMath());
        ac.close();
    }
    

    这里还需要说下,destroy-method只有在bean的scope配置为singleton,也就是单例模式下才会生效,原因等我们接下来学习到scope的时候再细说。

    2.7 lazy-init元素

      前文beans里说过,为了避免Spring容器启动的时候加载所有的bean,导致启动很慢的情况,所以beans标签提供了default-lazy-init标签来设置是否开启懒加载模式。
      同样,bean标签里的lazy-init元素也是用于对单个bean判断,是否需要开启懒加载机制,这样如果开启的话,那该bean的加载将不再是容器启动的时候进行调用,而是在第一次进行getBean的时候才进行加载。

    并且,该元素也是有truefalse可选,默认情况下是false,即不开启懒加载机制。而且bean里lazy-init的优先级也是高于最外层beans标签中default-lazy-init元素的。

    我们还是用上文的Student对象,添加一个默认构造方法:

    public class Student {
        private String id;
        private String name;
        private Score score;
    
        public Student() {
            System.out.println("test lazy init by construct");
        }
    }
    

    然后先不设置lazy-init元素,即采用默认值:

    <bean id="student" class="entity.Student" >
        <property name="id" value="id"/>
        <property name="name" value="name"/>
    </bean>
    

    然后测试下:

    ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
    

    可以看到,我们的构造方法被调用了:

    test lazy init by construct
    

    也就是说,我们只是启动了容器,并没有去直接获取bean,但构造方法还是被执行了。

    接下来,我们来配置下该属性为true,然后再看下结果:

    <bean id="student" class="entity.Student" lazy-init="true">
        <property name="id" value="id"/>
        <property name="name" value="name"/>
    </bean>
    

    再看下打印结果,可以看到,程序并没有执行我们的构造方法。然后我们手动获取下bean:

    ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
    Student student= ac.getBean("student", Student.class);
    

    然后再看下打印结果:

    test lazy init by construct
    

    这时候又执行了我们的构造方法。可以看到,程序在懒加载的时候,会延迟到第一次调用该bean的时候才进行加载该bean,这样对于程序启动的压力就会少许多。但或许会有额外的问题,比如说实例化的bean配置有问题,而这个问题也会延迟到运行的时候才会发觉。

    不过这里再多说一点:

    1. 如果一个bean1设置了立即加载,而该bean引用了一个延迟加载的bean2,那么bean1在容器启动的时候被实例化,而bean2由于也被bean1引用,所以也会立即实例化,而这种情况也符合延迟加载的bean在第一次调用时才被实例化的规则;
    2. 对于定时任务类似的这种东西,一般是不设置为延迟加载的;
    2.8 abstractparent标签

      很多时候,我们为了避免bean中属性的重复,会用到另外两个元素abstractparent。使用标签abstract修饰的bean被称为抽象的bean,这种bean不是用于实例化,而是用于继承的情况,也就是用于具体的子类继承的父类bean。该元素有truefalse两个值,默认情况下该值是false,当指定为true的时候,就是抽象bean,无法实例化。
      该值一般配合bean的另一个元素parent来使用,parent用于bean中的继承关系,使用该元素,可以继承父类的属性。这种子类bean的出现,是为了对父类bean中公共属性的封装,避免XML中属性的重复。

    1. Spring中的抽象bean和Java中的抽象类有相同点,但也有不同点。比如说都不能直接实例化,只能通过子类实例化的时候,先实例化父类,再实例化子类,相当于间接的实例化父类;
    2. 另一点是Spring中的抽象bean甚至不需要映射到某个类就可以被子类所引用,比如:
    <bean id="test1" abstract="true">
        <property name="name" value="name"/>
        <property name="id" value="id"/>
    </bean>
    <bean id="children" class="entity.Children" parent="test1">
        <property name="test" value="test/>
    </bean>
    

    这种情况就是将公共的属性进行统一注入,避免XML中各种属性配置的重复,但这种情况下,就需要子类必须要有相应的属性。对例子而言,也就是Children这个类中必须要有nameid属性。

    我们分两部分来看一个例子,一是抽象bean无法初始化,二是子类bean可以继承父类bean的属性:
    首先定义Parent对象:

    public class Parent {
        private String name;
    
        public Parent() {
            System.out.println("Parent初始化");
        }
    }
    

    配置bean:

    <bean id="parent" class="entity.Parent" abstract="true">
        <property name="name" value="name"/>
    </bean>
    

    测试下:

    ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
    

    可以看到,程序并未打印我们在Parent中设置的构造方法,也就是说我们没有配置懒加载,但Parent对象并没有实例化。

    然后我们再来看下属性的继承,再定义子类Children:

    public class Children extends Parent{
        private String id;
    
        public Children() {
            System.out.println("Children初始化");
        }
    }
    

    配置子类的bean:

    <bean id="children" class="entity.Children" parent="parent">
        <property name="id" value="id"/>
    </bean>
    

    看一下最终打印结果:

    ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
    Children children= ac.getBean("children", Children.class);
    System.out.println(children.getName() + "," + children.getId());
    ac.close();
    

    output:

    
    Parent初始化
    Children初始化
    name,id
    

    可以看到,先初始化了父类,再初始化子类,然后子类Children继承了Parent中的name属性。如果我们把parent这项配置给去掉,我这里为了方便不上传这部分代码,直接看下打印结果:

    Parent初始化
    Children初始化
    null,id
    

    很显然,父类Parent中的name属性并没有被继承过来。
    最后,再简单说下这里:

    1. 如果父类bean没有定义class属性,则子类bean必须定义class属性,并且子类中必须拥有父bean中所有属性;
    2. 还有一种情况,如果父bean有class属性,而子bean没有,那么子类的bean就和父bean是完全一样的bean,当然,子类的bean也就不能注入任何新的属性。
    2.9 depends-on标签

      如果一个bean是另一个bean的依赖项,通常将一个bean设置为另一个bean的属性,然后在XML中可以通过ref标签来引用。而有时,bean之间的关系并没有那么直接,比如实例化DAO之前先实例化DataBase这种情况,而这时候就可以使用depends-on标签。
      该标签用于指定bean初始化时的顺序,也就是说一个beanA初始化之前必须先初始化另一个beanB。对于depends-on而言,并不需要beanA持有beanB的一个引用,只是一种强制性的在beanA初始化之前对beanB进行初始化。

    <bean id="beanOne" class="ExampleBean" depends-on="manager"/>
    <bean id="manager" class="ManagerBean" />
    
    1. 很多情况下,depends-on标签可以使用bean的构造器注入或setter注入代替;还有一点,该bean的类型应该是单例的,因为单例的bean才会被Spring进行管理;
    2. 若要对多个bean进行依赖,depends-on提供了一个bean名称的列表,使用分号,逗号或空格来作为有效的分隔符;
    <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
        <property name="manager" ref="manager" />
    </bean>
    
    <bean id="manager" class="ManagerBean" />
    <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
    
    1. depends-on可以指定依赖bean在具体bean初始化之前先初始化,也可以指定依赖bean在具体bean销毁之前先销毁,同样,这时候bean的类型也只能是单例的。
      参考官网文件:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring/beans-factory-dependson
    2.10 primary标签

      我们在前面学习类型的自动注入的时候,可能会出现多个候选者,然后我们可以通过autowire-candidate注解可以设置某个或多个bean在候选bean中被排除掉,或者成为候选bean。而现在,primary提供了另一种方式。
      primary元素是说在类型注入的时候,如果多个bean被自动列入候选者之中,那么可以通过使用primary元素或@primary注解让该bean成为优先选择者,如果候选者之中只有一个primary修饰的bean,那么这个bean将会是自动注入的bean。

    该属性同样有truefalse两个值,默认情况下是false;

    还拿上面的例子来说:

    <bean id="score" class="entity.Score" >
        <property name="math" value="100"/>
    </bean>
    <bean id="score2" class="entity.Score" primary="true">
        <property name="math" value="200"/>
    </bean>
    <bean id="student" class="entity.Student">
        <property name="id" value="id"/>
    </bean>
    

    测试代码就不再写了,获取Student中的引用Score的math值:

    200
    

    可以看到,由于score2的优先级高,Student类型中自动注入的就是score2类型。

    @primary注解实现类似功能的还有@Qualifier注解,等我们接下来学习到的时候再来学习。

    2.11 factory-beanfactory-methd标签

      在前面的学习中,我们都是根据配置中的class全名通过反射进行实例化bean,而factory-beanfactory-method标签可以让我们通过工厂模式的方法来实例化bean。而工厂模式又大致分为两种,一种是静态工厂方法,另一种是实例工厂方法。

    1. 静态工厂,是指Factory本身并不需要实例化,而这个Factory类中提供了一个静态方法来生成bean对象;
    2. 而实例工厂是说Factory类中的方法不是静态的,这也就要求我们先实例一个工厂对象,然后通过这个工厂对象去获取我们所需要的bean对象。

    我们先看下静态工厂的简单例子:

    public class Student {
        private String id;
        private String name;
    }
        
    public class StudentFactory {
        public StudentFactory() {
            System.out.println("执行StudentFactory的构造方法进行初始化操作");
        }
    
        private static Student getInstance() {
            return new Student();
        }
    }
    

    我们定义了Student和静态工厂StudentFactory,再简单看下配置:

    <bean id="student" class="entity.StudentFactory" factory-method="getInstance">
        <property name="name" value="name"/>
    </bean>
    

    同样,我们再测试下:

    Student student =  ac.getBean("student", Student.class);
    System.out.println(student.getName());
    

    成功打印:

    name
    

    可以看到,程序并没有执行工厂类的默认构造方法,也就是没有初始化工厂类,而是通过工厂类的静态方法完成了我们bean对象的初始化工作。

    接下来我们再简单看下实例工厂:
    首先,将StudentFactory中的static去掉,变为实例方法,然后再看下配置:

    <bean id="studentFactory" class="entity.StudentFactory"/>
    <bean id="student" factory-bean="studentFactory" factory-method="getInstance">
        <property name="name" value="name"/>
    </bean>
    

    我们先实例化工厂类,再依靠工厂类去获取我们的bean,然后再看下执行结果:

    执行StudentFactory的构造方法进行初始化操作
    name
    

    可以,看到,程序先初始化了工厂类,然后再通过实例方法实例化了我们的bean对象。

    工厂模式还是挺方便的,我们在实例化对象的时候,可以尝试使用工厂模式来执行我们的初始化操作。

    2.12 scope标签

    scope标签表示的bean的作用域,默认情况下我们所创建的bean都是单例的。Spring支持以下几种作用域:

    1. singleton,默认作用域,在一个Spring容器中,一个bean只会有一个实例,这意味着所有的bean都是共享的,很适合无状态的bean定义;
    2. prototype,该作用域与singleton相反,每次请求都会返回新的实例,从某种意义上类似于Java的new操作符,适合有状态的bean定义。但需要注意一点就是Spring对于该模式的bean,创建完实例后,就交给对象本身管理自己的生命周期,不会自动做一些销毁的操作。所以由于该模式下生命周期并不是由Spring进行管理,所以这种情况下配置的如destory-method便不会生效。所以如果可以的话,我们可以在合适的适合调用Spring的销毁方法,让Spring来销毁对象释放资源。
    3. request,该模式是说每次HTTP请求中,每个Bean都会生成一个实例,也就是说每次HTTP请求都将会产生不同的实例。和prototype类似,但该作用域仅在Web请求中才有效;
    4. session,该模式是说每次HTTP Session中,每个bean都会生成一个实例,同样,作用域仅在Web请求中有效;
    5. globalSession,每个全局的HTTP Session,每个bean都会生成一个实例,该作用域仅在Portlet 上下文中有效,使用较少;Portlet规范是类似于Servlet的一种规范,有兴趣的童鞋可自行了解下。
    6. application,该模式和单例模式有点像,但applicationServletContext中是单例的,而singleton的应用范围是ApplicationContext,同样,仅在Web请求中有效;简单看下注解及XML配置:
    @Component
    @Scope("application")
    public class BeanClass {
    }
     
    //or
     
    @Component
    @ApplicationScope
    public class BeanClass {
    }
    <bean id="beanId" class="com.howtodoinjava.BeanClass" scope="application" />
    
    1. WebSocket,该模式用于HTTP中远程主机间进行双向通信,同样,也是仅在Web请求下有效。通常 WebSocket范围内的bean通常是单例的,并且比任何单独的WebSocket会话要活的更长。注解和XML如下:
    @Component
    @Scope("websocket")
    public class BeanClass {
    }
    <bean id="beanId" class="com.howtodoinjava.BeanClass" scope="websocket" />
    
    1. 还可以自定义线程的scope,这里就不多说了,想查看更多有关scope的内容,可参考官方文档,上面讲的非常清楚:
      https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-factory-scopes
      还可以参考:Spring Bean Scopes

    需要注意下,因为singleton适合无状态的情况,所以在多线程的时候,如果bean被定义为单例模式,可能会出现线程安全问题,这里需要注意下。

    总结下:

    1. 在Spring中,我们使用最多的就是singletonprototype模式,singleton是一种无状态的bean类型,当项目中不存在多线程共享对象的时候可以选择singleton模式,这样可以稍微节约资源。使用prototype的时候记得注意对象的销毁操作。
    2. 在Spring配置文件XML中直接配置application,websocket,会报错,还没搞清楚为什么。
    3. alias标签

    这个标签用于为bean提供别名。根据官方文档解释:

    1. 在bean本身定义中,我们可以通过id和name属性来为bean提供多个名称,这些名称等价于相同的bean,在某种情况下是很有用的,比如应用程序中的每个组件,使用特定于该组件本身的bean名称来引用公共的依赖关系。
    2. 但有的时候这种方式定义的别名并不总是能满足我们的所有需求,这时候就可以通过标签alias来实现另一种别名的定义。比如说在大型系统中,每个子系统都有自己的一套配置,组件A通过subsystemA-dataSource名称定义了一个数据源bean,而组件B想通过subsystemB-dataSource引用这一个数据源,而主系统也想通过myApp-dataSource来引用该数据源,这种情况下就可以使用alias标签来完成这种操作。
    3. 这样一来,每个组件及主程序就可以通过一个唯一的名字来引用同一个数据源,而相互间不会受到干扰。
    4. 官方文档地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-beanname-alias

    使用方式很简单:

    <alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
    <alias name="subsystemA-dataSource" alias="myApp-dataSource" />
    

    不过需要保证alias唯一,不能重复。

    4. description标签

    这个标签就不多说了,就是对该配置文件的描述信息,不过配置的时候注意下顺序,一般用于配置的最顶层。

    <description>Spring MVC Test</description>
    
    5. import标签

    在我们的Spring配置中,随着我们项目的越来越大,配置文件的越来越复杂,模块化则是我们需要考虑的问题了,而import标签则是充当了这样的角色,我们可以通过import标签将多个Spring配置文件引入到我们的applicationContext文件中。

    比如我们针对数据库配置,缓存配置,MQ配置,日志配置等模块,为每个模块配置相应的文件:

    applicationContext-cache.xml
    applicationContext-db.xml
    applicationContext-mp.xml
    applicationContext-log.xml
    

    然后在applicationContext配置文件中使用import进行引入:

    <?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"
            default-autowire="byType">
    
        <import resource="classpath*:spring/applicationContext-cache.xml"/>
        <import resource="classpath*:spring/applicationContext-db.xml"/>
        <import resource="classpath*:spring/applicationContext-log.xml"/>
        <import resource="classpath*:spring/applicationContext-mq.xml"/>
        
    </beans>
    

    这样,当我们需要新加配置的时候,只需要添加对应的配置文件,然后再使用import标签进行引入即可。这里再简单说下,resource的几种选择:

    1. <import resource="applicationContext-test.xml"/>,默认情况下,Spring会根据相对路径来加载对应的配置文件;
    2. <import resource="classpath:"/>,配置为classpath的情况下,是去class路径及相应的jar包下加载。而classpath:为精确配置,只能匹配一个,而classpath*:是一种通配符的形式,可以加载多个;
    3. <import resource="file:"/>,file是加载一个或多个文件系统的resource,比如file:D:/*.txt将加载D盘下所有的txt文件;
    4. <import resource="http:"/>,http就是从网络下加载相应的resource;

    针对classpath,后续我们会再仔细讨论这个问题,现在先大致了解下即可。

    参考资料:
    Spring中的destroy-method方法详解
    https://docs.spring.io/spring/docs/4.3.14/spring-framework-reference/beans.html

    相关文章

      网友评论

        本文标题:[spring]applicationContext.xml详解

        本文链接:https://www.haomeiwen.com/subject/eodjqftx.html