zoukankan      html  css  js  c++  java
  • @Scope、@DependsOn、@ImportResource、@Lazy

    面试问题

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

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

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

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

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

    @Scope:指定bean的作用域

    用法

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

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

    看一下其源码:

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Scope {

        @AliasFor("scopeName")
        String value() default "";

        @AliasFor("value")
        String scopeName() default "";

        ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

    }

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

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

    常见2种用法

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

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

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

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

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

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

    String SCOPE_SINGLETON = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

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

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

    @Configurable
    public class MainConfig2 {
        @Bean
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public ServiceA serviceA() {
            return new ServiceA();
        }
    }

    @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创建之前先创建好

    看一下其源码:

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DependsOn {

        String[] value() default {};

    }

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

    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

    package com.javacode2018.lesson001.demo27.test3;

    import org.springframework.stereotype.Component;

    @Component
    public class Service2 {
        public Service2() {
            System.out.println("create Service2");
        }
    }

    Service3

    package com.javacode2018.lesson001.demo27.test3;

    import org.springframework.stereotype.Component;

    @Component
    public class Service3 {
        public Service3() {
            System.out.println("create Service3");
        }
    }

    Service1

    package com.javacode2018.lesson001.demo27.test3;

    import org.springframework.context.annotation.DependsOn;
    import org.springframework.stereotype.Component;

    @DependsOn({"service2""service3"}) //@1
    @Component
    public class Service1 {
        public Service1() {
            System.out.println("create Service1");
        }
    }

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

    来个配置类

    package com.javacode2018.lesson001.demo27.test3;

    import org.springframework.context.annotation.ComponentScan;

    @ComponentScan
    public class MainConfig3 {
    }

    测试用例

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

    运行输出

    create Service2
    create Service3
    create Service1
    com.javacode2018.lesson001.demo27.test3.Service1@9f116cc

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

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

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

    package com.javacode2018.lesson001.demo27.test4;

    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.DependsOn;

    @Configurable
    public class MainConfig4 {

        @Bean
        @DependsOn({"service2""service3"})//@1
        public Service1 service1() {
            return new Service1();
        }

        @Bean
        public Service2 service2() {
            return new Service2();
        }

        @Bean
        public Service3 service3() {
            return new Service3();
        }

    }

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

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

    来个测试用例

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

    运行输出

    create Service2
    create Service3
    create Service1
    com.javacode2018.lesson001.demo27.test4.Service1@6e20b53a

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

    用法

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

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

    看一下这个注解的定义:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    public @interface ImportResource {

        @AliasFor("locations")
        String[] value() default {};

        @AliasFor("value")
        String[] locations() default {};

        Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;

    }

    通常将其用在配置类上。

    有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*:后面的部分,后面的部分是确定资源文件的位置地方,几种常见的如下:

    相对路径的方式

    classpath:com/javacode2018/lesson001/demo27/test5/beans.xml
    或者
    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结尾的文件

    *:目录通配符的方式

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

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

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

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

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

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

    案例代码

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

    ServiceA

    package com.javacode2018.lesson001.demo27.test5;

    public class ServiceA {
    }

    ServiceB

    package com.javacode2018.lesson001.demo27.test5;

    public class ServiceB {
    }

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

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"
    >

        <bean id="serviceA" class="com.javacode2018.lesson001.demo27.test5.ServiceA"/>

    </beans>

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

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"
    >

        <bean id="serviceB" class="com.javacode2018.lesson001.demo27.test5.ServiceB"/>

    </beans>

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

    package com.javacode2018.lesson001.demo27.test5;

    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.ImportResource;

    @Configurable
    @ImportResource("classpath:/com/javacode2018/lesson001/demo27/test5/beans*.xml")
    public class MainConfig5 {
    }

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

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

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

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

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

    运行输出

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

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

    @Lazy:延迟初始化

    用法

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

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

    来看一下其定义:

    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Lazy {

        boolean value() default true;

    }

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

    参数:

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

    常用3种方式

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

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

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

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

    案例1:和@Compontent一起使用

    Service1

    package com.javacode2018.lesson001.demo27.test6;

    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Component;

    @Component
    @Lazy //@1
    public class Service1 {
        public Service1() {
            System.out.println("创建Service1");
        }
    }

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

    配置类

    package com.javacode2018.lesson001.demo27.test6;

    import org.springframework.context.annotation.ComponentScan;

    @ComponentScan
    public class MainConfig6 {
    }

    测试用例

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

    运行输出

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

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

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

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

    配置类MainConfig7

    package com.javacode2018.lesson001.demo27.test7;

    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Lazy;

    @Lazy //@1
    @Configurable
    public class MainConfig7 {

        @Bean
        public String name() {
            System.out.println("create bean:name");
            return "路人甲Java";
        }

        @Bean
        public String address() {
            System.out.println("create bean:address");
            return "上海市";
        }

        @Bean
        @Lazy(false//@2
        public Integer age() {
            System.out.println("create bean:age");
            return 30;
        }
    }

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

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

    测试用例

    @Test
    public void test7() {
        System.out.println("准备启动spring容器");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig7.class);
        System.out.println("spring容器启动完毕");

        for (String beanName : Arrays.asList("name""age""address")) {
            System.out.println("----------");
            System.out.println("getBean:" + beanName + ",start");
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
            System.out.println("getBean:" + beanName + ",end");
        }
    }

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

    运行输出

    准备启动spring容器
    create bean:age
    spring容器启动完毕
    ----------
    getBean:name,start
    create bean:name
    name->路人甲Java
    getBean:name,end
    ----------
    getBean:age,start
    age->30
    getBean:age,end
    ----------
    getBean:address,start
    create bean:address
    address->上海市
    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一下,可以持续关注动态。

    参考:https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648934284&idx=1&sn=00126ad4b435cb31726a5ef10c31af25&chksm=88621fb2bf1596a41563db5c474873c62d552ec9a440037d913704f018742ffca9be9b598680&token=887127000&lang=zh_CN&scene=21#wechat_redirect

  • 相关阅读:
    CodeForces 510C Fox And Names (拓扑排序)
    Codeforces 1153D Serval and Rooted Tree (简单树形DP)
    HDU 6437 Problem L.Videos (最大费用)【费用流】
    Luogu P3381 (模板题) 最小费用最大流
    Codeforces 741B Arpa's weak amphitheater and Mehrdad's valuable Hoses (并查集+分组背包)
    Codeforces 1144F Graph Without Long Directed Paths (DFS染色+构造)
    HDU 2204 Eddy's 爱好 (容斥原理)
    Codeforces 939E Maximize! (三分 || 尺取)
    Codeforces 938D. Buy a Ticket (最短路+建图)
    CodeForces 959E Mahmoud and Ehab and the xor-MST (MST+找规律)
  • 原文地址:https://www.cnblogs.com/konglxblog/p/15408802.html
Copyright © 2011-2022 走看看