Spring核心-2装配Bean

Spring 配置的可选方案

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

尽可能地使用自动装配机制,当必须要显示配置 bean 的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置 bean 的时候),推荐使用类型安全并且比 XML 更加强大的 JavaConfig。

自动化装配 bean

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

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

组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将代码的显式配置降低到最少。

创建可被发现的 bean

package com.y.gui.spring.soundsystem;

public interface CompactDisc {
    void play();
}
package com.y.gui.spring.soundsystem.impl;

import com.y.gui.spring.soundsystem.CompactDisc;
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {
    @Override
    public void play() {
        System.out.println("SgtPeppers.play");
    }
}

为组件扫描的 bean 命名

@Component

import org.springframework.stereotype.Component;
@Component 注解的类表明该类会被做为组件类,但组件扫描默认是关闭的
bean 的默认 ID 为 sgtPeppers,即类名第一个字母变为小写。
为 bean 指定 ID:@Component("sgtABC")

@Named

import javax.inject.Named;

@Named 属于Java依赖注入规范(Java Dependency Injection)。
Spring 支持将 @Named 作为 @Component 注解的替代方案。
所以为 bean 命名的另一种方式为

@Named("sgtABC")

设置扫描组件的基础包

@ComponentScan

import org.springframework.context.annotation.ComponentScan;
@ComponentScan 启动 Spring 的组件扫描功能
Spring 默认扫描 @ComponentScan 配置类相同的包及子包,查找带有 @Component 注解的类并为其创建 bean。

指明扫描的基础包

@ComponentScan(“com.y.gui.spring.soundsystem”)
@ComponentScan(basePackages=“com.y.gui.spring.soundsystem”)

指明多个扫描的基础包

@ComponentScan(basePackages={“com.a”, “com.b”})

通过类或接口指明扫描基础包
可以考虑在包中创建一个用来进行扫描的空标记接口,更安全

@ComponentScan(basePackageClasses={A.class, B.class})

通过Java配置启动组件扫描

package com.y.gui.spring.soundsystem;

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

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

通过XML配置启动组件扫描

spring-context.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.y.gui.spring.soundsystem" />

</beans>

测试

SpringJUnit4ClassRunner

自动创建 Spring 应用上下文

@ContextConfiguration

表明要在 CDPlayerConfig 中加载配置

package com.y.gui.soundsystem;

import com.y.gui.spring.soundsystem.CDPlayerConfig;
import com.y.gui.spring.soundsystem.CompactDisc;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    private CompactDisc compactDisc;

    @Test
    public void play() {
        compactDisc.play();
    }
}

运行结果

15:41:25.119 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@22e357dc size = 1, maxSize = 32, parentContextCount = 0, hitCount = 2, missCount = 1]
15:41:25.119 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [928466577] from cache with key [[MergedContextConfiguration@6366ebe0 testClass = CDPlayerTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30a3107a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@52feb982, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@25af5db5], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
15:41:25.119 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@22e357dc size = 1, maxSize = 32, parentContextCount = 0, hitCount = 3, missCount = 1]
15:41:25.576 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [928466577] from cache with key [[MergedContextConfiguration@6366ebe0 testClass = CDPlayerTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30a3107a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@52feb982, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@25af5db5], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
15:41:25.576 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@22e357dc size = 1, maxSize = 32, parentContextCount = 0, hitCount = 4, missCount = 1]
SgtPeppers.play

基于XML的运行测试

package com.y.gui.spring.soundsystem;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XmlMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("static/spring-context.xml");
        CompactDisc cd = context.getBean(CompactDisc.class);
        cd.play();
        context.close();
    }
}

运行结果

15:43:55.415 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\IdeaProjects\gui\target\classes\com\y\gui\spring\soundsystem\impl\SgtPeppers.class]
15:43:55.567 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:43:55.570 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:43:55.570 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:43:55.570 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:43:55.587 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'CDPlayerConfig'
15:43:55.596 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'sgtPeppers'
SgtPeppers.play

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

package com.y.gui.spring.soundsystem.cd;

public interface MediaPlayer {
    void play();
}
package com.y.gui.spring.soundsystem.cd.impl;


import com.y.gui.spring.soundsystem.CompactDisc;
import com.y.gui.spring.soundsystem.cd.MediaPlayer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {

    private CompactDisc cd;

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

    @Override
    public void play() {
        this.cd.play();
    }
}

@Autowired

import org.springframework.beans.factory.annotation.Autowired;
@Autowired 注解不仅能够用在构造器上,还能用在类的任何方法上以及属性本身上。如:

@Autowired
private CompactDisc cd;
@Autowired
public void abc(CompactDisc cd) {
this.cd = cd;
}

不论任何形式,Spring 都会尝试满足所声明的依赖。假如有且只有一个 bean 匹配依赖需求的话,那么这个 bean 将会被装配进来。如果没有匹配的 bean,那么在应用上下文创建的时候,Spring 会抛出一个异常。如果有多个bean都能满足依赖关系的话,Spring 也将会抛出一个异常,表明没有明确指定要选择哪个 bean 进行自动装配。
为了避免异常的出现,可以将 @Autowired 的 required 属性设置为 false :

@Autowired(required=false)

如果 required 属性设置为 false 的话,需要谨慎对待,建议代码中进行 null 检查防止出现 NullPointerException 异常。

@Inject

import javax.inject.Inject;
@Inject 注解来源于 Java 依赖注入规范,该规范同时还为我们定义了 @Named 注解。在自动装配中,Spring 同时支持 @Inject@Autowired。尽管 @Inject@Autowired 之间有着一些细微的差别,但是在大多数场景下,它们都是可以互相替换的。

验证自动装配

package com.y.gui.soundsystem;

import com.y.gui.spring.soundsystem.CDPlayerConfig;
import com.y.gui.spring.soundsystem.cd.MediaPlayer;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {

    @Autowired
    private MediaPlayer mp;

    @Test
    public void mpTest() {
        mp.play();
    }
}

运行结果

17:17:24.552 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test method: context [DefaultTestContext@6366ebe0 testClass = CDPlayerTest, testInstance = com.y.gui.soundsystem.CDPlayerTest@166fa74d, testMethod = mpTest@CDPlayerTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@44f75083 testClass = CDPlayerTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@33c7e1bb, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a765367, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].
17:17:24.555 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [624271064] from cache with key [[MergedContextConfiguration@44f75083 testClass = CDPlayerTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@33c7e1bb, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a765367, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
17:17:24.555 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@10feca44 size = 1, maxSize = 32, parentContextCount = 0, hitCount = 2, missCount = 1]
17:17:24.556 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [624271064] from cache with key [[MergedContextConfiguration@44f75083 testClass = CDPlayerTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@33c7e1bb, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a765367, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
17:17:24.556 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@10feca44 size = 1, maxSize = 32, parentContextCount = 0, hitCount = 3, missCount = 1]
17:17:24.874 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [624271064] from cache with key [[MergedContextConfiguration@44f75083 testClass = CDPlayerTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@33c7e1bb, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7a765367, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@12cdcf4], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
17:17:24.874 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@10feca44 size = 1, maxSize = 32, parentContextCount = 0, hitCount = 4, missCount = 1]
SgtPeppers.play

通过 Java 代码装配 bean

在很多场景下通过组件扫描和自动装配实现 Spring 的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置 Spring。比如说,想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加 @Component@Autowired 注解的,因此就不能使用自动化装配的方案了。 在这种情况下,必须要采用显式装配的方式。
在进行显式配置的时候,有两种可选方案:JavaXML

package com.y.gui.spring.soundsystem;

import com.y.gui.spring.soundsystem.cd.impl.CDPlayer;
import com.y.gui.spring.soundsystem.impl.HardDaysNight;
import com.y.gui.spring.soundsystem.impl.Revolver;
import com.y.gui.spring.soundsystem.impl.SgtPeppers;
import com.y.gui.spring.soundsystem.impl.WhiteAlbum;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CDPlayerDisplayConfig {
    @Bean
    public CDPlayer cdPlayer() {
        return new CDPlayer(randomCD());
    }

    /*@Bean
    public CDPlayer cdPlayer(CompactDisc cd) {
        return new CDPlayer(cd);
    }*/


    @Bean(name="beanRandomCD")
    public CompactDisc randomCD() {
        int choice = (int) Math.floor(Math.random() * 4);
        if (choice == 0) {
            return new SgtPeppers();
        } else if (choice == 1) {
            return new WhiteAlbum();
        } else if (choice == 2) {
            return new HardDaysNight();
        } else {
            return new Revolver();
        }
    }
}

测试

package com.y.gui.soundsystem;

import com.y.gui.spring.soundsystem.CDPlayerDisplayConfig;
import com.y.gui.spring.soundsystem.cd.MediaPlayer;
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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerDisplayConfig.class)
public class CDPlayerDisplayTest {
    @Autowired
    private MediaPlayer mp;

    @Test
    public void mpTest() {
        mp.play();
    }
}

运行结果

16:23:49.558 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [928466577] from cache with key [[MergedContextConfiguration@6366ebe0 testClass = CDPlayerDisplayTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerDisplayConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30a3107a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@52feb982, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@25af5db5], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
16:23:49.558 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@223aa2f7 size = 1, maxSize = 32, parentContextCount = 0, hitCount = 3, missCount = 1]
16:23:49.827 [main] DEBUG org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate - Retrieved ApplicationContext [928466577] from cache with key [[MergedContextConfiguration@6366ebe0 testClass = CDPlayerDisplayTest, locations = '{}', classes = '{class com.y.gui.spring.soundsystem.CDPlayerDisplayConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30a3107a, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@52feb982, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@25af5db5], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
16:23:49.827 [main] DEBUG org.springframework.test.context.cache - Spring test ApplicationContext cache statistics: [DefaultContextCache@223aa2f7 size = 1, maxSize = 32, parentContextCount = 0, hitCount = 4, missCount = 1]
Revolver.play

CDPlayerDisplayConfig 配置类没有添加 @ComponentScan 注解,而是通过 @Bean 显示的装配 Bean。

@Bean

import org.springframework.context.annotation.Bean;
@Bean 注解会告诉 Spring 这个方法将会返回一个对象,该对象要注册为 Spring 应用上下文中的 bean。方法体中包含了最终产生 bean 实例的逻辑。

默认情况下,bean 的 ID 与带有 @Bean 注解的方法名是一样的。CDPlayer 的 bean 名称将为 cdPlayer。同时,也可以使用 @Beanname 属性指定名称。

看起来 cdPlayer() 方法中是通过 randomCD() 方法来获取 CompactDisc 的,但实际并非如此。
因为 randomCD() 方法上添加了 @Bean 注解,Spring 将会拦截所有对它的调用,并确保直接返回该方法所创建的 bean,而不是每次都对其进行实际的调用。

其实,通过将 CompactDisc 作为方法参数由 Spring 传递进来的方式更为妥当 (如上注释的代码),这样 CompactDisc 就将不会被限制装配方式,比如可以通过其它配置类或 XML 或自动扫描装配。

通过 XML 装配 bean

在 Spring 刚刚出现的时候,XML 是描述配置的主要方式。在 Spring 的名义下,我们创建了无数行 XML 代码。在一定程度上,Spring 成为了 XML 配置的同义词。
Spring 现在有了强大的自动化配置和基于 Java 的配置,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
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">
 <!-- configuration details to here -->
</beans>

借助 Spring Tool Suite 创建 XML 配置文件 创建和管理 Spring XML 配置文件的一种简便方式是使用 Spring Tool Suite(https://spring.io/tools/sts)。在 Spring Tool Suite 的菜单中,选择 File>New>Spring Bean Configuration File,能够创建 Spring XML 配置文件,并且可以选择可用的配置命名空间。

用来装配 bean 的最基本的 XML 元素包含在 spring-beans 模式之中,在上面这个 XML 文件中,它被定义为根命名空间。<beans> 是该模式中的一个元素,它是所有 Spring 配置文件的根元素。
在 XML中配置 Spring 时,还有一些其他的模式。

声明 bean

<bean id="sgtPeppers" class="com.y.gui.spring.soundsystem.impl.SgtPeppers"/>

如果没有明确给定 ID,那么这个 bean 将会根据 全限定类名 来进行命名。在本例中,bean 的 ID 将会是“ com.y.gui.spring.soundsystem.impl.SgtPeppers#0”。其中,“#0 ”是一个计数的形式,用来区分相同类型的其他 bean。

当 Spring 发现这个 <bean/> 元素时,它将会调用 SgtPeppers 的 默认构造器 来创建 bean。

借助构造器注入初始化 bean

在XML中声明DI时,会有多种可选的配置方案和风格。

具体到构造器注入,有两种基本的配置方案可供选择:

  • <constructor-arg> 元素
  • 使用Spring 3.0所引入的c-命名空间

两者的区别在很大程度就是是否冗长烦琐。可以看到,<constructor-arg> 元素比使用c-命名空间会更加冗长,从而导致XML更加难以读懂。
另外,有些事情 <constructor-arg> 可以做到,但是使用 c- 命名空间却无法实现。

要使用C-命名空间,必须要在XML的顶部声明其模式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       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>

构造器注入 bean 引用

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

    <bean id="sgtPeppers" class="com.y.gui.spring.soundsystem.impl.SgtPeppers"/>

    <bean id="cdPlayer" class="com.y.gui.spring.soundsystem.cd.impl.CDPlayer">
        <constructor-arg ref="sgtPeppers"/>
    </bean>
</beans>

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

    <bean id="sgtPeppers" class="com.y.gui.spring.soundsystem.impl.SgtPeppers"/>

    <bean id="cdPlayer" class="com.y.gui.spring.soundsystem.cd.impl.CDPlayer" c:cd-ref="sgtPeppers" />
</beans>

c: C命名空间的前缀
cd:构造器参数名
-ref:注入 bean 引用
"sgtPeppers":要注入的 bean 的 ID

<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_0-ref="sgtPeppers" />

_0:表示参数在整个参数中的位置信息
因为在XML中不允许数字作为属性的第一个字符,因此必须要添加一个下画线作为前缀。

<bean id="cdPlayer" class="soundsystem.CDPlayer" c:_-ref="sgtPeppers" />

_:如果只有一个构造器参数,那也可以不用去标示参数名或位置

c- 命名空间装配字面量值

package com.y.gui.spring.soundsystem.impl;

import com.y.gui.spring.soundsystem.CompactDisc;

import java.util.List;

public class BlankDisc implements CompactDisc {
    private String title;

    private String artist;
    
    private List<String> tracks;

    public BlankDisc(String title, String artist, List<String> tracks) {
        this.title = title;
        this.artist = artist;
        this.tracks = tracks;
    }
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
        for (String track : tracks) {
            System.out.println("-Track: " + track);
        }
    }
}
<bean id="blankDisc" class="com.y.gui.spring.soundsystem.impl.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
    <constructor-arg>
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omitted for brevity... -->
        </list>
    </constructor-arg>
</bean>

也可以使用<ref>元素替代<value>实现bean引用列表的装配,如:

public Discography(String artist, List<CompactDisc> cds) { ... }
<bean id="discography" class="com.y.gui.spring.soundsystem.impl.Discography">
    <constructor-arg value="The Beatles" />
    <constructor-arg>
    <!-- 当参数是java.util.Set类型时,<list>标签可以替换为<set> -->
    <list>
        <ref bean="sgtPeppers" />
        <ref bean="whiteAlbum" />
        <ref bean="hardDaysNight" />
        <ref bean="revolver" />
        ...
    </list>
    </constructor-arg>
</bean>

<constructor-arg> 比 c- 命名空间的属性更有优势
使用 c- 命名空间的属性无法实现装配集合的功能

设置属性

package com.y.gui.spring.soundsystem.cd.impl;

import com.y.gui.spring.soundsystem.CompactDisc;
import com.y.gui.spring.soundsystem.cd.MediaPlayer;

public class VideoPlayer implements MediaPlayer {

    private CompactDisc compactDisc;

    public void setCompactDisc(CompactDisc compactDisc) {
        this.compactDisc = compactDisc;
    }

    @Override
    public void play() {
        compactDisc.play();
    }
}
<bean id="cdPlayer" class="com.y.gui.spring.soundsystem.cd.impl.VideoPlayer">
    <property name="compactDisc" ref="compactDisc" />
</bean>

<property> 元素为属性的Setter方法所提供的功能与 <constructor-arg> 元素为构造器所提供的功能是一样的。

Spring提供了更加简洁的p-命名空间,作为 <property> 元素的替代方案。为了启用 p- 命名空间,必须要在XML文件中与其他的命名空间一起对其进行声明:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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>

使用 p- 命名空间为属性设值:

<bean id="cdPlayer" class="com.y.gui.spring.soundsystem.cd.impl.VideoPlayer" p:compactDisc-ref="compactDisc" />

p: p命名空间的前缀
compactDisc:属性名
-ref:注入 bean 引用
"compactDisc":要注入的 bean 的 ID

<bean id="blankDisc" class="com.y.gui.spring.soundsystem.impl.BlankDisc">
    <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
    <property name="artist" value="The Beatles" />
    <property name="tracks">
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omitted for brevity... -->
            </list>
    </property>
</bean>

使用 p- 命名空间的属性来完成该功能:

<bean id="blankDisc" class="com.y.gui.spring.soundsystem.impl.BlankDisc" 
    p:title="Sgt. Pepper's Lonely Hearts Club Band"
    p:artist="The Beatles">
    <property name="tracks">
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omitted for brevity... -->
        </list>
    </property>
</bean>

p- 命名空间不能用来装配集合

我们可以使用 Spring util- 命名空间中的一些功能来简化装配,首先要在XML中声明 util- 命名空间及其模式:

<?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:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util.xsd">
    ... ...
</beans>
<util:list id="trackList">
    <value>Sgt. Pepper's Lonely Hearts Club Band</value>
    <value>With a Little Help from My Friends</value>
    <value>Lucy in the Sky with Diamonds</value>
    <value>Getting Better</value>
    <value>Fixing a Hole</value>
    <!-- ...other tracks omitted for brevity... -->
</util:list>

<bean id="blankDisc" class="com.y.gui.spring.soundsystem.impl.BlankDisc"
    p:title="Sgt. Pepper's Lonely Hearts Club Band"
    p:artist="The Beatles"
    p:tracks-ref="trackList" />

Spring util- 命名空间中的元素

  • 元素
  • 描述
  • <util:constant>
  • 引用某个类型的 public static 域,并将其暴露为 bean
  • util:list
  • 创建一个 java.util.List 类型的 bean,其中包含值或引用
  • util:map
  • 创建一个 java.util.Map 类型的 bean,其中包含值或引用
  • util:properties
  • 创建一个 java.util.Properties 类型的 bean
  • util:property-path
  • 引用一个 bean 的属性(或内嵌属性),并将其暴露为 bean
  • util:set
  • 创建一个 java.util.Set 类型的 bean,其中包含值或引用

导入和混合配置

在JavaConfig中引用XML配置

@Import

可以将多个配置组合到一起

package com.y.gui.spring.soundsystem;

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

@Configuration
@Import({CDPlayerConfig.class, CDPlayerDisplayConfig.class})
public class SystemConfig {
    
}

@ImportResource

在 Java 中加载 xml 配置

package com.y.gui.spring.soundsystem;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

@Configuration
@Import({CDPlayerConfig.class, CDPlayerDisplayConfig.class})
@ImportResource("classpath:static/spring-soundsystem.xml")
public class SystemConfig {

}

在XML配置中引用 JavaConfig

<import/> 元素

可以引入其它配置文件

<?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">
    <import resource="cd-config.xml" />
    <bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" />
</beans>

<bean/>元素

导入Java配置

<?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 class="com.y.gui.spring.soundsystem.CDPlayerConfig" />
    <import resource="cdplayer-config.xml" />
</beans>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值