第二章 装配bean

本文详细介绍了Spring框架中bean的装配,包括组件扫描、自动装配和显式配置。通过@ComponentScan启用组件扫描,利用@Autowired进行自动装配,并探讨了通过JavaConfig和XML配置bean的方法。强调了在开发中尽量使用自动化装配,以减少显式配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章内容

  • 声明bean
  • 构造器注入和Setter方法注入
  • 装配bean
  • 控制bean的创建和销毁
在Spring中,对象无需自己查找或创建与其所关联的其他对象,相反,容器负责把需要协作的对象引用赋予给各个对象,创建应用对象之间协作关系的行为我们称之为装配(waring),这也是DI(依赖注入)的本质
注意 DI是Spring最基本的要素,所以在开发基于Spring的应用时,你随时都在使用这些技术

Spring配置中最常见的三种方法

当描述Bean如何进行装配时,Spring具有很大的灵活性,它提供了三种主要的装配机制

1.在Xml中进行显性配置
2.在java中进行显性配置
3.隐式的bean发现机制和自动装配

至于选择哪种装配机制,Spring的装配风格是多样的,你可以自由组合,但是应当尽可能的选择自动配置的机制,显式配置越少越好,当你需要显式配置bean的时候(比如这些代码不是由你维护,而你需要装配bean的时候),应当用类型安全并且比Xml更强大的JavaConfig,最后只有当你想要使用便利的Xml并且Java Config没有相关的实现时,才使用Xml


自动化装配bean(便利性最强)

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

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

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

创建可以被发现的bean
例子:这里有个cd播放器类,你必须将cd注入

package soundsystem;

public interface CompactDisc {
//    定义cd的接口概念
 void play();

}

在这里,接口的内容不重要,重要的是你即将其定义为了接口,作为接口,他定义了CD播放器对一盘CD能做的操作,他将cd播放器与 cd的耦合度降到了最小的程度

我们去创建一个CompactDisc的具体实现,事实上可以有多个实现,目前我们只创建这一个,也就是下面的sgtPeppers类

带有@Component注解的CompactDisc实现类sgtPeppers

    package soundsystem;

import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc{

private String title = "small love song";
private String artist = "jim";
@Override
public void play() {
    System.out.println("playing"+title+"by"+artist);
}
}

和CompactDisc接口一样,sgtPeppers的具体内容不重要,重要的是sgtPeppers类上使用了@Component注解,这个简单的注解会表明该类会成为组件类,并告知Spring要为这个类创建bean

但是,组件扫描默认是不启用的,我们还需要显式的配置下Spring,从而命令他去寻找带有@Component注解的类,并为其创建bean,下面的程序完成了这项简单的配置

@ComponentScan开启了组件扫描

    package soundsystem;

    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    //@ComponentScan注解开启了组件扫描
    @ComponentScan
    
    public class CDPlayerConfig {
    
    
    
    }

类CDPlayerConfig通过java代码定义了Spring的装配规则,稍后再详细了解基于Java的详细配置,现在我们主要观察CDPlayerConfig类并没有显式的声明任何bean,只不过它使用了@ComponentScan注解,这个注解能够在Spring中启用组件扫描

如果没有其他配置的话,@ComponentScan会首先扫描与配置类相同的包,因为CDPlayerConfig位于soundsystem包中,因此Spring将会扫描这个包与他之下的所有子包,找到带有@Conponent注解的类,这样就能发现CompactDisc,并且会在Spring中自动为其创建一个bean

通过Xml启用组件扫描

comtext:compoenet-scan会有与@CompoenetScan对应的属性和子元素

目前为止,创建了两个类,我们就已将能够完成组件测试了

看下面的测试实例(CompactDisc),我们创建一个简单的Junit测试,他会创建Spring上下文,并判断CompactDisc是不是真的被创建出来了
package soundsystem;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.Assert.assertNotNull;
//使用Spring的SpringJunit4ClassRunner,以便在测试开始的时候创建Spring的应用上下文
@RunWith(SpringJunit4ClassRunner.class)
//在ContextConfiguration告诉他需要在CDPlayConfig中加载配置
//因为CDPlayerConfig类中包含@ComponentScan,因此最终的应用上下文中应该包含CompactDisc bean
@ContextConfiguration(classes = CDPlayerConfig.class)

public class CDPlayTest {
//    @Autowired注解,以便将CompactDisc bean注入到测试代码中
@Autowired
private CompactDisc cd;
@Test
//简单的测试方法断言cd的属性不为null
// (不为null就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码中)
public void  sdShowNotBeNull(){

assertNotNull(cd);

}

}

因为Spring实战这本书没有(maven)pom.xml依赖详情,所以程序会出现错误,可以参照我的pom.xml,就可以完成测试

    <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.2.1.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.hamcrest</groupId>
          <artifactId>hamcrest-core</artifactId>
          <version>1.3.RC2</version>
          <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest-library -->
        <dependency>
          <groupId>org.hamcrest</groupId>
          <artifactId>hamcrest-library</artifactId>
          <version>1.3</version>
          <scope>test</scope>
        </dependency>
    
      </dependencies>


如果成功运行,测试区会显示绿色,至此为止,你已经写下一个简单的扫描组件测试类,

下面,我们将深入探讨@ComponentScan和@Component

为组件扫描的bean命名

Sprng应用上下文中的所有bean都会给定一个ID,之前的例子中,虽然没有给SgtPeppers bean设置ID,但Spring会根据类名为其指定一个ID,具体来说,这个bean所给定的ID为sgtPeppers,也就是将类名的第一个字母变小写.

  • 如果想为这个bean设置不同的ID,就要你所要期望的ID作为值传递给@Component注解,比如想将这个bean表示为lonelyHeartsClub

    @Component(“lonelyHeartsClub”)
    //就应该在SgtPeppers类的@Component配置为如下所示
    public class SgtPeppers implements CompactDisc{

    private String title = “small love song”;
    private String artist = “jim”;
    @Override
    public void play() {
    System.out.println(“playing”+title+“by”+artist);
    }
    }

  • 还有另一种为bean命名的方式,这种方式不使用@Component注解,而是使用java依赖注入规范( Java Dependency Injection)中所提供的@Named注解来为bean来设置ID

     package soundsystem;
    

    import javax.inject.Named;

    //使用java依赖注入规范( Java Dependency Injection)中所提供的@Named注解来为bean来设置ID
    @Named(“lonelyHeartsClub”)
    public class SgtPeppers implements CompactDisc{

    private String title = “small love song”;
    private String artist = “jim”;
    @Override
    public void play() {
    System.out.println(“playing”+title+“by”+artist);
    }
    }

Spring支持将@Named()作为@Component注解的替代方案,双方有些细微差异,但是大多数场景中,他们是可以相互转换的

对我而言,@Component注解更具有目的性,@Named很容易不知道他是干什么的,因此在之后我将不会使用@Named

设置组件扫描的基础包

到现在为止,我们没有为@ComponentScan设置任何属性,这意味着,按照默认规则,他会以配置类所在的包作为基础包(base package)来扫描组件

有一个原因会促使我们明确的设置基础包,那就是我们想要将配置类放在单独的包中,使其与其他的代码分开来,但是这样的话,默认的基础包就不能满足要求了

为了满足需求,只需要在@ComponentScan的value属性中指明包的名称
   @Configuration
//@ComponentScan注解开启了组件扫描
//在@ComponentScan的value属性中指明包的名称
@ComponentScan("soundsystem")

public class CDPlayerConfig {

}

如果你想更加清晰的表明你所设置的是基础包,那么你可以通过basePackage进行设置
@Configuration
//@ComponentScan注解开启了组件扫描

//可以通过basePackage属性更加清晰的表明所属包
@ComponentScan(basePackages = "soundsystem")
public class CDPlayerConfig {

}
有趣的是,basePackages是一个复数属性,这就意味着你可以给配置类指定多个基础包,只需要将basePackage属性设置为要扫描的一个数组即可
package soundsystem;


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

@Configuration
//@ComponentScan注解开启了组件扫描
@ComponentScan(basePackages = {"soundsystem", "video"})
public class CDPlayerConfig {

}
在上面的例子中,所设置的基础包是以String类型表示的,这种方法虽然可以,但是被认为是(not type-safe)类型不安全,因为如果你重构代码的话,那么所指定的基础包就可能会出现错误
除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口
package soundsystem;


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

@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig {

}
我们为basePackageClasses属性所设置的数组中包含了类,这些类所在的包将会作为组件扫描的基础包

尽管在样例中,我为basePaseageClass设置的是组件类,但是你可以考虑在包中创建一个用来进行扫描的空标记接口(marker interface),通过标记接口的方式,你依然能够保持对重构友好的接口引入,但是可以避免引用任何实际的应用程序代码(在稍后重构中,这些代码可能会从想要扫描的包中移除掉)

在你的应用中,如果所有队形都是独立的,彼此之间没有任何依赖,就像SgtPerpers bean 这样,那么你需要的就是组件扫描而已
但是,很多对象会依赖其他的对象才能完成任务,这样的话,我们就需要有一种方法能够将组件扫描到的bean和他们的依赖装配在一起,要完成这项任务,我们需要了解一下Spring自动化配置的另外一方面内容,那就是自动装配.

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

简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean,为了声明要自动装配,我们可以借助Spring的@Autowired注解

比如下面的CDPlayer bean的时候,会通过这个构造器来进行实例化并且传入一个可设置给CompactDisc类型的bean

package soundsystem;

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

@Component
public class CDPlayer implements MediaPlayer {



    private CompactDisc cd;

@Autowired
//这表明当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且传入一个可设置给CompactDisc类型的bean
    public CDPlayer(CompactDisc cd){

        this.cd = cd;

    }


    public void play(){
        cd.play();
    }
}

@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上,比如说,如果CDPlayer有一个setCompactDisc方法,可以采用如下的注解形式进行自动装配

例如

@Autowired

public void setCompactDisc(CompactDisc cd){

this.cd = cd;
    }
在Spring初始化bean之后,他会尽可能的去满足bean的依赖,在本例中,依赖是通过@Autowired注解的方法进行注解声明的,也就是setCompactDisc();
实际上,@Autowired可以用在任何方法上,同样会发生作用

不管是任何方法,Spring都会尝试满足方法参数上所声明的依赖,假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来.

注意

如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常.为了避免异常的出现,你可以将@Autowired的required属性设为false
@Autowired(required = false)

    public void setCompactDisc(CompactDisc cd){

    this.cd = cd;
}

将required属性设置为false时,如果没有匹配的bean的话,Spring会将这个bean处于未装配的状态,但是你设置为false时,如果你的代码中没有Null检查的话,这个处于未装配状态的属性很可能会出现空指针异常(NullPointerException),相反如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确的指定要选择哪个bean进行装配

@Autowired是Spring特有的注解,如果你不愿意在代码中到处使用Spring的特定注解来完成自动装配任务的话,那么你可以将其替换为@Inject
@Inject

public void setCompactDisc(CompactDisc cd){

this.cd = cd;
}

@Inject来源于java依赖注入规范,同时提供了@Name和@Inject注解,在大多数场景下,@Inject和@Autowired可以替换,你可以根据自己的情况,任意选择一个.


验证自动装配

现在,我们已经在CDPlayer的构造器中添加了@Autowired注解,Spring将把一个可分配给CompactDisc类型的bean注入进来,为了验证这一点,让我们修改一下CDPlayerTest,使其能够借助CDPlayer bean播放CD

    package soundsystem;
    
    
    import static org.junit.Assert.*;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
    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;
    
    import static org.junit.Assert.assertNotNull;
    //使用Spring的SpringJunit4ClassRunner,以便在测试开始的时候创建Spring的应用上下文
    @RunWith(SpringJUnit4ClassRunner.class)
    //在ContextConfiguration告诉他需要在CDPlayConfig中加载配置
    //因为CDPlayerConfig类中包含@ComponentScan,因此最终的应用上下文中应该包含CompactDisc bean
    @ContextConfiguration(classes = CDPlayerConfig.class)
    public class CDPlayTest {
        @Rule
    //    这个方法被列为不推荐使用,就是说他可能在下个jdk版本中被删除
    //    在测试代码中使用System.out.print()是很棘手的事,所以使用StandardOutputStreamLog规则,该规则基于控制台的输出编写断言
        public final StandardOutputStreamLog log = new StandardOutputStreamLog();
    
        @Autowired
        private MediaPlayer player;
    //    @Autowired注解,以便将CompactDisc bean注入到测试代码中
    @Autowired
    private CompactDisc cd;
    @Test
    //简单的测试方法断言cd的属性不为null
    // (不为null就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码中)
    public void  sdShowNotBeNull(){
    
    assertNotNull(cd);
    
    }
    @Test
        public void play(){
        player.play();
       assertEquals(
               "playing Sgt,small love song"+"jim\n",
               log.getLog()
       );
    }
    }

现在,除了注入CompactDisc,我们还将CDPlayer bean注入到测试代码的player成员变量之中(他是更为通用的Mediaplayer类型),在player测试方法中,我们可以调用CDPlayer的plar()方法,并断言他的行为与你预期的一致

现在,我们已经了解了组件扫描和自动装配的基础知识,在之后我们介绍如何处理自动装配的歧义性时,还会继续研究组件扫描

但是现在,我们先将组件扫描和自动装配放在一边,看一下Spring中如何显式的配置bean,首先从java代码编写配置开始



通过java代码装配bean

尽管大多数场景可以实现Spring自动化装配,但是当你想将第三方库中的应用和组件装配到你的应用中,是没办法在他的类上添加@Component和@Autowired注解的,因此就不能使用自动化的装配方案了

在这种情况下,你必须学会显式装配的方式,在进行显式装配的时候,有两种可选择方案:java和Xml
在进行显式配置时,javaConfig是更好的方案
  • 更强大,类型安全,和重构友好
  • 他就是java代码,就像应用程序中的java代码一样
javaConfig和其他的java代码又有所区别
  • 在概念上,他与应用程序中的业务逻辑和领域代码是不同的,javaConfig是配置代码
  • javaConfig不应该包含任何业务代码,也不应该侵入到业务代码中
  • 应该将javaConfig放到单独的包中,使他与其他的应用程序逻辑分离开来
如何javaConfig显式配置Spring
1. 创建配置类
    此时移除了,@ConponentScan,CDPlayerConfig类就没有任何作用了,因为组件扫描发现不了他们
    package soundsystem;
    
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    //@ConponentScan
    public class CDPlayerConfig {
    }
2. 声明简单的bean

要在JavaConfig中声明bean

  • 我们需要编写一个方法

  • 这个方法会创建所需类型的实例

  • 然后给这个方法添加bean注解

      例如
      
      @Bean
      public CompactDisc sgtPeppers(){
          return new SgtPeppers();
      }
      //方法体中包含了最终产生bean实例的逻辑
    

@Bean注解会告诉Spring这个方法将会产生一个对象,该对象要注册为Spring应用上下文中的bean

默认情况下,bean的Id与bean的方法名是一样的,如果你想重命名,也可以通过name属性指定一个不同的名字

     @Bean(name="smallLoveSong")
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }

因为是使用java进行描述的,因此我们可以发挥java提供的所有功能,只要最终返回一个CompactDisc实例即可

例如做点更疯狂的事情,随机选择一首cd播放


    @Bean
    public CompactDisc randomBeatlesCD(){
    
    int choice = (int)Math.floor(Math.random()*4);
    if(choice == 0){
        return new SgtPeppers();
    }else if(choice == 1){
        return new LinJunJie();
    }else if(choice ==2 ){
       return new ZhouJieLun();
    }else(choice == 3){
        return new ChenYiXun();
    }
    }
    
可以尽你最大的想象,发挥java的全部威力来产生bean

我们回过头来看下,在JavaConfig中,如何将CompactDisc注入到CDPlayer中

3.借助JavaConfig实现注入

我们前面声明的CompactDisc bean是很简单的,他自身没有其他的依赖,但现在我们需要声明CDPlayer bean

在javaConfig中装配bean的最简单方式就是引用创建bean的方法
例如:下面就是一种声明CDPlayer的可行方案:

@bean
public CDPlayer cdPlayer(){
    return new CDPlayer(SgtPeppers());
}

在这里并没有使用默认的构造器构建实例,而是调用了需要传入CompactDisc对象的构造器来 创建CDPlayer实例

看起来,CompactDisc是通过调用sgtPerppers()得到的,但实际上,Spring中的bean默认情况下都是单例的,因为sgtPrppers()方法上添加了@bean注解,Spring将会拦截所有对他的调用,并确保直接返回该方法创建的bean,而不是每次都进行实际的调用

除了方法调用,还有一种理解起来更简单的方式
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){

return new CDPlayer(compactDisc);
    
}

原理: cdPlayer()方法请求一个CompactDisc作为参数,当Spring调用cdPlayer方法()创建CDPlayerbean的时候,他会自动装配一个CompactDisc到配置方法中,然后,方法体就可以按照合适的方法来使用他.

  • 通过这种方式引用其他bean通常是最好的选择
  • 你可以将配置分散到多个配置类,Xml文件以及自动扫描和装配bean中(只要功能健全)

我们在这里使用了CDPlayer的构造器实现了DI功能,但是我们完全可以使用其他风格的DI配置

    例如:你想通过Setter方法注入CompactDisc的话
    
    @Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc){
        
        CDPlayer cdPlayer = new CompactDisc();
        cdPlayer.setCompactDisc(compactDisc);
        return cdPlayer;
    }
重点

带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例,构造器和Setter方法只是@bean的两个简单样例,这里所存在的可能性仅仅收到java语言的限制



通过Xml装配bean

学习原因:

Xml不应该是你的第一选择了,但是鉴于已经存在很多基于Xml的Spring配置,所以理解和使用如何在Spring中使用Xml配置还是很重要的,但是这部分知识只是为了维护现有的Xml配置,在新的Spring工作时,最好使用自动化配置和JavaConfig.

  • 1. 创建Xml配置规范

    在Xml配置中,这意味着要创建一个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"
                 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">
          </beans>
    

    可以通过编辑器自动创建Spring Xml文件,并且可以选择可用的命名空间

  • 用来装配bean的最基本的Xml元素包含在Spring-beans模式之中,在上面这个Xml文件当中,是该模式中的一个元素,他是所有Spring配置文件的根元素.

  • 在他没有声明任何bean之前,他是一个没有任何用处的配置,而不是使用JavaConfig和自动化配置.

2.声明一个简单的

要在基于Xml的Spring配置中声明一个bean,我们要使用Spring-beans模式的另外一个元素,元素类似于javaConfig中的@Bean注解.

例如
<bean class="soundsystem.SgtPeppers"/>
//这里声明了一个简单的bean,创建这个bean是通过class属性来指定的,并且要是用全限定的类名

由于没有给定一个Id,在本例中的Id将会是 “soundsystem.SgtPeppers#0”,其中 "#0"是一个计数的形式,如果你声明了另一个SgtPeppers,且没有明确标识,那么他自动得到的ID将是 “soundsystem.SgtPeppers#1”

通常来讲,更好的办法是借助id属性,为每个bean设置一个你自己选择的名字
 <bean id= "CompactDisc" class="soundsystem.SgtPeppers"/>

稍后将这个bean装配到CDPlayer bean之中的时候会用到这个Id

减少繁琐为了减少Xml中繁琐的配置,只对那些需要按名字引用的bean(比如,需要将他们的引用注入到另一个bean中)进行准确的命名
简单bean声明的特质
  • 你不需要直接创建SetPeppers的实例
  • 在这个简单的声明中,我们将bean以字符串的形式设置在了class属性中
借助构造器来初始化bean

在SpringXml中,只有一种声明bean的方式:使用元素并指定class属性.

Xml有两种基本的配置方案可供选择:

  • 元素
  • 使用Spring3.0所引入的c-命名空间

两者区别:是否冗长繁琐

  • 更加冗长,从而导致Xml更加难懂
  • 有些事情c-命名空间难以做到
构造器注入bean引用

在Xml中声明CDPlayer并通过ID引用SgtPeppers

     <bean id="cdPlayer" class="soundsystem.CDPlayer">
    <constructor-arg ref="compactDisc"/>
</bean>


- 当Spring遇到这个bean时,它会创建一个cdPlayer实例
- constructor-arg元素会告知Spring要将一个ID为compactDisc的bean传入CDPlayer的构造器中
作为替代方案,你也可以使用Spring的c-命名空间,是在Spring3.0中引入的,他是在Xml中更为简洁的描述构造器参数的方式.要使用它的话,必须在Xml顶部声明其他模式

在c- 命名空间和模式声明之后,我们就可以使用他来声明构造器参数了

例如:
//用c-命名空间来声明构造器函数
<bean id = "cdPlayer" class = "soundsystem.CDPlayer"
       c:cd-ref="compactDisc"/>
       
- c   命名空间的前缀
- cd  构造器参数名
- ref 注入bean的引用
- " " 要注入的bean的Id
c-命名空间的优化

原因:c-命名空间直接引用了参数的名称,这需要在编译代码的时候,将调试标志(debug symbol)保存在类代码中,如果你优化构建过程,将调试标志除掉,那么这种方式就无法正常执行了

替代方案:

1.使用参数在整个参数列表的位置信息

 //使用索引来识别构造器参数
 <bean id = "cdPlayer" class = "soundsystem.CDPlayer"
       c:_0-ref="compactDisc"/>

2.只有一个构造器参数时(这是c-命名空间中最奇特的一个)

//相比上面连索引都不用带
 <bean id = "cdPlayer" class = "soundsystem.CDPlayer"
       c:_-ref="compactDisc"/>

接下来看如何将字面量值装配到(literal value)装配到构造器之中

有时候,我们需要做的只是一个字面量来配置对象

    假设
    package soundsystem;

    public class BlankDisc implements CompactDisc{

private String title;
private String artist;


public BlankDisc(String title, String artist) {
    this.title = title;
    this.artist = artist;
}

@Override
public void play() {
    System.out.println("playing" +title+"song"+artist);
}
}

相比之前的SgtPeppers,你更能指定歌曲与艺术家,现在我们将SgtPeppers换下来

 1. //使用<constructor-arg>元素进行构造器注入

<bean id = "CompactDisc"
      class = "soundsystem.BlackDisk">
      
    <constructor-arg value="Small Love Song"/>
    <constructor-arg value="Jim"/>

<bean/>


 2.//使用c-命名空间
 
 <bean id="CompactDisc" class = "soundsystem.BlackDisc"
      c:_0 = "Small Love Song"
      c:_1 = "Jim"/>

在装配bean引用和字面量值方面,和c-命名空间的功能是相同的.

但是有一种情况是能够实现,但是c-命名空间做不到的,让我们看看如何让将集合装配到构造器参数中

装配集合

到现在为止,我们假设CompactDisc只有艺术家和歌曲的内容,但实际上CD上会有十多个磁道,每个磁道上包含一首歌

    例如:CompactDisc为真正的磁带建模,他应该是这样
    
    package soundsystem;
    
    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;
}

@Override
public void play() {
    System.out.println("playing" +title+"song"+artist);
    for(String track:tracks){
        System.out.println("-track:"+track);

    }
}
    }

这个变更会对Spring如何配置产生影响,在声明bean的时候,我们必须要提供一个磁道列表

方式:

  • 最简单的方式是将列表设置为null,将null传给构造器.但这不是解决问题的好办法,但在注入期他能正常执行(当调用play()方法时,他会出现NullPointException异常)

  • 更好的办法是提供一个磁道名称的列表, 要达到这一点,我们可以有多个可选方案,首先,可以用元素将其声明为一个列表

    //这表明一个包含值的列表将会传递到构造器中,其中value元素用来指定列表中的每个元素

    Small Love Song B
    Small Love Song C
    Small Love Song D
    Small Love Song E

与之类似,我们也可以使用元素代替value,实现bean引用列表的装配.

例如:有一个Discography类
//他的构造函数如下
public Discography(String artist,List<CompactDisc> cds){...};

那么可以用下面的方式配置bean

 <bean id = "beatlesDiscography"
      class = "soundsystem.Discography">

<constructor-arg value="The beats"/>
<constructor-arg >

//这表明一个包含值的列表将会传递到构造器中,其中value元素用来指定列表中的每个元素
        <List>

<ref bean = "ZhouJieLun"/>
<ref bean = "LinJunJie"/>
<ref bean = "ChenYiXun"/>
<ref bean = "CaiJianYa"/>

<List/>
        <constructor-arg/>

<bean/>

当构造器参数类型是java.unit.List时,使用 List元素时合情合理的,尽管如此,我们还可以按照同样的方式使用元素

 <bean id = "CompactDisc"
      class = "soundsystem.BlackDisk">

<constructor-arg value="Small Love Song"/>
<constructor-arg value="Jim"/>
<constructor-arg >

//这表明一个包含值的列表将会传递到构造器中,其中value元素用来指定列表中的每个元素
        <set>
<value> Small Love Song B</value>
<value> Small Love Song C</value>
<value> Small Love Song D</value>
<value> Small Love Song E</value>

<set/>
        <constructor-arg/>

<bean/>

和元素的区别

  • set的话,所有重复的值都会被忽略掉,存放顺序也不会受到保证
  • 但无论在那种情况下,和都可以用来装配List,Set甚至数组

目前,使用c-命名空间的属性无法达到实现装配集合的功能

通过Xml来设置属性

选择构造器注入和属性注入的情况

  • 对强依赖使用构造器注入

  • 对可选性依赖使用属性注入

    CDPlayer没有任何的构造器(除了隐含的默认构造器),他也没有任何的强依赖
    //将其声明为Spring bean

    但因为我们没有注入CDPlayer的CompactDisc属性,因此在测试中会出现空指针异常
    按照下面方法修改Xml配置可以解决该问题


           <bean/>
    

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

原理:他通过ref引用了Id为compactDisc的bean,并将其注入到compactDisc属性中(通过setCompactDisc()方法),现在进行测试就可以通过

元素替代方案

  • Spring为

    p-命名空间的属性与c命名空间相似

    • p:命名空间声明
    • compactDisc:属性名
    • ref:注入bean引用
    • compactDisc:所注入的bean
    将字面量注入到属性中

    属性注入字面量与构造器参数非常相似

        //BlackDisc完全通过属性注入,而不是构造器注入
        
        package soundsystem;
    
    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 setTitle(String title) {
        this.title = title;
    }
    
    public void setArtist(String artist) {
        this.artist = artist;
    }
    
    public void setTracks(List<String> tracks) {
        this.tracks = tracks;
    }
    
    @Override
    public void play() {
        System.out.println("playing" +title+"song"+artist);
        for(String track:tracks){
            System.out.println("-track:"+track);
    
        }
    }
    }
    

    现在他不再要求我们装配任何的属性,现在创建一个属性都是空的BlankDiscbean

     <bean id = "reallyBlankDisc"
             class = "soundsystem.BlankDisc"
            />
    

    下一步,我们需要借助的value属性来装配这些属性

     <bean id = "CompactDisc"
          class = "soundsystem.BlackDisk">
    
    <properly name ="title" value="Small Love Song"/>
    <properly name = "artist" value="Jim"/>
    <properly name = "tracks">
    
    //这表明一个包含值的列表将会传递到构造器中,其中value元素用来指定列表中的每个元素
            <List>
    <value> Small Love Song B</value>
    <value> Small Love Song C</value>
    <value> Small Love Song D</value>
    <value> Small Love Song E</value>
    
    <List/>
            <properly/>
    
    <bean/>
    
    我们也可以通过p-命名空间来装配这些属性,但是它不能装配集合,我们可以通过util-命名空间的一些功能来简化BlankDisc bean
        //同样要现在Xml中声明util-命名空间及其模式
    

    util-命名空间的功能之一就是util:list元素

    原理:他会创建一个列表的bean,并且声明到单独的bean之中

          <util:list id = "trackList">
    <value> Small Love Song B</value>
    <value> Small Love Song C</value>
    <value> Small Love Song D</value>
    <value> Small Love Song E</value>
    
    </util:list>
    

    然后就像使用其他bean那样,将列表bean注入带tracks属性中

     <bean id = "CompactDisc"
          class = "soundsystem.BlackDisk"
    
     p:title ="Small Love Song"
     p:artist ="Jim"
     p:tracks-ref = "trackList"/>
    
    util:List只是util-命名空间中的多个元素之一

    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,包含值和引用


    导入和混合配置

    在典型的Spring应用中,我们会同时使用自动化和显式配置

    关于混合配置: 需要明白自动装配时,它并不在意bean的来源

    假如希望将BlankDisc bean拆分到他自己的配置文件中(比如cd-config.xml),在Xml配置文件中,可以使用元素来引用该文件

    <bean class="soundsystem.CDConfig"/>
    
    <bean id = "cdPlayer"
        
        class="soundsystem.CDPlayer"
        c:cd-ref="compactDisc"/>
    

    现在,我们假设将其配置在JavaConfig当中,CDPlayer则继续配置在Xml当中

    <bean class="soundsystem.CDConfig"/>
    
    <bean id = "cdPlayer"
        
        class="soundsystem.CDPlayer"
        c:cd-ref="compactDisc"/>
    

    采用这样的方式,两种配置被组合在一起

    采用更高层次的配置文件,这个文件不声明任何bean,只是负责将两个或者更多的配置组合起来
        <bean class= "soundsystem.CDConfig"/>
        <import resource = "cdplayer-config.xml">
    
    不管是使用JavaConfig还是Xml进行装配,推荐创建一个根配置,这个配置会将更多的配置类和Xml文件组合起来

    小结

    • Spring框架的核心就是Spring容器,容器负责管理应用中组件的生命周期,它会创建这些组件并保证他们的依赖能够得到满足
    • 尽可能的使用自动化配置,以避免显式配置带来的维护成本
    • 不得不使用显式配置的时候,优先使用javaConfig
    • java语言描述比Xml功能强大,易于重构和类型安全
    • DI(依赖注入)是Spring技术的重要组成部分
内容概要:本文详细探讨了机组组合优化模型的构建,旨在通过合理安排各类发电机组的启停计划和优化出力分配,实现电力系统在经济性和稳定性上的最佳平衡。文章首先介绍了电力系统的四大主要组件——传统火电机组、风电机组、光伏机组和储能系统的参数及运行特性。接着,围绕最小化系统总运行成本这一目标,设计了优化目标函数,并明确了包括功率平衡约束、机组出力上下限约束、风光发电功率约束、弃风弃光约束、爬坡速率约束、储能系统荷电状态约束、充放电功率约束和充放电互斥约束在内的多项约束条件。最后,文章列出了求解机组组合优化模型所需的关键变量,如传统机组的开停状态、机组出力、启停成本、风电光伏实际出力、弃风弃光比例及储能系统的充放电功率和荷电状态,以实现系统的经济调度和可再生能源的最大化利用。 适合人群:从事电力系统研究、规划和调度工作的工程师和技术人员,以及对电力系统优化感兴趣的科研人员。 使用场景及目标:①帮助电力系统工程师理解不同类型发电机组的特点及其对系统稳定性、经济性和环保性的影响;②为制定合理的电力系统调度策略提供理论依据和技术支持;③促进可再生能源的有效整合,提高电力系统的灵活性和可靠性。 其他说明:本文提供的模型和方法不仅适用于当前的电力系统,也可为未来含高比例可再生能源接入的电力系统提供参考。文中涉及的具体数学公式和参数设定为实际应用提供了详细的指导,有助于提升电力系统的运行效率和经济效益。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值