1. Bean 的作用域和注入
1.1 作用域
scope属性
● singleton
:单例, 默认值,调用getBean
方法返回是同一个对象,实例会被缓存起来,效率比较高当一个bean被标识为singleton时候,spring的IOC容器中只会存在一个该bean
● prototype
: 多例,调用getBean方法创建不同的对象,会频繁的创建和销毁对象造成很大的开销
● 其他少用 (作用域 只在 WebApplicationContext)
○ request
:每个Http请求都会创建一个新的bean
○ session
: 每个Http Session请求都会创建一个新的bean
○ global session
(基本不用)
<!--<bean id="video" class="net.xdclass.sp.domain.Video" scope="singleton"> -->
<bean id="video" class="net.xdclass.sp.domain.Video" scope="prototype">
<property name="id" value="9"/>
<property name="title" value="Spring 5.X课程" />
</bean>
1.2 Spring5.X常见的注入方式
1.2.1 set注入
<bean id="video" class="net.xdclass.sp.domain.Video" scope="singleton">
<property name="id" value="9"/>
<property name="title" value="Spring 5.X课程" />
</bean>
1.2.2 构造器注入
- 匹配构造器的参数名称
<bean id="video" class="net.xdclass.sp.domain.Video" >
<constructor-arg name="title" value="面试专题课程第一季"></constructor-arg>
</bean>
- 匹配构造器的参数下标
<bean id="video" class="net.xdclass.sp.domain.Video" >
<constructor-arg index="0" value="面试专题课程第一季"></constructor-arg>
</bean>
- 匹配构造器的参数类型
<bean id="user" class="cn.tewuyiang.pojo.User">
<!-- 对于引用类型,需要使用限定类名 -->
<constructor-arg type="java.lang.String" value="aaa" />
<constructor-arg type="int" value="123" />
<constructor-arg type="com.wguo.pojo.Car" ref="car" />
</bean>
注意: 类的构造函数重写的时候,一定要保留空构造函数!!
若存在多个构造器匹配bean的定义,Spring容器总是使用最后一个满足条件的构造器。
1.2.3 静态工厂注入
静态工厂注入就是编写一个静态的工厂方法,这个工厂方法会返回一个需要的值,然后在配置文件中,指定使用这个工厂方法创建bean
public class SimpleFactory {
public static Car getCar() {
return new Car(12345, 5.4321);
}
}
在xml中配置car这个bean,并指定它由工厂方法进行创建
<bean id="car" class="cn.wguo.factory.SimpleFactory" factory-method="getCar"/>
1.2.4 实例工厂注入
实例工厂与静态工厂类似,不同的是,静态工厂调用工厂方法不需要先创建工厂类的对象,因为静态方法可以直接通过类调用,所以在上面的配置文件中,并没有声明工厂类的bean。但是,实例工厂,需要有一个实例对象,才能调用它的工厂方法
public class SimpleFactory {
/**
* 实例工厂方法,返回一个Car的实例对象
*/
public Car getCar() {
return new Car(12345, 5.4321);
}
/**
* 实例工厂方法,返回一个String
*/
public String getName() {
return "wguo";
}
/**
* 实例工厂方法,返回一个int,在Spring容器中会被包装成Integer
*/
public int getAge() {
return 128;
}
}
在上面的工厂类中,共定义了三个工厂方法,分别用来返回user所需的car,name以及age,而配置文件如下
<!-- 声明实例工厂bean,Spring容器需要先创建一个SimpleFactory对象,才能调用工厂方法 -->
<bean id="factory" class="cn.wguo.factory.SimpleFactory" />
<!--
通过实例工厂的工厂方法,创建三个bean,通过factory-bean指定工厂对象,
通过factory-method指定需要调用的工厂方法
-->
<bean id="name" factory-bean="factory" factory-method="getName" />
<bean id="age" factory-bean="factory" factory-method="getAge" />
<bean id="car" factory-bean="factory" factory-method="getCar" />
<bean id="user" class="cn.wguo.pojo.User">
<!-- 将上面通过实例工厂方法创建的bean,注入到user中 -->
<property name="name" ref="name"/>
<property name="age" ref="age"/>
<property name="car" ref="car"/>
</bean>
1.2.5 FactoryBean工厂注入
见下文!!! 3.2.2
1.3 Spring5.X List-Map类型的注入
- 复杂类型注入,添加两个属性
<bean id="video" class="net.xdclass.sp.domain.Video" >
<!--list类型注入-->
<property name="chapterList">
<list>
<value>第一章SpringBoot</value>
<value>第二章Mybatis</value>
<value>第三章Spring</value>
</list>
</property>
<property name="videoMap">
<map>
<entry key="1" value="SpringCloud课程"></entry>
<entry key="2" value="面试课程"></entry>
<entry key="3" value="javaweb课程"></entry>
</map>
</property>
</bean>
public class Video {
private int id;
private String title;
private List<String> chapterList;
private Map<Integer,String> videoMap;
//省略set get方法
}
1.4 IOC容器Bean之间的依赖和继承
1.4.1 容器Bean继承(parent
)
● bean继承:两个类之间大多数的属性都相同,避免重复配置,通过bean标签的parent属性重用已有的Bean元素的配置信息。
● 继承指的是配置信息的复用,和Java类的继承没有关系
<bean id="video" class="net.xdclass.sp.domain.Video" scope="singleton">
<property name="id" value="9"/>
<property name="title" value="Spring 5.X课程" />
</bean>
<bean id="video2" class="net.xdclass.sp.domain.Video2" scope="singleton" parent="video">
<property name="summary" value="这个是summary"></property>
</bean>
1.4.2 属性依赖(depend-on
)
如果类A是作为类B的属性, 想要类A比类B先实例化,设置两个Bean的依赖关系
<bean id="video" class="net.xdclass.sp.domain.Video" scope="singleton">
<property name="id" value="9"/>
<property name="title" value="Spring 5.X课程" />
</bean>
<!--设置两个bean的关系,video要先于videoOrder实例化-->
<bean id="videoOrder" class="net.xdclass.sp.domain.VideoOrder" depends-on="video">
<property name="id" value="8" />
<property name="outTradeNo" value="23432fnfwedwefqwef2"/>
<property name="video" ref="video"/>
</bean>
2. Bean 的生命周期和二次处理
2.1 Bean的生命周期的init和destroy方法
配置
<bean id="video" class="net.xdclass.sp.domain.Video" scope="singleton" init-method="init" destroy-method="destroy">
<property name="id" value="9"/>
<property name="title" value="Spring 5.X课程" />
</bean>
测试
public static void main(String [] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
((ClassPathXmlApplicationContext) context).registerShutdownHook();
}
2.2 Bean的二次加工-Spring5.x后置处理器 BeanPostProcessor
2.2.1 什么是BeanPostProcessor?
- 是Spring IOC容器给我们提供的一个扩展接口
- 在调用初始化方法前后对 Bean 进行额外加工,
ApplicationContext
会自动扫描实现了BeanPostProcessor
的 bean,并注册这些 bean 为后置处理器 - 是Bean的统一前置后置处理而不是基于某一个bean
2.2.2 执行顺序
- Spring IOC容器实例化Bean
- 调用
BeanPostProcessor
的postProcessBeforeInitialization
方法 - 调用bean实例的初始化方法
- 调用
BeanPostProcessor
的postProcessAfterInitialization
方法
注意:接口重写的两个方法不能返回
null
,如果返回null
那么在后续初始化方法将报空指针异常或者通过getBean()
方法获取不到bean
实例对象
public class CustomBeanPostProcessor implements BeanPostProcessor,Ordered {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("CustomBeanPostProcessor1 postProcessBeforeInitialization beanName="+beanName);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("CustomBeanPostProcessor1 postProcessAfterInitialization beanName="+beanName);
return bean;
}
public int getOrder() {
return 1;
}
}
可以注册多个BeanPostProcessor顺序
在Spring机制中可以指定后置处理器调用顺序,通过BeanPostProcessor
接口实现类实现Ordered
接口getOrder
方法,该方法返回整数,默认值为0
优先级最高,值越大优先级越低
2.3 Bean自动装配Autowire 属性
2.3.1 属性注入
前面学过属性注入,set方法、构造函数等,属于手工注入
有没办法实现自动装配注入?
2.3.2 Spring自动注入(XML)
使用元素的autowire
属性为一个 bean 定义指定自动装配模式
autowire
设置值
■ no:没开启
■ byName: 根据bean的id名称,注入到对应的属性里面
■ byType:根据bean需要注入的类型,注入到对应的属性里面(如果按照类型注入,存在2个以上bean的话会抛异常 expected single matching bean but found 2
■ constructor: 通过构造函数注入,需要这个类型的构造函数
<!--<bean id="videoOrder" class="net.xdclass.sp.domain.VideoOrder" autowire="byName">-->
<!--<bean id="videoOrder" class="net.xdclass.sp.domain.VideoOrder" autowire="byType">-->
<bean id="videoOrder" class="net.xdclass.sp.domain.VideoOrder" autowire="constructor">
<property name="id" value="8" />
<property name="outTradeNo" value="23432fnfwedwefqwef2"/>
</bean>
3. Spring中BeanFactory与FactoryBean的区别
3.1 BeanFactory
BeanFactor
是一个接口,它是Spring中工厂的顶层规范
,是SpringIoc容器的核心接口
,它定义了
getBean()
、containsBean()
等管理Bean的通用方法。Spring的容器都是它的具体实现如:
● DefaultListableBeanFactory
● XmlBeanFactory
● ApplicationContext
这些实现类又从不同的维度分别有不同的扩展。
3.1.1 源码
public interface BeanFactory {
//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;
//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//得到bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
3.1.2 使用场景
● 从Ioc容器中获取Bean(byName or byType)
● 检索Ioc容器中是否包含指定的Bean
● 判断Bean是否为单例
3.2 FactoryBean
首先它是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。
3.2.1 源码
public interface FactoryBean<T> {
//从工厂中获取bean
@Nullable
T getObject() throws Exception;
//获取Bean工厂创建的对象的类型
@Nullable
Class<?> getObjectType();
//Bean工厂创建的对象是否是单例模式
default boolean isSingleton() {
return true;
}
}
从它定义的接口可以看出,FactoryBean
表现的是一个工厂的职责。
即一个Bean A
如果实现了FactoryBean
接口,那么A就变成了一个工厂,通过BeanFactory
或ApplicationContext
根据A的名称获取到的实际上是工厂调用getObject()
返回的对象,而不是A
本身,如果要获取工厂A
自身的实例,那么需要在名称前面加上'&'
符号。
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
beanFactory.getBean("a");//获取到的是A的getObject返回值
beanFactory.getBean("&a");//获取到的是A本身
3.2.2 使用场景
工厂注入
@Getter
@Setter
@ToString
public class Animal {
protected String type;
protected String age;
public Animal() {
}
public Animal(String type) {
this.type = type;
}
}
public class Dog extends Animal{
public Dog() {
}
public Dog(String type) {
super(type);
}
}
public class Cat extends Animal{
public Cat() {
}
public Cat(String type) {
super(type);
}
}
@Setter
@Getter
public class AnimalFactoryBean implements FactoryBean<Animal> {
private String type;
@Override
public Animal getObject() throws Exception {
if ("dog".equalsIgnoreCase(type)){
return new Dog("dog");
}else if ("cat".equalsIgnoreCase(type)){
return new Cat("cat");
}else {
return null;
}
}
@Override
public Class<?> getObjectType() {
return Animal.class;
}
}
<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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="animal" class="com.wguo.test.AnimalFactoryBean">
<property name="type" value="cat"/>
</bean>
</beans>
public class App {
public static void main( String[] args ) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
Object animal = beanFactory.getBean("animal");//获取到的是A的getObject返回值
System.out.println(animal);
Object bean = beanFactory.getBean("&animal");//获取到的是A本身
System.out.println(bean);
Animal animal1 = beanFactory.getBean(Animal.class);
System.out.println(animal1);
Dog d = beanFactory.getBean(Dog.class);
System.out.println(d);
}
}
Animal(type=cat, age=null)
com.wguo.test.AnimalFactoryBean@4b4523f8
Animal(type=cat, age=null)
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.wguo.test.Dog' available
4. BeanFactory、FactoryBean区别
● 他们两个都是个工厂,但FactoryBean本质上还是一个Bean,也归BeanFactory管理
● BeanFactory是Spring容器的顶层接口,FactoryBean更类似于用户自定义的工厂接口。