• Spring Bean详细讲解


     

    什么是Bean?

    Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象。

    Spring 容器会自动完成@bean对象的实例化

    创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。

    Spring 三种配置方案

    1.在XML中进行显示配置
    2.使用Java代码进行显示配置
    3.隐式的bean发现机制和自动装配
    推荐方式: 3>2>1

    一、自动化装配bean

    1.组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的bean。
    2.自动装配(autowiring):Spring自动满足bean之间的依赖。

    package com.stalkers;
    
    /**
     * CD唱片接口
     * Created by stalkers on 2016/11/17.
     */
    public interface ICompactDisc {
        void play();
    }
    
    package com.stalkers.impl;
    
    import com.stalkers.ICompactDisc;
    import org.springframework.stereotype.Component;
    
    /**
     * Jay同名专辑
     * Created by stalkers on 2016/11/17.
     */
    @Component
    public class JayDisc implements ICompactDisc {
    
        private String title = "星晴";
    
        public void play() {
            System.out.println(title + ":一步两步三步四步,望着天上星星...");
        }
    }
    

    Component注解作用:
    表明该类会作为组件类。

    不过,组件扫描默认是不开启用的,我们还需要显示配置下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

    1.java code开启组件扫描:
    其中,如果CompoentScan后面没有参数的话,默认会扫描与配置类相同的包

    @Configuration
    @ComponentScan
    public class CDPlayerConfig {
        @Bean
        public ICompactDisc disc() {
            return new JayDisc();
        }
    }
    

    2.xml启动组件扫描

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
        <context:component-scan base-package="com.stalkers.impl"/>
    </beans>

    测试代码

    package com.stalkers;
    
    import com.stalkers.config.CDPlayerConfig;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * Created by stalkers on 2016/11/18.
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = CDPlayerConfig.class)
    public class TestPlay {
        @Autowired
        private ICompactDisc jayDisc;
    
        @Test
        public void play() {
            jayDisc.play();
        }
    }
    

    在ComponentScan扫描的包中,所有带有@Component注解的类都会创建为bean

    为组件扫描的bean命名

    Spring应用上下文种所有的bean都会给定一个ID。在前面的例子中,尽管我们没有明确地为JayDisc bean设置ID,但是Spring会默认为JayDisc设置ID为jayDisc,也就是将类名的第一个字母变成小写

    如果想为这个bean设置不同的ID,那就将期望的值传递给@Component注解

    @Component("zhoujielun")
    public class JayDisc implements ICompactDisc {
      ...
    }

    如果不使用@Component注解的话,则使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解bean的ID

    需要引入:

       <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
            </dependency>
    
    @Named("zhoujielun")
    public class JayDisc implements ICompactDisc {
      ....
    }

    设置组件扫描的基础包

    前面再给CDPlayerConfig类设置@ComponentScan,我们并没有设置任何属性,这个时候默认扫描默认包是:CDPlayerConfig类所在包及其包的子包。

    如果是下图这种情况,DisConfig与其这时候就需要设置@ComponentScan的扫描的包

    image

    @Configuration
    @ComponentScan(basePackages = {"com.stalkers.soundsystem"})
    public class DiscConfig {
    }
    

    basePackages使用的是复数,则意味着可以设置多个基础包。

    但是basePackages后面跟的是String类型,这种类型并不安全。可以使用basePackageClasses有下面这种写法:

    @Configuration
    @ComponentScan(basePackageClasses = {com.stalkers.soundsystem.JayCompactDisc.class})
    public class DiscConfig {
    }

    通过为bean添加注解实现自动装配

    如果所有的对象都是独立的,彼此之间没有任何依赖,那么使用组件扫描就能自动化装配bean。

    但是实际工作中,很多对象会依赖其他对象完成任务。这时候就需要能够将组件扫描得到的bean和他们依赖装配在一起。这就是自动装配(autowiring)

    使用Spring的Autowired

    public interface IMediaPlayer {
        void play();
    }
    @Component
    public class CDPlayer implements IMediaPlayer {
    
        private ICompactDisc cd;
        
        @Autowired
        public CDPlayer(ICompactDisc cd) {
            this.cd = cd;
        }
    
        public void play() {
            System.out.println("cd Play:");
            cd.play();
        }
    }

    CDPlayer类的构造器上添加了@Autowired注解,表明当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化

    Autowired的多种方式
    1.构造器注解(constructor)

    2.属性setter注解

    3.field注解

    不管使用上面3中的哪个方法,Spring都会满足声明的依赖。假如有且只有一个bean匹配依赖的话,那么这个bean将会被装配进来。

    如果使用2,3方式注解,有多个bean的话,则用Qualifier指定。

    如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,可以使用

    @Autowired(required = false)
    private IMediaPlayer CDPlayer;

    required=false表示如果没有匹配的话,Spring会让bean处于未装配的样子。使用未装配的属性,会出现NullPointerException

    总结:
    所以在使用开发的时候一般建议使用Resource(package javax.annotation)进行注解。但是Resource不支持构造器注解

    二、通过Java代码装配Bean

    尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化更为推荐,但是有时候行不通。比如引用第三方组件,没办法在它的类上添加@Component及@Autowired。所以就需要JavaConfig或者XML配置

    在进行显示配置的时候,JavaConfig是更好的解决方案。

    JavaConfig与其他的Java代码又有所区别,在概念上它与应用程序中的业务逻辑和领域代码又有所不同。JavaConfig是配置相关代码,不含任何逻辑代码。通常会将JavaConfig放到单独的包中。

    创建JavaConfig类

    @Configuration
    public class CDPlayerConfig {
    }

    使用@Configuration表明CDPlayerConfig是一个配置类

    声明简单的bean

    @Bean
    public IMediaPlayer cdplayer() {
        return new VCDPlayer(new JayCompactDisc());
    }

    @Bean注解会告诉Spring将返回一个对象。

    **默认情况下,@Bean的Id与带有@Bean的方法名一样**当然也可以通过@Bean的name属性指定额外的方法名

    借助JavaConfig注入

    在上面的例子中,初始化个VCDPlayer都需要new一个JayCompactDisc对象。如果其他的对象的也需要JayCompactDisc,所以优化如下:

    @Bean
    public IMediaPlayer cdplayer() {
        return new VCDPlayer(disc());
    }
    
    @Bean
    public ICompactDisc disc() {
        return new JayCompactDisc();
    }

    单独抽出disc()方法,在其方法上加上Bean注解,Spring上加@Bean注解的都是**默认单例模式**,不管disc()被多个方法调用,其disc()都是同一个实例。

    当然上面的初始化可以优化如下:

    @Bean
    public IMediaPlayer cdplayer(ICompactDisc disc) {
        return new VCDPlayer(disc);
    }

    三、通过XML装配Bean

    在xml配置中,创建一个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">
       
    </beans>

    在使用xml的时候,需要在配置文件顶部声明多个xml模式(XML Schema Definition xsd)文件

    对于我们需要配置bean的则在spring-beans模式中。

    <?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="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
    </beans>

    1.借助构造器注入初始化bean

    构造器注入的方案:
    1.元素

    <?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="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
        <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
            <constructor-arg ref="jayCompactDisc"/>
        </bean>
    </beans>

    2.使用Spring3.0所引入的c-命名空间

    使用c-命名空间,需要引入:

    xmlns:c="http://www.springframework.org/schema/c"
    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:c="http://www.springframework.org/schema/c"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="jayCompactDisc" class="com.stalkers.soundsystem.JayCompactDisc"></bean>
        <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:cd-ref="jayCompactDisc">
        </bean>
    </beans>

    解析:c-命名空间的语法:

    c:cd-ref="jayCompactDisc"

    1.c 代表命名空间前缀

    2.cd 代表VCDPlayer类的构造器参数名。当然我们也可以使用参数在整个参数列表的位置 c:_0-ref

     <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="jayCompactDisc">

    使用下划线因为参数不能以数字开头,所以加下划线。

    3.-ref 代表注入bean引用

    4.jayCompactDisc 要注入的bean的id

    注意:

    c-命名需要写在标签内,与constructor-arg写法差别很大

    将字面量注入到构造器中

    上面我们所做的DI通常指的是类型的装配,也就是将对象的引用装配到依赖他们的其他对象中,但是有时候我们传的只是一个字面量值

    public class VaeCompactDisc implements ICompactDisc {
        private String title;
    
        public VaeCompactDisc(String title) {
            this.title = title;
        }
    
        public void play() {
            System.out.println("大家好,我是Vae,下面这首:" + title + "献给大家的");
        }
    }
    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
    </bean>
    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
        <constructor-arg value="浅唱"></constructor-arg>
    </bean>

    c-命名空间的写法

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
    </bean>
    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="城府">
        <!--<constructor-arg value="浅唱"></constructor-arg>-->
    </bean>

    装配集合

    public class VaeCompactDisc implements ICompactDisc {
        private String title;
    
        private List<String> tracks;
    
        public VaeCompactDisc(String title, List<String> tracks) {
            this.title = title;
            this.tracks = tracks;
        }
    
        public void play() {
            System.out.println("大家好,我是Vae,下面这专辑:" + title + "献给大家的");
            for (String s : tracks) {
                System.out.println(s);
            }
        }
    }

    Spring配置使用constructor-arg。而c-命名的是无法使用装配集合的功能

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" c:_0-ref="vaeCompactDisc">
    </bean>
    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc">
        <constructor-arg name="title" value="自定义"></constructor-arg>
        <constructor-arg name="tracks">
            <list>
                <value>有何不可</value>
                <value>多余的解释</value>
            </list>
        </constructor-arg>
    </bean>

    2.使用属性Setter方法注入

    public class CDPlayer implements IMediaPlayer {
    
        private ICompactDisc cd;
    
        @Autowired
        public void setCd(ICompactDisc cd) {
            this.cd = cd;
        }
        
        public CDPlayer(ICompactDisc cd) {
            this.cd = cd;
        }
    
        public void play() {
            System.out.println("cd Play:");
            cd.play();
        }
    }
    

    Spring.xml配置里面

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer">
        <property name="cd" ref="jayCompactDisc"></property>
    </bean>

    元素为属性的Setter方法所提供的功能与元素为构造器所提供的功能是一样的。

    与c-命名空间的类似的作为property的替代方案:p-命名空间。使用p-命名空间需要引入:

    xmlns:p="http://www.springframework.org/schema/p"

    Spring.xml配置如下

    <bean id="cdPlayer" class="com.stalkers.soundsystem.VCDPlayer" p:cd-ref="vaeCompactDisc">

    语法解析:

    p:cd-ref="vaeCompactDisc"

    1.p-:命名空间的前缀

    2.cd:属性名称

    3.-ref:注入bean引用

    4.vaeCompactDisc:所注入的bean的id

    将字面量注入到属性中

    字面量注入到属性与上面将字面量注入到构造方法中方式一样。只不过标签名改成了property。

    装配list也是与上面的构造器的装配list一样。

    虽然我们无法使用c-及p-命名空间装配list,但是我们可以使用

    <bean id="vaeCompactDisc" class="com.stalkers.soundsystem.VaeCompactDisc" c:title="自定义" c:tracks-ref="songs">
    </bean>
    <util:list id="songs">
        <value>有何不可</value>
        <value>多余的解释</value>
    </util:list>

    Spring util命名空间的中的元素:

    元素描述
    util:constant 引用某个类型的public static 域
    util:list 创建一个java.util.List类型的bean,其中包含值或引用
    util:map 创建一个java.util.Map类型的bean,其中包含值或引用
    util:properties 创建一个java.util.Properties类型的bean
    util:property-path 引用一个bean的属性
    util: set 创建一个java.util.Set类型的bean

    四、导入和混合配置

    在Spring应用中,我们可以同时使用自动化和显示配置。

    如果一个JavaConfig配置太臃肿,我们可以把其进行拆分,然后使用@**Import**将拆分的类进行组合

    如果希望在JavaConfig里引用xml配置。则可以使用@**ImportResource**

     
     
  • 相关阅读:
    初步认识三层架构
    机器学习技法总结(六)Decision Tree Hypothesis
    spring 事件模式 源代码导读
    Missing &#39;name&#39; key attribute on element activity at AndroidMan
    【iOS】KVC 与 KVO
    可用谷歌ip地址
    android menu事件
    #103. 子串查找
    P2590 [ZJOI2008]树的统计
    带修改莫队算法
  • 原文地址:https://www.cnblogs.com/williamjie/p/9378630.html
Copyright © 2020-2023  润新知