1.0_Spring-IOC(Bean注入、BeanFactory、FactoryBean)

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
  • 调用BeanPostProcessorpostProcessBeforeInitialization方法
  • 调用bean实例的初始化方法
  • 调用BeanPostProcessorpostProcessAfterInitialization方法

注意:接口重写的两个方法不能返回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更类似于用户自定义的工厂接口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值