码迷,mamicode.com
首页 > 编程语言 > 详细

Spring的配置详解

时间:2016-04-29 20:01:41      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:

Spring的配置详解

3.1XML配置的结构

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 将其他资源文件引入到当前配置文件中 -->
    <import resource="resouce.xml"/>
    <!--为id为hello的Bean,取一个别名叫hello1  -->
    <alias name="hello" alias="hello1"/>
    <!-- id表示组件的名字,class表示组件的实现类 -->
    <bean id="hello" class="com.lizhenhua.test.HelloImpl"></bean>
</beans>

XML配置结构元素说明:

1.bean标签主要用来进行Bean定义
2.alias用于定义Bean别名
3.import用于导入其他配置文件的Bean定义。这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组,new String[] {"config1.xml","config2.xml"}传给ApplicationContext实现进行加载多个配置文件,哪个更适合由用户决定;这两种方式都是通过调用Bean Definition Reader 读取Bean定义,内部实现没有任何区别。
4.import标签可以放在任何位置,没有顺序关系

3.2Bean的配置

SpringIoc容器目的就是管理Bean,这些Bean将根据配置文件中的Bean定义进行创建,而Bean定义在容器内部由BeanDefinition对象表示,该定义主要包含以下信息:

全限定类名FQN:用于定义Bean的实现类
Bean行为定义:作用域(单例、原型创建)、是否惰性初始化及生命周期
Bean创建方式定义:通过构造器还是工厂方法创建Bean
Bean之间关系定义:对其他Bean的引用,也就是依赖关系定义,这些引用bean也可以称之为依赖bean,也就是依赖注入

Bean定义只有全限定类名在当使用构造器或静态工厂方法进行实例化bean时是必须的,其他都是可选的定义。

3.3Bean的命名

一、不指定id,只配置必须的全限定类名,由Ioc容器为其生成一个标识,客户端必须通过接口 T getBean(Class requireType)获取Bean

HelloI接口:用于测试

package com.lizhenhua.test;

public interface HelloI {
    public void sayHello();//用于测试
}

HelloI接口的实现类HelloImpl

package com.lizhenhua.test;

public class HelloImpl implements HelloI {

    @Override
    public void sayHello() {
        System.out.println("你好,世界");
    }

}

HelloI接口的实现类HelloImpl2

package com.lizhenhua.test;

public class HelloImpl2 implements HelloI {
    private String message;

    public HelloImpl2(){
        this.message = "Hello World!";
    }

    public HelloImpl2(String message){
        this.message = message;
    }

    @Override
    public void sayHello() {

        System.out.println(message);
    }
}

配置Bean

<!--定义HelloI的接口实现类HelloImpl的Bean的配置-->
<bean class="com.lizhenhua.test.HelloImpl"></bean>
<!--定义HelloI的接口实现类HelloImpl2的Bean的配置-->
<bean class="com.lizhenhua.test.HelloImpl2"></bean>
测试代码:
        @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据类型获取bean,注意该HelloI是一个接口
        HelloI helloI = beanFactory.getBean(HelloI.class);
        //3.执行业务逻辑
        helloI.sayHello();
    }

此代码运行HelloI helloI = beanFactory.getBean(HelloI.class);会报错,为什么呢?因为在该配置文件中有两个该接口的实现类。因此不知道返回哪个结果,所以报错。如何解决呢?
解决方法一:通过修改测试代码。

//解决方法一:指定是哪个具体的实现类,而不是接口
HelloI helloI = beanFactory.getBean(HelloImpl.class);
测试代码:
        @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据类型获取bean,注意该HelloI是一个接口
        HelloI helloI = beanFactory.getBean(HelloImpl.class);
        //3.执行业务逻辑
        helloI.sayHello();
    }

解决方法二:通过修改配置文件,去掉其中一个Bean的配置。原来测试代码不变。

二、指定id,必须在Ioc容器中唯一

<bean id="hello" class="com.lizhenhua.test.HelloImpl"></bean>
测试代码如下
        @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据id获取bean
        HelloI helloI = beanFactory.getBean("hello",HelloI.class);
        //3.执行业务逻辑
        helloI.sayHello();
    }

三、指定name,这样name就是”标识符”,必须在ioc容器中唯一

<bean name="hello" class="com.lizhenhua.test.HelloImpl"></bean>
测试代码如下
        @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据name获取bean
        HelloI helloI = beanFactory.getBean("hello",HelloI.class);
        //3.执行业务逻辑
        helloI.sayHello();
    }

四、指定id和name,id就是标识符,而name就是别名,必须在Ioc容器中唯一

<bean id="hello" name="alias" class="com.lizhenhua.test.HelloImpl"></bean>
测试代码如下
        @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据id获取bean
        HelloI helloI = beanFactory.getBean("hello",HelloI.class);
        //3.执行业务逻辑
        helloI.sayHello();
        //根据name获取bean
        HelloI helloI = beanFactory.getBean("alias",HelloI.class);
        //执行业务逻辑
        helloI.sayHello();
    }

注意:

<!-- 如果id和name一样,Ioc容器能检测到,并消除冲突 -->
    <bean id="hello" name="hello" class="com.lizhenhua.test.HelloImpl"></bean>

五、指定多个name,多个name用,、;分隔,第一个被用作标识符,其他的是别名

<bean  name="hello,hello2,hello3" class="com.lizhenhua.test.HelloImpl"></bean>

六、使用alias标签指定别名,别名也必须在Ioc容器中唯一

<bean  name="beanName" class="com.lizhenhua.test.HelloImpl"></bean>
    <alias name="beanName" alias="alias1"/>
    <alias name="beanName" alias="alias2">

实例化Bean

SpringIoc容器如何实例化Bean呢?传统应用程序可以通过new和反射方式进行实例化Bean.而Spring Ioc容器则需要根据Bean定义里的配置元数据使用反射机制来创建Bean。在SpringIoC容器中根据Bean定义创建Bean主要有以下几种方式:

一、使用构造器实例化Bean:这是最简单的方式,SpringIoc容器即能使用默认空构造器也能使用有参数构造器两种方式创建Bean。

1.使用空构造器进行定义,使用此种方式,class属性指定的类必须有空构造器

2.使用有参数构造器进行定义,使用此种方式,可以使用<constructor-arg>标签指定构造器参数值,其中index表示位置,value表示常量值,也可以指定引用,指定引用使用ref来引用另一个Bean定义。
<bean name="bean2" class="com.lizhenhua.test.HelloImpl">
        <!-- 指定构造器参数 -->
        <constructor-arg index="0" value="hello"></constructor-arg>
    </bean> 
使用构造器的示例代码

(1)准备Bean class,该类有一个空构造器和一个有参构造器

package com.lizhenhua.test;

public class HelloImpl2 implements HelloI {
    private String message;

    public HelloImpl2(){
        this.message = "Hello World!";
    }

    public HelloImpl2(String message){
        this.message = message;
    }

    @Override
    public void sayHello() {

        System.out.println(message);
    }

}

(2)在配置文件配置Bean定义。

<!-- 使用默认构造器 -->
    <bean id="bean1" class="com.lizhenhua.test.HelloImpl2"></bean>
    <!-- 使用有参数的构造器 -->
    <bean id="bean2" class="com.lizhenhua.test.HelloImpl2">
        <!-- 指定构造器参数 -->
        <constructor-arg index="0" value="Hello Spring"></constructor-arg>
    </bean>

(3)测试

@Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据类型获取bean ,使用无参构造器
        HelloI helloI = beanFactory.getBean("bean1",HelloI.class);
        //3.执行业务逻辑
        helloI.sayHello();
        //使用有参数构造器,
        helloI = beanFactory.getBean("bean2", HelloI.class);
        //执行业务逻辑
        helloI.sayHello();
    }
    输出结果:
    Hello World!
    Hello Spring

二、使用静态工厂方式实例化Bean,使用这种方式除了指定必须的class属性,还要指定factory-method属性来指定实例化Bean的方法,而且使用静态工厂方法也允许指定方法参数,spring IoC 容器将调用此属性指定的方法来获取Bean,配置如下所示:

(1)先编写静态工厂类代码

package com.lizhenhua.test;

public class HelloIStitaicFactory {
    //工厂方法
    public static HelloI newInstance(String message){
        //返回需要的Bean实例
        return new HelloImpl2(message);
    }   
}

(2)配置Bean定义

<!-- 使用静态工厂方法 -->
    <bean id="bean3" class="com.lizhenhua.test.HelloIStitaicFactory" factory-method="newInstance">
        <constructor-arg index="0" value="静态工厂方法"></constructor-arg>
    </bean>

(3)测试

package com.lizhenhua.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class HelloTest {
    @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用静态工厂获取Bean
        HelloI helloI = beanFactory.getBean("bean3", HelloI.class);
        //执行业务逻辑
        helloI.sayHello();
    }

三、使用实例工厂方法实例化Bean,使用这种方式不能指定class属性,此时必须使用factory-bean属性来指定工厂Bean,factory-method属性值指定实例化Bean的方法,而且使用实例工厂方法允许指定方法参数,方式和使用构造器方式一样。

①实例工厂代码

package com.lizhenhua.test;

public class HelloIInstanceFactory  {
    public HelloI newInstance(String message){
        return new HelloImpl2(message);
    }
}

②在配置文件中定义Bean

<!-- 1.定义实例工厂Bean -->
    <bean id="beanInstanceFactory" class="com.lizhenhua.test.HelloIInstanceFactory"></bean>
    <!-- 2.使用实例工厂Bean创建Bean -->
    <bean id="bean4" factory-bean="beanInstanceFactory" factory-method="newInstance">
        <!-- 指定带参数构造器 -->
        <constructor-arg index="0" value="实例工厂方法"></constructor-arg>
    </bean>

③测试

@Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用实例工厂创建bean
        HelloI helloI = beanFactory.getBean("bean4", HelloI.class);
        //执行业务逻辑
        helloI.sayHello();
    }
    测试输出结果
    实例工厂方法

通过以上例子我们已经基本掌握了如何实例化Bean了。
这三种方式只是配置不一样,从获取方式看完全一样,没有任何不同。这也是SpringIocDE MEILI .
SpringIoc帮我们创建Bean,只管使用就可以了。

1.DI的配置使用

1.1依赖和依赖注入

传统应用程序设计中所说的依赖一般指类之间的关系。

复习类之间的关系

泛化: 表示类与类之间的继承关系、接口与接口之间的继承关系

实现: 表示类对接口的实现

依赖: 当类与类之间有使用关系时就属于依赖关系,不同于关联关系,依赖不具有“拥有关系”,而是一种“相识关系”,只在某个特定地方(比如某个方法体内)才有关系

关联:表示类与类或类与接口之间的依赖关系,表现为“拥有关系”;具体到代码可以使用实例变量表示。

聚合:是关联的特殊情况,体现部分-整体关系,是一种弱拥有关系;整体和部分可以有不一样的生命周期;是一种弱关联

组合:是关联的特殊情况,也行了部分-整体关系,是一种强拥有关系,整体与部分由相同的生命周期,是一种强关联

Spring Ioc容器的依赖有两层含义:Bean依赖容器和容器注入Bean的依赖资源

Bean依赖容器: 也就是说Bean要依赖于容器,这里的依赖是指容器负责创建Bean并管理Bean的生命周期,正是由于容器来控制创建Bean并注入依赖,也就是控制权被反转了,这也正是Ioc名字的由来,此处的有依赖是指Bean和容器之间的依赖关系

容器注入Bean的依赖资源:容器负责注入Bean的依赖资源,依赖资源可以是Bean、外部文件、常量数据等,在JAVA中都反映为对象,并且由容器负责组装Bean之间的依赖关系。此处的依赖是指Bean之间的依赖关系,可以认为是传统类与类之间的关联、聚合、组合关系。


为什么要应用依赖注入,应用依赖注入能给我们带来哪些好处呢?

动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件。

更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现

更好实践优先使用对象组合,而不是类继承:因为Ioc容器采用注入依赖,也就是组合对象,从而更好的实践对象组合

采用对象组合,Bean的功能可能由几个依赖Bean的功能组合而成,其Bean本身可能只提供少许功能或根本无任何功能,全部委托给依赖Bean,对象组合具有动态性,能更方便的替换掉依赖Bean,从而改变Bean功能

而如果采用类继承,Bean没哟偶依赖Bean ,而是采用继承方式添加新功能,而且功能是在编译时就确定了,不具有动态性,而且采用类继承导致Bean与子Bean之间高度耦合,难以复用。

增加Bean可复用性:依赖于对象组合,Bean更可复用且复用更简单

降低Bean之间耦合:由于我们完全采用面向接口编程,在代码中没有直接引用Bean依赖实现,全部引用接口,而且不会出现显示的创建依赖对象代码,而且这些依赖是由容器来注入,很容易替换依赖实现类,从而降低Bean与依赖之间耦合。

代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。



从以上我们可以看出,其实依赖注入只是一种装配对象的手段,设计的类结构才是基础,如果设计的类结构不支持依赖注入,Spring Ioc容器也注入不了任何东西,从而从根本上说"如何设计好类结构才是关键,依赖注入只是一种装配对象手段"。

前边Ioc一章我们已经了解了Bean依赖容器,那容器如何注入Bean的依赖资源,SpringIoc容器注入依赖资源主要有两种基本方式实现:

构造器注入:就是容器实例化Bean时注入那些依赖,通过在Bean定义中指定构造器参数进行注入依赖,包括实例工厂方法参数注入依赖,但静态工厂方法参数不允许注入依赖。

setter注入:通过setter方法进行注入依赖

方法注入:能通过配置方式替换掉Bean方法,也就是通过配置改变Bean方法功能

1.2构造器注入

使用构造器注入通过配置构造器参数实现,构造器参数就是依赖。除了构造器方式,还有静态工厂,实例工厂方法可以进行构造器注入。

构造器注入可以根据参数索引注入、参数类型注入或Spring3支持的参数名注入,但参数名注入是有限制的,需要使用在编译程序时打开调试模式(即在编译时使用javac -g:vars在产拉丝少文件中生成变量调式信息,默认是不包含变量调试信息的,从而能获取参数名字,否则获取不到参数名字)或在构造器上使用@ConstructorProperties注解来指定参数名。({"message","index"})


一、根据参数索引注入,使用<constructor-arg index="1" value="1"/>来指定注入的依赖,其中index表示索引,从0开始,value来指定注入的常量值。


二、根据参数类型进行注入,使用标签<constructor-arg type="java.lang.String" value="nihao"/>来注入依赖。type表示需要匹配的参数类型,可以是基本类型也可以是其他类型


三、根据参数名进行注入<constructor-arg name="message" value="nihao"/>name表示需要匹配的参数名字,value来指定注入的常量值


四、静态工厂方法注入

(1)先编写静态工厂类代码

package com.lizhenhua.test;

public class HelloIStitaicFactory {
    //工厂方法
    public static HelloI newInstance(String message){
        //返回需要的Bean实例
        return new HelloImpl2(message);
    }   
}

(2)配置Bean定义

<!-- 使用静态工厂方法 -->
    <bean id="bean3" class="com.lizhenhua.test.HelloIStitaicFactory" factory-method="newInstance">
        <constructor-arg index="0" value="静态工厂方法"></constructor-arg>
    </bean>

(3)测试

package com.lizhenhua.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class HelloTest {
    @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用静态工厂获取Bean
        HelloI helloI = beanFactory.getBean("bean3", HelloI.class);
        //执行业务逻辑
        helloI.sayHello();
    }

五、实例工厂方法注入
①实例工厂代码

package com.lizhenhua.test;

public class HelloIInstanceFactory  {
    public HelloI newInstance(String message){
        return new HelloImpl2(message);
    }
}

②在配置文件中定义Bean

<!-- 1.定义实例工厂Bean -->
    <bean id="beanInstanceFactory" class="com.lizhenhua.test.HelloIInstanceFactory"></bean>
    <!-- 2.使用实例工厂Bean创建Bean -->
    <bean id="bean4" factory-bean="beanInstanceFactory" factory-method="newInstance">
        <!-- 指定带参数构造器 -->
        <constructor-arg index="0" value="实例工厂方法"></constructor-arg>
    </bean>

③测试

    @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用实例工厂创建bean
        HelloI helloI = beanFactory.getBean("bean4", HelloI.class);
        //执行业务逻辑
        helloI.sayHello();
    }
    测试输出结果
    实例工厂方法
注意:静态工厂方式和实例工厂方式根据参数名注入的方式只支持通过在class文件中添加变量调式信息方式才能运行,注解方式不能工作,因此,不建议使用根据

参数名进行构造器注入。。

1.3 setter注入

setter注入,是通过在通过构造器、静态工厂或实例工厂实例好Bean后,通过调用Bean类的setter方法进行注入依赖。

setter注入方式只有一种根据setter名字进行注入
<!--setter方法注入-->
<property name="message" value="hello world" />

setter注入的方法名要遵循JavaBean getter/setter方法命名约定
JavaBean:本质就是一个POJP类

该类必须要有公共的无参构造器
属性为private访问级别
属性必要时通过一组setter和getter方法来访问
setter方法,以set开头,后跟首字母大写的属性名
getter方法,一般属性以get开头,对于boolean类型一般以is开头,后跟首字母大写的属性名

还有一些特殊情况,比如属性有连续两个大写字母开头,URLsetter/getter方法为,setURL和getURL

1.4 注入常量

<property name="message" value="Hello"></property><property name="index">
            <value>1</value>
        </property>

注意:Spring类型转换对于boolean类型进行了容错处理除了可以使用true/fasle,还能使用yes/no 、on/off、 1/0

1.5注入 Bean ID

用于注入Bean的ID,ID是一个常量不是引用,且类似于注入常量,但提供错误验证功能,配置方式如下所示

<property name="id"><idref bean="bean1"></property>
        <property name="id"><idref local="bean1"></property>
        两种方式都可以,上述配置本质上在运行时等于如下方式
        <bean id="bean1" class="" />
        <bean id="idrefBean" calss="">
            <property name="id" value="bean1"/>
        </bean>
        第一种方式可以在容器初始化时校验引用的Bean是否存在,如果不存在将抛出异常,
        而第二种方式只有在Bean实际使用时才能发现传入的Bean的ID是否正确,可能发生不可预料的错误
测试
package com.lizhenhua.test2;

public class IdRefTestBean {

        private String id;
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }

}

    <bean id="bean1" class="java.lang.String">
        <constructor-arg index="0" value="test"></constructor-arg>
    </bean>
    <bean id="bean2" class="java.lang.String">
        <constructor-arg index="0" value="test2"></constructor-arg>
    </bean>
    <bean id="idrefBean1" class="com.lizhenhua.test2.IdRefTestBean">
        <property name="id">
            <idref bean="bean1"/>
        </property>
    </bean>
        <bean id="idrefBean2" class="com.lizhenhua.test2.IdRefTestBean">
        <property name="id">
            <idref local="bean2"/>
        </property>
    </bean>

    @Test
    public void testHelloWorld(){
        //1.获取BeanFactory   
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("/com/lizhenhua/test2/applicationContext.xml");
        IdRefTestBean helloI = beanFactory.getBean("idrefBean1", IdRefTestBean.class);
        //执行业务逻辑
        System.out.println(helloI.getId());
         helloI = beanFactory.getBean("idrefBean2", IdRefTestBean.class);
            //执行业务逻辑
            System.out.println(helloI.getId());
    }

测试结果 
bean1
bean2

为什么是bean1和bean2  而不是 test test1

1.6注入集合、数组和字典

一、注入集合类型:包括Colleaction set、list
List类型

        <property name="values">
            <!-- value-type:条目的类型 merge用于父子Bean情况是否合并list条目 -->
            <list value-type="java.lang.String" merge="default">
                <!-- value指定列表条目值 -->
                <value>nihao1</value>
                <value>nihao2</value>
            </list>
        </property>

    1、可选的value-type属性,表示列表中条目的数据的类型,比如value-type="java.lang.String"
    2.也可以采用泛型,Spring能根据泛型数据自动检测到List里条目的数据类型,比如java.util.List<String>,Spring能自动识别列表需要条目为String数据类型;

    3.如果既没有指定value-type属性List也不是泛型的则默认就是String类型

    Set类型、Collection类型都是与List的规则一样
    只是将标签改为对应的标签
二、注入数组类型:用array标签
二维数组
        <array>
            <array>
                <value>1</value>
            </array>
            <array>
                <value>4</value>
            </array>
        </array>
三、注入字典Map类型
方式一
        <property name="values">
            <map key-type="java.lang.String" value-type="java.lang.String">
                <entry key="22" value="22"></entry>
            </map>
        </property>
        方式二
            <map key-type="java.lang.String" value-type="java.lang.String">
                <entry>
                    <!-- 注入key的值 -->
                    <key><value>333</value></key>
                    <!-- 注入值的值 -->
                    <value>2222</value>
                </entry>
            </map>
四、Properties注入
<props value-type="java.util.String" merge="default">
                <prop key="22">22ss</prop>
                <prop key="33">33ss</prop>
            </props>

    注意:props的键和值必须是String类型,否则无效

1.7引用其他Bean

使用ref属性代替value属性或者<ref>标签代替value标签,与常量一样

    其他引用方式:

    (1)<ref local="bean1"/>用于引用通过<bean id="beanName"/>方式中通过id属性指定的Bean,它能利用XML解析器的验证功能在读取配置文件时来验证引用的Bean是否存在。因此如果在当前配置文件中有相互引用的Bean可以采用ref local 方式。如果配置错误能在开发调式时就发现错误。

    (2)<ref parent="bean2"/>配置方式:用于引用父容器中的Bean,不会引用当前容器中的Bean,当然父容器中的Bean和当前容器的Bean是可以重名的,获取顺序是先从父容器找Bean,找不到在找当前容器中的Bean

1.8内部Bean定义

内部Bean就是在<property>或 <constructor-arg>内通过bean标签定义的bean,该bean不管是否指定idname,该bean都会有唯一的匿名标识符,而且不能指定别名,该内部bean对其他外部Bean不可见

1.9处理null值

用<bull/>标签注入null

1.10对象图导航注入支持

所谓对象图导航是指类似a.b.c这种点缀访问形式的访问或修改值。Spring支持对象图导航方式依赖注入。对象图导航依赖注入有一个限制就是比如a.b.c对象导航图注入中,a和b必须为非null值才能注入c,否则将抛出空指针异常支持数组、列表、字典、Properties数据类型的导航

对于set类型无法支持,因为无法导航
例如:
    array[0] list[1]    
    不过需要注意数组越界

1.11配置简写

一、构造器注入
1.常量值
    简写<constructor-arg index="0" value="常量"></constructor-arg>
    全写<constructor-arg index="0" ><value>常量</value></constructor-arg>

    2.引用
    简写<constructor-arg index="0" ref="引用"></constructor-arg>
    全写<constructor-arg index="0" ><ref bean="引用"></constructor-arg>

    二、setter注入

    1.常量值
        简写:<property name="message" value="常量"/>
        全写:<property name="message" ><value>常量</value></property>
    2.引用
        简写:<property name="message" ref="引用"/>
        全写:<property name="message" ><ref bean="引用"></property>
    3.数组
    4.列表
    5.集合
    6.字典
    简写:

        <map>
            <entry key="键常量" value="值常量">
            <entry key-ref="键引用" value-ref="值引用">
        </map>
    全写:
            <map>
                <entry>

                    <key><value>键常量</value></key>
                    <value>值常量</value>
                </entry>
                <entry>

                    <key><ref bean="键引用"/></key>
                    <ref bean="值引用"/>
                </entry>
            </map>


三、使用p命名空间简化setter注入 
首先指定:xmlns:p="http://www.springframework.org/schema/p"
    <bean id="" class="" p:id="value"/>
    常量setter注入方式,其等价于<property name="id" value="value"/>
    <bean id="" class="" p:id-ref="bean1"/>:引用setter注入方式,等价于<property name="id" ref="bean1">

循环依赖

什么是循环依赖
循环依赖就是循环引用。就是两个或多个Bean相互之间的持有对方。

注意:开发中不要构成循环引用,否则会出现异常

更多的DI知识

延迟初始化Bean

    延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。

    配置方式很简单,只需在Bean标签上指定lazy-init="true"即可延迟初始化bean

    <bean id="bean" class="com.lizhenhua.Test" p:id="value" lazy-init="true"/>

    Spring容器会在创建容器时提前初始化singleton作用域的Bean。singleton就是单例的意思即整个容器每个Bean只有一个实例。
    Spring容器预先初始化Bean通常能帮助我们提前发现配置错误,所以如果没有什么情况建议开启,除非有某个Bean可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到,可以设置为延迟初始化。

    延迟初始化的Bean通常会在第一次使用时被初始化,或者在被非延迟初始化Bean作为依赖对象注入时会随着初始化该Bean时被初始化。

    容器管理初始化Bean消除了编程实现延迟初始化,完全由容器控制,只需在需要延迟初始化的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"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- 配置bean  id是bean的名字  -->
    <bean id="helloWorld" class="com.lizhenhua.spring.beans.HelloWord">
        <!-- name对应setter方法名,value是setter方法的默认值 -->
        <property name="name" value="Spring"></property>
    </bean>

    <!-- 使用构造器注入属性值可以指定参数的位置和参数的类型!以区分重载的构造器 -->
    <bean name="car2" class="com.lizhenhua.spring.beans.Car">
    <!-- 如果字面值包含特殊字符可以使用![CDATA[]]包裹起来 -->
    <!-- 属性值可以使用value子节点设值 -->
        <constructor-arg value="奔驰" type="java.lang.String"></constructor-arg>
        <constructor-arg type="java.lang.String">
            <value><![CDATA[<ShangHai^>]]></value>
        </constructor-arg>
        <!-- 如果是数值类型的字面值可以使用value子节点设值 -->
        <constructor-arg type="int">
            <value>100</value>
        </constructor-arg>
    </bean>

    <bean id="person" class="com.lizhenhua.spring.beans.Person">
        <property name="name" value="Tom"></property>
        <property name="age" value="24"></property>
        <!-- ref 设置引用之间的关系 -->
    <!-- 方式一     -->
        <property name="car" ref="car2"></property> 

        <!-- 方式二 -->
    <!--    <property name="car">
            <ref bean="car2"/>
        </property>
        -->
        <!-- 方式三:内部bean :内部bean不能够再外部被引用。-->
        <!--  
        <property name="car">
            <bean class="com.lizhenhua.spring.beans.Car">
                <constructor-arg>

                    <null></null>
                </constructor-arg>
                <constructor-arg value="changsha"></constructor-arg>
                <constructor-arg value="2000" type="int"></constructor-arg>
            </bean>
        </property>


        -->
        <!-- 为级联属性赋值,必须先初始化 -->
    <property name="car.speend" value="222"></property> 
    </bean> 
</beans>

3.2使用depends-on

depends-on是指Bean初始化及销毁时的顺序, 意思是:depends-on 属性中定义的依赖bean 要在定义该属性的Bean之前销毁。 但是实际是错误的,定义depends-on 属性的Bean会首先被销毁,然后才是depends-on指定的Bean被销毁。

测试depends-on
定义类
package com.lizhenhua.spring.beans;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ResourceBean {
    private FileOutputStream fos;
    private File file;
    //初始化方法
    public void init(){
        System.out.println("ResouceBean:----初始化");
        //加载资源,在此只是演示
        System.out.println("ResouceBean:----加载资源,执行一些预操作");
        try {
            this.fos = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    //销毁资源
    public void destroy(){
        System.out.println("ResouceBean:----销毁");
        //释放资源
        System.out.println("ResouceBean:----释放资源,执行一些清理操作");
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public FileOutputStream getFos(){
        return fos;
    }

    public void setFile(File file) {
        this.file = file;
    }
}


package com.lizhenhua.spring.beans;

import java.io.IOException;

public class DependentBean {
    ResourceBean resourceBean;
    public void write(String ss) throws IOException{
        System.out.println("DependentBean:-----写资源");
        resourceBean.getFos().write(ss.getBytes());
    }

    //初始化方法
    public void init() throws IOException{
        System.out.println("DependentBean:-----初始化");
        resourceBean.getFos().write("DependentBean:-----初始化".getBytes());
    }

    //销毁方法
    public void destroy() throws IOException{
        System.out.println("DependentBean:-----销毁");
        resourceBean.getFos().write("DependentBean:-----销毁".getBytes());

    }
    public void setResourceBean(ResourceBean resourceBean) {
        this.resourceBean = resourceBean;
    }
}

配置Bean

<!-- init-method:"init" :指定初始化方法,在构造器注入和setter注入完毕后执行,destroy-method="destroy" 指定销毁方法,只有“singleton”作用域能销

毁,prototype作用域的一定不能,其他作用域不一定能;  -->
    <bean id="resouceBean" class="com.lizhenhua.spring.beans.ResourceBean" init-method="init" destroy-method="destroy">
         <!-- Spring容器能够自动把字符串转换为java.io.File -->
        <property name="file" value="D:/test.txt"></property>
    </bean>
    <!-- depends-on:resouceBean 因为dependentBean必须先初始化resouceBean类,才能执行dependentBean类中的方法 -->
    <bean id="dependentBean" class="com.lizhenhua.spring.beans.DependentBean" init-method="init" destroy-method="destroy" depends-on="resouceBean">
        <property name="resourceBean" ref="resouceBean"></property>
    </bean>

测试类

public static void main(String[] args) throws IOException {


        //1.创建SpringIOC容器对象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //注册销毁回调,否则我们定义的销毁方法不执行
        ctx.registerShutdownHook();
        DependentBean dependentBean = 
            ctx.getBean("dependentBean", DependentBean.class);
        dependentBean.write("nihao");
    }
运行结果
ResouceBean:----初始化
ResouceBean:----加载资源,执行一些预操作
DependentBean:-----初始化
DependentBean:-----写资源
DependentBean:-----销毁
ResouceBean:----销毁
ResouceBean:----释放资源,执行一些清理操作

3.3自动装配

自动装配是指由Spring来自动地注入依赖对象,无需人工参与。

    目前Spring3.0 支持“no”、“byName”、“byType”“constructor”四种自动装配,默认是“no” 指不支持自动装配的。

    其中Spring3.0已不推荐使用之前版本的autodetect自动装配,推荐使用java5支持的@Autowired注解方式代替;如果想支持autodetect自动装配,请将schema改为spring-beans-2.5.xsd或去掉

    ①减少构造器注入和setter注入配置,减少配置文件的长度。

    一、default: 表示使用默认的自动装配,默认的自动装配需要在<beans>标签中使用default-autowire属性指定。
    二、no:意思是不支持自动装配,必须明确指定依赖
    三、byName:通过设置Bean定义属性 autowire="byName",意思是根据名字进行自动装配,只能用于setter注入。
    例如:我们有方法setHelloApi 则byName 方式 Spring容器将查找名字为helloApi的Bean并注入,如果找不到指定的Bean,将什么也不注入。

    如果一个bean有很多setter注入,通过byName方式能够减少很多property配置。

    注意:在根据名字注入时,将把当前Bean自己排除在外。


    四、byType:根据类型注入,用于setter注入,比如如果指定自动装配方式为byType,而setHelloApi方法需要注入HelloApi类型数据,则Spring容器将查找

HelloApi类型数据,如果找到一个则注入该Bean,如果找不到将什么也不注入,如果找到多个Bean将优先注入bean标签primary属性为true的Bean,否则抛出异常来表明有多

个Bean发现但不知道使用哪个。    

    注意:在根据类型注入时,将把当前Bean自己排除在外。

    自动装配候选者中的首选本案:提升byType自动装配的优先权通过<bean primary="true">进行设置,因为byType找到的符合类型的不止一个
    从自动装配候选者中去除: <bean autowire-candidate="false">进行设置

    五、constructor:功能和byType功能一样 ,根据类型注入构造器参数,只是用于构造器注入方式。

    六、autodetect:自动检测是使用constructor还是byType自动配置方式,已不推荐使用。如果有空构造器那么将采用byType自动装配方式,否则使用constructor自动装配方式

    可以采用<beans>标签中通过default-autowire属性指定全局的自动装配方式。

    不是所有类型都能自动装配:

        不能自动装配的数据类型:Object、基本数据类型(Date、CharSequence、Number、URL、URI、Class、int)等
        通过<beans>标签的default-autowire-candidates属性指定的匹配模式,不匹配的将不能作为自动装配的候选者,例如:指定*Service,*Dao,将只把匹配这些模式的Bean作为候选者,而不匹配的不会作为候选者。

        通过bean标签的autowire-candidates属性设置为false,从而该bean将不会作为依赖注入的候选者

    数组、集合、字典类型的根据自动装配和普通类型的自动装配是有区别的

        数组类型、集合接口类型:将根据泛型获取匹配的所有候选者并注入到数组或集合中,如List<HelloApi> list将选择所有的HelloApi类型Bean并注入到list中,而对于集合的具体类型将只选择一个候选者,如ArrayList<HelloApi> list 将选择一个类型为ArrayList的Bean注入,而不是选择所有的HelloApi类型Bean注入

        字典Map接口类型:同样根据泛型信息注入,键必须为String类型的Bean名字,值根据泛型信息获取。如:Map<String,HelloApi> map 将选择所有的HelloApi类型Bean并注入到map中,而对于具体字典类型如 HashMap<String,HelloApi> map 将只选择类型为HashMap的Bean注入,而不是选择所有的HellApi类型Bean注入    

    注意:
        首先,自动装配确实减少了配置文件的量,其次,byType自动装配能在相应的Bean更改了字段类型时自动更新,即修改Bean类不需要修改配置,确实简单了
        缺点:超找注入错误时麻烦。

        配置注入的数据会覆盖自动装配注入的数据

    采用自动装配方式时如果没有找到合适的Bean时什么也不做,这样的程序中莫名其妙的发生一些空指针异常,而且是在程序运行期间才能发现,有没有办法能提前发现这些错误呢?

3.4依赖检查

用于检查bean定义的属性都注入数据了,不管是自动装配还是配置方式注入的都能检查,如果没有注入数据将报错,从而提前发现注入错误,只检查具有setter方法的属性。
    Spring3也不推荐配置方式依赖检查了,建议采用java5 @Required注解方式。

    测试时XMLschema降低2.5版本的,和自动装配中autodetect配置方式的xsd一样。

    dependency-check: none|simple|objects|all

    一、none:默认方式,不检查
    二、objects:检查除基本数据类型外的依赖对象。
    三、simple:对基本类型进行依赖检查,包括数据类型,其他依赖不报错;
    四、all:对所有类型进行依赖检查

    依赖检查也可以通过<beans>标签中default-dependency-check属性来指定全局依赖配置检查

3.5方法注入

通过配置方式覆盖或拦截指定的方法,通常通过代理模式实现。

    Spring提供两种方法注入:查找方法注入和方法替换注入

    因为Spring是通过CGLIB动态代理方式实现方法注入,也就是通过动态修改类的字节码来实现的,本质就是生成需方法注入的类的子类方式实现。

    在进行测试之前,我们需要确保将com.springsouce.cn.sf.cglib-2.2.0.jar放到lib里并添加到javabuildpath中的libararies中。否则报错。

    一、查找方法注入:又称为Lookup方法注入,用于注入方法返回结果,也就是说能通过配置方式替换方法返回结果。
    使用<lookup-method name="方法名" bean="bean名字"/>配置
    其中name属性指定方法名,bean属性指定方法需要返回的Bean。

    方法定义格式:访问级别必须是publicprotected ,保证能被子类重载,可以是抽象方法,必须有返回值,必须是无参数方法,查找方法的类和被重载的方法必

须为非final:

    <public|protected> [abstract] <return-type> theMethodName(no-arguments);

    因为Singleton Bean在容器中只有一个实例,而prototype Bean是每次获取容器都返回一个全新的实例,所以如果singletonBean在使用prototypeBean情况时,那么

prototypeBean由于是singleton Bean 的一个字段属性,所以获取的这个prototype Bean 就和它所在的singleton Bean具有同样的生命周期,所以不是我们所期待的结果。

因此查找方法注入就是用于解决这个问题

    首先定义我们需要的类,Printer类 是一个有状态的类,counter字段记录访问次数
package com.lizhenhua.spring.bean;

public class Printer {
    private int counter = 0 ;
    public void print(String type){
        System.out.println(type + " printer:" + counter++) ;
    }
}


    HelloImpl类用于打印欢迎信息,其中包括setter注入和方法注入,此处特别需要注意的是该类是抽象的,充分说明了需要容器对其进行子类化处理,还定义了一个

抽象方法createPrototypePrinter用于创建prototype Bean ,createSingletonPrinter方法用于创建singleton Bean,此处注意方法会被Spring拦截,不会执行方法体代码。
package com.lizhenhua.spring.bean;
/**
 * 打印欢迎信息
 * @author Administrator
 *HelloImpl类是抽象化的,如果需要创建,必须有实现类。这里的实现类是由SpirngIoc容器对齐进行子类化处理。
 */
public abstract class HelloImpl implements HelloI {
    private Printer printer;

    @Override
    public void sayHello() {
        printer.print("setter");
        createPrototypePrinter().print("prototype");

    }
    /**
     * 该方法用于创建prototype Bean ,在此处也就是与printer属性相对应。
     * @return
     */
    public abstract Printer createPrototypePrinter();

    /**
     * 创建单例printer
     * @return
     */
    public Printer createSingletonPrinter(){
        System.out.println("该方法不会被执行,如果输出就错了");
        return new Printer();
    }

    public void setPrinter(Printer printer) {
        this.printer = printer;
    }
}

    配置

<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 配置一个prototype的Printer Bean -->
    <bean id="prototypePrinter" class="com.lizhenhua.spring.bean.Printer" scope="prototype" />
    <!-- 配置一个singleton的Printer Bean 单例 -->
    <bean id="singletonPrinter" class="com.lizhenhua.spring.bean.Printer" scope="singleton" />
    <!-- 配置一个singleton的HelloImpl Bean -->
    <bean id="hello1" class="com.lizhenhua.spring.bean.HelloImpl" scope="singleton">
        <property name="printer" ref="prototypePrinter"></property>
        <lookup-method name="createPrototypePrinter" bean="prototypePrinter"></lookup-method>
        <lookup-method name="createSingletonPrinter" bean="singletonPrinter"></lookup-method>
    </bean>
    <!-- 配置prototype的HelloImpl Bean -->
    <bean id="hello2" class="com.lizhenhua.spring.bean.HelloImpl" scope="prototype">
        <property name="printer" ref="prototypePrinter"></property>
        <lookup-method name="createPrototypePrinter" bean="prototypePrinter"></lookup-method>
        <lookup-method name="createSingletonPrinter" bean="singletonPrinter"></lookup-method>
    </bean>
</beans>

public class Test {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("singleton sayHello===");
        HelloI helloI = context.getBean("hello1",HelloI.class);
        helloI.sayHello();

        helloI = context.getBean("hello1",HelloI.class);
        helloI.sayHello();


        System.out.println("prototype sayHello===");
        HelloI  helloI2 = context.getBean("hello2",HelloI.class);
        helloI2.sayHello();
        helloI2 = context.getBean("hello2",HelloI.class);
        helloI2.sayHello();

    }
}
singleton sayHello===
setter printer:0
prototype printer:0
setter printer:1
prototype printer:0
prototype sayHello===
setter printer:0
prototype printer:0
setter printer:0
prototype printer:0


    二、替换方法注入: 也叫MethodReplacer注入,和查找注入方法不一样的是,他主要用来替换方法体。

Bean的作用域

scope:singleton|prototype|request|session

    singleton:单例

    prototype:原型。每次向Spring容器请求获取Bean都返回一个全新的Bean。相对于singleton来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean

    Web应用中的作用域:

    request、session、globalSession

    request作用域:表示每个请求需要容器创建一个全新Bean

    session作用域:表示每个会话需要容器创建一个全新Bean

    globalSession:类似于session作用域,只是其用于portlet环境的web应用。如果非portlet环境将设为session作用域


    自定义作用域

Spring的配置详解

标签:

原文地址:http://blog.csdn.net/li286487166/article/details/51225764

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!