SSM+shiro等我爱编程

Spring装配bean基础

2017-03-13  本文已影响48人  xdoyf

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

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

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

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 {

}
上一篇下一篇

猜你喜欢

热点阅读