文章目录

  • XML 方式装配Bean
  • 一、实例化 javaBean
  • 二、属性注入
  • 三、使用Spring 表达式语言进行装配(SpEL)
  • 注解装配Bean
  • 一、装配Bean
  • 二、注入属性
  • 整合多个Spring 文件
  • 自动装配



前言


Spring 是一个基于容器的框架,但是如果没有配置那它就是一个空容器,所以我们需要配置Spring 来告诉Spring 加载哪些Bean以及如何装配这些Bean。


我们主要来讲在XML文件里怎么装配Bean。

XML 方式装配Bean

一、实例化 javaBean

首先我们需要创建Spring 配置。
在XML文件中声明Bean时,Spring配置文件的根元素来源于Spring beans 命名空间所定义的元素,下面是一个典型的Spring XML配置文件

<?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">
<!--bean 在这儿定义-->
</beans>

1.bean 实例化的三种方式

  • 通过无参构造器创建
  • 通过静态工厂创建
  • 通过一般工厂创建

1.通过无参构造器创建
这是一个javaBean 类

public class Student{
    public void show(){
        System.out.println("Student show……");
    }
}

在XML文件里面进行声明

<bean id = "student" class = "com.tulun.bean.Student" "/>

测试一下

//使用一般工厂获取对象
    public static void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
        Student student = (Student) context.getBean("student");
        student.show();
    }

spring基于xml注入bean的三种方法 spring xml bean_spring

2.通过静态工厂
这是一个静态工厂

public class StaticFactory {
    public static Student getBean(){
        return new Student();
    }
}

在XML里面配置

<!--通过静态工厂创建student类-->
    <bean id="student2" class="com.tulun.factory.StaticFactory" factory-method="getBean" scope="prototype"/>

class :表示我们想要的对应的类。
factory-method:表示我们要调用的类里面包含的方法
测试方法

//使用静态工厂获取对象
    public static void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
        Student student = (Student) context.getBean("student2");
        student.show();
    }

3.使用一般工厂创建对象
一般工厂类

public class Factory {
    //通过普通工厂类创建student类
    public Student getBean(){
        return new Student();
    }
}

配置类

<!--通过一般工厂来创建Student 类-->
    <bean id="factory" class="com.tulun.factory.Factory" />
    <bean id="student3" factory-bean="factory" factory-method="getBean"/>

测试

//使用一般工厂获取对象
    public static void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
        Student student = (Student) context.getBean("student3");
        student.show();
    }

注意:在这些Bean 中必须要有无参构造器,这些类是通过反射得到的,必须要有无参构造器。
2.Bean 的作用域
所有的Spring Bean 默认都是单例的。当容器分配一个Bean时,它总是返回的是同一个实例。那有时候我们需要获得唯一的Bean ,那么我们可以在配置Bean 时为其声明一个作用域

<bean id = "student" class = "com.tulun.bean.Student" scope="singleton"/>

scope 属性表示了这个Bean 的作用域

作用域

定义

singleton

在每一个Spring容器中,一个Bean定义只有一个对象实例

prortotype

允许Bean 的定义可以被实例化任意次(每次调用都创建一个实例)

request

在一次HTTP请求中,每个Bean 定义对应一个实例。该作用域仅在基于Web的Spring 上下文(例如Spring MVC)中才生效

session

在一次HTTP Session请求中,每个Bean 定义对应一个实例。该作用域仅在基于Web的Spring 上下文(例如Spring MVC)中才生效

global-session

在一个全局HTTP Sesion中,每个Bean定义对应一个实例。该作用域仅在Portlet上下文中才有效

3.Bean 的初始化和销毁
当实例化一个Bean 时,可能需要一些初始化的操作确保该Bean 处于可用状态。同样,当不再需要Bean,将其从容器中移除,可能也需要一些清除工作。Spring 提供了Bean生命周期的各种方法。初始化和销毁只需要用init-method 和destory-method 参数来配置元素。
给Student 类添加这么两个方法

public void start(){
        System.out.println("开始");
    }
    public void end(){
        System.out.println("结束");
    }

修改配置文件

<bean id = "student"
          class = "com.tulun.bean.Student"
          scope="singleton"
          init-method="start"
          destroy-method="end">
 </bean>

spring基于xml注入bean的三种方法 spring xml bean_spring_02


因为我们没有进行销毁,所以并没有调用end()方法,但是在对象刚被实例化的时候它就调用了start()方法,这足以说明问题。

InitializingBean 和DisposableBean
为Bean 定义初始化和销毁方法的另一种可选的方式是让Bean 实现Spring 的这两个接口。Spring 容器以特殊的方式对待实现这两个接口的Bean,允许它们进入Bean的生命周期。InitializingBean 声明了一个afterPropertiesSet()方法作为初始化方法。而DisPosableBean 声明了一个destory()方法,该方法在Bean 从应用文上下移除时会被调用。
优点:Spring 能够自动检测实现了这些接口的Bean,而无需额外的配置。
缺点:Bean 与Spring 的API产生了耦合。唯一可以使用Spring 生命周期接口的场景是开发一个明确在Spring 容器内使用的框架Bean。

默认的init-method 和destory-method
如果在上下文中定义的很多Bean都拥有相同名字的初始化和销毁方法,可以使用 元素的default-init-method 和defaul-destory-method属性。
default-init-method 属性为应用上下文中所有的bean 设置了共同的初始化方法。
defaul-destory-method 属性为应用上下文中所有的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-init-method="start" 
       default-destroy-method="end">
 </beans>

二、属性注入

上面这个类没有任何属性,那么我们怎么样注入属性给javaBean呢?
java 支持三种属性注入

  • 构造器注入
  • setter 方法注入
  • 接口注入

Spring 框架支持的属性注入

  • 构造器注入
  • setter 方法注入
    1.一般属性注入
    1.构造器注入
    先给Student 类加一个属性name 吧。既然是构造器注入,那得加一个带参的构造函数吧。
public class Student {
    private String name;
	public Student() {
    }
    public Student(String name) {
        System.out.println("构造器");
        this.name = name;
    }
	public String getName() {
        return name;
    }
   public void setName(String name) {
       System.out.println("set 方法注入属性");
        this.name = name;
    }
}

配置文件

<bean id = "student" class = "com.tulun.bean.Student" scope="singleton">
        <!--通过有参构造器进行属性注入-->
        <constructor-arg name="name" value="张三"></constructor-arg>
 </bean>

测试

public static void iocTest(){
        //加载spring配置文件,创建spring对象
        ApplicationContext context = new ClassPathXmlApplicationContext("springbean.xml");
        Student student = (Student) context.getBean("student");
    }

如果调用构造器那么控制台会打印出构造器三个字

spring基于xml注入bean的三种方法 spring xml bean_spring_03

2.setter 方法注入

<bean id = "student" class = "com.tulun.bean.Student" scope="singleton">
        <property name="name" value="李四"></property>
 </bean>

重新测试一下,控制台应该会打印“set 方法注入属性”

spring基于xml注入bean的三种方法 spring xml bean_构造器_04


2.复杂对象的注入

对象

这里有两个类,UserService 和UserDao

public class UserService {
    UserDao userDao1;

    public UserDao getUserDao1() {
        return userDao1;
    }

    public void setUserDao1(UserDao userDao1) {
        this.userDao1 = userDao1;
    }

    public void add(){
        userDao1.add();
    }
}

public class UserDao {
    public void add(){
        System.out.println("图论");
    }
}

在XML里面装配它

<!--注入一个对象-->
    <!-- 创建dao 层对象-->
    <bean id="userDao" class="com.tulun.dao.UserDao"></bean>
    <!--创建service 层对象-->
    <bean id="userService" class="com.tulun.service.UserService">
        <property name="userDao1" ref="userDao"></property>
    </bean>

ref 表示是一个引用。

我们给Student 里面增加这么几个属性

private String[] str;
    private List<String> list;
    private Map<String,String> map;
    private Properties sp;

数组

<!--数组-->
        <property name="str">
            <list>
                <value>1</value>
                <value>2</value>
            </list>
        </property>

标签表示数组,在这个标签下有value 属性,我们可以定义任意多个 value 给数组
集合(List,Set)

<!--list-->
        <property name="list">
            <list>
                <value>你</value>
                <value>好</value>
            </list>
        </property>

list 集合和数组是一样的,当是Set集合的时候,可以把<list>标签改为<set> 即可
Map 集合

<!--map-->
       <property name="map">
           <map>
               <entry key="tulun" value="tulun"></entry>
           </map>
       </property>

Map 集合和另外两个集合优点不一样,标签表示这是一个map,map 存储的是键值对,则代表一个节点,节点包括key 和value。
entry 中的元素不仅仅有这两个,看一下这个表

属性

用途

key

指定map 中entry 的键为String

key-ref

指定map中entry 的键为Spring 上下文中其他的Bean 引用

value

指定map中entry的值为String

value-ref

指定map中entry 的值为Spring上下文中其它的Bean 引用

Properties 集合
如果配置的map 和值都为String,我们可以考虑使用Properties代替map。Properties 提供了和map大致相同的功能,但是它指定键和值都必须为String

<!--properties-->
<property name="sp">
    <props>
         <prop key ="drive">wjm</prop>
    </props>
</property>

<props>元素构建了一个Properties 值,这个Properties 的每一个成员都由<prop>元素定义,每一个元素都由一个key 属性,其定义了Properties 每个成员的键,而每一个成员由<prop>元素的内容所定义。
**装配内部Bean **
或许你可能很熟悉内部类的概念了,类似的,内部Bean 是定义在其他Bean 内部的Bean。
用那个UserService和UserDao 的例子

<bean id = "userService2" class = "com.tulun.service.UserService">
        <property name="userDao1">
            <bean class="com.tulun.dao.UserDao"></bean>
        </property>
    </bean>

内部Bean 不仅使用与setter 注入,还适用于构造器注入

<bean id ="userService3" class="com.tulun.service.UserService">
        <constructor-arg>
            <bean class="com.tulun.dao.UserDao"></bean>
        </constructor-arg>
    </bean>

内部Bean 没有id,我们也不可能通过名字来引用内部的Bean。所以内部Bean 也不能被复用,仅仅适用于一次性注入。

装配空值
在有些场景下,如果不能完全确定属性的值为null,那么就必须显式的为该属性装配一个null值。

<bean id="userService4" class="com.tulun.service.UserService">
        <property name="userDao1"><null></null></property>
    </bean>

3.使用Spring 的命名空间p来装配属性
命名空间p的schema URI为xmlns:p=“http://www.springframework.org/schema/p”,只需要在Spring 的XML配置中增加一下声明就可以用了

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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">

可以使用p:作为<bean>元素所有属性的前缀来装配<bean>属性

<bean id = "student4"
          class = "com.tulun.bean.Student"
        p:name="wjm">
    </bean>

三、使用Spring 表达式语言进行装配(SpEL)

SpEL是在Spring3时提出的,在这个过程中所使用的表达式会在运行时计算得到值,然后以一种强大和简洁的方式将值装配到Bean属性和构造器参数中去。
怎样使用SpEL表达式
SpE表达式要放到#{ }中,在括号里面就是表达式了。

表示字面值

#{1}

这就是一个简单的表达式,表示为1,这个表达式没有任何意义,直接用1也可以的,这只是为了让大家了解什么是表达式。不仅如此,它还可以用来表示浮点数、String以及Boolean,甚至数值还可以用科学计数法的方式进行表示。

#{1.36}    //表示浮点
#{'hello'}  //表示String
#{false}  //表示Boolean
#{9.87E4} //科学计数法

引用Bean 、属性和方法
假设我们现在有一个ID 为 student 的bean,可以通过 #{student} 把这个Bean 装配给其它Bean

#{student}//调用bean
#{student.name}//调用属性
#{student.show()}//调用方法

在表达式中使用类型
如果要在SpEL表达式中使用类作用域的方法和常量就使用T{}

#{T(java.lang.Math).PI}//访问Math类的常量
#{T(java.lang.Math).random()}//访问Math类的静态方法

SpEL 运算符

算数运算

+ 、-、 *、 / 、%、^

比较运算

<、>、==、<=、>=、 lt、gt、eq、le、ge

逻辑运算

and、 or、 not

条件运算

?:(ternary)、?:(Elvis)

正则表达式

matches

算数运算符和在java 里的作用一样
比较运算符也没变,结果是个Boolean 值。

计算集合
SpEL最神奇的地方就是与集合和数组相关的。[] 中括号运算符用来从数组或者集合中获取元素,不仅如此,它还可以从String 中获取一个字符

#{student.list[1]}  //表示从student 这个bean 的list 属性拿到第2个元素,下标从0开始
#{student.name[2]} //从student 的name属性拿到了字符串中的第三个字符

SpEL表达式还提供了查询运算符(.?[])用来对集合进行过滤
假设我们有一个Student 的集合students,Student 有一个class 的属性

//我们得到了students 中subject 属性为math的所有的class
#{students.class.?[subject eq "math"]}

(.^[])和(.$[])运算符分别表示在集合中查询的第一个匹配项和最后一个匹配项

//在列表里查第一个subject 为math的 calss 
#{students.class.^[subject eq "math"]}
//在列表里查最后一个subject 为math的 calss
#{students.class.$[subject eq "math"]}

SpEL 还提供了投影运算符( .![] ),它会从集合的每个成员中选择特定的属性放到另外一个集合中。

//将subject 属性映射到一个新的String 集合中去
#{students.class.![subject]}

上面说的这些运算符都可以混合使用

注解装配Bean

一、装配Bean

讲完了XML装配Bean,我们通过一个例子简单看一下注解方式怎么装配Bean。
XML 配置
使用注解之前,我们要先导入4+2+aop的jar包,同时引入约束 beans+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-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!--创建Controller、service、dao层对象-->
    <bean id="userDao" class="com.tulun.dao.UserDao"/>
    <bean id="userService" class="com.tulun.service.UserService"/>
    <bean id="userController" class="com.tulun.controller.UserController">

在配置文件加上开启注解扫描的标签

<!--
    开启注解扫描
    (1)到包里面扫描类方法、属性上是否有注解
    -->
    <context:component-scan base-package="com.tulun"></context:component-scan>
</beans>

加上这句话,Spring会自动的去我们指定的包里扫面类以及方法,看其是否有注解。

@Component(value = "user")
//等同于 <bean id = "user" class  = "com.tulun.bean.User"/>
@Scope(value = "singleton")
public class User {
    private  int id;
     @PostConstruct 
    public void add(){
        System.out.println("add……");
    }
}
  • @Component(value = “user”)
    这个注解对类女性标注,它可以被Spring 容器识别,Spring 容器将自动将类转化成容器管理的Bean。

除了@Component,Spring 还提供了三个与其等效的注解。一般用于Web 项目,对DAO,service,web层进行注解。

  • @Repository:对DAO实现类进行注解
  • @Service:对service实现类进行注解
  • @Controller:对web层Controller实现类进行注解

之所以用着几个注解,是为了让注解类的用途清晰化。下面这个是设置Bean 的生命周期范围。

  • @Scope(value = “singleton”):Bean 的作用域,和XML 中Scope 属性作用一样

下面这两个注解修饰方法,相当于bean的init-method和destory-method属性的功能。

  • @PostConstruct:在类被创建时调用
  • @PreDestroy:类被销毁是时调用

二、注入属性

1.注入一般的属性

  • @Value:为Bean 进行属性注入
    上面那个类里面有一个id的属性,用这个注解来为其注入一个值
@Value(value = "1")
    private  int id;

通过Setter 也可以注入

@Value(value = "1")
    public void setId(int id) {
        this.id = id;
    }

2.注入引用类型属性

@Autowired  
    UserService userService;
    
	@Autowired  
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

除了@Autowired ,Spring 还提供了其它两个注解,三个注解的区别如下:

  • @Autowired
    @Autowired默认按类型匹配的方式,在容器中查找匹配的Bean,当有且只有一个匹配的Bean时,Spring将其注入到@Autowired注解的变量中。但是如果容器中有超过一个以上的匹配Bean时,例如有两个UserService类型的Bean,这时就不知道将哪个Bean注入到变量中,就会出现异常
  • @Qualifier(“userservice”)
    这个注解按照指定的名字注入属性
  • @Resource(name=“userservice”)
    结合和前两个注解,当容器中只有一个匹配的Bean 时,直接注入,当容器中有多个匹配的Bean时,按照指定的名字匹配

整合多个Spring 文件

对于一个大型项目而言,可能有多个XML配置文件,在启动Spring容器时,可以通过一个String数组指定这些配置文件。Spring还允许我们通过< import >标签将多个配置文件引入到一个文件中,进行配置文件的集成,这样启动Spring容器时,就仅需指定这个合并好的配置文件即可。

1.使用Spring 数组整合所有文件

ApplicationContext ac=new ClassPathXmlApplicationContext(new String[]{"bean1.xml","bean2.xml"});

2.使用import 标签

//resource属性指定配置文件位置,支持Spring标准的资源路径
 <import resource="classthpath:com/cad/domain/bean1.xml"/>
 <import resource="classthpath:com/cad/domain/bean2.xml"/>

自动装配

Spring 提供了四种自动装配的策略

  • byName
    把与Bean 的属性具有相同名字的其它Bean 自动装配到Bean 对应的属性中
  • byType
    把与Bean 的属性具有相同类型的Bean 自动装配到Bean 的属性中。但是如果当前应用上下文中有多个与Bean 属性具有相同类型的其它Bean,Spring 会抛出异常。
    那么当有多个Bean 都满组条件的时候,我们可以为其首选一个Bean,可以使用<bean> 元素的primary属性,将这个属性设置为true即可,但是primary 属性默认被设置为true,所以等于所有的Bean 都是首选Bean,所以也就不存在首选了,所以我们不得不讲其它的都设置为false。
    如果我们在自动装配时想忽略某个Bean,可以设置这些Bean 的autowire-candidate属性为false。这等于是取消了其作为候选Bean 的资格。
  • constructor
    把与Bean 的构造器入参具有相同类型的Bean 自动装配到构造器对应的入参中,值得注意的是,它还是用类型进行匹配,所以依然会碰到上面的问题,而且,当Bean 有多个构造器,Spring 不会帮我们判断使用哪个构造器,它会直接抛出异常。
  • autodetect
    它会首先使用constructor 进行动装配,如果失败再尝试用byType。

使用
只需要在Bean 标签里面加上autowire就可以啦

<bean id="userdao" class = "com.tulun.dao.UserDao" autowire="byType"></bean>

默认的自动装配 default-autowire
默认情况下这个属性为false,表示所有的属性都不使用自动郑培,除非在bena 上设置了autowire。
当我们想让配置文件中所有的Bean 都进行自动装配的时候,在beans 上增加属性default-autowire

<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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"
       default-autowire="byType">

注意
使用了默认的装配策略,我们依然可以使用<bean>元素的autowire 属性来覆盖默认的装配策略。
而且,我们使用了自动装配策略,并不代表我们不能对该Bean的某些属性进行显式装配。