Spring-装配Bean(基础知识)

2 篇文章 0 订阅

装配Bean

在Spring中,对象无需自己查找或创建与其所关联的其他对象,统一由容器负责把需要相互协作的对象引用赋予各个对象。
创建应用对象之间协作关系的行为通常称为装配(wiring),也是依赖注入的本质(DI)。
Spring的三种装配机制

  • 在XML中显示配置
  • 在java中进行显示配置
  • 隐式的bean发现机制和自动装配

自动化装配Bean

Spring 从两个角度实现自动化装配

  • 组件扫描:Spring会自动发现应用上下文中所创建的bena。
  • 自动装配:Spring会自动满足bean之间的依赖。

创建可被发现(扫描)的bean:@Component

package soundsystem;

/**
 *  soundsystem : 声响系统
 *  CompactDisc :compact disc 唱片,光盘
 *  这是一个唱片接口
 */
public interface CompactDisc {
    void play();
}
package soundsystem;

import org.springframework.stereotype.Component;

/**
 * @Component 该注解表名,要将该类作为组件类,并告知Spring为这个类创建bean
 * 注意:
 *  组件扫描默认是不启用的,需要显式配置Spring,让其寻找带有@Component注解的类
 */
@Component
public class SgtPeppers implements CompactDisc {
    // 名称
    private String title = "Hello kk";
    // artist 唱作人
    private String artist = "XiaoZhang";
    public void play() {
        System.out.println("播放:"+title+"by"+artist);
    }
}

创建配置类:@Configuration,启用注解扫描:@ComponentScan

package soundsystem;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @Configuration
 * @ComponentScan 启用Spring组件扫描,默认扫描与配置类相同的包,以及下面的子包
 *          通过XML启用组件扫描:<context:component-scan>元素
 *          <context:component-scan base-package="soundsystem">
 */
@Configuration
@ComponentScan
public class CDPlayerConfig {
}

对组件扫描进行测试:@RunWith、@ContextConfiguration

package soundsystem;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/*
    指定在单元测试启动的时候自动创建Spring的上下文对象
    手动获取Spring上下文对象
        ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 */
@ContextConfiguration(classes = CDPlayerConfig.class)
/*
    RunWith的value属性指定以spring test的SpringJUnit4ClassRunner作为启动类
    如果不指定启动类,默认启用的junit中的默认启动类
 */
@RunWith(value = SpringJUnit4ClassRunner.class)
public class CDPlayerTest {

    ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);

    /*
        自动装配
        手动装配:
            CompactDisc compactDisc = (CompactDisc) context.getBean("sgtPeppers");
     */
    @Autowired
    private CompactDisc compactDisc;

    @Test
    public void compactDiscClassTest(){
        System.out.println(compactDisc);
    }

}

为组件扫描的bean命名

spring应用上下文中,所用的bean都会给定一个ID(在XML中,装配bean需要指定ID)。
@Component:默认将类名的首字母小写,设置为bean的ID

@Component源码:

public @interface Component {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	String value() default "";
}

自定义bean ID:

@Component("mySgtPeppers")
public class SgtPeppers implements CompactDisc {...}

设置组件扫描的基础包

@ComponentScan部分源码:

public @interface ComponentScan {

	/**
	 * 指定要扫描的包:默认属性
	 */
	@AliasFor("basePackages")
	String[] value() default {};

	/**
	 * 要扫描的多个包
	 */
	@AliasFor("value")
	String[] basePackages() default {};

	/**
	 * 指定包中所包含的类或接口,这些类中所包含的包将作为组件扫描的基础包。
	 * 可以在包中,通过标记接口的方式, 保持对重构友好的接口引用,同事避免引用任何实际的应用程序代码
	 */
	Class<?>[] basePackageClasses() default {};

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

自动装配就是让Spring自动满足bean依赖的一种方法。
@Autowired可以使用在属性、构造器、setter和其他方法上,Spring会尝试满足方法参数上声明的依赖。
@Autowired(required = false):当required = false,Spring依然会进行装配,但如果没有匹配的bean,Spring将会让这个bean处于未装配状态(可能会面临空指针异常)。

package soundsystem;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * CD播放器类
 */
@Component
public class CDPlayer {
    private CompactDisc cd;

    @Autowired
    public CDPlayer(CompactDisc cd){
        this.cd = cd;
    }

    public void play(){
        System.out.println("CD播放器播放音乐...");
        cd.play();
    }
}

通过Java代码装配bean

在有的时候,自动化装配不能进行时,我们就需要用到显式装配的方式。
相比于XML配置,Java配置更加方便、强大、安全,且对重构友好。

创建配置类

package soundsystem;

import org.springframework.context.annotation.Configuration;

/**
 * @Configuration 指定该类为配置类
 *      该类应该包含在Spring应用上下文中如何创建bean的细节
 */
@Configuration
public class CDPlayerConfig {
}

声明简单的bean

package soundsystem;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Configuration 指定该类为配置类
 *      该类应该包含在Spring应用上下文中如何创建bean的细节
 * 在JavaConfig中声明bean,需要编写一个方法,这个方法需要创建所需类型的实例,然后给该方法加上@Bean注解
 */
@Configuration
public class CDPlayerConfig {

    /**
     * @Bean 注解会告诉Spring这个方法会返回一个对象,该对象要注册为Spring应用上下文的bean。
     * 默认情况下bean的ID与方法名一致,可以通过@Bean(name="mySgtPeppers")进行自定义。
     */
    @Bean()
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }

}

借助JavaConfig实现注入

@Bean
public CDPlayer cdPlayer(){
    return new CDPlayer(sgtPeppers());
}

或者

/**
* 这种方式相比上中方式事实上更好,降低了依赖。这里甚至没有强求CompactDisc必须在JavaConfig中声明。
* 实际上可以通过组价扫描功能自动发现或通过XML来进行配置。
*/
@Bean
public CDPlayer cdPlayer(CompactDisc sgtPeppers){
    return new CDPlayer(sgtPeppers);
}

看起来CompactDisc的实例是通过sgtPeppers()方法获取,但并非如此。
因为在sgtPeppers()方法上添加@Bean注解,Spring会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
注: 在spring容器中,默认情况所有bean都是单例的。

带有@Bean注解的方法可以采用任何有必要的Java功能来产生实例。

通过XML装配Bean

创建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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

声明一个简单的bean

<?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/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
">

    <context:component-scan base-package="soundsystem"/>

    <!--
        类似于JavaConfig中的@Bean注解
        在没有明确指明id的情况下,将会根据全限定类名来进行命名
            soundsystem.SgtPeppers#0,其中#0是用来计数的
        通常会使用id属性进行明确的指定
        XML 配置的缺点:不能从编译期的类型检查中受益(在IDEA、STS这种比较强大的编译期中,可以检查出来)
    -->
    <bean class="soundsystem.SgtPeppers" id="compactDisc" />

</beans>

借助构造器注入初始化bean

在XML中只有一种声明bean的方式:使用元素,并制定class属性。但在依赖注入的时候,有多种方案和风格。

构造器注入bean引用
  1. <constructor-arg/>方式
<bean class="soundsystem.CDPlayer" id="cdPlayer">
    <constructor-arg ref="compactDisc"/>
</bean>
  1. c-命名空间方式
<!--
    使用c-命名空间来声明构造函数参数
    c:cd-ref
        c: 命名空间的前缀
        cd:要装配的构造器参数名,public CDPlayer(CompactDisc cd){...}
        -ref:命名的约定,告诉spring装配的是一个bean引用,这个bean的名字是compactDisc而不是字面量
    c:_0-ref="compactDisc"
        _0:在这里表示参数索引,即参数位置
        _:如果只有一个参数,可以写作 c:_-ref="compactDisc"
-->
<bean class="soundsystem.CDPlayer" id="cdPlayer" c:cd-ref="compactDisc"/>
将字面量注入到构造器中
  1. constructor-arg元素注入
<bean class="soundsystem.SgtPeppers" id="compactDisc">
    <!--
        value 表名给定的值要以字面量形式注入到构造器中
        index 参数索引,可选
    -->
    <constructor-arg value="老街" index="0"/>
    <constructor-arg value="lrh" index="1"/>
</bean>
  1. c-命名空间
<bean class="soundsystem.SgtPeppers" id="compactDisc"
          c:_0="老街"
          c:_1="李荣浩"
          />
XML装配集合

注意:c-命名空间对于集合无法进行装配

<bean class="soundsystem.BlankDisc" id="blankDisc">
        <constructor-arg index="0" value="耳朵" />
        <constructor-arg index="1" value="李荣浩" />
        <constructor-arg>
            <list>
                <value>耳朵</value>
                <value>麻雀</value>
                <value>贝贝</value>
                <value>老街</value>
            </list>
        </constructor-arg>
    </bean>

<list/>, <set/>, <map/>, <props/>
对于list、set、array相互之间可以通用(可以无视报错)

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

设置属性:property

对强依赖使用构造器注入,对可选性依赖使用属性注入。
<property>元素为属性的setter方法所提供的功能与<constructor-arg>为构造器提供的功能是一样的。
c-命名空间替代<constructor-arg>
p-命名空间替代<property>:添加xmlns:p="http://www.springframework.org/schema/p"

使用<property>元素

<bean class="soundsystem.CDPlayer" id="cdPlayer">
    <property name="cd" ref="blankDisc" />
</bean>

使用p-命名空间

<!--
    p:cd-ref="blankDisc"
        p-:p命名空间前缀
        cd:属性名
        -ref:注入bean引用,不带-ref就装配字面量
        "blankDisc":所注入的bean的id
-->
<bean class="soundsystem.CDPlayer" id="cdPlayer" p:cd-ref="blankDisc"/>
package soundsystem;

/**
 * CD播放器类
 */
public class CDPlayer {

    private CompactDisc cd;

    public CDPlayer(){}

    public void play(){
        System.out.println("CD播放器播放音乐...");
        cd.play();
    }

    // 提供setter方法,用来实现属性注入
    public void setCd(CompactDisc cd) {
        this.cd = cd;
    }
}
package soundsystem;

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;

/*
    指定在单元测试启动的时候自动创建Spring的上下文对象
    手动获取Spring上下文对象
        ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 */
@ContextConfiguration(locations = "classpath:applicationContext.xml")
/*
    RunWith的value属性指定以spring test的SpringJUnit4ClassRunner作为启动类
    如果不指定启动类,默认启用的junit中的默认启动类
 */
@RunWith(value = SpringJUnit4ClassRunner.class)
public class CDPlayerTest {

    @Autowired
    private CDPlayer cdPlayer;
    
    @Test
    public void compactDiscClassTest(){
        cdPlayer.play();
    }
}
util-命名空间

使用util-命名空间需要添加
命名空间:xmlns:util="http://www.springframework.org/schema/util"
约束:http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
用它可以完成集合的定义,并用-ref的方式应用

<bean class="soundsystem.BlankDisc" id="blankDisc"
      c:_0="《耳朵》"
      c:_1="李荣浩"
      c:_2-ref="trackList" />

<util:list id="trackList">
    <value>耳朵</value>
    <value>麻雀</value>
    <value>贝贝</value>
    <value>老街</value>
</util:list>

下方引用形式也正确

<bean class="soundsystem.BlankDisc" id="blankDisc">
    <constructor-arg index="0" value="耳朵"/>
    <constructor-arg index="1" value="李荣浩"/>
    <constructor-arg index="2" ref="trackList" />
</bean>

在这里插入图片描述

导入和混合配置

自动化装配、JavaConfig和XML配置在Spring中相互之间是不排斥的。
自动装配会考虑Spring容器中所有的bean,不管他在JavaConfig或者XMl中声明,通过组件扫描的方式都能获取到。

JavaConfig的相互引用

使用@Import(CDConfig.class)可以进行JavaConfig之间的互相引用

CDPlayerConfig

@Configuration
@Import(CDConfig.class)
@ComponentScan
public class CDPlayerConfig {

    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc){
        return new CDPlayer(compactDisc);
    }

}

CDConfig

@Configuration
public class CDConfig {

    @Bean
    public CompactDisc compactDisc() {
        return new SgtPeppers();
    }

}
在JavaConfig中引用XML配置

通过@ImportResource({"class:applicationContext.xml"})注解引入一个或多个XML配置文件
自动装配的时候,优先使用明确指定id的类

@Configuration
@ImportResource({"classpath:applicationContext.xml"})
public class CDPlayerConfig {

    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc){
        return new CDPlayer(compactDisc);
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd
">
    <bean class="soundsystem.BlankDisc" id="blankDisc"
          c:_0="《耳朵》"
          c:_1="李荣浩"
          c:_2-ref="trackList"
          />
    <util:list id="trackList">
        <value>耳朵</value>
        <value>麻雀</value>
        <value>贝贝</value>
        <value>老街</value>
    </util:list>
</beans>
在XML配置中引用XML配置
<import resource="applicationContext01.xml" />
在XML配置中引用JavaConfig
<bean class="soundsystem.CDPlayerConfig" />
根配置

不管使用JavaConfig还是使用XML进行装配,我们通常都会创建一个根配置(rootconfiguration),这个配置会将两个或两个以上的装配类或XML文件组合起来。同时也会在根配置中启用组件扫描。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值