飞道的博客

想拿月薪4万,99%公司必备的技术,你得先掌握好啊!

391人阅读  评论(0)

面试问题

  1. @Scope是做什么的?常见的用法有几种?

  2. @DependsOn是做什么的?常见的用法有几种?

  3. @ImportResource干什么的?通常用在什么地方?

  4. @Lazy做什么的,通常用在哪些地方?常见的用法有几种?

上面几个问题中涉及到了4个注解,都是比较常用的,下面我们来一一介绍。

@Scope:指定bean的作用域

用法

关于什么是bean的作用域,可以去看一下之前的一篇文章:Spring系列第6篇:玩转bean scope,避免跳坑里!

@Scope用来配置bean的作用域,等效于bean xml中的bean元素中的scope属性。

看一下其源码:


   
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @ interface Scope {
  5.     @AliasFor( "scopeName")
  6.     String value()  default  "";
  7.     @AliasFor( "value")
  8.     String scopeName()  default  "";
  9.     ScopedProxyMode proxyMode()  default ScopedProxyMode.DEFAULT;
  10. }

@Scope可以用在类上和方法上

参数:value和scopeName效果一样,用来指定bean作用域名称,如:singleton、prototype

常见2种用法

  1. 和@Compontent一起使用在类上

  2. 和@Bean一起标注在方法上

案例1:和@Compontent一起使用在类上


   
  1. @Component
  2. @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) //@1
  3. public class ServiceA {
  4. }

上面定义了一个bean,作用域为单例的。

@1:ConfigurableBeanFactory接口中定义了几个作用域相关的常量,可以直接拿来使用,如:

String SCOPE_SINGLETON = "singleton";

String SCOPE_PROTOTYPE = "prototype";

案例2:和@Bean一起标注在方法上

@Bean标注在方法上,可以通过这个方法来向spring容器中注册一个bean,在此方法上加上@Scope可以指定这个bean的作用域,如:


   
  1. @Configurable
  2. public class MainConfig2 {
  3.     @Bean
  4.     @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  5.     public ServiceA serviceA() {
  6.          return  new ServiceA();
  7.     }
  8. }

@DependsOn:指定当前bean依赖的bean

用法

前面有篇文章中介绍了bean xml中depend-on的使用,建议先看一下:Spring系列第9篇:depend-on到底是干什么的?

@DependsOn等效于bean xml中的bean元素中的depend-on属性。

spring在创建bean的时候,如果bean之间没有依赖关系,那么spring容器很难保证bean实例创建的顺序,如果想确保容器在创建某些bean之前,需要先创建好一些其他的bean,可以通过@DependsOn来实现,@DependsOn可以指定当前bean依赖的bean,通过这个可以确保@DependsOn指定的bean在当前bean创建之前先创建好

看一下其源码:


   
  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @ interface DependsOn {
  5.     String[] value()  default {};
  6. }

可以用在任意类型和方法上。

value:string类型的数组,用来指定当前bean需要依赖的bean名称,可以确保当前容器在创建被@DependsOn标注的bean之前,先将value指定的多个bean先创建好。

常见2种用法

  1. 和@Compontent一起使用在类上

  2. 和@Bean一起标注在方法上

案例1:和@Compontent一起使用在类上

下面定义3个bean:service1、service2、service3;service1需要依赖于其他2个service,需要确保容器在创建service1之前需要先将其他2个bean先创建好。

看代码:

Service2


   
  1. package com.javacode2018.lesson001.demo27.test3;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service2 {
  5.     public Service2() {
  6.         System.out. println( "create Service2");
  7.     }
  8. }

Service3


   
  1. package com.javacode2018.lesson001.demo27.test3;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service3 {
  5.     public Service3() {
  6.         System.out. println( "create Service3");
  7.     }
  8. }

Service1


   
  1. package com.javacode2018.lesson001.demo27.test3;
  2. import org.springframework.context.annotation.DependsOn;
  3. import org.springframework.stereotype.Component;
  4. @DependsOn({ "service2""service3"})  //@1
  5. @Component
  6. public class Service1 {
  7.     public Service1() {
  8.         System.out. println( "create Service1");
  9.     }
  10. }

@1:使用了@DependsOn,指定了2个bean:service2和service3,那么spring容器在创建上面这个service1的时候会先将@DependsOn中指定的2个bean先创建好

来个配置类


   
  1. package com.javacode2018.lesson001.demo27.test3;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig3 {
  5. }

测试用例


   
  1. @Test
  2. public void test3() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig3.class);
  4.     System.out. println(context.getBean(Service1.class));
  5. }

运行输出


   
  1. create Service2
  2. create Service3
  3. create Service1
  4. com.javacode2018.lesson001.demo27.test3.Service1@ 9f116cc

从输出中可以看到,spring容器在创建service1之前,先将service2和service3创建好了。

案例2:和@Bean一起标注在方法上

下面通过配置文件的方式来创建bean,如下:


   
  1. package com.javacode2018.lesson001.demo27.test4;
  2. import org.springframework.beans.factory.annotation.Configurable;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.DependsOn;
  5. @Configurable
  6. public class MainConfig4 {
  7.     @Bean
  8.     @DependsOn({ "service2""service3"}) //@1
  9.     public Service1 service1() {
  10.          return  new Service1();
  11.     }
  12.     @Bean
  13.     public Service2 service2() {
  14.          return  new Service2();
  15.     }
  16.     @Bean
  17.     public Service3 service3() {
  18.          return  new Service3();
  19.     }
  20. }

上面是一个spring的配置类,类中3个方法定义了3个bean

@1:这个地方使用了@DependsOn,表示service1这个bean创建之前,会先创建好service2和service3

来个测试用例


   
  1. @Test
  2. public void test4() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig4.class);
  4.     System.out. println(context.getBean(com.javacode2018.lesson001.demo27.test4.Service1.class));
  5. }

运行输出


   
  1. create Service2
  2. create Service3
  3. create Service1
  4. com.javacode2018.lesson001.demo27.test4.Service1@ 6e20b53a

@ImportResource:配置类中导入bean定义的配置文件

用法

有些项目,前期可能采用xml的方式配置bean,后期可能想采用spring注解的方式来重构项目,但是有些老的模块可能还是xml的方式,spring为了方便在注解方式中兼容老的xml的方式,提供了@ImportResource注解来引入bean定义的配置文件。

bean定义配置文件:目前我们主要介绍了xml的方式,还有一种properties文件的方式,以后我们会介绍,此时我们还是以引入bean xml来做说明。

看一下这个注解的定义:


   
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. public @ interface ImportResource {
  5.     @AliasFor( "locations")
  6.     String[] value()  default {};
  7.     @AliasFor( "value")
  8.     String[] locations()  default {};
  9.     Class<? extends BeanDefinitionReader> reader()  default BeanDefinitionReader.class;
  10. }

通常将其用在配置类上。

有3个参数:

  • value和locations效果一样,只能配置其中一个,是一个string类型的数组,用来指定需要导入的配置文件的路径。

  • reader:用来指定bean定义的读取器,目前我们知道的配置bean的方式有xml文件的方式,注解的方式,其实还有其他的方式,比如properties文件的方式,如果用其他的方式,你得告诉spring具体要用那种解析器去解析这个bean配置文件,这个解析器就是BeanDefinitionReader,以后我们讲BeanDefinition的时候再细说。

资源文件路径的写法

通常我们的项是采用maven来组织的,配置文件一般会放在resources目录,这个目录中的文件被编译之后会在target/classes目录中。

spring中资源文件路径最常用的有2种写法:

  1. 以classpath:开头:检索目标为当前项目的classes目录

  2. 以classpath*:开头:检索目标为当前项目的classes目录,以及项目中所有jar包中的目录,如果你确定jar不是检索目标,就不要用这种方式,由于需要扫描所有jar包,所以速度相对于第一种会慢一些

那我们再来说classpath:和classpath*:后面的部分,后面的部分是确定资源文件的位置地方,几种常见的如下:

相对路径的方式


   
  1. classpath:com/javacode2018/lesson001/demo27/test5/beans.xml
  2. 或者
  3. classpath*:com/javacode2018/lesson001/demo27/test5/beans.xml

/:绝对路径的方式

classpath:/com/javacode2018/lesson001/demo27/test5/beans.xml

*:文件通配符的方式

classpath:/com/javacode2018/lesson001/demo27/test5/beans-*.xml

会匹配test5目录中所有以beans-开头的xml结尾的文件

*:目录通配符的方式


   
  1. classpath:/com/javacode2018/lesson001/demo27 /*/beans-*.xml

会匹配demo27中所有子目录中所有以beans-开头的xml结尾的文件,注意这个地方只包含demo27的子目录,不包含子目录的子目录,不会进行递归

**:递归任意子目录的方式

classpath:/com/javacode2018/**/beans-*.xml

**会递归当前目录以及下面任意级的子目录

ok,继续回到@ImportResource上来,来看案例

案例代码

来2个类,这两个类我们分别用2个xml来定义bean

ServiceA


   
  1. package com.javacode2018.lesson001.demo27.test5;
  2. public class ServiceA {
  3. }

ServiceB


   
  1. package com.javacode2018.lesson001.demo27.test5;
  2. public class ServiceB {
  3. }

beans1.xml来定义serviceA这个bean,如下


   
  1. <?xml version= "1.0" encoding= "UTF-8"?>
  2. <beans xmlns= "http://www.springframework.org/schema/beans"
  3.        xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  4.        xsi:schemaLocation= "http://www.springframework.org/schema/beans
  5.     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
  6.     <bean id= "serviceA" class= "com.javacode2018.lesson001.demo27.test5.ServiceA"/>
  7. </beans>

beans2.xml来定义serviceB这个bean,如下


   
  1. <?xml version= "1.0" encoding= "UTF-8"?>
  2. <beans xmlns= "http://www.springframework.org/schema/beans"
  3.        xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  4.        xsi:schemaLocation= "http://www.springframework.org/schema/beans
  5.     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
  6.     <bean id= "serviceB" class= "com.javacode2018.lesson001.demo27.test5.ServiceB"/>
  7. </beans>

下面来个配置类,来引入上面2个配置文件


   
  1. package com.javacode2018.lesson001.demo27.test5;
  2. import org.springframework.beans.factory.annotation.Configurable;
  3. import org.springframework.context.annotation.ImportResource;
  4. @Configurable
  5. @ImportResource( "classpath:/com/javacode2018/lesson001/demo27/test5/beans*.xml")
  6. public class MainConfig5 {
  7. }

这个类上使用了@Configurable表示这是个配置类

并且使用了@ImportResource注解来导入上面2个配置文件

来个测试用例加载上面这个配置类


   
  1. @Test
  2. public void test5() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig5.class);
  4.      for (String beanName : context.getBeanDefinitionNames()) {
  5.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  6.     }
  7. }

上面会输出MainConfig5配置类中所有定义的bean

运行输出


   
  1. mainConfig5->com.javacode2018.lesson001.demo27.test5.MainConfig5@ 4ec4f3a0
  2. serviceA->com.javacode2018.lesson001.demo27.test5.ServiceA@ 223191a6
  3. serviceB->com.javacode2018.lesson001.demo27.test5.ServiceB@ 49139829

从输出中可以看出2个xml中定义的bean也被注册了

@Lazy:延迟初始化

用法

@Lazy等效于bean xml中bean元素的lazy-init属性,可以实现bean的延迟初始化。

所谓延迟初始化:就是使用到的时候才会去进行初始化。

来看一下其定义:


   
  1. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @ interface Lazy {
  5.     boolean value()  default  true;
  6. }

可以用在任意类型、方法、构造器、参数、字段上面

参数:

value:boolean类型,用来配置是否应发生延迟初始化,默认为true。

常用3种方式

  1. 和@Compontent一起标注在类上,可以是这个类延迟初始化

  2. 和@Configuration一起标注在配置类中,可以让当前配置类中通过@Bean注册的bean延迟初始化

  3. 和@Bean一起使用,可以使当前bean延迟初始化

来看一下这3种方式案例代码。

案例1:和@Compontent一起使用

Service1


   
  1. package com.javacode2018.lesson001.demo27.test6;
  2. import org.springframework.context.annotation.Lazy;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Lazy  //@1
  6. public class Service1 {
  7.     public Service1() {
  8.         System.out. println( "创建Service1");
  9.     }
  10. }

@1:使用到了@Lazy,默认值为true,表示会被延迟初始化,在容器启动过程中不会被初始化,当从容器中查找这个bean的时候才会被初始化。

配置类


   
  1. package com.javacode2018.lesson001.demo27.test6;
  2. import org.springframework.context.annotation.ComponentScan;
  3. @ComponentScan
  4. public class MainConfig6 {
  5. }

测试用例


   
  1. @Test
  2. public void test6() {
  3.     System.out. println( "准备启动spring容器");
  4.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig6.class);
  5.     System.out. println( "spring容器启动完毕");
  6.     System.out. println(context.getBean(com.javacode2018.lesson001.demo27.test6.Service1.class));
  7. }

运行输出


   
  1. 准备启动spring容器
  2. spring容器启动完毕
  3. 创建Service1
  4. com.javacode2018.lesson001.demo27.test6.Service1@ 4fb61f4a

可以看出service1这个bean在spring容器启动过程中并没有被创建,而是在我们调用getBean进行查找的时候才进行创建的,此时起到了延迟创建的效果。

案例2:和@Configuration一起使用加在配置类上

@Lazy和@Configuration一起使用,此时配置类中所有通过@Bean方式注册的bean都会被延迟初始化,不过也可以在@Bean标注的方法上使用@Lazy来覆盖配置类上的@Lazy配置,看下面代码:

配置类MainConfig7


   
  1. package com.javacode2018.lesson001.demo27.test7;
  2. import org.springframework.beans.factory.annotation.Configurable;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Lazy;
  5. @Lazy  //@1
  6. @Configurable
  7. public class MainConfig7 {
  8.     @Bean
  9.     public String name() {
  10.         System.out. println( "create bean:name");
  11.          return  "路人甲Java";
  12.     }
  13.     @Bean
  14.     public String address() {
  15.         System.out. println( "create bean:address");
  16.          return  "上海市";
  17.     }
  18.     @Bean
  19.     @Lazy( false//@2
  20.     public Integer age() {
  21.         System.out. println( "create bean:age");
  22.          return  30;
  23.     }
  24. }

@1:配置类上使用了@Lazy,此时会对当前类中所有@Bean标注的方法生效

@2:这个方法上面使用到了@Lazy(false),此时age这个bean不会被延迟初始化。其他2个bean会被延迟初始化。

测试用例


   
  1. @Test
  2. public void test7() {
  3.     System.out. println( "准备启动spring容器");
  4.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig7.class);
  5.     System.out. println( "spring容器启动完毕");
  6.      for (String beanName : Arrays.asList( "name""age""address")) {
  7.         System.out. println( "----------");
  8.         System.out. println( "getBean:" + beanName +  ",start");
  9.         System.out. println(String.format( "%s->%s", beanName, context.getBean(beanName)));
  10.         System.out. println( "getBean:" + beanName +  ",end");
  11.     }
  12. }

上面会输出配置类中定义的3个bean的信息。

运行输出


   
  1. 准备启动spring容器
  2. create bean:age
  3. spring容器启动完毕
  4. ----------
  5. getBean:name,start
  6. create bean:name
  7. name->路人甲Java
  8. getBean:name,end
  9. ----------
  10. getBean:age,start
  11. age-> 30
  12. getBean:age,end
  13. ----------
  14. getBean:address,start
  15. create bean:address
  16. address->上海市
  17. getBean:address,end

输出中可以看到age是在容器启动过程中创建的,其他2个是在通过getBean查找的时候才创建的。

总结

  1. 本文介绍的几个注解也算是比较常用的,大家一定要熟悉他们的用法

  2. @Scope:用来定义bean 的作用域;2种用法:第1种:标注在类上;第2种:和@Bean一起标注在方法上

  3. @DependsOn:用来指定当前bean依赖的bean,可以确保在创建当前bean之前,先将依赖的bean创建好;2种用法:第1种:标注在类上;第2种:和@Bean一起标注在方法上

  4. @ImportResource:标注在配置类上,用来引入bean定义的配置文件

  5. @Lazy:让bean延迟初始化;常见3种用法:第1种:标注在类上;第2种:标注在配置类上,会对配置类中所有的@Bean标注的方法有效;第3种:和@Bean一起标注在方法上

案例源码

https://gitee.com/javacode2018/spring-series

路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。

Spring系列

  1. Spring系列第1篇:为何要学spring?

  2. Spring系列第2篇:控制反转(IoC)与依赖注入(DI)

  3. Spring系列第3篇:Spring容器基本使用及原理

  4. Spring系列第4篇:xml中bean定义详解(-)

  5. Spring系列第5篇:创建bean实例这些方式你们都知道?

  6. Spring系列第6篇:玩转bean scope,避免跳坑里!

  7. Spring系列第7篇:依赖注入之手动注入

  8. Spring系列第8篇:自动注入(autowire)详解,高手在于坚持

  9. Spring系列第9篇:depend-on到底是干什么的?

  10. Spring系列第10篇:primary可以解决什么问题?

  11. Spring系列第11篇:bean中的autowire-candidate又是干什么的?

  12. Spring系列第12篇:lazy-init:bean延迟初始化

  13. Spring系列第13篇:使用继承简化bean配置(abstract & parent)

  14. Spring系列第14篇:lookup-method和replaced-method比较陌生,怎么玩的?

  15. Spring系列第15篇:代理详解(Java动态代理&cglib代理)?

  16. Spring系列第16篇:深入理解java注解及spring对注解的增强(预备知识)

  17. Spring系列第17篇:@Configration和@Bean注解详解(bean批量注册)

  18. Spring系列第18篇:@ComponentScan、@ComponentScans详解(bean批量注册)

  19. Spring系列第18篇:@import详解(bean批量注册)

  20. Spring系列第20篇:@Conditional通过条件来控制bean的注册

  21. Spring系列第21篇:注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)

更多好文章

  1. Java高并发系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. Mybatis系列(共12篇)

  5. 聊聊db和缓存一致性常见的实现方式

  6. 接口幂等性这么重要,它是什么?怎么实现?

  7. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

感谢大家的阅读,也欢迎您把这篇文章分享给更多的朋友一起阅读!谢谢!

路人甲java

▲长按图片识别二维码关注

路人甲Java:工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!


转载:https://blog.csdn.net/likun557/article/details/105259881
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场