写在前面的碎碎念,不知不觉来公司干开发已经三年多了,一直没有重视文章写作的相关积累。这次打算从基础入手,培养下写作习惯。

一、Bean的实例化

  Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常使用的就是XML格式的配置方式。Spring容器支持XML和Properties两种格式的配置文件,在实际开发中,最常使用的就是XML格式的配置方式。
  XML配置文件的根元素是<beans><beans>中包含了多个<bean>子元素,每一个<bean>子元素都定义了一个bean,并描述了该bean如何被装配到Spring容器中。
  实例化Bean有三种方法:1.构造器实例化 2.静态工厂方式实例化 3.实例工厂方式实例化

1.构造器实例化

  构造器实例化是指Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean。

package com.demo.instance.constructor;

public class Bean1 {
}
<?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 id="bean1" class="com.demo.instance.constructor.Bean1"/>
</beans>

2.静态工厂方式实例化

  使用设值注入时,在Spring配置文件中,需要使用<bean>元素的子元素<property>来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。

package com.demo.instance.static_factory;

public class Bean2 {
}
package com.demo.instance.static_factory;

public class MyBean2Factory {

	public static Bean2 createBean2() {

		return new Bean2();
	}
}
<?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 id="bean2" class="com.demo.instance.static_factory.MyBean2Factory" factory-method="createBean2"/>

</beans>

3.实例工厂方式实例化

  还有一种实例化Bean的方式就是采用实例工厂。此种方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向的实例化类,而是通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。

package com.demo.instance.factory;

public class Bean3 {

}
package com.demo.instance.factory;

public class MyBean3Factory {

	public MyBean3Factory() {

	}

	public Bean3 createBean3() {

		return new Bean3();
	}
	
}
<?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 id="myBean3Factory"
		class="com.demo.instance.factory.MyBean3Factory" />
    <!-- 使用factory-bean属性指向配置的实例工厂,
    使用factory-method属性确定使用工厂中的哪个方法 -->
	<bean id="bean3" factory-bean="myBean3Factory"
		factory-method="createBean3" />

</beans>

二、Bean的作用域

作用域名称

说明

singleton(单例)

使用singleton定义的Bean在Spring容器中将只有一个实例,无论有多少个Bean引用它,始终将指向同一个对象,这也是Spring容器默认的作用域

prototype(原型)

每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例

request

在一次HTTP请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Request内有效

session

在一次HTTP Session请求中,容器会返回该Bean的同一个实例。对不同的HTTP请求则会产生一个新的Bean,而且该Bean仅在当前HTTP Session内有效

globalSesson

在一个全局的HTTP Session中,容器会返回该Bean的同一个实例。仅在使用protlet上下文时有效

application

为每个ServletContex对象创建一个实例。仅在Web相关的ApplicationContext中生效

websocket

为每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效

<?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 id="bean1" class="com.demo.instance.constructor.Bean1" scope="prototype"/>

</beans>

三、Bean的生命周期

  Spring容器可以管理singleton作用域的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。
  对于prototype作用域的Bean, Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。
  每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。

Bean的生命周期的整个执行过程描述如下:

  1. 根据配置情况调用Bean构造方法或工厂方法实例化Bean
  2. 利用依赖注入完成Bean中的所有属性值的配置注入
  3. 如果Bean实现了BeanNameAware接口,则Spring调用BeansetBeanName()方法传入当前Bean的id值
  4. 如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用
  5. 如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用
  6. 如果BeanPostProcessorBean关联,则Spring将调用该接口的预初始化postProcessBeforeInitialzation()Bean进行加工操作(这个非常重要,Spring的Aop就是用它实现的)
  7. 如果Bean实现了IntializingBean接口,则Spring将调用afterPropertiesSet()方法
  8. 如果配置文件中通过init-method属性制定了初始化方法,则调用该初始化方法
  9. 如果有BeanPostProcessorBean关联,则Spring将调用该接口的初始化方法postProcessAfterInitializition()。此时,Bean已经可以被应用系统使用了
  10. 如果在<bean>中制定了该Bean的租用范围为scope="singleton",则将该Bean放入Spring Ioc的缓存池中,将出发Spring对该Bean的生命周期管理,如果<bean>中指定了该Bean的作用范围为scope="prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不在管理该Bean
  11. 如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁,如果在配置文件中通过destory-method属性制定了Bean的销毁方法,则Spring将调用该方法进行销毁

四、Bean的装配模式

  Bean的装配模式即Bean的依赖注入方式,Spring有三种装配模式:基于XML的装配、基于注解(Annotation)的装配和自动装配。(其中最常用的是基于注解的装配)

1.基于XML的装配

  Spring提供了两种基于XML的装配方式:设值注入(Setter Injection)和构造注入(Constructor Injection)
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。因此,设值注入要求一个Bean必须满足以下两点要求。

  • Bean类必须提供一个默认的无参构造方法。
  • Bean类必须为需要注入的属性提供对应的setter方法。

元素的子元素来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用元素的子元素来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。

package com.demo.assemble;

import java.util.List;

public class User {

    private String username;
    private Integer password;
    private List<String> list;

    /**
     * 1.使用构造注入
     * 1.1提供带所有参数的有参构造方法
     * @param username
     * @param password
     * @param list
     */
    public User(String username, Integer password, List<String> list) {
        super();
        this.username = username;
        this.password = password;
        this.list = list;
    }

    /**
     * 2.使用设值注入
     * 2.1提供默认空参数构造方法
     * 2.2为所有属性提供setter方法
     */
    public User() {
        super();
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(Integer password) {
        this.password = password;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password=" + password +
                ", list=" + list +
                '}';
    }
}
<?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 id="user1" class="com.demo.assemble.User">
        <constructor-arg index="0" value="tom"/>
        <constructor-arg index="1" value="123456"/>
        <constructor-arg index="2">
            <list>
                <value>"constructorValue1"</value>
                <value>"constructorValue2"</value>
            </list>
        </constructor-arg>
    </bean>
    
    <!--设值注入-->
    <bean id="user2" class="com.demo.assemble.User">
        <property name="username" value="jerry"/>
        <property name="password" value="654321"/>
        <property name="list">
            <list>
                <value>"setListValue1"</value>
                <value>"setListValue2"</value>
            </list>
        </property>

    </bean>
</beans>

2.基于Annotation的装配

  在Spring中,尽管使用XML配置文件可以实现Bean的装配工作,但如果应用中有很多Bean时,会导致XML配置文件过于臃肿,给后续的维护和升级工作带来一定的困难。为此,Spring提供了对Annotation(注解)技术的全面支持。
  Spring中定义了一系列的注解,常用的注解如下所示。

注解

说明

@Component

可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

@Repository

用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同。

@Service

通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。

@Controller

通常作用在控制层(如Spring MVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。

@Autowired

用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配。

@Resource

其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配。 [1]

@Qualifier

与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。

  在上面几个注解中,虽然@Repository、@Service与@Controller功能与@Component注解的功能相同,但为了使标注类本身用途更加清晰,建议在实际开发中使用@Repository、@Service与@Controller分别对实现类进行标注。

package com.demo.annotation;

public interface UserDao {
    public void save();
}
package com.demo.annotation;

import org.springframework.stereotype.Repository;

//@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void save() {
        System.out.println("userDao...save...");
    }

}
package com.demo.annotation;

public interface UserService {
    public void save();
}
package com.demo.annotation;

import org.springframework.stereotype.Service;

import javax.annotation.Resource;

//@Service("userService")
public class UserServiceImpl implements UserService {
//    @Resource(name = "userDao")
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        this.userDao.save();
        System.out.println("userService...save...");
    }
}
package com.demo.annotation;

import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

//@Controller("userController")
public class UserController {
//    @Resource(name = "userService")
    private UserService userService;

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

    public void save() {
        this.userService.save();
        System.out.println("userController...save...");
    }

}
<?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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--    <context:component-scan base-package="com.demo.annotation"/>-->

    <bean id="userDao" class="com.demo.annotation.UserDaoImpl"/>
    <bean id="userService" class="com.demo.annotation.UserServiceImpl" autowire="byName"/>
    <bean id="userController" class="com.demo.annotation.UserController" autowire="byName"/>

</beans>

  演示的为自动装配写法,注释掉的写法为Annotation的装配


  1. @Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。 ↩︎