Spring(Bean详解)

GoF之工厂模式

GoF是指二十三种设计模式

GoF23种设计模式可分为三大类:

  • 创建型(5个):解决对象创建问题。

      • 单例模式

      • 工厂方法模式

      • 抽象工厂模式

      • 建造者模式

      • 原型模式

  • 结构型(7个):一些类或对象组合在一起的经典结构。

      • 代理模式

      • 装饰模式

      • 适配器模式

      • 组合模式

      • 享元模式

      • 外观模式

      • 桥接模式

  • 行为型(11个):解决类或对象之间的交互问题。

      • 策略模式

      • 模板方法模式

      • 责任链模式

      • 观察者模式

      • 迭代子模式

      • 命令模式

      • 备忘录模式

      • 状态模式

      • 访问者模式

      • 中介者模式

      • 解释器模式

工厂模式是解决对象创建问题的,所以工厂模式属于创建型设计模式。这里为什么学习工厂模式呢?这是因为Spring框架底层使用了大量的工厂模式。

1.1工厂模式的三种形态

工厂模式通常有三种形态:

  • 第一种:简单工厂模式(Simple Factory):不属于23种设计模式之一。简单工厂模式又叫做静态工厂模式。简单工厂模式是工厂模式的一种特殊实现。

  • 工厂方法模式(Factory Method):是二十三种设计模式之一。

  • 抽象工厂模式(Abstract Factory):是二十三种设计模式之一。

1.2 简单工厂模式

简单工厂模式的角色包括三个:

  • 抽象产品:角色

  • 具体产品:角色

  • 工厂类:角色

代码如下:

抽象产品角色:

package com.powernode.factory;
​
/**
 * 武器(抽象产品角色)
 * @author 耀耀
 * @version 1.0
 * @className Weapon
 * @since 1.0
 **/
public abstract class Weapon {
    /**
     * 所有的武器都有攻击行为
     */
    public abstract void attack();
}

具体产品角色:

package com.powernode.factory;
​
/**
 * 坦克(具体产品角色)
 * @author 耀耀
 * @version 1.0
 * @className Tank
 * @since 1.0
 **/
public class Tank extends Weapon{
    @Override
    public void attack() {
        System.out.println("坦克开炮!");
    }
}
package com.powernode.factory;
​
/**
 * 战斗机(具体产品角色)
 * @author 耀耀
 * @version 1.0
 * @className Fighter
 * @since 1.0
 **/
public class Fighter extends Weapon{
    @Override
    public void attack() {
        System.out.println("战斗机投下原子弹!");
    }
}

 

package com.powernode.factory;
​
/**
 * 匕首(具体产品角色)
 * @author 耀耀
 * @version 1.0
 * @className Dagger
 * @since 1.0
 **/
public class Dagger extends Weapon{
    @Override
    public void attack() {
        System.out.println("砍他丫的!");
    }
}
​

 

 

工厂类角色:

package com.powernode.factory;
​
/**
 * 工厂类角色
 * @author 耀耀
 * @version 1.0
 * @className WeaponFactory
 * @since 1.0
 **/
public class WeaponFactory {
    /**
     * 根据不同的武器类型生产武器
     * @param weaponType 武器类型
     * @return 武器对象
     */
    public static Weapon get(String weaponType){
        if (weaponType == null || weaponType.trim().length() == 0) {
            return null;
        }
        Weapon weapon = null;
        if ("TANK".equals(weaponType)) {
            weapon = new Tank();
        } else if ("FIGHTER".equals(weaponType)) {
            weapon = new Fighter();
        } else if ("DAGGER".equals(weaponType)) {
            weapon = new Dagger();
        } else {
            throw new RuntimeException("不支持该武器!");
        }
        return weapon;
    }
}
​

 

测试程序(客户端程序):

package com.powernode.factory;
​
/**
 * @author 耀耀1
 * @version 1.0
 * @className Client
 * @since 1.0
 **/
public class Client {
    public static void main(String[] args) {
        Weapon weapon1 = WeaponFactory.get("TANK");
        weapon1.attack();
​
        Weapon weapon2 = WeaponFactory.get("FIGHTER");
        weapon2.attack();
​
        Weapon weapon3 = WeaponFactory.get("DAGGER");
        weapon3.attack();
    }
}

 

简单工厂模式的优点:

  • 客户端程序不需要关心对象的创建细节,需要哪个对象时,只需要向工厂索要即可,初步实现了责任的分离。客户端只负责“消费”,工厂负责“生产”。生产和消费分离。

简单工厂模式的缺点:

  • 缺点1:工厂类集中了所有产品的创造逻辑,形成一个无所不知的全能类,有人把它叫做上帝类。显然工厂类非常关键,不能出问题,一旦出问题,整个系统瘫痪。

  • 缺点2:不符合OCP开闭原则,在进行系统扩展时,需要修改工厂类。

Spring中的BeanFactory就使用了简单工厂模式。

1.3 工厂方法模式

工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点。

工厂模式的角色包括:

  • 抽象工厂角色

  • 具体工厂角色

  • 抽象产品角色

  • 具体产品角色

代码如下:

package com.powernode.factory;
​
/**
 * 武器类(抽象产品角色)
 * @author 耀耀
 * @version 1.0
 * @className Weapon
 * @since 1.0
 **/
public abstract class Weapon {
    /**
     * 所有武器都有攻击行为
     */
    public abstract void attack();
}

 

具体产品角色:

 




package com.powernode.factory;

​

/**

* 具体产品角色

* @author 耀耀

* @version 1.0

* @className Gun

* @since 1.0

**/

public class Gun extends Weapon{

   @Override

   public void attack() {

       System.out.println("开枪射击!");

  }

}

​

抽象工厂角色:

 


package com.powernode.factory;

​

/**

* 武器工厂接口(抽象工厂角色)

* @author 耀耀

* @version 1.0

* @className WeaponFactory

* @since 1.0

**/

public interface WeaponFactory {

   Weapon get();

}

​

​

具体工厂角色:

 


package com.powernode.factory;

​

/**

* 具体工厂角色

* @author 耀耀

* @version 1.0

* @className GunFactory

* @since 1.0

**/

public class GunFactory implements WeaponFactory{

   @Override

   public Weapon get() {

       return new Gun();

  }

}

​

具体工厂角色:

 


package com.powernode.factory;

​

/**

* 具体工厂角色

* @author 耀耀

* @version 1.0

* @className FighterFactory

* @since 1.0

**/

public class FighterFactory implements WeaponFactory{

   @Override

   public Weapon get() {

       return new Fighter();

  }

}

​

客户端程序:

 


package com.powernode.factory;

​

/**

* @author 耀耀

* @version 1.0

* @className Client

* @since 1.0

**/

public class Client {

   public static void main(String[] args) {

       WeaponFactory factory = new GunFactory();

       Weapon weapon = factory.get();

       weapon.attack();

​

       WeaponFactory factory1 = new FighterFactory();

       Weapon weapon1 = factory1.get();

       weapon1.attack();

  }

}

​

如果想扩展一个新的产品,只要新增一个产品类,再新增一个该产品对应的工厂即可,例如新增:匕首

增加:具体产品角色

 


package com.powernode.factory;

​

/**

* @author 耀耀

* @version 1.0

* @className Dagger

* @since 1.0

**/

public class Dagger extends Weapon{

   @Override

   public void attack() {

       System.out.println("砍丫的!");

  }

}

​

增加:具体工厂角色

 


package com.powernode.factory;

​

/**

* @author 耀耀

* @version 1.0

* @className DaggerFactory

* @since 1.0

**/

public class DaggerFactory implements WeaponFactory{

   @Override

   public Weapon get() {

       return new Dagger();

  }

}

​

我们可以看到在进行功能扩展的时候,不需要修改之前的源代码,显然工厂方法模式符合OCP原则。

工厂方法模式的优点:

  • 一个调用者想创建一个对象,只要知道其名称就可以了。

  • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

  • 屏蔽产品的具体实现,调用者只关心产品的接口。

工厂方法模式的缺点:

  • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

2,Bean的实例化方式

Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)

第一种:通过构造方法实例化

默认情况下,会调用Bean的无参数构造方法

第二种:通过简单工厂模式实例化

第一步:定义一个Bean

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className Vip

* @since 1.0

**/

public class Vip {

}

​

第二步:编写简单工厂模式当中的工厂类

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className VipFactory

* @since 1.0

**/

public class VipFactory {

   public static Vip get(){

       return new Vip();

  }

}

​

第三步:在Spring配置文件中指定创建该Bean方法(使用factory-method属性指定)

 


<bean id="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>

第四步:编写测试程序

 


@Test

public void testSimpleFactory(){

   ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

   Vip vip = applicationContext.getBean("vipBean", Vip.class);

   System.out.println(vip);

}

第三种:通过factory-bean实例化

这种方式本质上是:通过工厂方法模式经行实例化。

第一步:定义一个Bean

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className Order

* @since 1.0

**/

public class Order {

}

​

第二步:定义具体工厂类,工厂类中定义实力方法

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className OrderFactory

* @since 1.0

**/

public class OrderFactory {

   public Order get(){

       return new Order();

  }

}

​

第三步:在Spring配置文件中指定factory-bean以及factory-method

 


<bean id="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/>

<bean id="orderBean" factory-bean="orderFactory" factory-method="get"/>

第四步:编写测试程序

 


@Test

public void testSelfFactoryBean(){

   ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

   Order orderBean = applicationContext.getBean("orderBean", Order.class);

   System.out.println(orderBean);

}

第四种:通过FactoryBean接口实例化

以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。

在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。

factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。

第一步:定义一个Bean

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className Person

* @since 1.0

**/

public class Person {

}

​

第二步:编写一个类实现FactoryBean接口

 


package com.powernode.spring6.bean;

​

import org.springframework.beans.factory.FactoryBean;

​

/**

* @author 耀耀

* @version 1.0

* @className PersonFactoryBean

* @since 1.0

**/

public class PersonFactoryBean implements FactoryBean<Person> {

​

   @Override

   public Person getObject() throws Exception {

       return new Person();

  }

​

   @Override

   public Class<?> getObjectType() {

       return null;

  }

​

   @Override

   public boolean isSingleton() {

       // true表示单例

       // false表示原型

       return true;

  }

}

​

第三步:在Spring配置文件中配置FactoryBean

 


<bean id="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>

测试程序

 


@Test

public void testFactoryBean(){

   ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

   Person personBean = applicationContext.getBean("personBean", Person.class);

   System.out.println(personBean);

​

   Person personBean2 = applicationContext.getBean("personBean", Person.class);

   System.out.println(personBean2);

}

FactoryBean在Spring中是一个接口。被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean。所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的

6.5 BeanFactory 和 FactoryBean的区别

BeanFactory

Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,"Bean工厂"负责创建Bean对象。

FactoryBean

FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。

在Spring中,Bean可以分为两类:

  • 第一类:普通Bean

  • 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)

3,Bean的生命周期

3.1 什么是Bean的生命周期

Spring其实就是一个管理Bean对象的工厂。它负责对象的创建,对象的销毁等。

所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。

什么时候创建Bean对象?

创建Bean对象的前后会调用什么方法?

Bean对象什么时候销毁?

Bean对象的销毁前后调用什么方法?

3.2 为什么要知道Bean的生命周期

其实生命周期的本质是:在哪个时间节点上调用了哪个类的哪个方法。

我们需要充分的了解在这个生命线上,都有哪些特殊的时间节点。

只有我们知道了特殊的时间节点都在哪,到时我们才可以确定代码写到哪。

我们可能需要在某个特殊的时间点上执行一段特定的代码,这段代码就可以放到这个节点上。当生命线走到这里的时候,自然会被调用。

3.3 Bean的生命周期之五步

Bean生命周期的管理,可以参考Spring的:AbstractAutowireCapableBeanFactory类的doCreateBean()方法。

Bean生命周期可以粗略的划分为五大步:

  • 第一步:实例化Bean

  • 第二步:Bean属性赋值

  • 第三步:初始化Bean

  • 第四步:使用Bean

  • 第五步:销毁Bean

代码测试一下

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className User

* @since 1.0

**/

public class User {

   private String name;

​

   public User() {

       System.out.println("1.实例化Bean");

  }

​

   public void setName(String name) {

       this.name = name;

       System.out.println("2.Bean属性赋值");

  }

​

   public void initBean(){

       System.out.println("3.初始化Bean");

  }

​

   public void destroyBean(){

       System.out.println("5.销毁Bean");

  }

​

}

​
 


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

​

   <!--

   init-method属性指定初始化方法。

   destroy-method属性指定销毁方法。

   -->

   <bean id="userBean" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">

       <property name="name" value="zhangsan"/>

   </bean>

​

</beans>

测试程序

 


package com.powernode.spring6.test;

​

import com.powernode.spring6.bean.User;

import org.junit.Test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

​

/**

* @author 耀耀

* @version 1.0

* @className BeanLifecycleTest

* @since 1.0

**/

public class BeanLifecycleTest {

   @Test

   public void testLifecycle(){

       ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

       User userBean = applicationContext.getBean("userBean", User.class);

       System.out.println("4.使用Bean");

       // 只有正常关闭spring容器才会执行销毁方法

       ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;

       context.close();

  }

}

​

注意事项:

  • 第一:只有正常关闭的spring容器,bean的销毁才会被调用。

  • 第二:ClassPathXmlApplicationContex类才有ckose()方法。

  • 第三:配置文件中的int-method指定初始化方法。destroy-method指定销毁方法。

3.4 Bean生命周期之七步走

在以上的5步中,第3步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”。

编写一个类实现BeanPostProcessor类,并且重写before和after方法:

 


package com.powernode.spring6.bean;

​

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanPostProcessor;

​

/**

* @author 耀耀

* @version 1.0

* @className LogBeanPostProcessor

* @since 1.0

**/

public class LogBeanPostProcessor implements BeanPostProcessor {

   @Override

   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

       System.out.println("Bean后处理器的before方法执行,即将开始初始化");

       return bean;

  }

​

   @Override

   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

       System.out.println("Bean后处理器的after方法执行,已完成初始化");

       return bean;

  }

}

​

在spring.xml文件中配置“Bean后处理器”:

 


<!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->

<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>

一定要注意:在spring.xml文件中配置的Bean后处理器将作用于当前配置文件中所有的Bean。

4,Bean的循环依赖问题

4.1什么是Bena的循环依赖

A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。

比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。

 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className Husband

* @since 1.0

**/

public class Husband {

   private String name;

   private Wife wife;

}

​
 


package com.powernode.spring6.bean;

​

/**

* @author 耀耀

* @version 1.0

* @className Wife

* @since 1.0

**/

public class Wife {

   private String name;

   private Husband husband;

}

​

在singleton + set注入的情况下,循环依赖是没有问题的。Spring可以解决这个问题。

当循环依赖的所有Bean的scope="prototype"的时候,产生的循环依赖,Spring是无法解决的,会出现BeanCurrentlyInCreationException异常。

大家可以测试一下,以上两个Bean,如果其中一个是singleton,另一个是prototype,是没有问题的。

构造方法注入会导致**实例化对象的过程和对象属性赋值的过程没有分离开,必须在一起完成导致的。**

Spring为什么可以解决set + singleton模式下循环依赖?

根本的原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。

实例化Bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。

给Bean属性赋值的时候:调用setter方法来完成。

两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。

也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(我们可以称之为缓存),所有的单例Bean全部实例化完成之后,以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。

那么在Spring框架底层源码级别上是如何实现的呢?请看:

在以上类中包含三个重要的属性:

  • *Cache of singleton objects: bean name to bean instance.* 单例对象的缓存:key存储bean名称,value存储Bean对象【一级缓存】

  • *Cache of early singleton objects: bean name to bean instance.* 早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象【二级缓存】

  • *Cache of singleton factories: bean name to ObjectFactory.* 单例工厂缓存:key存储bean名称,value存储该Bean对应的ObjectFactory对象【三级缓存】

这三个缓存其实本质上是三个Map集合。

我们再来看,在该类中有这样一个方法addSingletonFactory(),这个方法的作用是:将创建Bean对象的ObjectFactory对象提前曝光。

从源码中可以看到,spring会先从一级缓存中获取Bean,如果获取不到,则从二级缓存中获取Bean,如果二级缓存还是获取不到,则从三级缓存中获取之前曝光的ObjectFactory对象,通过ObjectFactory对象获取Bean实例,这样就解决了循环依赖的问题。

总结:

Spring只能解决setter方法注入的单例bean之间的循环依赖。ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在创建ClassA对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值