spring bean 简单总结

创建对象的三种方式

无参构造方法

创建一个实体类User:

package com.example.demo.test;

public class User {
    private  String name ;
    private Integer old;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getOld() {
        return old;
    }

    public void setOld(Integer old) {
        this.old = old;
    }

    public User() {
    }

    public User(String name, Integer old) {
        this.name = name;
        this.old = old;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", old=" + old +
                '}';
    }
}

无参构造方法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">

    <bean id="user" class="com.example.demo.test.User"></bean>

</beans>

测试类:

 @Test
    void contextLoads() {
        //1.加载配置文件
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取bean对象
        User user = (User) app.getBean("user");
        System.out.println(user);
    }

运行结果:

有参构造方法:

无参构造方法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">

    <bean id="user" class="com.example.demo.test.User">
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="old" value="18"></constructor-arg>
    </bean>

</beans>

运行结果:

 工厂模式创建对象:

创建一个实体类UsersFactory:

package com.example.demo.test;

public class UsersFactory {

    public User getInstance(){
        return new User("李四",19);
    }
}
工厂模式创建对象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">

    <bean id="user" class="com.example.demo.test.User">
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="old" value="18"></constructor-arg>
    </bean>

    <!-- 工厂模式创建对象 -->
    <bean id="factory" class="com.example.demo.test.UsersFactory"></bean>
    <bean id="user2" factory-bean="factory" factory-method="getInstance"></bean>
</beans>

运行结果:

 Bean生命周期:

生命周期图:

 生命周期测试:

创建MyBeanPostProcessor实现BeanPostProcessor接口完成:

package com.example.demo.test;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

// @Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        System.out.println("MyBeanPostProcessor前置:"+beanName);
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor后置");
        return bean;
    }
}

创建user实体类:

package com.example.demo.test;

import org.springframework.beans.factory.InitializingBean;

public class User  {
    private  String name ;
    private Integer old;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("第二步填充属性");
        this.name = name;
    }

    public Integer getOld() {
        return old;
    }

    public void setOld(Integer old) {
        this.old = old;
    }

    public User() {
        System.out.println("第一步初始化:");
    }

    public void initUser(){
        System.out.println("第三步初始化");
    }

    public void destory(){
        System.out.println("第五步销毁");
    }

    public User(String name, Integer old) {
        this.name = name;
        this.old = old;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", old=" + old +
                '}';
    }


}

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">

    <bean id="beanPostProcessor" class="com.example.demo.test.MyBeanPostProcessor">
    </bean>
    <bean id="user" class="com.example.demo.test.User" init-method="initUser" destroy-method="destory">
        <property name="name" value="张三"></property>
        <property name="old" value="18"></property>
    </bean>

    <!-- 工厂模式创建对象
    <bean id="factory" class="com.example.demo.test.UsersFactory"></bean>
    <bean id="user2" factory-bean="factory" factory-method="getInstance"></bean> -->
</beans>

运行结果:

循环依赖

什么是循环依赖?

在bean创建过程中,a一个属性依赖b  ,b一个属性依赖a ,这样a和b 创建就出现循环依赖

1、先创建A对象,实例化A对象,此时A对象中的b属性为空,填充属性b
2、从容器中查找B对象,如果找到了,直接赋值不存在循环依赖问题(不通),找不到直接创建B对象
3、实例化B对象,此时B对象中的a属性为空,填充属性a
4、从容器中查找A对象,找不到,直接创建

形成闭环这就是循环依赖

spring 三级缓存 解决循环依赖

    如果仔细琢磨的话,会发现A对象是存在的,只不过此时的A对象不是一个完整的状态,只完成了实例化但是未完成初始化,如果在程序调用过程中,拥有了某个对象的引用,能否在后期给他完成赋值操作,可以优先把非完整状态的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用,所以解决问题的核心在于实例化和初始化分开操作,这也是解决循环依赖问题的关键,
当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中,此时在容器中存在对象的几个状态,完成实例化=但未完成初始化,完整状态,因为都在容器中,所以要使用不同的map结构来进行存储,此时就有了一级缓存和二级缓存,如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,因为他们的查找顺序是1,2,3这样的方式来查找的。

 

一级缓存中放的是完整对象,二级缓存中放的是非完整对象
为什么需要三级缓存?三级缓存的value类型是ObjectFactory,是一个函数式接口,singleFactory.getObject()方法每次都会生成一个新的代理对象,然后把这个对象放入二级缓存中,每次使用先去二级缓存查看是否存在,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个

我们可以看到填充属性的时候,spring会提前将已经实例化的bean通过ObjectFactory半成品暴露出去,为什么称为半成品是因为这时候的bean对象实例化,但是未进行属性填充,是一个不完整的bean实例对象

spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去解决的,所说的缓存其实也就是三个Map

可以看到三级缓存各自保存的对象,这里重点关注二级缓存earlySingletonObjects和三级缓存singletonFactory,一级缓存可以进行忽略。前面我们讲过先实例化的bean会通过ObjectFactory半成品提前暴露在三级缓存中

singletonFactory是传入的一个匿名内部类,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference方法。再来看看循环依赖中是怎么拿其它半成品的实例对象的。

我们假设现在有这样的场景AService依赖BService,BService依赖AService

\1. AService首先实例化,实例化通过ObjectFactory半成品暴露在三级缓存中

\2. 填充属性BService,发现BService还未进行过加载,就会先去加载BService

\3. 再加载BService的过程中,实例化,也通过ObjectFactory半成品暴露在三级缓存

\4. 填充属性AService的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory

拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。下面是重点,我们发现这个二级缓存好像显得有点多余,好像可以去掉,只需要一级和三级缓存也可以做到解决循环依赖的问题???

只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,下面来看一下bean被AOP进行了切面代理的场景

我们发现AService的testAopProxy被AOP代理了,看看传入的匿名内部类的getEarlyBeanReference返回的是什么对象

发现singletonFactory.getObject()返回的是一个AService的代理对象,还是被CGLIB代理的。再看一张再执行一遍singletonFactory.getObject()返回的是否是同一个AService的代理对象

我们会发现再执行一遍singleFactory.getObject()方法又是一个新的代理对象,这就会有问题了,因为AService是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为AService是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。还有一个注意的点

既然singleFactory.getObject()返回的是代理对象,那么注入的也应该是代理对象,我们可以看到注入的确实是经过CGLIB代理的AService对象。所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值