• 7 -- Spring的基本用法 -- 8... 抽象Bean与子Bean;Bean继承与Java继承的区别;容器中的工厂Bean;获得Bean本身的id;强制初始化Bean


        7.8 深入理解容器中的Bean

          7.8.1 抽象Bean与子Bean

            把多个<bean.../>配置中相同的信息提取出来,集中成配置模版------这个配置模版并不是真正的Bean,因此Spring不应该创建该配置模版,于是需要为该<bean.../>配置增加abstract=“true” ------这就是抽象Bean。

            抽象Bean不能被实例化,Spring容器不会创建抽象Bean实例。抽象Bean的价值在于被继承,抽象Bean通常作为父Bean被继承。

            抽象Bean只是配置信息的模版,指定abstract=“true”即可阻止Spring实例化该Bean,因此抽象Bean可以不指定class属性。

            将大部分相同信息配置成抽象Bean之后,就实际Bean实例配置成该抽象Bean的子Bean即可。子Bean定义可以从父Bean继承实现类、构造器参数、属性值等配置信息,除此之外,子Bean配置可以增加新的配置信息,并可指定新的配置信息覆盖父Bean的定义。

            通过为一个<bean.../>元素指定parent属性即可指定该Bean是一个子Bean,parent属性指定该Bean所继承的父Bean的id。

            子Bean无法从父Bean继承如下属性:depends-on、autowire、singleton、scope、lazy-init,这些属性将总是从子Bean定义中获得,或采用默认值。

            Interface : Axe

    package edu.pri.lime._7_8_1.bean;
    
    public interface Axe {
        public String chop();
    }

            Class : SteelAxe

    package edu.pri.lime._7_8_1.bean.impl;
    
    import edu.pri.lime._7_8_1.bean.Axe;
    
    public class SteelAxe implements Axe{
    
        public String chop() {
            return "用钢斧砍柴真快";
        }
    
    }

            Interface : Person

    package edu.pri.lime._7_8_1.bean;
    
    public interface Person {
        public void useAxe();
    }

            Class : Chinese

    package edu.pri.lime._7_8_1.bean.impl;
    
    import edu.pri.lime._7_8_1.bean.Axe;
    import edu.pri.lime._7_8_1.bean.Person;
    
    public class Chinese implements Person{
    
        private String name;
        private Axe axe;
        public void useAxe() {
            System.out.println(name + " 说:" + axe.chop());
        }
        public Axe getAxe() {
            return axe;
        }
        public void setAxe(Axe axe) {
            this.axe = axe;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
    }

            Class : American

    package edu.pri.lime._7_8_1.bean.impl;
    
    import edu.pri.lime._7_8_1.bean.Axe;
    import edu.pri.lime._7_8_1.bean.Person;
    
    public class American implements Person{
    
        private String name;
        private Axe axe;
        public void useAxe() {
            System.out.println(name + " say: " + axe.chop());
        }
        public Axe getAxe() {
            return axe;
        }
        public void setAxe(Axe axe) {
            this.axe = axe;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
    }

            XML : 

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
        <!-- 定义Axe实例 -->
        <bean id="steelAxe" class="edu.pri.lime._7_8_1.bean.impl.SteelAxe" />
        <!-- 指定abstract=“true”定义抽象Bean -->
        <!-- 如果父Bean指定了class 属性,那么子Bean连class属性都可以省略,子Bean将采用与父Bean相同的实现类 -->
        <bean id="personTemplate" abstract="true">
            <property name="name" value="lime" />
            <property name="axe" ref="steelAxe" />
        </bean>
        <!-- 通过指定parent属性指定Bean配置可从父Bean继承得到配置信息 -->
        <bean id="chinese" class="edu.pri.lime._7_8_1.bean.impl.Chinese"
            parent="personTemplate" />
        <bean id="american" class="edu.pri.lime._7_8_1.bean.impl.American"
            parent="personTemplate">
            <property name="name" value="oracle" />
        </bean>
    </beans>

            Class : SpringTest

    package edu.pri.lime._7_8_1.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_8_1.bean.Person;
    import edu.pri.lime._7_8_1.bean.impl.American;
    import edu.pri.lime._7_8_1.bean.impl.Chinese;
    
    public class SpringTest {
    
        public static void main(String[] args){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_8_1.xml");
            Person chinese = (Person) ctx.getBean("chinese",Chinese.class);
            Person american = (Person) ctx.getBean("american",American.class);
            chinese.useAxe();
            american.useAxe();
        }
    }

            Console : 

    lime 说:用钢斧砍柴真快
    oracle say: 用钢斧砍柴真快

          7.8.2 Bean继承与Java继承的区别

            Spring中的Bean继承与Java中的继承中,Spring中的Bean继承是实例与实例之间参数值的延续,Java中的继承是一般到特殊的细化;Spring中的Bean继承是对象与对象之间的关系,Java中的继承是类与类之间的关系。

            区别:

              ⊙ Spring中的子Bean和父Bean可以是不同类型,但Java中的继承则可保证子类是一种特殊的父类。

              ⊙ Spring中Bean的继承是实例之间的关系,因此主要表现为参数值的延续;而Java中的继承是类之间的关系,主要表现为方法、属性的延续。

              ⊙ Spring中的子Bean不可作为父Bean使用,不具备多态性;java中的子类实例完全可当成父类实例使用。

            说到底,Spring中的Bean继承与Java中的继承其实就是两码事。表混淆就行了。

          7.8.3 容器中的工厂Bean

            Spring容器中的工厂Bean是一种特殊的Bean,必须实现FactoryBean接口。

            FactoryBean接口是工厂Bean的标准接口,把工厂Bean(实现FactoryBean接口的Bean)部署在容器中之后,如果程序通过getBean()方法来获取它时,容器返回的不是FactoryBean实现类的实例,而是返回FactoryBean的产品(即该工厂Bean的getObject()方法的返回值).

            FactoryBean接口中的方法:

              ⊙ T getObject() : 实现该方法负责返回该工厂Bean生成的Java实例。

              ⊙ Class<?> getObjectType() : 实现该方法返回该工厂Bean生成的Java实例的实现类。

              ⊙ boolean isSingleton() : 实现该方法表示该工厂Bean生成的Java实例是否为单例模式。

            程序把获取指定类的、指定类变量的实现逻辑放在getObject()方法中即可。

            Class : GetFieldFactoryBean

    package edu.pri.lime._7_8_3.bean.impl;
    
    import java.lang.reflect.Field;
    
    import org.springframework.beans.factory.FactoryBean;
    
    public class GetFieldFactoryBean implements FactoryBean<Object>{
    
        private String targetClass;
        private String targetField;
        
    //    负责返回工厂Bean生成的Java实例
        public Object getObject() throws Exception {
    //        froName(String) : 返回与带有给定字符串名的类或接口相关联的 Class 对象。
            Class<?> clazz = Class.forName(targetClass);
    //        getField(String) : 返回一个指定名称的公共属性
            Field field = clazz.getField(targetField);
    //        get(Object) : 返回指定对象上此Field表示的字段的值
    //        为什么get(null):静态属性直接通过.get(null)获取
            Object obj = field.get(null);
            return obj;
        }
        public Class<? extends Object> getObjectType() {
    //        哪里来的Object?
            return Object.class;
        }
    //    返回该工厂Bean所生产的产品是否为单例
        public boolean isSingleton() {
    //        非单例Bean
            return false;
        }
        public String getTargetClass() {
            return targetClass;
        }
        public void setTargetClass(String targetClass) {
            this.targetClass = targetClass;
        }
        public String getTargetField() {
            return targetField;
        }
        public void setTargetField(String targetField) {
            this.targetField = targetField;
        }
    }

            XML : 

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
        <!-- 
        下面配置相当于如下代码: 
        FactoryBean factory = new edu.pri.lime._7_8_3.bean.impl.GetFieldFactoryBean(); 
        factory.setTargetClass("java.awt.borderLayout"); facotry.setTargetField("NORTH"); 
        north = factory.getObject();
         -->
        <bean id="north" class="edu.pri.lime._7_8_3.bean.impl.GetFieldFactoryBean">
            <property name="targetClass" value="java.awt.BorderLayout"/>
            <property name="targetField" value="NORTH"/>
        </bean>
        <bean id="theValue" class="edu.pri.lime._7_8_3.bean.impl.GetFieldFactoryBean">
            <property name="targetClass" value="java.sql.ResultSet"/>
            <property name="targetField" value="TYPE_SCROLL_SENSITIVE"/>
        </bean>
    </beans>

            Class : SpringTest

    package edu.pri.lime._7_8_3.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class SpringTest {
    
        public static void main(String[] args){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_8_3.xml");
    //        获取FactoryBean的产品
            System.out.println(ctx.getBean("north"));
            System.out.println(ctx.getBean("theValue"));
    //        获取FactoryBean本身
    //        在Bean id前增加&符号,会让Spring返回FactoryBean本身
            System.out.println(ctx.getBean("&theValue"));
        }
    }

            Spring容器会自动检测容器中的所有Bean,如果发现某个Bean实现类实现了FactroyBean接口,Spring容器就会在实例化该Bean、根据<property.../>执行setter方法之后,额外调用该Bean的getObject()方法,并将该方法的返回值作为容器中的Bean。

            Spring框架提供的FieldRetrievingFactoryBean,实现原理与GetFieldFactoryBean的原理基本相同。

          7.8.4 获得Bean本身的id

            对于实际的Java应用而言,Bean与Bean之间的关系是通过依赖注入管理的,通常不会通过调用容器的getBean()方法来获取Bean实例。可能的情况是,应用中已经获得了Bean实例的引用,但程序无法知道配置该Bean时指定的id,可是程序又确实需要获取配置该Bean时指定的id属性。

            除此之外,当程序员在开发一个Bean类时,该Bean何时被部署到Spring容器中,部署到Spring容器时所指定的id是什么,开发该Bean类的程序员无法提前预知。

            在某些极端情况下,业务要求程序员在开发Bean类时能预先知道该Bean的配置id,此时可借助Spring提供的BeanNameAware接口,通过该接口即可提前预知该Bean的配置id。

            BeanNameAware接口提供了一个方法:setBeanName(String name),该方法的name参数就是Bean的id,实现该方法的Bean类就可以通过该方法来获取部署该Bean的id了。

            Class : Chinese

    package edu.pri.lime._7_8_4.bean;
    
    import org.springframework.beans.factory.BeanNameAware;
    
    //Spring容器会检测容器中的所有Bean,如果发现某个Bean实现了BeanNameAware接口,
    //Spring容器就会在创建该Bean之后,自动调用该Bean的setBeanName()方法,
    //调用该方法时,会将该Bean的配置id作为参数传给该方法------
    //该方法的实现部分将SPring容器的参数(Bean的配置id)赋给该Chinese对象的name实例变量,
    //之后可通过该name实例变量来访问容器本身
    public class Chinese implements BeanNameAware {
    
        private String name;
    
        public void setBeanName(String name) {
            this.name = name;
        }
    
        public void info() {
            System.out.println("Chinese实现类" + ",部署该Ben时指定的id为:" + name);
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

            XML :

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 配置文件的根元素,使用Spring-beans-4.0.xsd语义约束 -->
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    
        <bean id="chinese" class="edu.pri.lime._7_8_4.bean.Chinese" />
    
    </beans>

            Class : SpringTest

    package edu.pri.lime._7_8_4.main;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import edu.pri.lime._7_8_4.bean.Chinese;
    
    //Spring容器初始化Chinese Bean时回调setBeanName()方法,回调该方法时,
    //该Bean的配置id将会作为参数传给beanName实例变量。
    public class SpringTest {
        public static void main(String[] args){
            ApplicationContext ctx = new ClassPathXmlApplicationContext("app_7_8_4.xml");
            Chinese chinese = ctx.getBean("chinese",Chinese.class);
            chinese.info();
        }
    
    }

          7.8.5 强制初始化Bean

            在大多数情况下,Bean之间的依赖非常直接,Spring容器在放回Bean实例之前,先要完成Bean依赖关系的注入。

            在极端情况下,Bean之间的依赖不够直接。比如:某个类的初始化块中使用其他Bean,当初始化块时,被依赖Bean可能还没有被实例化,此时将会引发异常。

            为了显示指定被依赖Bean在目标Bean之前初始化,可以使用depends-on属性,该属性可以在初始化主调Bean之前,强制初始化一个或多个Bean。

        <bean id="chinese" class="edu.pri.lime._7_8_4.bean.Chinese" depends-on="steelAxe"/>
        <bean id="steelAxe" class="edu.pri.lime._7_8_5.bean.SteelAxe"/>

    啦啦啦

  • 相关阅读:
    【CQOI2015】网络吞吐量
    【SDOI2010】所驼门王的宝藏
    【NOIP2013】华容道
    【SNOI2019】通信
    【IOI2016】railroad
    【AtCoder3611】Tree MST
    【AtCoder2134】ZigZag MST
    【CF891C】Envy
    【BZOJ4883】棋盘上的守卫
    【CF888G】Xor-MST
  • 原文地址:https://www.cnblogs.com/ClassNotFoundException/p/6262540.html
Copyright © 2020-2023  润新知