Spring提供了三种方法进行配置:

  1. 在XML文件中显式配置
  2. 在Java的接口和类中实现配置
  3. 隐式Bean的发现机制和自动装配原则

方式选择的原则:

一)最优先:通过隐式Bean的发现机制和自动装配的原则。

  • 好处:减少程序开发者的决定权,简单灵活。

二)其次:Java接口和类中实现配置

  • 好处:避免XML配置的泛滥,也更容易。

三)最后:XML方式配置

  • 好处:简单易懂

通过XML配置装配Bean

使用XML装配Bean需要定义对应的XML,需要引入对应的XML模式(XSD)文件,这些文件会定义配置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>

然后在beans标签中,添加对应的bean标签来定义Spring Bean。

     <!--id:bean唯一标识 class:需要注册到Ioc容器的Bean对象的实现类-->
     <bean id="account" class="com.company.pojo.Account">
        <!--
            bean标签注入属性值
            1) property标签
            name:属性名(setXXX()方法名除去set之后的名称)
            value:对应属性值
            ref:引用已经定义的Bean对象
        -->
        <property name="id" value="3"></property>
        <property name="name" value="may"></property>
        <property name="money" value="300"></property>
        <!--
            2)constructor-arg标签
            name:构造方法中参数名称
            value:对应的参数值
            ref:引用已经定义的Bean对象
        -->
        <constructor-arg name="id" value="3"></constructor-arg>
        <constructor-arg name="name" value="may"></constructor-arg>
        <constructor-arg name="money" value="500"></constructor-arg>
    </bean>

装配集合

public class ComplexCollection {
    private int id;
    private List<String> list;
    private Map<String,String> map;
    private Properties properties;
    private Set<String> set;
    private String[] array;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Set<String> getSet() {
        return set;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public String[] getArray() {
        return array;
    }

    public void setArray(String[] array) {
        this.array = array;
    }
}

对应xml配置

    <bean id="complexCollection" class="com.company.pojo.ComplexCollection">
        <!--装配Integer类型的id-->
        <property name="id" value="10"></property>
        <!--装配List类型的list-->
        <property name="list">
            <list>
                <value>123</value>
                <value>456</value>
                <value>789</value>
            </list>
        </property>
        <!--装配Map类型的map-->
        <property name="map">
            <map>
                <entry key="a" value="123"></entry>
                <entry key="b" value="456"></entry>
                <entry key="c" value="789"></entry>
            </map>
        </property>
        <!--装配Properties类型的properties-->
        <property name="properties">
            <props>
                <prop key="driver">com.microsoft.sqlserver.jdbc.SQLServerDriver</prop>
                <prop key="url">jdbc:sqlserver://localhost:1433;DatabaseName=testdb</prop>
                <prop key="username">sa</prop>
                <prop key="password">123.abc</prop>
            </props>
        </property>
        <!--装配Set类型的set-->
        <property name="set">
            <set>
                <value>monday</value>
                <value>tuesday</value>
                <value>wednesday</value>
                <value>thursday</value>
                <value>friday</value>
                <value>saturday</value>
                <value>sunday</value>
            </set>
        </property>
        <!--装配String[]类型的array-->
        <property name="array">
            <array>
                <value>january</value>
                <value>february</value>
                <value>march</value>
                <value>april</value>
            </array>
        </property>
    </bean>

总结:

  • List属性为对应的<list>标签进行装配,然后通过多个<value>标签设值。
  • Map属性为对应的<map>标签进行装配,然后通过多个<entry>标签设值,只是<entry>包含一个键值对(key-value)的设置。
  • Properties属性为对应的<properties>标签进行装配,通过一个<props>标签设值,只是含有多个prop标签,其中有一个必填属性key,然后可以设置值。
  • Set属性为对应的<set>标签进行装配,然后通过多个<value>标签设值。
  • 对于数组而言,可以使用<array>标签进行装配,然后通过多个<value>标签设值。

对于上述的集合,都可以ref引用已经设置好的Bean对象来设置值。

命名空间装配

除了上述的配置之外,Spring还提供了对应的命名空间的定义,只是在使用命名空间的时候要先引入对应的命名空间和XML模式(XSD)文件。

【c-命名空间】

c-命名空间是在Spring 3.0中引入的,它是在XML中更为简洁地描述构造器参数的方式,要使用它,必须在XML的顶部声明其模式。

 

 注意:是通过构造器参数的方式

Student.java

public class Student {

    int id;
    String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    // setter and getter
}

在c-命名空间和模式声明之后,就可以使用它来声明构造器参数了。

<!-- 引入 c-命名空间之前 -->
<bean name="student1" class="pojo.Student">
    <constructor-arg name="id" value="1" />
    <constructor-arg name="name" value="学生1"/>
</bean>

<!-- 引入 c-命名空间之后 -->
<bean name="student2" class="pojo.Student"
      c:id="2" c:name="学生2"/>

c-命名空间属性名已“c:”开头,也就是命名空间的前缀。接下来就是装配的构造器参数名,在此之后如果需要注入对象的话则要跟上-ref(如 c:card-ref="idcard",则对card这个构造器参数注入之前配置名为idcard的bean)。

很显然,使用c-命名空间属性要比使用<constructor-arg>元素精简,并且会直接引用构造器之中参数的名称,有利于使用的安全性。

另外一种替代方式

<bean name="student2" class="pojo.Student"
      c:_0="3" c:_1="学生3"/>

将参数的名称替换成“0”和“1”,也就是参数的索引。因为在XML中不允许数字作为属性的第一个字符,因此必须要添加一个下划线来作为前缀。

【p-命名空间】

c-命名空间通过构造器注入的方式来配置bean,p-命名空间则是用setter的注入方法来配置bean,同样的,需要引入声明。

 

 通过p-命名空间来设置属性

<!-- 引入p-命名空间之前 -->
<bean name="student1" class="pojo.Student">
    <property name="id" value="1" />
    <property name="name" value="学生1"/>
</bean>

<!-- 引入p-命名空间之后 -->
<bean name="student2" class="pojo.Student" 
      p:id="2" p:name="学生2"/>

但是需要先删掉Student类中的构造函数,不然XML约束会提示配置<contructor-arg>元素。

同样的,如果属性需要注入其他Bean的话也可以在后面跟上-ref:

    <bean name="student2" class="pojo.Student"
          p:id="2" p:name="学生2" p:cdCard-ref="cdCard1"/>

【util-命名空间】

工具类的命名空间,可以简化集合类元素的配置,同样的需要引入其声明。

  引入前后的变化

<!-- 引入util-命名空间之前 -->
<property name="list">
    <list>
        <ref bean="bean1"/>
        <ref bean="bean2"/>
    </list>
</property>

<!-- 引入util-命名空间之后 -->
<util:list id="list">
    <ref bean="bean1"/>
    <ref bean="bean2"/>
</util:list>

<util:list>只是util-命名空间中的多个元素之一,下标提供了util-命名空间提供的所有元素。

元素描述
<util:constant> 引用某个类型的 public static 域,并将其暴露为 bean
<util:list> 创建一个 java.util.List 类型的 bean,其中包含值或引用
<util:map> 创建一个 java.util.map 类型的 bean,其中包含值或引用
<util:properties> 创建一个 java.util.Properties 类型的 bean
<util:property-path> 引用一个 bean 的属性(或内嵌属性),并将其暴露为 bean
<util:set> 创建一个 java.util.Set 类型的 bean,其中包含值或引用

 引入其他配置文件

在实际开发中,随着应用程序规模的增加,系统中<bean>元素配置的数量也会大大增加,导致applicationContext.xml配置文件变得非常臃肿难以维护。

解决方法:让applicationContext.xml文件包含其他配置文件即可,使用<import>元素引入其他Spring配置文件。

<import resource="bean.xml" />

隐式的Bean发现机制和自动装配

这种方式是采用注解(annotation)的方式去装配Bean,这样可以减少XML的配置,当配置多的时候,臃肿难以维护;功能更加强大,既能实现XML的功能,也提供了自动装配的功能,采用了自动装配后,程序员开发需要做的决断就减少了。

在spring中,它提供了两种方式来让Spring IoC容器发现bean:

  • 组件扫描:通过定义资源的方式,让Spring Ioc容器扫描对应的包,从而把bean装配进来。
  • 自动装配:通过注解定义,使得一些依赖关系可以通过注解完成。

使用@Component装配Bean

package pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "student1")
public class Student {

    @Value("1")
    int id;
    @Value("student_name_1")
    String name;

    // getter and setter
}
  • @Component注解:表示Spring IoC会把这个类扫描成一个bean实例,而其中的value属性代表这个类在Spring中的id,这就相当于在XML中定义的Bean的id:<bean id="student1" class="pojo.Student"/>也可以简单写成@Component("Student1"),甚至直接写成@Component,对于不写的,Spring IoC容器就默认以类名来命名作为id,只不过首字母小写,配置到容器中。
  • @Value注解:表示值的注入,跟在XML中写value属性是一样的。

注解装配类似于XML的声明

<bean name="student1" class="pojo.Student">
    <property name="id" value="1" />
    <property name="name" value="student_name_1"/>
</bean>

但是注解声明了这个类,并不能进行任何的测试,因为Spring IoC并不知道这个Bean的存在,这就需要使用StudentConfig类去告诉Spring IoC:

package pojo;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan("com.company")
public class StudentConfig {
}

使用Spring 定义好的Spring IoC容器的实现类--AnnotationConfigApplicationContext去生成IoC容器。

ApplicationContext context = new AnnotationConfigApplicationContext(StudentConfig.class);
Student student = (Student) context.getBean("student1", Student.class);
student.printInformation();

缺点:对于@ComponentScan注解,它只是扫描所在包的java类,但是更多时候希望的是可以扫描指定的类;并且@Value注解只能注入简单的值。

@Component注解存在着两个配置项:

  • basePackages:它可以配置一个java包的数组,Spring会根据它的配置扫描对应的包和子包,将配置的Bean装配进来。
  • basePackageClasses:它可以配置多个类,Spring会根据配置的类所在的包,为包和子包进行扫描装配对应配置的Bean。
package pojo;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "pojo")
public class StudentConfig {
}

//  ———————————————————————————————————— 
package pojo;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackageClasses = pojo.Student.class)
public class StudentConfig {
}

@Autowired自动装配

自动装配是一种Spring 自己发现对应的Bean,自动完成装配工作的方式。Spring会根据类型去寻找定义的Bean然后将其注入。

@Qualifier注解与@AutoWired注解一起使用,用来表示将指定的Bean对象作为属性值注入到目标Bean中。

1.StudentService接口

package service;

public interface StudentService {
    public void printStudentInfo();
}

2.为上面的接口创建一个StudentServiceImp实现类

package service;

import org.springframework.beans.factory.annotation.Autowired;
import pojo.Student;

@Component("studentService")
public class StudentServiceImp implements StudentService {

    @Autowired
    @Qualifier("student")
    private Student student = null;

     // getter and setter

    public void printStudentInfo() {
        System.out.println("学生的 id 为:" + student.getName());
        System.out.println("学生的 name 为:" + student.getName());
    }
}

3.测试类

// 第一步:修改 StudentConfig 类,告诉 Spring IoC 在哪里去扫描它:
package pojo;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = {"pojo", "service"})
public class StudentConfig {
}

// 或者也可以在 XML 文件中声明去哪里做扫描
<context:component-scan base-package="pojo" />
<context:component-scan base-package="service" />

// 第二步:编写测试类:
package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import pojo.StudentConfig;
import service.StudentService;
import service.StudentServiceImp;

public class TestSpring {

    public static void main(String[] args) {
        // 通过注解的方式初始化 Spring IoC 容器
        ApplicationContext context = new AnnotationConfigApplicationContext(StudentConfig.class);
        StudentService studentService = context.getBean("studentService", StudentServiceImp.class);
        studentService.printStudentInfo();
    }
}

在Java的接口和类中实现配置

使用@Bean注解装配Bean

@Component装配Bean的问题:只能注解在类上,当需要使用引用第三方包的(jar文件),而且往往并没有这些包的源码,这时候将无法为这些包的类加入@Component注解,让它们变成开发环境中的Bean资源。

解决方法:使用@Bean注解,注解到方法之上,使其成为Spring中返回对象为Spring的Bean资源。

package pojo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanTester {

    @Bean(name = "testBean")
    public String test() {
        String str = "测试@Bean注解";
        return str;
    }
}

注意:@Configuration注解相当于XML文件的根元素,必须要,有了才能解析其中的@Bean注解。

在测试类中编写代码,从Spring IoC容器中获取到这个Bean:

// 在 pojo 包下扫描
ApplicationContext context = new AnnotationConfigApplicationContext("pojo");
// 因为这里获取到的 Bean 就是 String 类型所以直接输出
System.out.println(context.getBean("testBean"));

@Bean注解的配置项中包含四个配置项

  • name:是一个字符串数组,允许配置多个BeanName。
  • autowire:表明是否是一个引用的Bean对象,默认值Autowire.NO。
  • initMethod:自定义初始化方法。
  • destoryMethod:自定义销毁方法。

使用@Bean注解的好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。或者说将Spring和其他组件分离(其他组件不依赖Spring,但是又想Spring管理生产的Bean)。

Bean的作用域

在默认的情况下,Spring IoC容器只会对一个Bean创建一个实例,但是有时候,希望能够通过Spring IoC容器获取多个实例,可以通过@Scope注解或者<bean>元素中的scope属性来设置。

// XML 中设置作用域
<bean id="" class="" scope="prototype" />
// 使用注解设置作用域
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Spring 提供了5种作用域,它会根据情况来决定是否生成新的对象:

  1. Singleton(单例):在Spring IoC容器中仅存在一个Bean实例(默认的scope)
  2. prototype(多例):每次从容器中调用时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XXXBean():不会在容器启动时创建对象。
  3. request(请求):用于web开发,将Bean放入request范围,request.setAttribute("XXX"),在同一个request获得同一个Bean。
  4. session(会话):用于web开发,将Bean放入session范围,在同一个session获得同一个Bean。
  5. globalSession(全局会话):一般用于Porlet应用环境,分布式系统存在全局session概念(单点登录),如果不是porlet环境,globalSession等同于Session。
 posted on 2019-10-30 22:18  会飞的金鱼  阅读(224)  评论(0)    收藏  举报