美文网首页
Spring入门笔记

Spring入门笔记

作者: 渐丶忘 | 来源:发表于2018-11-04 01:58 被阅读0次

    Spring入门

    使用Spring容器

    Spring有两个核心接口,BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。它们都可代表Spring容器,Spring容器是生成Bean实例的工厂,并管理容器中的Bean.

    Spring容器

    BeanFactory是Spring最基本的容器,包含如下几个基本方法:

    // 判断Spring容器中是否包含id为name的Bean实例
    boolean containsBean(String name);
    // 获取Spring容器中输入requiredType类型的、唯一的Bean实例
    <T> T getBean(Class<T> requiredType);
    // 返回容器id为name的Bean实例
    Object getBean(String name);
    // 返回容器中id为name,并且类型类requiredType的Bean
    <T> T getBean(String name,class<T> requiredType);
    // 返回容器中id为name的Bean实例的类型
    

    BeanFactory常用的实现类为DefaultListableBeanFactory

    // 搜索类加载路径下的beans.xml文件创建Resource对象
    Resource isr = new ClassPathResource("beans.xml");
    DefaultListableBeanFactory beanFactory =  new DefaultListableBeanFactory;
    new XmlBeanDefinitionReader(beanFactor).loadBeanDefinitions(isr);
    

    如果应用需要加载多个配置文件来创建Spring容器,则应该采用BeanFactory的子接口ApplicationContext来创建BeanFactory的实例。ApplicationContext接口包含FileSystemXmlApplicationContext和ClassXmlApplicationContext两个常用类,分别代表从类路径加载配置文件和从文件系统加载配置文件。

    ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml", "server.xml");
    

    使用ApplicationContext

    ApplicationContext支持BeanFactory的全部功能,还支持如下额外功能:

    • ApplicationContext默认会初始化所有的singleton Bean,也可以通过配置取消初始化
    • ApplicationContext继承MessageSource接口,提供国际化支持
    • 同时加载多份配置文件
    • 事件机制
    • 以声明方式启动并创建Spring容器

    当系统创建ApplicationContext容器时,默认会预初始化所有的singleton Bean,所以系统前期创建ApplicationContenxt将消耗较大的系统开销,但一旦初始化完成,程序后面获取singleton Bean实例会有较好的性能。

    BeanFactory在创建时并不会初始化所有的Bean,只有在getBean方法显式调用时才会初始化Bean。

    ApplicationContext包括BeanFactory的全部功能,因此建议优先使用ApplicationContext。除非对于哪些内存非常关键的引用,才考虑使用BeanFactory.

    无可阻止Spring容器预初始化容器中的singleton Bean,可以为<bean.../>元素指定lazy-init="true",该属性用于阻止预初始化该Bean

    ApplicationContext的事件支持

    ApplicationContext的事件机制是观察者模式的实现。如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListeners Bean将自动触发

    Spring的事件框架依赖如下两个重要成员:

    • ApplicationEvent:容器事件,必须由ApplicationContext发布
    • ApplicationListener:监听器,可由容器中的任何监听器Bean担任

    只要一个Java类继承了ApplicationEvent基类,那该对象就可以作为Spring容器的容器事件。

    容器事件的监听器类必须实现ApplicationListener接口,该接口必须实现如下方法:

    • onApplicationEvent(ApplicationEvent event):每当容器内发生任何事件时,该方法自动被触发

    在配置文件配置监听类Bean时,可省略id属性,Spring容器会自动识别该监听器

    当系统创建Spring容器、加载Spring容器时会触发容器事件,容器事件监听器可以监听到这些事件。程序也可调用ApplicaionContext的publicEvent()方法来主动触发容器事件。
    如果开发者需要在Spring容器初始化、销毁时回调自定义方法,就可以通过事件监听器来实现。

    注:如果希望Bean发布容器事件,则必须让该Bean实现ApplicationContextAware或BeanFactoryAware接口,以获得对容器的引用。

    让Bean获取Spring容器

    为了让Spring获取它所在的Spring容器,可以让Bean实现BeanFactoryAware接口,实现如下方法:

    setBeanFactory(BeanFacotry beanFactory); //方法中的参数指向创建它的BeanFactory
    

    类似的还有ApplicationContextAware接口,需实现的方法如下:

    setApplicationContext(ApplicationContext context); //方法中的参数指向创建它的ApplicationContext
    

    Spring容器会检查所有的Bean,如果发现某个Bean实现了如上接口,将自己调用该Bean的setBeanFactory或setApplicaionContext方法将自身作为参数传递给该方法。

    容器中的Bean

    Bean的基本定义和Bean别名

    <beans.../>元素是Spring配置文件的根元素,可以指定如下属性

    • default-lazy-init:指定该<beans.../>元素下配置的所有Bean默认的延迟初始化行为
    • default-merge:指定该<beans.../>元素下配置的所有Bean的merge行为
    • default-autowire:指定该<beans.../>元素下配置的所有Bean默认的自动装配行为
    • default-autowire-candidates:指定该<beans.../>元素下配置的所有Bean默认是否作为自动自动装配的候选Bean
    • default-init-method:指定该<beans.../>元素下配置的所有Bean默认的初始化方法
    • default-destory-method:指定该<beans.../>元素下配置的所有Bean默认的回收方法

    在单个Bean中应用,只需将defalut去掉便可以应用于该Bean,<bean.../>下指定的属性会覆盖<beans.../>下指定的属性。

    在定义Bean时,通常需指定如下两个属性:

    • id:确定该Bean在Spring容器中的唯一标识,容器对Bean的管理、访问,以及该Bean的依赖关系都依赖于此属性
    • class:指定该Bean的具体实现类,不能是接口。

    id属性默认不能包含特殊字符,如*@/等。可通过name属性指定别名使用

    • name:该属性指定一个Bean示例的标识名,表明将为该Bean示例指定别名
    • alias:指定一个别名
    <bean id="person" class="..." name="#abc,@123,abc*"/>
    <alias name="person" alias="jack"/>
    <alias name="jack" alias="jackee"/>
    

    容器中Bean的作用域

    Spring支持如下6种作用域:

    • singleton:单例模式,在整个SpringIoC容器中,该实例只生成一次。默认属性
    • prototype:每次通过同期的getBean()方法获取prototype作用域的Bean时,都会生成新的实例。
    • request:同一次请求中获取的Bean只生成一次
    • session:同一个会话中获取的Bean只生成一次
    • application:对于整个Web应用中获取的Bean为相同的实例
    • websocket:在整个WebSocket的通信过程中,只生成一个实例

    常用的为singleton和pototype作用域,其它四种作用域只应用于Web环境中。
    对于singleton作用域实例,由容器负责跟踪Bean实例的状态,负责该Bean的整个生命行为。对于prototype作用域的的Bean,容器只负责生成,一旦创建成功便不再跟踪

    配置依赖

    • 设值注入:通过<property.../>元素驱动Spring执行setter方法
    • 构造注入:通过<constructor-arg.../>元素驱动Spring执行带参数的构造器

    通常不建议使用配置文件管理Bean的基本类型的属性值;通常只使用配置文件管理容器中Bean之间的依赖关系。
    通过<constructor-arg.../>配置依赖时时根据参数顺序进行注入依赖,也可通过index属性显式指定参数顺序:

    <bean id="Person" class="com.learn.Person">
      <constructor-arg index="1" value="35"/>
      <constructor-arg index="0" value="张三"/>
    </bean>
    

    设置普通属性值

    <value.../>元素用于指定基本类型及其包装、字符串类型的参数值,Spring通过XML解析器来解析出这些数据,然后利用java.beans.PropertyEditor完成类型转换:从String类型转换为所需的参数类型

    配置合作者Bean

    <ref.../>元素用于指定所依赖的其它Bean,指定该Bean的id属性值

    使用自动装配注入合作者Bean

    autowire、default-autowire接收如下参数值:

    • no:不使用自动装配。Bean依赖必须通过ref显式配置。默认配置,不建议更改
    • byName:根据setter方法名进行自动装配。Spring容器查找容器中的全部Bean,找出其id与setter方法名去掉set前缀,并小写首字母后同名的Bean来完成注入。如没有找到,则不会进行任何注入。
    • byType:根据setter方法的形参类型来自动匹配。Spring容器查找容器中的全部Bean,如果正好有一个Bean类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这样的Bean,则抛出异常;如果没有找到,则什么都不会发生。
    • constructor:与byType类型,区别是用于自动匹配构造器的参数
    • autodetect:Spring容器根据Bean内部结构,自行决定使用constructor或byType策略。如果找到一个默认的构造参数,那么就会应用byType策略。

    当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则显式指定的依赖覆盖自动装配的依赖。

    如果希望将某些Bean排除在自动装配之外,不作为Spring自动装配策略的候选者,可<bean.../>设置该Bean的autowire-candidate属性为false。还可以通过在<beans.../>中指定default-autowire-candidates属性将一批Bean排除在自动装配之外(该属性值允许使用模式字符串,可指定多个模式字符串)。

    自动装配将Bean之间的耦合关系降低到代码层次,不利于高层次解耦;降低了依赖关系的透明性和清晰性。故在大型项目中不推荐使用自动装配。

    注入嵌套Bean

    如果某个Bean所依赖的Bean不想被Spring容器直接访问,则可以使用嵌套Bean。
    <beans.../>配置成<property.../><constructor-ages.../>的子元素,那么该<beans.../>元素配置的Bean仅仅作为setter注入、构造注入的参数。由于容器不能获取嵌套Bean,因此可不需指定id属性。

    <bean id="chinese" class="org.learn.impl.Chinese">
      <property name="axe">
        <bean class="org.learn.impl.SteelAxe"/>
      </property>
    </bean>
    

    注入集合值

    如果需要使用形参为集合的setter方法,或调用形参为集合的构造器,则可使用集合元素<list.../>、<set.../>、<map.../>、<props.../>分别来设置类型为List、Set、Map和Properties的集合参数值。

    <bean id="chinese" class="org.learn.impl.Chinese">
      <property name="schools">
        <list>
          <value>小学</value>
          <value>中学</value>
          <value>大学</value>
        </list>
      </property>
      <property name="scores">
        <map>
          <entry key="数学" value="87"/>
          <entry key="英语" value="90"/>
        </map>
      </property>
      <property name="phaseAxes">
        <map>
          <entry key="原始社会" value-ref="stoneAxe"/>
          <entry key="农业社会" value="steelAxe"/>
        </map>
      </property>
      <property name="health">
        <props>
          <prop key="血压"/>正常</prop>
          <prop key="身高"/>175</prop>
        </props>
      </property>
      <property name="axes">
        <set>
          <value>字符串</value>
          <bean class="..." />
          <ref bean="stoneAxe"/>
        </set>
      </property>
    </bean>
    

    <list.../><set.../><key../>可接受如下子元素:

    • value:指定集合元素是基本数据类型或字符串类型值
    • ref:指定集合元素是容器中的另一个Bean示例
    • bean:指定集合元素是一个嵌套Bean
    • list、set、map及props:指定集合元素又是集合

    <props.../>元素用于配置Properties类型的参数值,其key和value只能是字符串

    <map.../>元素的<entry.../>子元素配置一组键值对,其支持如下属性:

    • key:如果Map key是基本类型或字符串
    • key-ref:如果Map key是容器中的另外一个Bean实例,使用该属性指定引用Bean的id
    • value:如果Map value是基本类型或字符串
    • value-ref:如果Map value是容器中的另外一个Bean实例,使用该属性指定引用Bean的id

    SpringIoc容器支持集合的合并,子Bean中的集合属性值可以从其父Bean中的集合属性值继承和覆盖而来

    <beans>
      <bean id="parent" abstract="true" class="org.learn.impl.ComplexObject">
        <property name="adminEmails">
          <props>
            <prop key="admin"/>admin@163.com</prop>
            <prop key="support"/>support@163.com</prop>
          </props>
        </property>
      </bean>
      <bean id="child" parent="parent">
        <property name="adminEmails">
          <props>
            <prop key="sales"/>sales@163.com</prop>
            <prop key="support"/>support@163.com</prop>
          </props>
        </property>
      </bean>
    </beans>
    

    此时child Bean的adminEmails属性值通过继承和覆盖变为三个

    组合属性

    <bean id="chinese" class="org.learn.impl.Chinese">
      <property name="foo.bar.x.y" value="xxxx"/>
    </bean>
    

    对于这种注入组合属性值的形式,组合属性只有最后一个属性才调用setter方法,前面各属性实际上对应于调用getter方法。故除最后一个属性外,其他属性均不能为null

    Spring提供的Java配置管理

    @Configuration
    public class AppConfig
    {
      @Value("孙悟空") String personName;
      
      @Bean("name=chinese")
      public Person person()
      {
        Chinese p = new Person();
        p.setName(personName);
        p.setAxe(stoneAxe());
        return p;
      }
      
      @Bean(name="stoneAxe")
      public Axe stoneAxe()
      {
        return new StoneAxe();
      }
      
      @Bean(name="steelAxe")
      public Axe steelAxe()
      {
        return new SteelAxe;
      }
    }
    
    • @Configuration:用于修饰一个Java配置类
    • @Bean:用于修饰一个方法,将该方法的返回值定义成容器的一个Bean,可通过那么属性指定该 Bean 的 Id,name 属性可省略Id默认为方法名
    • @Value:用于修饰一个Field,用于为该Field配置一个值,相当于配置一个变量

    一旦使用了Java配置类来管理Spring容器中的Bean及其依赖关系,此时就需要如下方式来创建Spring容器:

    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    
    • @Import:修饰一个Java配置类,用于向当前Java配置类中导入其他Java配置类
    • @Scope:用于修饰一个方法,指定该方法对应的Bean的作用域
    • @Lazy:用于修饰一个方法,指定该方法对应的Bean是否需要延迟初始化
    • @DependsOn:用于修饰一个方法,指定在初始化该方法对应的Bean之前先初始化指定的Bean

    可混合使用XML配置文件和Java配置类

    1. 如果以XML配置为主,就需要让XML配置文件能加载Java类配置。在XML中增加如下代码:
    <!-- 导入其他 Bean 配置 -->
    <import resource="beans.xml"/>
    <context:annotation-config/>
    <!-- 加载Java配置类-->
    <bean class="org.learn.app.config.AppConfig"/>
    
    1. 如果以Java配置为主,就需要让Java配置类能加载XML配置。需要使用@ImportResource注解,导入指定的XML配置文件:
     @Configuration
     @ImportResource("classpath:/beans.xml")
     public class AppConfig
     {
       .....
     }
    

    创建Bean的3种方式

    使用构造器创建Bean实例

    使用静态工厂创建Bean

    使用静态工厂方法创建Bean实例时,class属性指向静态工厂类,Spring通过该属性知道是哪个工厂类来创建Bean实例。使用factory-method指定工厂方法,此方法必须是静态的。如果静态工厂需要参数,则使用<constructor-arg.../>元素传入。

    <bean id="dog" class="org.learn.app.BeingFactory" factory-method="getBeing">
        <!-- 配置静态工厂方法的参数 -->
        <constructor-age value="dog"/>
        <!-- 驱动Spring以“我是狗”为参数来执行dog的setMsg方法 -->
        <property name="msg" value="我是狗"/>
    </bean>
    

    在这个过程中,Spring不再负责创建Bean实例,Bean实例时由用户提供的静态工厂类提供创建的。当静态工厂方法创建了Bean实例后,Spring依然可以管理该Bean实例的的依赖关系,包括为其注入所需的依赖Beean、管理其生命周期等。

    调用实例工厂方法创建Bean

    使用实例工厂方法时,配置Bean实例的<bean.../>元素无须class属性,因为Spring容器不再直接实例化该Bean,Spring容器仅仅调用实例工厂的工厂方法,工厂方法负责创建Bean实例。需指定如下参数:

    • factory-bean:该属性的值为工厂Bean的id
    • factory-method:该属性指定实例工厂的工厂方法

    如果需要在调用实例方法时传入参数,则使用<constructor-arg...>元素指定参数值

    <bean id="personFactory" class="org.learn.factory.PersonFactory"/>
    <bean id="chinese" factory-method="personFactory" factory-method="getPerson">
        <constructor-arg value="chin"/>
    </bean>
    </bean>
    

    深入理解容器中的Bean

    抽象Bean与子Bean

    将多个<bean.../>配置中相同的信息提取出来,指定<bean.../>中的abstract属性为tru,集中成配置模板---这个模板不是真正的Bean,不能被实例化。抽象Bean的价值在于被继承,抽象Bean通常作为父Bean被继承。抽象Bean可不指定class属性。

    <beans>
        <bean id="personTemp" abstract="true">
            <property name="name" value="张三"/>
            <property name="axe" ref="steelAxe"/>
        </bean>
        <bean id="chinese" class="org.learn.impl.Chinese" parent="personTemp"/>
        <bean id="american" class="org.learn.impl.Chinese" parent="personTemp"/>
    

    当子Bean指定的配置信息与父Bean模板所指定的配置信息不一致时,子Bean所指定的配置信息将覆盖父Bean所指定的配置信息。子Bean无法从父Bean中继承如下属性:depends-on、autowire、singleton、scope、lazy-init。
    如果父Bean指定了class属性,那么子Bean连class属性都可以省略,子Bean将采用与父Bean相同的实现类。

    注:此处的抽象与继承和Java中的抽象继承无关,只是配置文件模板化、复用。

    容器中的工厂Bean

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

    T getObject(); //实现该方法负责返回该工厂Bean生成的Java实例
    Class<?> getObjectType(); //实现该方法返回该工厂Bean生成的Java实例的实现类
    boolean isSingleton(); //实现该方法表示该工厂Bean生成的Java实例是否为单例模式 
    

    配置FactoryBean与配置普通Bean的定义没有区别,但当程序向Spring容器请求该Bean时,容器返回该BeanFactoryBean的产品,而不是返回该FactoryBean本身。
    当程序需要获得FactoryBean本身时,并不是直接请求Bean id,而是在Bean id前增加&符号,容器则返回FactoryBean本身,而不是它生产的Bean。

    获得Bean自身的id

    如果开发Bean时需要预知该Bean的配置id,则可实现BeanNameAware接口,通过该接口即可提前预知该Bean的配置id。BeanNameAware提供如下方法:

    setBeanName(String name); //该方法的name参数就是Bean的配置id
    

    此接口的setter方法会由Spring容器自动调用,将部署该Bean的id属性作为参数传入。

    强制初始化Bean

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

    依赖关系注入之后的行为

    Spring提供两种方式在Bean全部属性设置成功后执行特定行为:

    • 使用init-method属性
      使用init-method属性指定某个方法在Bean全部依赖关系设置结束后自动执行。使用这种方式不需要将代码将Spring的接口耦合在一起,代码污染小。推荐使用
    • 实现InitializingBean接口
      达到同样的效果,要求Bean必须继承InitializingBean接口,该接口提供一个方法 ---void afterPropertiesSet() throws Excepion;>

    如果同时使用两种方式,则Spring容器先执行InitializingBean接口中定义的方法,然后执行init-method属性指定的方法。

    Bean销毁之前的行为

    Spring同样提供两张方式定制Bean实例销毁之前的特定行为:

    • 使用destory-method属性
      使用destory-method属性指定某个方法在Bean销毁之前自动执行。使用这种方式不需要将代码将Spring的接口耦合在一起,代码污染小。推荐使用
    • 实现DisposableBean接口
      可以实现同样的效果,但必须实现DisposableBean接口,该接口提供void destory() throw Exception;

    如果同时使用两种方式,则Spring容器先执行DisposableBean接口中定义的方法,然后执行destory-method属性指定的方法。

    如果处于一个非Web应用的环境下,为了让Spring容器优雅地关闭,并调用singleton Bean相应的析构回调方法,则需要在JVM里注册一个关闭钩子。

    public class BeanTest
    {
        public static void main(String[] args)
        {
            AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
            Person p = ctx.getBean("chinese", Person.class);
            p.useAxe();
            
            //为Spring容器注册关闭钩子
            ctx.registerShutdownHook();
        }
    }
    

    如果在<beans.../>中指定了default-init-method="init",意味着只要Spring容器中的Bean实例具有init()方法,Spring就会在该Spring的所有依赖关系被设置之后,自动调用该Bena实例的init方法。

    协调作用域不同的步的Bean

    当prototype作用域的Bean依赖singleton作用域的Bean时,使用Spring提供的依赖注入进行管理即可。
    当singleton作用域的Bean依赖prototype作用域的Bean时,一旦singleton Bean初始化完成,它就持有了一个prototype Bean,容器再也不会为singleton Bean执行注入了。解决此问题有如下方法:

    • 放弃依赖注入:singleton作用域的Bean每次需要prototype作用域的Bean时,主动向容器请新的Bean实例,即可保证每次注入的prototype Bean实例都是最新的实例
    • 利用方法注入

    使用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个non-singleton Bean(尽管可以是一个singleton的)。Spring通过使用JDK动态代理或cglib库修改客户端的二进制码,从而实现上述要求。
    为了实现lookup方法注入,大致需要如下两步:

    1. 将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean
    2. 在<bean.../>元素中添加<lookup-method.../>子元素让Spring为调用者Bean的实现类实现指定的抽象方法。

    <lookup-method.../>元素需要如下两个属性:

    • name:指定需要让Spring实现的方法
    • bean:指定Spring实现该方法的返回值
    public abstract class Chinese 
    {
        private Dog dog;
        public abstract Dog getDog();
        public void hunt()
        {
            getDog().run();
        }
    }
    
    <bean id="personTemp" abstract="true">
        <!-- Spring只要检测到lookup-method元素,Spring会自动为该元素的name属性指定的方法提供实现体 -->
        <lookup-method name="getDog" bean="gunDog"/>
    </bean>
    <bean id="gunDog" class="org.leanrn.impl.GunDog" scope="prototype">
        <property name="name" value="旺财"/>
    </bean>
    

    Spring会采用运行时动态增强的方式来实现<lookup-method.../>元素所指定的抽象方法,如果目标抽象类实现过接口,Spring会采用JDK动态代理来实现该抽象类,并为之实现抽象方法;如果目标抽象类没有实现过接口,Spring会采用cglib实现该抽象类,并为之实现抽象方法。

    要保证<lookup-method.../>方法注入每次都产生新的Bean实例,必须将目标Bean部署成prototype作用于;否则,如果容器中只有一个被依赖的Bean实例,即使采用lookup方法注入,每次也依然返回同一个Bean实例。

    高级依赖关系配置

    获取其他Bean的属性值

    PropertyPathFactoryBean用来获取目标Bean的属性值(实际上就是它的getter方法的返回值),获得的值可注入其他Bean,也可直接定义成新的Bean。使用PropertyPathFactoryBean调用其他Bean的getter方法需要指定如下信息:

    调用哪个对象。由PropertyPathFactoryBean的setTargetObject(Object targetObject)方法指定;
    调用哪个getter方法。由PropertyPathFactoryBean的setPropertyPath(String propertyPath)方法指定;

    <!-- target bean to be referenced by name -->
     <bean id="tb" class="org.springframework.beans.TestBean" singleton="false">
       <property name="age" value="10"/>
       <property name="spouse">
         <bean class="org.springframework.beans.TestBean">
           <property name="age" value="11"/>
         </bean>
       </property>
     </bean>
    
     <!-- will result in 12, which is the value of property 'age' of the inner bean -->
     <bean id="propertyPath1" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
       <property name="targetObject">
         <bean class="org.springframework.beans.TestBean">
           <property name="age" value="12"/>
         </bean>
       </property>
       <property name="propertyPath" value="age"/>
     </bean>
    
     <!-- will result in 11, which is the value of property 'spouse.age' of bean 'tb' -->
     <bean id="propertyPath2" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
       <property name="targetBeanName" value="tb"/>
       <property name="propertyPath" value="spouse.age"/>
     </bean>
    
     <!-- will result in 10, which is the value of property 'age' of bean 'tb' -->
     <bean id="tb.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
    

    可以使用<util:property-path...>作为PropertyPathFactoryBean的简化配置,需要如下两个属性:

    • id:该属性指定将getter方法的返回值定义成名为id的Bean的实例
    • path:该属性指定将哪个Bean实例、哪个属性(支持复合属性)暴露出来
     <!-- will result in 10, which is the value of property 'age' of bean 'tb' -->
     <util:property-path id="name" path="testBean.age"/>
    

    PropertyPathFactoryBean就是工厂Bean,在这种配置方式下,配置PropertyPathFactoryBean工厂Bean时指定的id属性,并不是该Bean的唯一标识,而是用于指定属性表达式的值。

    获取Field值

    通过FieldRetrievingFactoryBean类,可访问类的静态Field或对象的实例Field值。FieldRetrievingFactoryBean获得指定Field的值之后,即可将获取的值注入其他Bean,也可直接定义成新的Bean。使用FieldRetrievingFactoryBean分一下两种情况:

    • 如果要访问的Field是静态Field,则需要指定:

    调用哪个类。由FieldRetrievingFactoryBean的setTargetClass(String targeClass)方法指定;
    访问哪个Field。由FieldRetrievingFactoryBean的setTargetField(String targetField)方法指定。

    • 如果要访问的Field是实例Field(要求实例Field以public修饰),则需要指定:

    调用哪个对象。由FieldRetrievingFactoryBean的setTargetObject(Object targetObject)方法指定
    访问哪个Field。由FieldRetrievingFactoryBean的setTargetField(String targetField)方法指定。
    FieldRetrievingFactoryBean还提供了一个setStaticField(String staticField)方法,该方法可同时指定获取哪个类的哪个静态Field的值。

     // standard definition for exposing a static field, specifying the "staticField" property
     <bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
       <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
     </bean>
    
     // convenience version that specifies a static field pattern as bean name
     <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
           class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
    

    <util:constant.../>元素可作为FieldRetrievingFactoryBean访问静态Field的简化配置,使用该元素时可指定如下两个属性:

    • id:该属性指定将静态Field的值定义成名为id的Bean实例
    • static-field:该属性指定访问哪个类的哪个静态Field
    <util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
    

    获取方法返回值

    通过MethodInvokingFactoryBean工厂Bean可以调用任意类的类方法,也可调用任意对象的实例方法,如果调用的方法有返回值,既可将该指定方法的返回值定义成容器中的Bean,也可将指定方法的返回值注入给其他Bean。使用MethodInvokingFactoryBean分一下两种情况:

    • 如果希望调用的方法时静态方法,则需要指定:

    调用哪个类。通过MethodInvokingFactoryBean的setTargetClass(String targetClass)方法指定;
    调用哪个方法。通过MethodInvokingFactoryBean的setTargetMethod(String targetMethod)方法指定;
    调用方法的参数。通过MethodInvokingFactoryBean的 setArguments(Object[] arguments)方法指定。

    • 如果希望调用的方法是实例方法,则需要指定:

    调用哪个对象。通过MethodInvokingFactoryBean的 setTargetObject(Object targetObject)指定;
    调用哪个方法。通过MethodInvokingFactoryBean的setTargetMethod(String targetMethod)方法指定;
    调用方法的参数。通过MethodInvokingFactoryBean的 setArguments(Object[] arguments)方法指定。

    bean定义的一个示例(在基于XML的bean工厂定义中),它使用此类来调用静态工厂方法:

     <bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
       <property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
     </bean>
    

    调用静态方法然后使用实例方法获取Java系统属性的示例

     <bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
       <property name="targetClass" value="java.lang.System"/>
       <property name="targetMethod" value="getProperties"/>
     </bean>
    
     <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
       <property name="targetObject" ref="sysProps"/>
       <property name="targetMethod" value="getProperty"/>
       <property name="arguments" value="java.version"/>
     </bean>
    

    基于XML Schema 的简化配置方式

    使用 p:命名空间简化配置(简化设值注入)

    需要导入 XML Schema 里的 p:命名空间xmlns:p="http://www.springframework.org/shema/p"

    <bean id="chinese" class="org.learn.impl.Chinese" p:age="29" p:axe-ref="stoneAxe"/>
    <bean id="stoneAxe" class="org.learn.impl.StoneAxe"/>
    

    使用c:命名控件简化配置(简化构造注入)

    需要导入 XML Schema 里的 c:命名空间xmlns:c="http://www.springframework.org/shema/c

    <bean id="chinese" class="org.learn.impl.Chinese" c:age="29" c:axe-ref="stoneAxe"/>
    <bean id="stoneAxe" class="org.learn.impl.StoneAxe"/>
    

    可使用 c_N 的方式指定构造器参数位置。

    <bean id="chinese" class="org.learn.impl.Chinese" c:_0="29" c:_1-ref="stoneAxe"/>
    

    使用 util:命名空间简化配置

    需要导入 XML Schema 里的 util:命名空间 xmlns:util="http://www.springframework.org/shema/util

    constant:该元素用于获取指定类的静态 Field
    property-path:该元素用于获取指定对象的 getter 方法的返回值
    list:该元素用于定一个一个 List Bean,支持使用<value.../>、<ref.../>、<bean.../>等子元素来定义 List 集合元素。该标签支持如下三个属性:

    • id :该属性指定定一个名为 id 的 List Bean 实例
    • list-class:该属性指定 Spring 通过那个 List 实现类来创建 Bean 实例。默认使用 ArrayList
    • scope:指定该 List Bean 的作用域

    set:该元素用于定一个一个 Set Bean,支持使用<value.../>、<ref.../>、<bean.../>等子元素来定义 Set 集合元素。该标签支持如下三个属性:

    • id :该属性指定定一个名为 id 的 Set Bean 实例
    • set-class:该属性指定 Spring 通过那个 Set 实现类来创建 Bean 实例。默认使用 HashSet
    • scope:指定该 Set Bean 的作用域

    map:该元素用于定一个一个 Map Bean,支持使用<entry.../>元素来定义 Map 的键值对。该标签支持如下三个属性:
    > + id :该属性指定定一个名为 id 的 Map Bean 实例

    • map-class:该属性指定 Spring 通过那个 Map 实现类来创建 Bean 实例。默认使用 HashMap
    • scope:指定该 Map Bean 的作用域

    properties:该元素用于加载一份资源文件,并根据加载的资源文件创建一个 Properties Bean 实例。该标签支持如下三个属性:

    • id :该属性指定定一个名为 id 的 Properties Bean 实例
    • location:指定资源文件的位置
    • scope:指定该 Properties Bean 的作用域

    SpEL 语法详述

    1. 直接量表达式
      {3.14159} {'Hello'} {false}
    2. 引用 bean、属性和方法
      SpEL 通过 ID 引用其它的 Bean{sgtPeppers}

      引用 Bean 中的属性{sgtPeppers.artist}

      引用 Bean 中的方法{sgtPeppers.selectArtist()}
    3. 安全导航
      {foo?.bar} 先判断 foo是否为 null,如果 foo 为 null 就直接返回 null
    4. 在表达式中使用类型
      {T(java.lang.math).PI}这里的 T()运算符的结果会是一个 Class 对象,表示访问类作用域的方法和常量
    5. 运算符
      {2 * T(java.lang.Math).PI * circle.radius}
      {disc.title ?: 'Hello World'}此处的?:表示判断 disc.title 是否为 null,如果为 null 的话指定默认值为 Hello World
    6. 计算集合
      {jukebox.songs[4].title} 表示查询 jukebox 中 songs 集合的第5个元素的 title,此处[]中表示第几个元素
      {jukebox.songs.?[artist eq 'Hello']} 此处的.?[]运算符表示对指定集合过滤,得到集合的一个子集
      {jukebox.songs.^[artist eq 'Hello']}jukebox.songs.?[artist eq 'Hello']此处中.^[]和.$[]分别表示查询集合中的第一个匹配项和最后一个匹配项
      jukebox.songs.![title]此处的.![]是投影运算符,表示将集合的每个成员中选择特定的属性放到另外一个集合中。

    相关文章

      网友评论

          本文标题:Spring入门笔记

          本文链接:https://www.haomeiwen.com/subject/jlzlxqtx.html