Spring IOC官方文档学习笔记(十二)之基于Java的容器配置 ...

打印 上一主题 下一主题

主题 870|帖子 870|积分 2610

1.@Bean与@Configuration
(1) 标注于类之上的@Configuration注解与标注于方法之上的@Bean注解是支持基于Java的容器配置的核心,被@Bean注解标注的方法用于实例化bean并将其注入至容器中,它与基于xml配置中的标签起着相同的作用,@Bean可用在任何被@Component注解标注的类中,不过绝大部分情况下它们都被用于被@Configuration注解标注的类中;被@Configuration注解标注的类通常作为bean的定义源,如同基于xml配置中的标签,此外,还可在@Configuration标注的类中配置bean之间的依赖关系,如下
  1. //两个普通的类,其中ExampleB依赖ExampleA
  2. public class ExampleA { }
  3. public class ExampleB {
  4.     private ExampleA exampleA;
  5.     public ExampleB(ExampleA exampleA) {
  6.         this.exampleA = exampleA;
  7.     }
  8. }
  9. //配置类
  10. @Configuration
  11. public class Config {
  12.     //注入bean ExampleA
  13.     @Bean
  14.     public ExampleA exampleA() {
  15.         return new ExampleA();
  16.     }
  17.     //调用exampleA()方法来配置ExampleB
  18.     @Bean
  19.     public ExampleB exampleB() {
  20.         return new ExampleB(exampleA());
  21.     }
  22.     //上下这两个exampleB方法等价,注意,在下面这个例子中,Spring会自动帮我们注入ExampleA对象
  23. //    @Bean
  24. //    public ExampleB exampleB(ExampleA exampleA) {
  25. //        return new ExampleB(exampleA);
  26. //    }
  27. }
  28. //启动容器,打印注入的对象是否相同
  29. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
  30. System.out.println(ctx.getBean(ExampleB.class).getExampleA() == ctx.getBean(ExampleA.class));
  31. //观察结果,可见Spring执行了依赖注入,注入了容器中的ExampleA,而非new出来了一个新的ExampleA
  32. true
复制代码
(2) @Bean也可用在任何被@Component注解标注的类中,此时,我们称其为lite @Bean模式,而在这种lite模式下,我们不能配置bean之间的依赖关系,如下所示
  1. //取上面的例子,其他保持不变,只将Config类上的@Configuration注解变更为@Component注解,此时其中的@Bean注解就处于lite @Bean模式
  2. @Component
  3. public class Config {
  4.     //保持不变...
  5. }
  6. //启动容器,观察打印结果,为false,可见此时Spring并没有执行依赖注入,而是直接new出来了一个新的ExampleA给ExampleB,因此在这种lite模式下,我们不能配置bean之间的依赖关系
复制代码
2.通过AnnotationConfigApplicationContext实例化Spring容器
(1) AnnotationConfigApplicationContext作为Spring的容器,它不仅可以接受@Configuration类(同时这个类本身也会被注册为一个bean)作为参数,还可以接受@Component类或用JSR-330注解标注的类作为参数,如下
  1. //例一: 一个Spring配置类
  2. @Configuration
  3. public class Config {
  4. }
  5. public static void main(String[] args) {
  6.     //使用@Configuration类作为输入,完全摆脱掉基于xml的配置  
  7.     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
  8.     //打印,会观察到Config也被注入到了容器中
  9.     Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
  10. }
  11. //例二: ExampleB被JSR 330标准注解标注
  12. @Component
  13. public class ExampleA {
  14. }
  15. @Named
  16. public class ExampleB {
  17.     @Inject
  18.     private ExampleA exampleA;
  19.     public ExampleA getExampleA() {
  20.         return exampleA;
  21.     }
  22. }
  23. public static void main(String[] args) {
  24.     //使用@Component类或用JSR-330注解标注的类作为参数,容器会对它们进行依赖注入
  25.     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ExampleA.class, ExampleB.class);
  26.     System.out.println(ctx.getBean(ExampleB.class).getExampleA());
  27. }
复制代码
(2) 可以使用AnnotationConfigApplicationContext类的register(Class…​)方法编程式的向容器中注册,如下
  1. public class ExampleA { }
  2. public static void main(String[] args) {
  3.     //使用无参构造函数
  4.     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  5.     //编程式的向容器中注册bean ExampleA
  6.     ctx.register(ExampleA.class);
  7.     //refresh()方法用于处理bean,一定要调用此方法,否则容器将抛出异常
  8.     ctx.refresh();
  9.     System.out.println(ctx.getBean(ExampleA.class));
  10. }
复制代码
(3) 我们可以使用@ComponentScan(basePackages = "...")注解或标签来开启注解扫描,此外,AnnotationConfigApplicationContext也提供了scan(String…​)方法来开启注解扫描,如下
  1. //ExampleA位于cn.example.spring.boke包下
  2. @Component
  3. public class ExampleA { }
  4. public static void main(String[] args) {
  5.     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  6.     //开启注解扫描,扫描cn.example.spring.boke"包下的bean
  7.     ctx.scan("cn.example.spring.boke");
  8.     ctx.refresh();
  9.     System.out.println(ctx.getBean(ExampleA.class));
  10. }
复制代码
(4) 在web环境中,可以使用AnnotationConfigWebApplicationContext,来配置ContextLoaderListener监听器或DispatcherServlet,具体配置细节可参考官方文档
3.使用@Bean注解
(1) @Bean注解是一个方法级别的注解,类似于基于xml配置中的标签,我们可以在@Configuration类或@Component类中使用该注解,它常用于声明一个bean,如下
  1. @Configuration
  2. public class Config {
  3.     //@Bean注解用于将方法的返回值注册为容器中的一个bean,方法返回类型就是该bean的类型,默认情况下,方法名就是bean的名称
  4.     //下面这个例子:向容器中注入一个类型为ExampleA,名称为exampleA的bean
  5.     @Bean
  6.     public ExampleA exampleA() {
  7.         return new ExampleA();
  8.     }
  9. }
复制代码
(2) 针对@Bean方法的返回类型,也有些细节需要注意,如下所示
  1. //有两个接口A和B
  2. public interface A { }
  3. public interface B { }
  4. //ExampleA实现了这两个接口
  5. public class ExampleA implements A, B { }
  6. //而另一个bean ExampleB,它依赖了类型为B的bean
  7. @Component
  8. public class ExampleB {
  9.     @Autowired
  10.     private B b;
  11.     public B getB() {
  12.         return b;
  13.     }
  14. }
  15. //配置类
  16. @Configuration
  17. @ComponentScan(basePackages = "cn.example.spring.boke")
  18. public class Config {
  19.     //注意,我们返回了ExampleA的实例,不过却将返回类型声明为了A
  20.     @Bean
  21.     public A exampleA() {
  22.         return new ExampleA();
  23.     }
  24. }
  25. //接着,启动容器,会发现容器抛出NoSuchBeanDefinitionException: No qualifying bean of type 'cn.example.spring.boke.B'的异常,可见容器将返回的ExampleA仅视作了A类型然后就用于注入,虽然它也可以被视作B类型
  26. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
  27. System.out.println(ctx.getBean(ExampleB.class).getB());
  28. //针对上面的情况,有两种办法解决:
  29. //方法一:最直接,直接返回类型B而非A,如下
  30. @Bean
  31. public B exampleA() {
  32.     return new ExampleA();
  33. }
  34. //方法二:其他的不变,只在我们的ExampleB上,加上@Lazy注解,让容器有时间去充分的识别ExampleA,使它意识到ExampleA还可以视作B类型
  35. @Component
  36. @Lazy
  37. public class ExampleB {
  38.     //省略...
  39. }
复制代码
(3) @Bean方法可以拥有任意数量的参数,这些参数就是这个bean的依赖项,如下
  1. @Configuration
  2. public class Config {
  3.    
  4.     @Bean
  5.     public ExampleB exampleB() {
  6.         return new ExampleB();
  7.     }
  8.    
  9.     //ExampleA依赖了ExampleB,Spring会为我们自动注入容器中的这个ExampleB对象,类似于基于构造函数的依赖注入
  10.     @Bean
  11.     public ExampleA exampleA(ExampleB exampleB) {
  12.         return new ExampleA(exampleB);
  13.     }
  14. }
复制代码
(4) 同普通的bean,由@Bean方法注入的bean也支持由JSR-250所定义的生命周期回调注解,支持InitializingBean,DisposableBean或Lifecycle等接口,支持各种Aware接口用于注入容器内置组件,同时@Bean还提供了initMethod和destroyMethod属性用于配置初始化和销毁回调,同标签的属性,如下
  1. public class ExampleA implements ApplicationContextAware {
  2.    
  3.     private ApplicationContext applicationContext;
  4.     @Override
  5.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  6.         this.applicationContext = applicationContext;
  7.     }
  8.    
  9.     @PostConstruct
  10.     public void init() {
  11.         System.out.println("init...");
  12.     }
  13.    
  14.     public void destroy() {
  15.         System.out.println("destroy...");
  16.     }
  17. }
  18. @Configuration
  19. public class Config {
  20.     @Bean(destroyMethod = "destroy")
  21.     public ExampleA exampleA() {
  22.         return new ExampleA();
  23.     }
  24. }
复制代码
(5) 默认情况下,当我们使用基于Java的配置来定义一个bean的时候,如果在这个bean中有声明public修饰的close或shutdown方法,那么这些方法会随着该bean的销毁回调的触发而同时被调用,这是Spring提供的一个默认机制,无需任何的配置都会生效,但如果我们想关闭掉这一机制,可通过设置@Bean中的属性destroyMethod=""来达到这一目的,如下
  1. //设置destroyMethod = "",禁用掉Spring会自动触发public修饰的close或shutdown方法的机制
  2. @Bean(destroyMethod = "")
  3. public ExampleA exampleA() {
  4.     return new ExampleA();
  5. }
复制代码
(6) 我们可以通过使用@Scope注解来指定bean的作用域,如下
  1. @Configuration
  2. public class Config {
  3.     //由@Bean方法所声明的bean的作用域默认为singleton
  4.     //@Scope注解还可用于类上
  5.     @Bean
  6.     @Scope("prototype")
  7.     public ExampleA exampleA() {
  8.         return new ExampleA();
  9.     }
  10. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表