Spring装配bean基础

作者: xdoyf | 来源:发表于2017-03-13 16:32 被阅读48次

    Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,我们需要做的就是告诉Spring要创建哪些bean以及这些bean的依赖情况。
    Spring提供了三种主要的装配机制,选择哪种方案很大程度上就是个人喜好的问题,你尽可以选择自己最喜欢的方式。

    1.隐式的bean发现机制和自动装配

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

    • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
    • 自动装配(autowiring):Spring自动满足bean之间的依赖。
       首先,定义一个接口:
    package soundsystem;
    public interface CompactDisc {
            void play{};
    }
    

    以及,CompactDisc实现类SgtPeppers:

    package soundsystem;
    import org.springframework.stereotype.Component;
    @Component("LonelyHeartsClub")  // or @Named
    public class SgtPeppers implements CompactDisc {
              private String title = "Sgt. Pepper's Lonely Hearts Club Band";  
              private String artist = "The Beatles";
    
              public void play() {
                  System.out.println("Playing " + title + " by " + artist);
              }
    }
    

    需要注意的是,SgtPeppers类使用了@Component注解,这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建一个id为LonelyHeartsClub的bean。不过,组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

       package soundsystem;
       import org.springframework.context.annotation.ComponentScan;
       import org.springframework.context.annotation.Configuration;
    
       @Configuration
       @ComponentScan
       public class CDPlayerConfig { 
    }
    

    CDPlayerConfig使用了@Configuration注解,表明这是一个spring的配置类,而@ComponentScan注解则能够在Spring中启用组件扫描功能。如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现CompactDisc,并且会在Spring中自动为其创建一个bean。如果你想要将config类放入一个独立的包中以方便管理,也可以为@ComponentScan传入你想要作为扫描基础包的包名,如:

     @ComponentScan("org.xxx.packagename")
    

    或者

    @ComponentScan(basePackages={"org.xxx.packagename","org.xxx.otherpackagename"})
    

    来指定多个基础包。
     为了以后方便重构(有可能改变包名),那么可以采用更加安全的方式

       @ComponentScan(basePackageClasses={abc.class, 123.class})
    

    不再使用String类型的名称来指定包,为basePackageClasses属性所设置的数组中包含了类。这些类所在的包将会作为组件扫描的基础包。
     如果你更倾向于使用XML来启用组件扫描的话,那么可以使用Spring context命名空间的<context:component-scan>元素。

       <?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:mvc="http://www.springframework.org/schema/mvc"
      xmlns:context="http://www.springframework.org/schema/context"   xmlns:aop="http://www.springframework.org/schema/aop"
         xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
       <context:component-scan base-package="package" />
    

    显然,在实际的项目开发中,bean之间的关系不会像上文所列举的那样简单,不同的组件往往相互依赖来完成复杂的任务,所以,我们需要了解一下Spring自动化配置的另外一方面内容,那就是自动装配。
     通过自动装配,将一个CD注入到CDPlayer之中:

       package soundsystem;
       import org.springframework.beans.factory.annotation.Autowired;
       import org.springframework.stereotype.Component;
    
       @Component // or @Named
       public class CDPlayer implements MediaPlayer {
         private CompactDisc cd;
    
         @Autowired // or @Inject
         public CDPlayer(CompactDisc cd) {
           this.cd = cd;
         }
    
         public void play() {
           cd.play();
         }
       }
    

    构造器上添加了@Autowired注解,这表明当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且会传入一个实现了CompactDisc的bean。
     @Autowired注解不仅能够用在构造器上,还能用在类的任何方法上。比如说,如果CDPlayer有一个insertDisc()方法,那么可以采用如下的注解形式进行自动装配:

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

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

       @Autowired(required=false)
       public insertDisc(CompactDisc cd) {
           this.cd = cd;
       }
    

    将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。但是,把required属性设置为false时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException。

    2.通过Java代码装配bean

    大部分情况下组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通时,比如说,你想要将第三方库中
    的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。

    让我们修改一下CDPlayerConfig类:

       package soundsystem;
       import org.springframework.context.annotation.Configuration;
    
       @Configuration
       public class CDPlayerConfig { 
       }
    

    我们去掉了@ComponentScan注解,意味着spring容器不再自动扫描我们带有@Component(或@Named)注解的类了,下面我们将在CDPlayerConfig类中显式地配置之前创建的类。
     我们在config类中加入以下代码:

       package soundsystem;
       import org.springframework.context.annotation.Configuration;
    
       @Configuration
       public class CDPlayerConfig { 
          @Bean
          public CompactDisc sgtPeppers() {
                return new SgtPeppers();
          }
       }
    

    @Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。bean的名字默认为方法名,如有需要,可以使用如下方法修改:

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

    使用java配置类进行装配同样要考虑依赖注入的问题,下面就是一种声明CDPlayer的可行方案:

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

    看起来,cdPlayer所依赖的CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。因为sgtPeppers()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用,也就是说sgtPeppers仍然是单例的。
     但是上面的写法有一个不足,那就是如果sgtPeppers()方法没有定义在CDPlayerConfig中,而是定义在另一个config或xml配置文件中,cdPlayer方法是无法调用的,所以,这种方式并不合理,来看另一种方法:

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

    在这里,cdPlayer()方法请求一个CompactDisc作为参数。当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配(不管以何种方式)一个CompactDisc到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,cdPlayer()方法也能够将CompactDisc注入到CDPlayer的构造器中,而且不用明确引用CompactDisc的@Bean方法。

    再次强调一遍,带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。

    3.通过XML装配bean

    // TODO

    4.解决不同配置方式之间的依赖关系

    现在,我们已经知道cdPlayer bean需要依赖于compactDisc bean,假如我们的cdPlayer bean依然由CDPlayerConfig来装配,而compactDisc bean则改由CDConfig来装配:

       package soundsystem;
    
       import org.springframework.context.annotation.Bean;
       import org.springframework.context.annotation.Configuration;
    
       @Configuration
       public class CDConfig {
         @Bean
         public CompactDisc compactDisc() {
           return new SgtPeppers();
         }
       }
    

    一种方法就是在CDPlayerConfig中使用@Import注解导入CDConfig:

       package soundsystem;
       import org.springframework.context.annotation.Configuration;
       import org.springframework.context.annotation.Import;
       import org.springframework.context.annotation.Bean;
    
       @Configuration
       @Import(CDConfig.class)
       public class CDPlayerConfig { 
           @Bean
           public CDPlayer cdPlayer(CompactDisc  compactDisc ) {
                return new CDPlayer(compactDisc );
           }
       }
    

    或者采用一个更好的办法,也就是不在CDPlayerConfig中使用@Import,而是创建一个更高级别的SoundSystemConfig,在这个类中使用@Import将两个配置类组合在一起:

    package soundsystem;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportResource;
    
    @Configuration
    @Import({CDPlayerConfig.class, CDConfig.class})
    public class SoundSystemConfig {
    
    }
    

    同样的,假如还有一部分bean定义在bean-config.xml中,则需要用@ImportResource注解导入:

    package soundsystem;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportResource;
    
    @Configuration
    @Import({CDPlayerConfig.class, CDConfig.class})
    @ImportResource("classpath:bean-config.xml")
    public class SoundSystemConfig {
    
    }
    

    相关文章

      网友评论

        本文标题:Spring装配bean基础

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