动力节点Spring (5-8)

五、Bean的作⽤域

5.1 singleton

默认情况下,Spring的IoC容器创建的Bean对象是单例的。来测试⼀下:
SpringBean类:
public class SpringBean {
    public SpringBean(){
        System.out.println("Spring的无参数构造方法执行了");
    }
}

spring-scope.xml文件:

    <bean id="sb" class="com.powernode.spring6.bean.SpringBean"></bean>
@Test
    public void testBeanScope(){
        /*
        * 1.Spring默认情况下是如何管理这个Bean的
        *       默认情况下Bean是单例的(singleton)
        *       在Spring上下文初始化的时候实例化
        *       每一次调用getBean()方法的时候,都返回那个单例的对象
        * */
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
        SpringBean sb1 = applicationContext.getBean("sb", SpringBean.class);
        SpringBean sb2 = applicationContext.getBean("sb", SpringBean.class);
        SpringBean sb3 = applicationContext.getBean("sb", SpringBean.class);
        System.out.println(sb1);
        System.out.println(sb2);
        System.out.println(sb3);
        //由此可见在默认情况下是单例的
//        com.powernode.spring6.bean.SpringBean@7b9a4292
//        com.powernode.spring6.bean.SpringBean@7b9a4292
//        com.powernode.spring6.bean.SpringBean@7b9a4292
}
通过测试得知:Spring的IoC容器中,默认情况下,Bean对象是单例的。
通过测试得知,默认情况下,Bean对象的创建是在初始化Spring上下⽂的时候就完成的。

5.2 prototype

如果想让Spring的Bean对象以多例的形式存在,可以在bean标签中指定scope属性的值为:
prototype ,这样Spring会在每⼀次执⾏getBean()⽅法的时候创建Bean对象,调⽤⼏次则创建⼏次。
SpringBean类相同
spring-scope.xml文件:
    <bean id="sb" class="com.powernode.spring6.bean.SpringBean" scope="prototype"></bean>
    @Test
    public void testBeanScope(){
        /*
        * 2.当将bean的scope属性设置为prototype
        *       bean是多例的
        *       spring上下文初始化的时候,并不会初始化这些prototype的bean
        *       每一次调用getBean()方法的时候,才会实例化该bean对象
        *       prototype翻译为:原型
        * */
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
        SpringBean sb1 = applicationContext.getBean("sb", SpringBean.class);
        SpringBean sb2 = applicationContext.getBean("sb", SpringBean.class);
        SpringBean sb3 = applicationContext.getBean("sb", SpringBean.class);
        System.out.println(sb1);
        System.out.println(sb2);
        System.out.println(sb3);

        //在spring-scope.xml文件中添加scope="prototype",此时是多例的
//        Spring的无参数构造方法执行了
//        Spring的无参数构造方法执行了
//        Spring的无参数构造方法执行了
//        com.powernode.spring6.bean.SpringBean@4f6ee6e4
//        com.powernode.spring6.bean.SpringBean@4466af20
//        com.powernode.spring6.bean.SpringBean@a514af7
}
可以看到这⼀次在初始化Spring上下⽂的时候,并没有创建Bean对象。
通过测试得知,没有指定scope属性时,默认是singleton单例的。

5.3 其它scope

scope属性的值不⽌两个,它⼀共包括8个选项:
   ●singleton:默认的,单例。
   ●prototype:原型。每调⽤⼀次getBean()⽅法则获取⼀个新的Bean对象。或每次注⼊的时候都是新对象。
   ●request:⼀个请求对应⼀个Bean。 仅限于在WEB应⽤中使⽤
   ●session:⼀个会话对应⼀个Bean。 仅限于在WEB应⽤中使⽤
   ●global session: portlet应⽤中专⽤的 。如果在Servlet的WEB应⽤中使⽤global session的话,和session⼀个效果。(portlet和servlet都是规范。servlet运⾏在servlet容器中,例如Tomcat。
portlet运⾏在portlet容器中。)
   ●application:⼀个应⽤对应⼀个Bean。 仅限于在WEB应⽤中使⽤。
   ●websocket:⼀个websocket⽣命周期对应⼀个Bean。 仅限于在WEB应⽤中使⽤。
   ●⾃定义scope:很少使⽤。
接下来咱们⾃定义⼀个Scope,线程级别的Scope,在同⼀个线程中,获取的Bean都是同⼀个。跨线程则是不同的对象:(以下内容作为了解)
●第⼀步:⾃定义Scope。(实现Scope接⼝)
   ○spring内置了线程范围的类:org.springframework.context.support.SimpleThreadScope,可
以直接⽤。
●第⼆步:将⾃定义的Scope注册到Spring容器中。
    <!--配置我们自定义的作用域-->
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="threadScope">
                    <!--这个Scope接口的实现类使用的是Spring框架内置的。也可以自定义。-->
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>
●第三步:使⽤Scope。
<bean id="sb" class="com.powernode.spring6.beans.SpringBean" scope="myThread" />

六、GoF之⼯⼚模式

设计模式:⼀种可以被重复利⽤的解决⽅案。
GoF(Gang of Four),中⽂名——四⼈组。
《Design Patterns: Elements of Reusable Object-Oriented Software》(即《设计模式》⼀
书), 1995 年由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著。这⼏位作者常被称为"四⼈组(Gang of Four)"。
该书中描述了 23 种设计模式。我们平常所说的设计模式就是指这 23 种设计模式。
不过除了GoF 23 种设计模式之外,还有其它的设计模式,⽐如:JavaEE的设计模式(DAO模式、MVC模式等)。
GoF 23 种设计模式可分为三⼤类:
        ○ 创建型 5 个):解决对象创建问题。
              ■ 单例模式
              ■ ⼯⼚⽅法模式
              ■ 抽象⼯⼚模式
              ■ 建造者模式
              ■ 原型模式
        ○ 结构型 7 个):⼀些类或对象组合在⼀起的经典结构。
              ■ 代理模式
              ■ 装饰模式
              ■ 适配器模式
              ■ 组合模式
              ■ 享元模式
              ■ 外观模式
              ■ 桥接模式
        ○ ⾏为型 11 个):解决类或对象之间的交互问题。
              ■ 策略模式
              ■ 模板⽅法模式
              ■ 责任链模式
              ■ 观察者模式
              ■ 迭代⼦模式
              ■ 命令模式
              ■ 备忘录模式
              ■ 状态模式
              ■ 访问者模式
              ■ 中介者模式
              ■ 解释器模式
⼯⼚模式是解决对象创建问题的,所以⼯⼚模式属于创建型设计模式。这⾥为什么学习⼯⼚模式
呢?这是因为Spring框架底层使⽤了⼤量的⼯⼚模式。

6.1 ⼯⼚模式的三种形态

⼯⼚模式通常有三种形态:
第⼀种: 简单⼯⼚模式(Simple Factory):不属于 23 种设计模式之⼀。简单⼯⼚模式⼜叫做:静态 ⼯⼚⽅法模式。简单⼯⼚模式是⼯⼚⽅法模式的⼀种特殊实现。
第⼆种:⼯⼚⽅法模式(Factory Method):是 23 种设计模式之⼀。
第三种:抽象⼯⼚模式(Abstract Factory):是 23 种设计模式之⼀。

6.2 简单⼯⼚模式

简单⼯⼚模式的⻆⾊包括三个:
      ● 抽象产品 ⻆⾊
      ● 具体产品 ⻆⾊
      ● ⼯⼚类 ⻆⾊
简单⼯⼚模式的代码如下:
抽象产品⻆⾊:
Weapon类:
//抽象产品角色
public abstract class Weapon {
    //所有的武器都可以攻击
    public abstract  void attack();
}
具体产品⻆⾊:
Tank类:
//具体产品角色
public class TanK extends Weapon{
    @Override
    public void attack() {
        System.out.println("坦克开炮!!!");
    }
}
Dagger类:
//具体产品角色
public class Dagger extends Weapon{
    @Override
    public void attack() {
        System.out.println("是兄弟就来侃我!!!");
    }
}
Fighter类:
//具体产品角色
public class Fighter extends Weapon{
    @Override
    public void attack() {
        System.out.println("战斗机扔下炸弹!!!");
    }
}
⼯⼚类⻆⾊:
WeaponFactory类:
//工厂类角色
public class WeaponFactory {
    //静态方法:要获取什么产品? 就看你传什么参数,传Tank获取坦克,传Dagger获取匕首,传Fighter获取战斗机
    //简单工厂模式中有一个静态方法,所以被成为:静态工厂方法模式
    public static Weapon get(String weaponType){
        if ("TANK".equals(weaponType)) {
            return  new TanK();
        }else if("DAGGER".equals(weaponType)){
            return  new Dagger();
        }else if("FIGHTER".equals(weaponType)){
            return  new Fighter();
        }else{
            throw new RuntimeException("不支持该武器的生产");
        }
    }
}
客户端程序:
Test类:
//客户端程序
public class Test {
    public static void main(String[] args) {
        //需要坦克
        // 对于我客户端来说,坦克的生产细节,我不需要关心,我只需要向工厂索要即可。
        // 简单工厂模式达到了什么呢?职责分离。客户端不需要关心产品的生产细节。
        // 客户端只负责消费。工厂类负责生产。一个负责生产,一个负责消费。生产者和消费者分离了。这就是简单工厂模式的作用。
        Weapon tank = WeaponFactory.get("TANK");
        tank.attack();//坦克开炮!!!

        //需要匕首
        Weapon dagger = WeaponFactory.get("DAGGER");
        dagger.attack();//是兄弟就来侃我!!!

        //需要战斗机
        Weapon fighter = WeaponFactory.get("FIGHTER");
        fighter.attack();//战斗机扔下炸弹!!!

    }
}
简单⼯⼚模式的优点:
      ●客户端程序不需要关⼼对象的创建细节,需要哪个对象时,只需要向⼯⼚索要即可,初步实现了责任的分离。客户端只负责“消费”,⼯⼚负责“⽣产”。⽣产和消费分离。
简单⼯⼚模式的缺点:
      ●缺点1 :⼯⼚类集中了所有产品的创造逻辑,形成⼀个⽆所不知的全能类,有⼈把它叫做上帝类。显然⼯⼚类⾮常关键,不能出问题,⼀旦出问题,整个系统瘫痪。
      ●缺点2 :不符合OCP开闭原则,在进⾏系统扩展时,需要修改⼯⼚类。
Spring中的BeanFactory就使⽤了简单⼯⼚模式。
设计模式之:简单工厂模式 Simple Factory Pattern

1. 简单工厂模式是工厂方法模式的一种特殊实现,又被称为:静态工厂方法模式。

2. 简单工厂模式解决什么问题呢?
    优点:客户端程序不需要关心对象的创建细节,需要哪个对象时,只需要向工厂索要即可,初步实现了责任的分离。
        客户端只负责“消费”,工厂负责“生产”。生产和消费分离。

3. 简单工厂模式中的角色:
    * 抽象产品角色
    * 具体产品角色
    * 工厂类角色

4. 简单工厂模式的缺点?
    缺点一:假设现在需要扩展一个新的产品,WeaponFactory工厂类的代码是需要修改的,显然违背了OCP原则。
    缺点二:工厂类的责任比较重大,不能出现任何问题,因为这个工厂类负责所有产品的生产,称为全能类,或者有人把它叫做上帝类。
        这个工厂类一旦出问题,整个系统必然全部瘫痪。(不要把所有鸡蛋放到一个篮子里面哦。)

6.3 ⼯⼚⽅法模式

⼯⼚⽅法模式既保留了简单⼯⼚模式的优点,同时⼜解决了简单⼯⼚模式的缺点。
⼯⼚⽅法模式的⻆⾊包括:
      ●抽象⼯⼚⻆⾊
      ●具体⼯⼚⻆⾊
      ●抽象产品⻆⾊
      ●具体产品⻆⾊
抽象产品⻆⾊:
//抽象产品角色
abstract public class Weapon {
    public abstract void attack();
}

具体产品⻆⾊:

Dagger类:

//具体产品角色
public class Dagger extends Weapon{
    @Override
    public void attack() {
        System.out.println("是兄弟就来侃我!!!");
    }
}

Gun类:

//具体产品角色
public class Gun extends Weapon{
    @Override
    public void attack() {
        System.out.println("开枪射击!!!");
    }
}

抽象⼯⼚⻆⾊:

//抽象工厂角色
abstract public class WeaponFactory {
    //这个方法不是静态的,是实例方法
    public abstract Weapon get();
}

具体⼯⼚⻆⾊:

DaggerFactory:

//具体工厂角色
public class DaggerFactory extends WeaponFactory{
    @Override
    public Weapon get() {
        return new Dagger();
    }
}

GunFactory:

//具体工厂角色
public class GunFactory extends WeaponFactory{
    @Override
    public Weapon get() {
        return  new Gun();
    }
}
客户端程序:
public class Test {
    public static void main(String[] args) {
        WeaponFactory weaponFactory = new DaggerFactory();
        Weapon dagger = weaponFactory.get();
        dagger.attack();//是兄弟就来侃我!!!

        WeaponFactory weaponFactory1= new GunFactory();
        Weapon gun = weaponFactory1.get();
        gun.attack();//开枪射击!!!
    }
}
我们可以看到在进⾏功能扩展的时候,不需要修改之前的源代码,显然⼯⼚⽅法模式符合OCP原则。
⼯⼚⽅法模式的优点:
      ●⼀个调⽤者想创建⼀个对象,只要知道其名称就可以了。
      ●扩展性⾼,如果想增加⼀个产品,只要扩展⼀个⼯⼚类就可以。
      ●屏蔽产品的具体实现,调⽤者只关⼼产品的接⼝。
⼯⼚⽅法模式的缺点:
      ● 每次增加⼀个产品时,都需要增加⼀个具体类和对象实现⼯⼚,使得系统中类的个数成倍增加,在⼀定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

工厂方法模式:Factory Method Pattern

1. 工厂方法模式可以解决简单工厂模式当中的OCP问题。
    怎么解决的?一个工厂对应生产一种产品。
    这样工厂就不是全能类了,不是上帝类了。
    另外,也可以符合OCP原则。

2. 工厂方法模式中的角色:
    * 抽象产品角色 Weapon
    * 具体产品角色 Dagger Gun
    * 抽象工厂角色 WeaponFactory
    * 具体工厂角色 DaggerFactory GunFactory

3. 工厂方法模式的优点:
    当你扩展一个产品的时候,符合OCP原则,因为只需要添加两个类,一个类是具体产品类,一个类是具体工厂类。都是添加类,没有修改之前的代码,所以符合OCP。
    ● 一个调用者想创建一个对象,只要知道其名称就可以了。
    ● 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
    ● 屏蔽产品的具体实现,调用者只关心产品的接口。

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

 6.4 抽象⼯⼚模式(了解)

抽象⼯⼚模式相对于⼯⼚⽅法模式来说,就是⼯⼚⽅法模式是针对⼀个产品系列的,⽽抽象⼯⼚模式是针对多个产品系列的,即⼯⼚⽅法模式是⼀个产品系列⼀个⼯⼚类,⽽抽象⼯⼚模式是多个产品系列⼀个⼯⼚类。
抽象⼯⼚模式特点:抽象⼯⼚模式是所有形态的⼯⼚模式中最为抽象和最具⼀般性的⼀种形态。抽象⼯⼚模式是指当有多个抽象⻆⾊时,使⽤的⼀种⼯⼚模式。抽象⼯⼚模式可以向客户端提供⼀个接⼝,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。它有多个抽象产品类,每个抽象产品类可以派⽣出多个具体产品类,⼀个抽象⼯⼚类,可以派⽣出多个具体⼯⼚类,每个具体⼯⼚类可以创建多个具体产品类的实例。每⼀个模式都是针对⼀定问题的解决⽅案,⼯⼚⽅法模式针对的是⼀个产品等级结构;⽽抽象⼯⼚模式针对的是多个产品等级结果。
抽象⼯⼚中包含 4 个⻆⾊:
抽象⼯⼚⻆⾊
具体⼯⼚⻆⾊
抽象产品⻆⾊
具体产品⻆⾊
抽象⼯⼚模式的类图如下:

具体代码看文档。

抽象⼯⼚模式的优缺点:
优点:当⼀个产品族中的多个对象被设计成⼀起⼯作时,它能保证客户端始终只使⽤同⼀个产品族中的对象。
缺点:产品族扩展⾮常困难,要增加⼀个系列的某⼀产品,既要在AbstractFactory⾥加代码,⼜要在具体的⾥⾯加代码。

七、Bean的实例化⽅式

Spring为Bean提供了多种实例化⽅式,通常包括 4 种⽅式。(也就是说在Spring中为Bean对象的创建准
备了多种⽅案,⽬的是:更加灵活)
第⼀种:通过构造⽅法实例化
第⼆种:通过简单⼯⼚模式实例化
第三种:通过factory-bean实例化
第四种:通过FactoryBean接⼝实例化

7.1 通过构造⽅法实例化

我们之前⼀直使⽤的就是这种⽅式。默认情况下,会调⽤Bean的⽆参数构造⽅法。
SpringBean类:
public class SpringBean {
    public SpringBean(){
        System.out.println("Spring Bean的无参数构造方法执行!");
    }
}
spring.xml文件:
<!--Spring提供的实例化方式,第一种:在Spring配置文件中直接配置类全路径,Spring会自动调用该类的无参数构造方法来实例化Bean-->
    <bean id="sb" class="com.powernode.spring6.bean.SpringBean"/>
 @Test
    public void testInstantiation1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        SpringBean sb = applicationContext.getBean("sb", SpringBean.class);
        System.out.println(sb);
//        Spring Bean的无参数构造方法执行!
//        com.powernode.spring6.bean.SpringBean@1fa268de
    }

7.2 通过简单⼯⼚模式实例化

Star类:

public class Star {
    public Star(){
        System.out.println("Star的无参数构造方法执行!");
    }
}

StarFactory类:

//简单工厂模式中的工厂角色,星工厂
public class StarFactory {
    //工厂类中有一个静态方法
    //简单工厂模式又叫做:静态工厂方法模式
    public static Star get(){
        //这个Star对象最终实际上创建的时候还是我们负责new的对象
        return new Star();
    }
}

spring.xml文件:

    <!--Spring提供的实例化方式,第二种:通过简单工厂模式。你需要在Spring配置文件中告诉Spring框架,调用哪个类的哪个方法获取Bean-->
    <!--factory-method 属性指定的是工厂类当中的静态方法。也就是告诉Spring框架,调用这个方法可以获取Bean。-->
    <bean id="star" class="com.powernode.spring6.bean.StarFactory" factory-method="get"/>
    @Test
    public void testInstantiation2(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Star star = applicationContext.getBean("star", Star.class);
        System.out.println(star);
//        Star的无参数构造方法执行!
//        com.powernode.spring6.bean.Star@222545dc
    }

7.3 通过factory-bean实例化

这种⽅式本质上是:通过⼯⼚⽅法模式进⾏实例化。
Gun类:
//具体产品角色
public class Gun {
    public Gun(){
        System.out.println("Gun的无参数构造方法!");
    }
}
GunFactory类:
//工厂方法模式中:具体工厂角色
public class GunFactory {
    //工厂方法模式中的具体工厂角色的方法是:实例方法
    public Gun get(){
        //实际上new这个对象还是我们自己new的
        return new Gun();
    }
}
spring.xml文件:
    <!--Spring提供的实例化方式,第三种:通过工厂方法模式。通过 factory-bean属性 + factory-method属性来共同完成。-->
    <!--告诉Spring框架,调用哪个对象的哪个方法来获取Bean。-->
    <bean id="gunFactory" class="com.powernode.spring6.bean.GunFactory"/>
    <!--以下的配置很关键,factory-bean属性告诉Spring调用哪个对象。factory-method告诉Spring调用该对象的哪个方法。-->
    <bean id="gun" factory-bean="gunFactory" factory-method="get"/>
    @Test
    public void testInstantiation3(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Gun gun = applicationContext.getBean("gun", Gun.class);
        System.out.println(gun);
//        Gun的无参数构造方法!
//        com.powernode.spring6.bean.Gun@595b007d
    }

7.4 通过FactoryBean接⼝实例化

以上的第三种⽅式中,factory-bean是我们⾃定义的,factory-method也是我们⾃⼰定义的。
在Spring中,当你编写的类直接实现FactoryBean接⼝之后,factory-bean不需要指定了,factory-
method也不需要指定了。
factory-bean会⾃动指向实现FactoryBean接⼝的类,factory-method会⾃动指向getObject()⽅法。
Person类:
public class Person {//普通的Bean
    public Person(){
        System.out.println("Person的无参数构造方法执行!");
    }
}
PersonFactoryBean类:
public class PersonFactoryBean implements FactoryBean<Person> {
    //PersonFactoryBean也是一个Bean,只不过这个Bean比较特殊,叫做工厂Bean
    //通过工厂这个特殊的Bean,可以获取普通Bean

    @Override
    public Person getObject() throws Exception {
        //最终这个Bean的创建还是我们自己new的
        return new Person();
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    //这个方法在接口中有默认实现
    //默认返回true,表示单例的
    //如果想多例,直接将这个方法修改为return false;即可
    @Override
    public boolean isSingleton() {
        return true;
    }
}
spring.xml文件:
    <!--Spring提供的实例化方式,第四种:通过FactoryBean接口来实现。-->
    <!--这种方式实际上就是第三种方式的简化。-->
    <!--由于你编写的类实现了FactoryBean接口,所以这个类是一个特殊的类,不需要你再手动指定:factory-bean、factory-method-->
    <!--通过一个特殊的Bean:工厂Bean。来返回一个普通的Bean Person对象。-->
    <!--通过FactoryBean这个工厂Bean主要是想对普通Bean进行加工处理。-->
    <bean id="person" class="com.powernode.spring6.bean.PersonFactoryBean"/>
FactoryBean在Spring中是⼀个接⼝。被称为“⼯⼚Bean”。“⼯⼚Bean”是⼀种特殊的Bean。所有
的“⼯⼚Bean”都是⽤来协助Spring框架来创建其他Bean对象的。

7.5 BeanFactory和FactoryBean的区别

7.5.1 BeanFactory

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

7.5.2 FactoryBean

FactoryBean:它是⼀个Bean,是⼀个能够 辅助Spring 实例化其它Bean对象的⼀个Bean。
在Spring中,Bean可以分为两类:
      ●第⼀类:普通Bean
      ●第⼆类:⼯⼚Bean(记住:⼯⼚Bean也是⼀种Bean,只不过这种Bean⽐较特殊,它可以辅助Spring实例化其它Bean对象。)

7.6 注⼊⾃定义Date

我们前⾯说过,java.util.Date在Spring中被当做简单类型,简单类型在注⼊的时候可以直接使⽤value属性或value标签来完成。但我们之前已经测试过了,对于Date类型来说,采⽤value属性或value标签赋值的时候,对⽇期字符串的格式要求⾮常严格,必须是这种格式的:Mon Oct 10 14 : 30 : 26 CST 2022 。其他格式是不会被识别的。如以下代码:
Student类:
public class Student {
    //java.util.Date 在Spring当中被当做简单类型,但是简单类型的话,注入的日期字符串格式有要求
    //java.util.Date 在Spring当中也可以被当做非简单类型
    private Date birth;

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Student{" +
                "birth=" + birth +
                '}';
    }
}
spring.xml文件:
    <!--这种方式只能获取系统当前的时间,不能作为学生的生日-->
    <bean id="nowTime" class="java.util.Date"/>

    <bean id="student" class="com.powernode.spring6.bean.Student">
        <!--把日期类型当作简单类型处理-->
        <!--        <property name="birth" value="Mon Oct 10 14:30:26 CST 2022"/>-->
        <!--把日期类型当作非简单类型处理-->
        <property name="birth" ref="nowTime"/>

    </bean>
  @Test
    public void testDate(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = applicationContext.getBean("student", Student.class);
        System.out.println(student);//Student{birth=Tue Oct 11 04:30:26 CST 2022}
        //Student{birth=Thu Aug 10 14:05:51 CST 2023}
}

当我们想自己输入的日期时:

DateFactoryBean类:

public class DateFactoryBean implements FactoryBean<Date> {
    //DateFactoryBean这个工厂Bean协助我们Spring创建这个普通的Bean:Date;
    private String strDate;

    public DateFactoryBean(String strDate) {
        this.strDate = strDate;
    }

    @Override
    public Date getObject() throws Exception {
        SimpleDateFormat std = new SimpleDateFormat("yyyy-MM-dd");
        Date date = std.parse(strDate);
        return date;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

spring.xml文件:

    <!--通过工厂Bean:DateFactoryBean 来返回普通Bean:java.util.Date -->
    <bean id="date" class="com.powernode.spring6.bean.DateFactoryBean">
        <constructor-arg index="0" value="1999-07-18"/>
    </bean>

    <bean id="studentBean" class="com.powernode.spring6.bean.Student">
        <property name="birth" ref="date"/>
    </bean>
@Test
    public void testDate(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = applicationContext.getBean("studentBean", Student.class);
        System.out.println(student);//Student{birth=Tue Oct 11 04:30:26 CST 2022}
        //Student{birth=Thu Aug 10 14:05:51 CST 2023}
        //Student{birth=Sun Jul 18 00:00:00 CST 1999}
    }

⼋、Bean的⽣命周期

8.1 什么是Bean的⽣命周期

Spring其实就是⼀个管理Bean对象的⼯⼚。它负责对象的创建,对象的销毁等。
所谓的⽣命周期就是:对象从创建开始到最终销毁的整个过程。
什么时候创建Bean对象?
创建Bean对象的前后会调⽤什么⽅法?
Bean对象什么时候销毁?
Bean对象的销毁前后调⽤什么⽅法?

8.2 为什么要知道Bean的⽣命周期

其实⽣命周期的本质是:在哪个时间节点上调⽤了哪个类的哪个⽅法。
我们需要充分的了解在这个⽣命线上,都有哪些特殊的时间节点。
只有我们知道了特殊的时间节点都在哪,到时我们才可以确定代码写到哪。
我们可能需要在某个特殊的时间点上执⾏⼀段特定的代码,这段代码就可以放到这个节点上。当⽣命线⾛到这⾥的时候,⾃然会被调⽤。

8.3 Bean的⽣命周期之5

Bean⽣命周期的管理,可以参考Spring的源码: AbstractAutowireCapableBeanFactory类的
doCreateBean()⽅法
Bean⽣命周期可以粗略的划分为五⼤步:
第⼀步:实例化Bean
第⼆步:Bean属性赋值
第三步:初始化Bean
第四步:使⽤Bean
第五步:销毁Bean

编写测试程序:

User类:

/*
* Bean的生命周期按照粗略的五步的话:
 * 第一步:实例化Bean(调用无参数构造方法。)
 * 第二步:给Bean属性赋值(调用set方法。)
 * 第三步:初始化Bean(会调用Bean的init方法。注意:这个init方法需要自己写,自己配。)
 * 第四步:使用Bean
 * 第五步:销毁Bean(会调用Bean的destroy方法。注意:这个destroy方法需要自己写,自己配。)*/
public class User {
    private String name;

    public User(){
        System.out.println("第一步:无参数构造方法执行!");
    }

    public void setName(String name) {
        System.out.println("第二步:给对象的属性赋值!");
        this.name = name;
    }

    //这个方法需要自己写,自己配
    public void initBean(){
        System.out.println("第三步:初始化Bean!");
    }

    public void destroyBean(){
        System.out.println("第五步:销毁Bean!");
    }
}

 spring.xml文件:

    <!--选要手动指定初始化方法和销毁方法-->
    <bean id="user" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
        <property name="name" value="猪猪侠"/>
    </bean>
@Test
    public void testBeanLifecycleFive(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println("第四步:使用Bean:"+user);

        //注意:必须手动关闭Spring容器,这样Spring容器才会销毁Bean
        //close()这个方法只有ClassPathXmlApplicationContext才有,而applicationContext没有,所以使用castvar进行转型
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        context.close();

//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        第三步:初始化Bean!
//        第四步:使用Bean:com.powernode.spring6.bean.User@3270d194
//        第五步:销毁Bean!
    }
需要注意的:
      ●第⼀:只有正常关闭spring容器,bean的销毁⽅法才会被调⽤。
      ●第⼆:ClassPathXmlApplicationContext类才有close()⽅法。
      ●第三:配置⽂件中的init-method指定初始化⽅法。destroy-method指定销毁⽅法。

8.4 Bean⽣命周期之7

在以上的 5 步中,第 3 步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加⼊“Bean后处理器”。
编写⼀个类实现BeanPostProcessor类,并且重写before和after⽅法:
LogBeanPostProcessor类:
 
//日志Bean后处理器
public class LogBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行Bean后处理器的before方法!");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    //方法有两个参数
    //第一个参数:刚创建的Bean对象
    //第二个参数:bean的名字
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行Bean后处理器的after方法!");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }
}

spring.xml文件:

    <!--配置Bean后处理器-->
    <!--注意:这个Bean后处理器将作用于整个配置文件中所有的bean-->
    <bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
   @Test
    public void testBeanLifecycleFive(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println("第四步:使用Bean:"+user);

        //注意:必须手动关闭Spring容器,这样Spring容器才会销毁Bean
        //close()这个方法只有ClassPathXmlApplicationContext才有,而applicationContext没有,所以使用castvar进行转型
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        context.close();

//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        第三步:初始化Bean!
//        第四步:使用Bean:com.powernode.spring6.bean.User@3270d194
//        第五步:销毁Bean!


//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        执行Bean后处理器的after方法!
//        第三步:初始化Bean!
//        执行Bean后处理器的before方法!
//        第四步:使用Bean:com.powernode.spring6.bean.User@65fb9ffc
//        第五步:销毁Bean!
⼀定要注意:在spring.xml⽂件中配置的Bean后处理器将作⽤于当前配置⽂件中所有的Bean。
如果加上Bean后处理器的话,Bean的⽣命周期就是 7 步了:

8.5 Bean⽣命周期之10

如果根据源码跟踪,可以划分更细粒度的步骤, 10 步:

上图中检查Bean是否实现了Aware的相关接⼝是什么意思?

Aware相关的接⼝包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
      ● 当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。
      ● 当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。
      ● 当Bean实现了BeanFactoryAware,Spring会将Bean⼯⼚对象传递给Bean。
测试以上 10 步,可以让User类实现 5 个接⼝,并实现所有⽅法:
      ● BeanNameAware
      ● BeanClassLoaderAware
      ● BeanFactoryAware
      ● InitializingBean
      ● DisposableBean
User类:
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean,DisposableBean {
    private String name;

    public User(){
        System.out.println("第一步:无参数构造方法执行!");
    }

    public void setName(String name) {
        System.out.println("第二步:给对象的属性赋值!");
        this.name = name;
    }

    //这个方法需要自己写,自己配
    public void initBean(){
        System.out.println("第四步:初始化Bean!");
    }

    public void destroyBean(){
        System.out.println("第七步:销毁Bean!");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("Bean这个类的加载器:"+classLoader);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("生产这个Bean的工厂对象是:"+beanFactory);
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("这个Bean的名字是:"+name);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean 's afterPropertiesSet 执行!");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean 's destroy 执行!");
    }
}
  @Test
    public void testBeanLifecycleFive(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println("第六步:使用Bean:"+user);

        //注意:必须手动关闭Spring容器,这样Spring容器才会销毁Bean
        //close()这个方法只有ClassPathXmlApplicationContext才有,而applicationContext没有,所以使用castvar进行转型
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        context.close();

//        Bean⽣命周期之5步
//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        第三步:初始化Bean!
//        第四步:使用Bean:com.powernode.spring6.bean.User@3270d194
//        第五步:销毁Bean!

//        Bean⽣命周期之7步
//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        第三步:执行Bean后处理器的before方法!
//        第四步:初始化Bean!
//        第五步:执行Bean后处理器的after方法!
//        第六步:使用Bean:com.powernode.spring6.bean.User@78691363
//        第七步:销毁Bean!


        //Bean⽣命周期之10步
//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        这个Bean的名字是:user
//        Bean这个类的加载器:jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
//        生产这个Bean的工厂对象是:org.springframework.beans.factory.support.DefaultListableBeanFactory@4c15e7fd: defining beans [com.powernode.spring6.bean.LogBeanPostProcessor#0,user]; root of factory hierarchy
//        第三步:执行Bean后处理器的before方法!
//        InitializingBean 's afterPropertiesSet 执行!
//        第四步:初始化Bean!
//        第五步:执行Bean后处理器的after方法!
//        第六步:使用Bean:com.powernode.spring6.bean.User@3590fc5b
//        DisposableBean 's destroy 执行!
//        第七步:销毁Bean!



    }
Spring容器只对singleton的Bean进行完整的生命周期管理。如果是prototype作用域的Bean,Spring容器只负责将该Bean初始化完毕。等客户端程序一旦获取到该Bean之后,Spring容器就不再管理该对象的生命周期了。
通过测试可以看出来:
InitializingBean的⽅法早于init-method的执⾏。
DisposableBean的⽅法早于destroy-method的执⾏。

8.6 Bean的作⽤域不同,管理⽅式不同

Spring 根据Bean的作⽤域来选择管理⽅式。
对于singleton作⽤域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
⽽对于 prototype 作⽤域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其⽣命周期。
我们把之前User类的spring.xml⽂件中的配置scope设置为prototype:
    <!--选要手动指定初始化方法和销毁方法-->
    <bean id="user" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean" scope="prototype">
        <property name="name" value="猪猪侠"/>
    </bean>
 @Test
    public void testBeanLifecycleFive(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println("第六步:使用Bean:"+user);

        //注意:必须手动关闭Spring容器,这样Spring容器才会销毁Bean
        //close()这个方法只有ClassPathXmlApplicationContext才有,而applicationContext没有,所以使用castvar进行转型
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        context.close();

        //当我们修改成prototype时
//        第一步:无参数构造方法执行!
//        第二步:给对象的属性赋值!
//        这个Bean的名字是:user
//        Bean这个类的加载器:jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
//        生产这个Bean的工厂对象是:org.springframework.beans.factory.support.DefaultListableBeanFactory@4c15e7fd: defining beans [com.powernode.spring6.bean.LogBeanPostProcessor#0,user]; root of factory hierarchy
//        第三步:执行Bean后处理器的before方法!
//        InitializingBean 's afterPropertiesSet 执行!
//        第四步:初始化Bean!
//        第五步:执行Bean后处理器的after方法!
//        第六步:使用Bean:com.powernode.spring6.bean.User@3590fc5b
}
只执⾏了前 8 步,第 9 10 都没有执⾏。
Bean的生命周期五步:
    第一步:实例化Bean
    第二步:Bean属性赋值
    第三步:初始化Bean
    第四步:使用Bean
    第五步:销毁Bean

Bean生命周期七步:比五步添加的那两步在哪里?在初始化Bean的前和后。
    第一步:实例化Bean
    第二步:Bean属性赋值
    第三步:执行“Bean后处理器”的before方法。
    第四步:初始化Bean
    第五步:执行“Bean后处理器”的after方法。
    第六步:使用Bean
    第七步:销毁Bean

Bean生命周期十步:比七步添加的那三步在哪里?
    点位1:在“Bean后处理器”before方法之前
        干了什么事儿?
            检查Bean是否实现了Aware相关的接口,如果实现了接口则调用这些接口中的方法。
            然后调用这些方法的目的是为了给你传递一些数据,让你更加方便使用。

    点位2:在“Bean后处理器”before方法之后
        干了什么事儿?
            检查Bean是否实现了InitializingBean接口,如果实现了,则调用接口中的方法。

    点位3:使用Bean之后,或者说销毁Bean之前
        干了什么事儿?
            检查Bean是否实现了DisposableBean接口,如果实现了,则调用接口中的方法。

    添加的这三个点位的特点:都是在检查你这个Bean是否实现了某些特定的接口,如果实现了这些接口,则Spring容器会调用这个接口中的方法。



8.7 ⾃⼰new的对象如何让Spring管理

有些时候可能会遇到这样的需求,某个java对象是我们⾃⼰new的,然后我们希望这个对象被Spring容器管理,怎么实现?
Student类:
public class Student {

}
  @Test
    public void testRegisterBean(){
        //自己new对象
        Student student = new Student();
        System.out.println(student);//com.powernode.spring6.bean.Student@7907ec20

        //将以上自己new的这个对象纳入Spring容器来管理。半路上交给Spring来管理
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerSingleton("studentBean",student);

        //从Spring容器中获取
        Object studentBean = factory.getBean("studentBean");
        System.out.println(studentBean);//com.powernode.spring6.bean.Student@7907ec20
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值