一、Spring Bean的配置
由 Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息被创建。所谓配置Bean就是告诉Spring的IOC容器将要去管理的对象。
1、传统方式(XML文件)
采用XML文件的形式进行配置,spring-context.xml
1.1 基础类
Person类:
public class Emp{
private Integer id;
private String ename;
private String email;
private Double salary;
private Dept dept;
//相关set方法、get方法、构造方法...
}
public class Dept {
private Integer did;
private String dname;
private List<Emp> empList;
private Map<Integer,String> maps;
private Emp emp1;//部门领导
//相关set方法、get方法、构造方法...
}
public class Student implements InitializingBean, DisposableBean {
private Integer sid;
private String sname;
//相关set方法、get方法、构造方法...
@Override
public void destroy() throws Exception {
System.out.println("正在执行DisposableBean接口的bean销毁方法.......");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("正在执行InitializingBean接口的bean初始化方法.......");
}
public void inits(){
System.out.println("正在执行本地的bean初始化方法.......");
}
public void destorys(){
System.out.println("正在执行本地的bean销毁方法.......");
}
}
1.2 配置文件
<?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="emp" class="org.example.pojo.Emp">
<description>描述bean的信息</description>
</bean>
<alias name="emp" alias="EMP"><!--设置别名--></alias>
<import resource="spring-ioc.xml"><!--导入其他spring的xml配置文件--></import>
</beans>
- 标签:
- bean
- 创建一个bean对象并放置在IOC容器内,对应某个类的一个实例对象;
- alias
- 用来给某个bean起别名(可以设置多个别名,用逗号或者空格或者分号来分隔即可),name对应某个bean的id值;
- import
- 可以使用import来导入其他spring的xml配置文件;
- description
- 该标签用于来描述某个bean的信息,在bean标签的内部使用;
- bean
- 属性:
- id :
- bean对象的唯一标识符(名称),不可重复,在IOC容器中必须是唯一的;
- 若id没有指定,Spring会自动将类的全局限定类名作为bean的名字;
- class:
- 被创建对象的类的包路径,Spring会通过反射机制创建该类的对象;
- 要求该类中必须存在无参构造方法;
- id :
1.3 依赖注入
1.3.1 基于setter方法的依赖注入
<bean id="emp1" class="org.example.pojo.Emp" primary="true">
<property name="id" value="100001"></property>
<property name="ename" value="唐僧"></property>
<property name="email" value="123456@163.com"></property>
<property name="salary" value="10000"></property>
</bean>
- 注意:
name属性对应的是set方法的名字
- 例如:setId ——> name=“id” ===> setXxx ——> name=“xxx”
1.3.2 基于构造器方法的依赖注入
<bean id="emp2" class="org.example.pojo.Emp" autowire-candidate="false">
<constructor-arg name="id" value="100002"></constructor-arg>
<constructor-arg name="ename" value="孙悟空"></constructor-arg>
<constructor-arg name="email" value="23456@163.com"></constructor-arg>
<constructor-arg name="salary" value="9999"></constructor-arg>
<!--<constructor-arg index="0" value="100003"></constructor-arg>-->
</bean>
- 注意:
- (1)可以基于name属性来映射构造函数的参数;
- (2)也可以省略name属性,直接使用value属性设置值,但是,要注意参数的顺序必须按照构造函数的参数顺序设置;
- (3)还可以使用index属性来设置参数的下标,从下标从0开始;
1.3.3 复杂数据类型的依赖注入
<bean id="dept1" class="org.example.pojo.Dept">
<property name="did" value="1"></property>
<!--设置null-->
<property name="dname"><null></null></property>
<!--设置“”-->
<!--<property name="dname" value=""></property>-->
<!--引用外部bean-->
<!--<property name="emp1" ref="emp1"></property>-->
<!--引用内部bean-->
<property name="emp1">
<bean class="org.example.pojo.Emp">
<property name="id" value="1111111"></property>
<property name="ename" value="如来佛祖"></property>
<property name="email" value="11111111@163.com"></property>
<property name="salary" value="10000000"></property>
</bean>
</property>
<!--List注入-->
<property name="empList">
<!--注意:
(1)如果泛型是基本数据类型:则使用<value>员工1</value>
(2)如果泛型是对象:则使用<bean></bean>引用内部bean或者<ref></ref>去引用外部bean-->
<list>
<ref bean="emp1"></ref>
<ref bean="emp2"></ref>
</list>
</property>
<!--Map注入-->
<property name="maps">
<!--注意:
(1)如果value是基本数据类型:则使用<entry key="1" value="100人"></entry>
(2)如果value是对象:则使用<entry value-ref=""></entry>-->
<map>
<entry key="1" value="100人"></entry>
<entry key="2" value="200人"></entry>
</map>
</property>
</bean>
1.3.4 高级用法
- depends-on:控制bean
加载顺序
(默认从配置文件中从上到下依次加载),当一个bean想让另一个bean先加载则可以用它来达到目的。可以使用设置多个值,逗号分隔。<bean id="stu1" class="org.example.pojo.Student" depends-on="dept1"></bean>
- lazy-init:
懒加载
,只有当使用到这个bean的时候才会被加载,而不是在spring容器加载的时候就被加载了。<bean id="stu2" class="org.example.pojo.Student" lazy-init="true"></bean>
- scope:bean的
作用域
,可以设置bean是单例(singleton
:不管加载多少次,从始至终只创建一次实例对象)还是多例(prototype
:每次加载时,都会创建一个新的实例对象)。<bean id="stu3" class="org.example.pojo.Student" scope="prototype"></bean>
- 自动注入
- (1)
byType
:根据类型去自动匹配,当出现多个相同的类型时就会报错。 - (2)
byName
:根据set方法的名字去自动匹配。 - (3)
constructor
:根据构造器自动匹配。- a、会优先根据构造器中的参数的名称去匹配注入;
- b、如果参数名称匹配失败,就会根据构造器的参数类型去进行完整的匹配注入,例如:Person(User user,Wife wife5),要保证IOC容器中同时都存在着两个参数的bean;
- c、如果b中匹配到多个相同类型的bean时,则自动注入失败但不会报错;
- d、当根据类型匹配到多个时,可以设置使用bean的优先级以及不参与自动注入;
- e、
primary="true "
:设置某个bean被优先被自动注入; - f、
autowire-candidate="false"
:设置某个bean不参与到任何的自动注入当中;
<bean id="dept" class="org.example.pojo.Dept" autowire="constructor"></bean>
- (1)
- 生命周期函数回调
- 方式一:接口实现的方式
- (1)初始化方法:实现接口
InitializingBean
重写afterPropertiesSet
方法,初始化时会自动调用该方法; - (2)销毁方法:实现接口
DisposableBean
重写destroy
方法 销毁的时候会自动调用该方法;
- (1)初始化方法:实现接口
- 方式二:指定具体的方法实现生命周期的回调
- 在bean中创建两个对应初始化和销毁的方法;
- 在配置文件中进行设置指定;
<bean id="student" class="org.example.pojo.Student" init-method="inits" destroy-method="destorys"></bean>
- 如果以上两种方式的实现都存在,它会先执行接口方式的实现,再执行自定义设置方式的方法。
- 方式一:接口实现的方式
1.3.5 配置第三方bean对象
<!--引入外部资源文件-->
<context:property-placeholder location="db.properties" ></context:property-placeholder>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
<property name="url" value="${db.url}"></property>
<property name="driverClassName" value="${db.driver}"></property>
</bean>
1.4 获取bean
- 首先是加载Spring的IOC容器,加载方式有以下几种:
ApplicationContext
是Spring顶层的核心接口;ClassPathXmlApplicationContext
会根据项目路径的xml配置文件来实例化Spring容器(可以同时加载多个配置文件中的bean,用逗号分隔不同的xml文件即可);FileSystemXmlApplicationContext
会根据磁盘路径的xml配置文件来实例化Spring容器;AnnotationConfigApplicationContext
会根据JavaConfig来配置实例化Spring容器;
- 在容器实例化时,就会加载所有的bean;
- 接着是获取bean,获取方式有以下几种:
- 通过类的.class来获取bean ——>
getBean(Emp.class)
- 通过bean的名字或者id来获取bean ——>
getBean("emp")
- 通过类和bean的名字来获取bean ——>
getBean("emp",Emp.class)
- 通过类的.class来获取bean ——>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc.xml");
Student student = (Student) applicationContext.getBean("student");
2、注解方式
- 在之前的示例中,都是通过xml文件注册bean并进行属性赋值。其实,在企业开发中使用的最多的方式还是使用注解来快速将bean注册到容器中。
2.1 相关注解
@Controller
标记在控制层的类注册为bean组件@Service
标记在业务逻辑层的类注册为bean组件@Repository
标记数据持久层的类注册为bean组件@Compoment
标记非三层的普通类注册为bean组件- 注意:
- 上述注解应该使用在类中而不是接口(如果在接口中使用则在注入容器时会被忽略掉)中;
- 使用上面的注解时会自动将类的类名的首字母小写然后作为bean的名称(id);
2.2 开启组件扫描
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="org.example" use-default-filters="true">
<!-- 设置包含扫描具体的哪一个类-->
<context:include-filter type="assignable" expression="org.example.impl.DeptServiceImpl"/>
<!--设置排除扫描具体的哪一个组件-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
- <context:component-scan base-package=“” use-default-filters=“”>
- 作用:开启组件扫描
- 属性:
base-packsge
:设置需要被扫描的包路径,多个值用逗号分隔,它会扫描当前包以及其子包;use-default-filters
:设置开启或者关闭全局扫描(默认开启);
- <context:include-filter type=“” expression=“”>
- 作用:包含扫描,设置需要包含扫描的选项;
- 属性:
type
:
(1)annotation 根据注解的完整限定名设置排除/包含
(2)assignable 根据类的完整限定名设置排除/包含
(3)aspectj 根据切面表达式设置排除/包含
(4)regx 根据正则表达式设置排除/包含
(5)custom 根据接口org.springframework.core.type.TypeFilter设置排除/包含expression
:相关表达式或者某个类的全局限定名
- <context:exclude-filter type=“” expression=“”>
- 作用:排除扫描,设置排除扫描具体的哪一个组件;
2.3 依赖注入
@Service(value = "empService")
public class EmpServiceImpl {
@Value("金融产业大脑")
private String name;
@Autowired
@Qualifier("empDao2")
private EmpDao empDaos;
/**
* 新增员工信息
* @param emp
*/
public void insertEmpInfo(Emp emp){
empDaos.addEmp(emp);
System.out.println(name);
}
}
- 在注解实现自动注入的过程中,会通过byType和byName两种方式来实现;
- 默认优先按照类型去注入,如果匹配到多个类型就会去按照名称去匹配,如果没有匹配到名称,则就会报错;
例如:
@Autowired
private EmpDao empDaos
(1)它匹配到多个类型的EmpDao的bean时,就会按照empDaos的名称去匹配;
(2)如果按名称去匹配没有发现id = empDaos的bean时,就会报错,此时有以下两种解决方案:
a、可以将属性的名称改成对应bean的id的名称,即:empDaos 修改为 empDao
b、可以去修改对应bean的id的名称为对应属性的名称,即:
@Repository(value = "empDaos")
public class EmpDaoImpl2 implements EmpDao {}
c、结合使用@Qualifier()注解,指定按某个名称去匹配,不按当前属性的名称empDaos去匹配
即:
@Autowired
@Qualifier("empDao")
private EmpDao empDaos;
d、可以通过@Primary 设置其中一个bean为主要的自动注入bean
2.4 生命周期函数回调
在类中使用注解自定义指定初始化和销毁方法:
@PostConstruct
public void init(){
System.out.println("生命周期函数回调之初始化");
}
@PreDestroy
public void destory(){
System.out.println("生命周期函数回调之销毁");
}
2.4 获取bean
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
EmpServiceImpl empService = applicationContext.getBean("empService", EmpServiceImpl.class);
empService.insertEmpInfo(new Emp());
3、JavaConfig方式
- JavaConfig原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本,JavaConfig 已正式成为Spring4 的核心功能 。
3.1 容器配置
-
创建一个Java配置类,在配置类中对容器和bean进行的相关配置;
@Configuration //标识该类是一个配置类 @ComponentScan(basePackages = "org.example") //设置组件扫描的目标包(当前包以及其子包) public class IocJavaConfig {}
3.2 配置第三方bean
@Configuration //标识该类是一个配置类
@ComponentScan(basePackages = "org.example") //设置组件扫描的目标包(当前包以及其子包)
@PropertySource({"classpath:db.properties"}) //引入外部属性资源文件,可以引入多个文件,它本身是个数组
@Import(MyImportBeanDefinitionRegistar.class)
public class IocJavaConfig {
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
@Value("${db.url}")
private String url;
@Value("${db.driver}")
private String driver;
/**
* (1)默认这个bean的id其实就是方法的名称 == dataSource,也可以通过name属性来重新设置bean的id
* (2)将一个创建好的实例化对象交给Spring,这个过程其实是干预了Spring对bean的实例化过程
* 自动依赖外部bean
* 直接在方法里面写上需要依赖的参数即可,不需要格外使用自动注入的注解
* 自动依赖内部bean
* 直接调用方法即可
*/
@Bean(name = "dbSource",initMethod = "",destroyMethod = "")
@Scope("prototype")
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setName(username);
dataSource.setPassword(password);
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
return dataSource;
}
}
3.3 @Import注解的四大作用
- 1、可以导入其他配置类 @Import(SpringConfig.class)
- 2、可以将类注册为bean @Import(Student.class)
- 3、通过导入ImportSelector的实现类,来注册多个bean @Import(MyImportSelector.class)
@Component public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //可以以字符串的形式注册多个Bean //字符串必须是类的完全限定名,所以,getBean必须要根据class类型来获取,无法通过名称来获取 return new String[]{"org.example.pojo.Dept","org.example.pojo.Emp","org.example.pojo.Student"}; } }
- 4、导入ImportBeanDefinitionRegistrar实现类,可以注册多个BeanDefinition
@Component public class MyImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); //指定它的类型 beanDefinition.setBeanClass(Student.class); //注册bean registry.registerBeanDefinition("student",beanDefinition); } }
3.4 获取bean
//加载Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(IocJavaConfig.class);
Student student = applicationContext.getBean("student", Student.class);
System.out.println(student);
二、Spring Bean的实例化
1.通过构造方法实例化Bean
Spring IoC容器能使用默认的无参构造方法,也能使用有参的构造方法,底层通过反射机制来实例化Bean对象。
2.通过工厂实例化Bean
- 调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而无需关心创建对象的细节;
- 要声明通过静态方法创建的bean,需要在bean的class属性里指定拥有该工厂的方法的类,同时在factory-method属性里指定工厂方法的名称。最后,使用元素为该方法传递方法参数;
Cat类:
public class Cat {
private String name;
private int age;
public Cat() {
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
CatStaticFactory.java:
/**
* 一个用于创建cat对象的静态工厂
*/
public class CatStaticFactory {
/**
* 提供一个创建对象的静态方法
*/
public static Cat getInstance(String name, int age){
return new Cat(name, age);
}
}
applicationContext.xml:
<!-- 静态工厂-->
<bean id="cat1" class="com.newcapec.factory.CatStaticFactory" factory-method="getInstance">
<constructor-arg value="汤姆猫"/>
<constructor-arg value="2"/>
</bean>
3.通过FactoryBean实例化Bean
- Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean;
- 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象;
CatFactoryBean.java:
public class CatFactoryBean implements FactoryBean<Cat> {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
/**
* 获取对象
*/
@Override
public Cat getObject() throws Exception {
return new Cat(name, age);
}
/**
* 生成对象的Class类型
*/
@Override
public Class<?> getObjectType() {
return Cat.class;
}
/**
* 设置该对象是否为单例模式
*/
@Override
public boolean isSingleton() {
return true;
}
}
applicationContext.xml:
<!-- FactoryBean配置bean -->
<bean id="cat3" class="com.newcapec.factory.CatFactoryBean">
<property name="name" value="加菲猫"/>
<property name="age" value="5"/>
</bean>
4. 元素常用的属性或子元素
在 XML 配置的 元素中可以包含多个属性或子元素,常用的属性或子元素如下表所示:
三、Spring容器
IoC
思想是基于 IoC 容器
实现的,IoC 容器底层其实就是一个 Bean 工厂,Spring 框架为我们提供了两种不同类型的 IoC 容器,它们分别是 BeanFactory
和 ApplicationContext
。
1.BeanFactory
- BeanFactory 是 IoC 容器的基本实现,也是 Spring 提供的最简单的 IoC 容器。
- 它提供了 IoC 容器最基本的功能,由
org.springframework.beans.factory.BeanFactory
接口定义。 - BeanFactory 采用
懒加载(lazy-load)机制
,容器在加载配置文件时并不会立即创建对象,只有在获取(使用)这个对象时才会创建。
2.ApplicationContext
- ApplicationContext 是 BeanFactory 接口的子接口,是对 BeanFactory 的扩展。
- ApplicationContext 在 BeanFactory 的基础上增加了许多企业级的功能,例如 AOP(面向切面编程)、国际化、事务支持等。
3.ApplicationContext的主要实现类
ClassPathXmlApplicationContext
:从类路径上加载配置文件;FileSystemXmlApplicationContext
:从文件系统中加载配置文件;WebApplicationContext
:专门为Web应用而准备的,它允许从相对于Web根目录的路径中完成初始化工作;
4.从容器中获取Bean
getBean(String name)
方法,通过Bean的id名称从容器中获取Bean对象;getBean(Class requiredType)
方法,通过Bean的Class类型从容器中获取Bean对象;getBean(String name, Class requiredType)
方法,通过Bean的id和Class类型从容器中获取Bean对象;
注意:当IOC容器中存放着多个同类型对象时,不能通过Class类型来获取Bean对象。
BeanTest2测试类
public class BeanTest2 {
@Test
public void testGetBean() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//1.参数为字符串类型
Person p1 = (Person) ac.getBean("person1");
//2.参数为Class类型,缺点:当IOC容器中存在多个此类型对象时,抛出异常
Person person = ac.getBean(Person.class);
//3.参数为字符串类型+Class类型
Person p2 = ac.getBean("person2", Person.class);
}
}
四、依赖注入
1.基于属性注入
- 在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用默认的构造方法(无参构造方法)实例化 Bean(Java 对象),然后通过 Java 的反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。
- 使用 Setter 注入的方式进行属性注入,大致步骤如下:
(1)在 Bean 中提供一个默认的无参构造函数(如果没有带参构造函数),并为所有需要注入的属性提供一个 setXxx() 方法;
(2)在 Spring 的 XML 配置文件中,使用 及其子元素 对 Bean 进行定义;
(3)在 元素内使用 元素对各个属性进行赋值,使用name属性指定Bean的属性名称,value属性或子标签指定属性值。属性注入是实际应用中最常用的注入方式。
applicationContext.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">
<!-- 通过setter方法注入属性值 -->
<bean id="person" class="com.newcapec.bean.Person">
<!--property标签:表示通过属性的set方法为属性赋值,也叫做依赖注入
属性:
name: 对象中的属性名称
value: 属性值 -->
<property name="name" value="张三"/>
<property name="age" value="20"/>
<property name="money"><value>3600.5</value></property>
</bean>
</beans>
2.基于构造方法注入
- 使用构造函数实现属性注入大致步骤如下:
(1)在 Bean 中添加一个有参构造函数,构造函数内的每一个参数代表一个需要注入的属性;
(2)在Spring 的 XML 配置文件中,通过 及其子元素 对 Bean 进行定义;
(3)在 元素内使用 元素,对构造函数内的属性进行赋值,Bean 的构造函数内有多少个参数,就需要使用多少个 元素。 - Car类:
public class Car {
private String name;
private String type;
private double price;
private int doors;
public Car(String name, String type, double price, int doors) {
this.name = name;
this.type = type;
this.price = price;
this.doors = doors;
}
public Car(String name, String type, int doors) {
this.name = name;
this.type = type;
this.doors = doors;
}
public Car(String name, String type, double price) {
this.name = name;
this.type = type;
this.price = price;
}
public Car(String n, String t) {
this.name = n;
this.type = t;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", type='" + type + '\'' +
", price=" + price +
", doors=" + doors +
'}';
}
}
- applicationContext.xml:
注意:如果该类中有多个构造方法,可以通过index、type或name对构造方法进行精确选取。
<?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="car1" class="com.newcapec.bean.Car">
<!-- constructor-arg : 表示创建该类型的对象时,使用的构造方法的参数
属性:
value : 构造方法的参数值
index : 构造方法参数的索引
type : 构造方法参数的类型
name : 构造方法参数的名称 -->
<constructor-arg value="宝马"/>
<constructor-arg value="轿车"/>
<constructor-arg value="360000"/>
<constructor-arg value="4"/>
</bean>
<!-- 按索引匹配构造方法参数 -->
<bean id="car2" class="com.newcapec.bean.Car">
<constructor-arg value="越野" index="1"/>
<constructor-arg value="奔驰" index="0"/>
<constructor-arg value="4" index="3"/>
<constructor-arg value="560000" index="2"/>
</bean>
<!-- 按类型匹配构造方法参数 -->
<bean id="car3" class="com.newcapec.bean.Car">
<constructor-arg value="大众" type="java.lang.String"/>
<constructor-arg value="商务车" type="java.lang.String"/>
<constructor-arg value="290000" type="double"/>
</bean>
<!-- 按参数名称匹配构造方法参数-->
<bean id="car4" class="com.newcapec.bean.Car">
<constructor-arg value="电动车" name="t"/>
<constructor-arg value="特斯拉" name="n"/>
</bean>
</beans>
3.Autowire自动装配
- 我们把 Spring 在 Bean 与 Bean 之间建立依赖关系的行为称为“装配”。 Spring 的 IOC 容器虽然功能强大,但它本身不过只是一个空壳而已,它自己并不能独立完成装配工作。还需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。
- Spring 的自动装配功能可以让 Spring 容器依据某种规则(自动装配的规则,有五种),为指定的 Bean 从应用的上下文(AppplicationContext 容器)中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。
- Spring 的自动装配功能能够有效地简化 Spring 应用的 XML 配置,因此在配置数量相当多时采用自动装配降低工作量。
- Spring 框架式默认不支持自动装配,要想使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置。
- Spring 共提供了 五种自动装配规则,它们分别与 autowire 属性的 5 个取值对应,具体说明如下表:
五、Bean的作用域
- 默认情况下,所有的 Spring Bean 都是单例的,也就是说在整个 Spring 应用中,Bean 的实例只有一个。
- 我们可以在 元素中添加 scope 属性来配置 Spring Bean 的作用范围。例如,如果每次获取 Bean 时,都需要一个新的 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。
- Spring 5 共提供了 6 种 scope 作用域,如下表:
1.Singleton
singleton
是 Spring 容器默认的作用域。当 Bean 的作用域为 singleton 时,Spring IoC 容器中只会存在一个共享的 Bean 实例。这个 Bean 实例将存储在高速缓存中,所有对于这个 Bean 的请求和引用,只要 id 与这个 Bean 定义相匹配,都会返回这个缓存中的对象实例。- 如果一个 Bean 定义的作用域为 singleton ,那么这个 Bean 就被称为 singleton bean。在 Spring IoC 容器中,singleton bean 是 Bean 的默认创建方式,可以更好地重用对象,节省重复创建对象的开销。
- 在 Spring 配置文件中,可以使用 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:
<!-- singleton -->
<bean id="book" class="com.newcapec.bean.Book" p:id="101" p:name="西游记" p:price="98.5" p:author="吴承恩" scope="singleton"/>
2.Prototype
- 如果一个 Bean 定义的作用域为 prototype,那么这个 Bean 就被称为 prototype bean。对于 prototype bean 来说,Spring 容器会在每次请求该 Bean 时,都创建一个新的 Bean 实例。
- 从某种意义上说,Spring IoC 容器对于 prototype bean 的作用就相当于 Java 的 new 操作符。它只负责 Bean 的创建,至于后续的生命周期管理则都是由客户端代码完成的。
- 在 Spring 配置文件中,可以使用 元素的 scope 属性将 Bean 的作用域定义成 prototype,其配置方式如下所示:
<!-- prototype -->
<bean id="book" class="com.newcapec.bean.Book" p:id="101" p:name="西游记" p:price="98.5" p:author="吴承恩" scope="prototype"/>
六、Bean的生命周期
- 在传统的 Java 应用中,Bean 的生命周期很简单,使用 Java 关键字 new 进行 Bean 的实例化后,这个 Bean 就可以使用了。一旦这个 Bean 长期不被使用,Java 自动进行垃圾回收。
- Spring 中 Bean 的生命周期较复杂,SpringIOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务,
大致可以分为以下 5 个阶段:
Bean 的实例化
——>Bean 属性赋值
——>Bean 的初始化
——>Bean 的使用
——>Bean 的销毁
- Spring 根据 Bean 的作用域来选择 Bean 的管理方式,
(1) 对于 singleton 作用域的 Bean 来说,Spring IoC 容器能够精确地控制 Bean 何时被创建、何时初始化完成以及何时被销毁;
(2) 对于 prototype 作用域的 Bean 来说,Spring IoC 容器只负责创建,然后就将 Bean 的实例交给客户端代码管理,Spring IoC 容器将不再跟踪其生命周期。
1. Spring的生命周期流程
Spring Bean 的完整生命周期从创建 Spring IoC 容器开始,直到最终 Spring IoC 容器销毁 Bean 为止,其具体流程如下图所示。
2.Bean 生命周期的整个执行过程
- Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。
- 对 Bean 进行属性注入。
- 如果 Bean 实现了
BeanNameAware
接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。 - 如果 Bean 实现了
BeanFactoryAware 接
口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。 - 如果 Bean 实现了
ApplicationContextAware
接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。 - 如果 Bean 实现了
BeanPostProcessor
接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。 - 如果 Bean 实现了
InitializingBean
接口,则 Spring 将调用 afterPropertiesSet() 方法。 - 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
- 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
- 如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
- 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。