美文网首页我爱编程Spring
[spring注解]Spring相关注解(四)

[spring注解]Spring相关注解(四)

作者: 骑着乌龟去看海 | 来源:发表于2018-05-12 11:37 被阅读296次

    前言

    接上文,接着学习Spring的注解。本篇博客所学习注解基本都是spring-context对应jar包下的注解,Spring版本:4.3.14,包含注解:ComponentControllerServiceRepositorybeanComponentScanComponentScan.FilterComponentScansConfigurationDependsOnDescriptionImportLazyPrimaryScopePropertySourceConditionalProfileAsyncScheduled 。该jar包下其他注解平时使用不多,如果哪天用到了,再添加进来。

    一、Spring-Context相关注解

    1. Component注解

      该注解是Spring的元注解,意思是它可以用于标注其他注解,被它标注的注解和它具有相同或者相似的功能。Spring用它来标注为Spring组件的bean,用它标注的类,默认情况下在Spring进行扫描的时候,会自动注册为Spring的Bean。而Spring利用它定义了一些我们常用的注解如:@Controller,@Service,@Repository等。
      我们可以简单看下Controller注解的定义:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Controller {
    }
    

      当然,我们也可以仿照Controller注解,自定义我们的注解,然后标注到某个类上,这样Spring在进行扫描的时候就会自动将该类注册为Bean。该注解一般泛指各种通用的组件,比如说我们的类既不属于@Controller,@Service,@Repository的时候,就可以使用该注解。

    参数只有一个value,表示该bean的名称;

    2. Controller注解

      这个注解应该是我们使用最多的注解了,在Spring MVC中作为控制器Controller,负责处理DispatcherServlet 分发的请求,接受到请求后解析处理数据,然后再返回给浏览器。而在Spring MVC中,定义Controller也是特别简单,只需要一个@Controller注解就可以标记一个类是Controller,然后使用RequestMapping等注解来定义URL和Controller之间的映射。
      该注解标记于类上,但也只是定义了一个控制器类,而使用@RequestMapping注解的方法才是真正处理请求的处理器,然后我们配置扫描路径后,就可以访问这些具体的控制器方法了。

    该注解只有一个参数:

    value,Spring扫描Controller后,会生成对应的bean,该参数用于定义bean的名称,如果不指定value,则Controller默认的bean名称是该类的类名,其中首字母小写。

    3. Service注解

    基于@Component注解,用于标注某个类为服务组件。也就是说用它标注的类表示Service层的实现,同样参数只有value,用于表示该bean的名称,也就是在Spring环境中的id。不过需要简单注意下:

    在文章 spring-applicationContext.xml-context标签 的<context:component-scan>标签的 name-generator 这里,我们已经了解到,由于Spring默认的bean的命名策咯的实现类是AnnotationBeanNameGenerator,根据源码也就是说,默认情况下如果我们不设置value属性,那么默认的bean名称就是类名,第一个字母是小写;当然还有一个特殊的处理,当类的名字是以两个或两个以上的大写字母开头的话,那么bean的名称就和类名称是一样的,可以参考下源码:

    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                // Explicit bean name found.
                return beanName;
            }
        }
        // 处理默认的bean名称
        return buildDefaultBeanName(definition, registry);
    }
    

    然后,单步调试 buildDefaultBeanName 方法,一直到Introspector.decapitalize (shortClassName):

    /**
     * Utility method to take a string and convert it to normal Java variable
     * name capitalization.  This normally means converting the first
     * character from upper case to lower case, but in the (unusual) special
     * case when there is more than one character and both the first and
     * second characters are upper case, we leave it alone.
     * <p>
     * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
     * as "URL".
     *
     */
    public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        // 如果类的前两个字母都是大写,直接返回类名
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){
            return name;
        }
        // 将类的第一个字母转成小写
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }
    

    然后看一下注释即对应的实现,就可以看到相应的实现了。至于如何修改bean的命名策咯,前文已经了解过,这里就不多讲了。

    4. Repository 注解

    Repository注解和@Controller,@Service这两个功能是类似的,@Controller作为Web层组件,@Service作为服务层组件,而@Repository是作为数据访问层的组件,也就是,一般情况下,通过该bean对数据库进行各种操作。其余和Service功能并无很大区别,不多说。

    5. bean注解

      这里我们来学习bean相关的注解,由于前文已经学习了XML中bean的配置,所以有可能有重复的,如果有重复的,这里就一笔带过。首先来看一下bean注解。
      @Bean注解是一个方法级别的注解,和XML中配置<bean/>标签功能是一样的。通常情况下是与@Configuration注解一起使用,并且通过和@Scope,@Lazy等注解一起来控制Bean的一些配置。

    @Configuration
    public class AppConfig {
    
        @Bean
        public TransferServiceImpl transferService() {
            return new TransferServiceImpl();
        }
    }
    

    等价于在Spring XML中的配置:

    <beans>
        <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>
    

    我们来简单看下@Bean的各个参数:

    1. valuename,bean的名称;
    2. autowire,注入方式,可以是ByName,byType,也可也选择NO,默认是Autowire.NO;
    3. initMethoddestroyMethod,实例化bean的时候的初始化方法,及销毁前执行的方法;

    官网文档:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-java-bean-annotation

    6. ComponentScan注解

    该注解对应于XML配置中的 <context:component-scan>,用来扫描相应的注解并自动注入bean。该注解于XML配置有些许不同:

    1. 该注解没有 annotation-config对应的属性,因为我们程序几乎所有的情况,都会扫描@Autowired等这一类<context:annotation-config>所对应的注解,所以也就相当于有这个参数,但这个参数一直是true;
    2. 此外,该注解多了basePackageClasses这个数组参数,表示指定特定的要扫描的类,注入的时候也只注入这些类;
    3. 还有一个参数:lazyInit,表示扫描的时候根据bean是否配置了lazy来决定是否延迟初始化该bean。默认是false,也就是说无论bean是否配置了lazy属性,该bean被扫描到后都会立刻初始化,设置为true之后,如果bean配置了lazy属性,那么会根据bean的lazy属性来选择是否延迟初始化;
    7. ComponentScan.Filter注解

      ComponentScan注解的内部的一个注解,用在ComponentScanincludeFiltersexcludeFilters,而这里的用法和配置文件中的context:exclude-filtercontext:include-filter是一致的,目的就是为了扫描的时候过滤用的。至于Filter中的参数和我们前面学习的XML中的参数差不多,不多说了。

    @Configuration 
    @EnableSpringConfigured
    @ComponentScan(basePackages = {"com.example"}, excludeFilters={
      @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
    public class MySpringConfiguration {}
    

    具体使用可参考:Spring - @ComponentScan custom filtering

    8. ComponentScans注解

      这个注解是Spring4.3之后引入的,目的就是配置多个扫描器(当然也可以在一个类上配置多个ComponentScan注解)。学习过XML的配置我们知道,在XML中我们是可以配置多个<context:component-scan>标签的,同样,使用该注解可以让我们在程序里配置多个ComponentScan注解。该注解参数只有一个,就是数组类型的ComponentScan

    @Configuration
    @ComponentScans({@ComponentScan(value = {"controller", "global","controller2"}),
            @ComponentScan(value = {"service"})})
    @EnableWebMvc
    @EnableSwagger2
    public class SpringConfig extends WebMvcConfigurerAdapter {
    }
    
    9. Configuration注解

      Spring的@Configuration注解修饰的类其实就相当于XML中的一个配置文件,可以通过该注解声明一个配置类来减少繁琐的XML配置,然后使用@Bean注解标记相应的方法。在这里@Configuration就相当于XML中的beans标签,而@Bean就相当于bean标签。
    举个简单的例子:

    @Configuration
    public class AppConfig {
    
        @Bean
        public MyBean myBean() {
            // instantiate, configure and return bean ...
        }
    }
    

      由于Component注解是Configuration的元注解,所以也会被@ComponentScan所扫描到。而我们在使用注解调用的时候,是通过AnnotationConfigApplicationContext或者web相关的AnnotationConfigWebApplicationContext

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class);
    ctx.refresh();
    MyBean myBean = ctx.getBean(MyBean.class);
    // use myBean ...
    
    

    一般,该注解有以下几种用法:

    1. 会配合ComponentScan注解进行扫描,并且配合EnableWebMvc注解来开启MVC:
    @Configuration
    @ComponentScan("com.acme.app.services")
    @EnableWebMvc
    public class AppConfig {
        // various @Bean definitions ...
    }
    
    1. 配合PropertySource注解读取properties文件中的值:
    @Configuration
    @PropertySource("classpath:/com/acme/app.properties")
    public class AppConfig {
        @Inject Environment env;
        @Bean
        public MyBean myBean() {
            return new MyBean(env.getProperty("bean.name"));
        }
    }
    
    1. 配合@Value注解获取properties文件中的值:
    @Configuration
    @PropertySource("classpath:/com/acme/app.properties")
    public class AppConfig {
    
        @Value("${bean.name}") 
        String beanName;
    
        @Bean
        public MyBean myBean() {
            return new MyBean(beanName);
        }
    }
    
    1. 配合@Import注解获取导入其他的配置类:
    @Configuration
    public class DatabaseConfig {
    
        @Bean
        public DataSource dataSource() {
            // instantiate, configure and return DataSource
        }
    }
    
    @Configuration
    @Import(DatabaseConfig.class)
    public class AppConfig {
    
        private final DatabaseConfig dataConfig;
    
        public AppConfig(DatabaseConfig dataConfig) {
            this.dataConfig = dataConfig;
        }
    
        @Bean
        public MyBean myBean() {
            // reference the dataSource() bean method
            return new MyBean(dataConfig.dataSource());
        }
    }
    
    1. 配合@Profile注解获取当前环境信息(开发,测试,生产):
     @Profile("development")
     @Configuration
     public class EmbeddedDatabaseConfig {
    
         @Bean
         public DataSource dataSource() {
             // instantiate, configure and return embedded DataSource
         }
     }
    
     @Profile("production")
     @Configuration
     public class ProductionDatabaseConfig {
    
         @Bean
         public DataSource dataSource() {
             // instantiate, configure and return production DataSource
         }
     }
    

    同样,@Profile注解也可也位于Bean上:

    @Configuration
    public class ProfileDatabaseConfig {
    
        @Bean("dataSource")
        @Profile("development")
        public DataSource embeddedDatabase() { ... }
    
        @Bean("dataSource")
        @Profile("production")
        public DataSource productionDatabase() { ... }
    }
    
    1. 配合@ImportResource注解导入对应的配置文件,如XML等:
    @Configuration
    @ImportResource("classpath:/com/acme/database-config.xml")
    public class AppConfig {
    
        @Inject DataSource dataSource; // from XML
    
        @Bean
        public MyBean myBean() {
            // inject the XML-defined dataSource bean
            return new MyBean(this.dataSource);
        }
    }
    
    1. 使用@Configuration嵌套注解:
    @Configuration
    public class AppConfig {
    
        @Inject DataSource dataSource;
    
        @Bean
        public MyBean myBean() {
            return new MyBean(dataSource);
        }
    
        @Configuration
        static class DatabaseConfig {
            @Bean
            DataSource dataSource() {
                return new EmbeddedDatabaseBuilder().build();
            }
        }
    }
    
    1. 单元测试,引入@Configuration修饰的类:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes={AppConfig.class, DatabaseConfig.class})
    public class MyTests {
    
        @Autowired MyBean myBean;
        @Autowired DataSource dataSource;
    
        @Test
        public void test() {
            // assertions against myBean ...
        }
    }
    

    关于Configuration注解,需要注意下:

      1. 该注解修饰的必须是类,不能是接口;
      1. 配置类必须不能是final类型的;
      1. 配置类不能是匿名类;
      1. 嵌套的配置类必须是static的;

    更多有关Configuration的内容,可以参考官方API:https://docs.spring.io/spring/docs/current/javadoc-api/
    而有关Spring @Configuration 和 @Component 区别可以参考:
    Spring @Configuration 和 @Component 区别

    10. DependsOn注解

      DependsOn注解和bean的 depends-on方法类似,用于一个beanA初始化之前必须先初始化另一个beanB,由于前面已经仔细分析过,这里就不多说,具体可参考:applicationContext.xml详解一beans标签

    11. Description注解

      同样,和bean标签中的description属性类似,用于对bean添加描述信息,同样是直接或间接使用了@Component或者@Bean注解的类或方法上。

    12. Import注解

      对应于beans标签的import子标签,用于模块化过程中引入其他的配置文件,而在对应的注解中,则是为了引入@Configuration注解所修饰的配置类。当然,不但但可以导入配置类,也可以导入普通的java类,并将其声明成一个bean。而如果需要导入XML或者其他非@Configuration所定义的资源,可以通过注解@ImportResource 来实现。

    参数value 数组,声明用于导入的具体配置类的类型;

    可以看个简单的例子:

    public class BeanService {
        public void test() {
            System.out.println("test:BeanService");
        }
    }
    
    @Configuration
    @Import(BeanService.class)
    public class BeanConfig {
    }
    
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
        BeanService beanService = ac.getBean(BeanService.class);
        beanService.test();
        ac.close();
    }
    
    13. Lazy注解

      该注解表示该bean是否延迟加载,和bean标签的lazy-init属性功能是相同的。同样,可以在任何直接或间接的使用了@Component或者@Bean注解的类或方法上使用该注解。如果该注解没有用于@Component或@Bean定义中,那么就会立即初始化,而不会延迟。该注解只有一个参数value,默认是true,也就是延迟初始化。

    如果该注解用于@Configuration修饰的配置类上,那么@Configuration中的所有的@Bean方法都会被延迟初始化。而如果@Configuration中的bean上已经配置了Lazy注解,那么以Bean中的优先。

    14. Primary注解

      在前面学习Spring bean标签的时候,我们当时已经学习了bean的primary属性,其实它和@Primary注解功能是一样的。是说在使用类型注入的时候,如果存在多个相同类型的bean,那么可以通过使用primary属性或者@Primary注解让该bean成为优先候选者,如果候选者之中只有一个primary修饰的bean,那么这个bean将会是自动注入的bean。

    不过需要注意下,就是如果在XML中配置了bean的primary属性,那么该bean对应的@Primary注解将会被忽略掉,并且记得配置扫描路径。

    可以在任何直接或间接的使用了@Component或者@Bean注解的类或方法上使用该注解。

    15. Scope注解

    Scope注解表示bean的作用域,默认情况下我们创建的bean都是单例的。前文已经说过,这里就不多说了,看一下参数:

    • scopeNamevalue,作用域类型的名称;
    • proxyMode,作用域代理相关的配置,对应的值是 ScopedProxyMode对象的值。
    16. PropertySource 和 PropertySources注解

    该注解是Spring提供的用于读取properties文件的,通过配合Environment 类来实现该功能,该注解用于Configuration 所修饰的类。先来看一个例子,其实上文已经简单介绍过:

    @Configuration
    @PropertySource("classpath:/com/myco/app.properties")
    public class AppConfig {
    
        @Autowired
        Environment env;
    
        @Bean
        public TestBean testBean() {
            TestBean testBean = new TestBean();
            testBean.setName(env.getProperty("testbean.name"));
            return testBean;
        }
    }
    

    再简单来看一下该注解的参数:

    1. value,要加载的资源位置,数组格式,和XML中支持的功能一样强大,包括classpathfile;但不支持类似的资源通配符:**/*.properties,也就是.properties必须要精确到具体位置;
    2. name,对应属性资源的名称;
    3. encoding,字符编码,比如 UTF-8 格式;
    4. ignoreResourceNotFound,如果找不到对应的属性资源,是否忽略。默认是false,这种情况下,如果找不到的话将会提示异常;
    5. factory,指定一个资源工厂对象PropertySourceFactory;、

    一般情况下,该注解还会配合@Value来一起使用,这时候记得配置PropertySourcesPlaceholderConfigurer对象来解析占位符,在XML配置中我们可以通过<context:property-placeholder>,在注解中我们可以将该对象注册为bean,配置如下:

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
    

    而对于@PropertySources注解,则是可以加载多个PropertySource,不多说了。

    17. Conditional 注解

      Spring在4.0之后引入的条件注解,就是根据满足某个特定的条件创建一个特定的bean,而我们实现的方式就是实现Condition接口,然后重写matches方法来构造判断条件,比如在Windows下和在Linux下执行不同的操作,具体的例子可以参考:Spring 条件注解(@Conditional)

    Configuration  
    @ComponentScan(basePackages="com.chenfeng.xiaolyuh.conditional")  
    public class ConditionConfig {  
          
        @Bean  
        @Conditional(LinuxCondition.class)// 使用@Conditional注解,符合Linux条件就实例化LinuxListService  
        public ListService linuxListService() {  
            return new LinuxListService();  
        }  
      
        @Bean  
        @Conditional(WindowsCondition.class)// 使用@Conditional注解,符合Windows条件就实例化WindowsListService  
        public ListService windowsListService() {  
            return new WindowsListService();  
        }  
    }  
    

      可以用于类或方法上,当用于@Configuration修饰的类上时,那么该类下所有bean都将受此条件限制。

    有关更多可参考:Spring @Conditional Annotation

    18. Profile 注解

    @Profile是Spring用于获取当前运行环境的注解,比如我们的开发,测试,UAT,生产等,或者还有其他的几套环境,而我们就可以通过该注解来获取当前环境信息。
    看一个简单的例子:

    @Profile("Development")
    @Configuration
    public class DevDatabaseConfig implements DatabaseConfig {
     
        @Override
        @Bean
        public DataSource createDataSource() {
            System.out.println("Creating DEV database");
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            /*
             * Set MySQL specific properties for Development Environment
             */
            return dataSource;
        }
     
    }
    

    如果要查看更多,可以参考:【译】Spring 4 @Profile注解示例
    官网地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-java

    19. Async 注解

      @Async是Spring用于异步调用的注解,该注解用于方法或类上。@Async标注的方法称为异步方法,这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。而@Async如果标注在类上的话,则表示该类的所有方法都将是异步的。

    1. 异步方法的返回值必须是void或者Future,方法必须是public类型的;
    2. 如果要异步的话,首先必须要在配置类上添加@EnableAsync注解来开启异步;
    3. @Async不能与生命周期函数一起使用,必须使用一个单独的初始化Spring bean来调用目标上的@Async注释方法;
    4. 在XML中没有与@Async等价的标签;

    如果EnableAsync满足不了我们的需求的话,我们还可以配置EnableAsync的一些属性:

    • annotation - 默认情况下, @EnableAsync 会扫描使用了Spring @Async与EJB 3.1 javax.ejb.Asynchronous的方法;此选项也可以用来扫描其他的,如用户自定义的注解类型;
    • mode - 指定应该使用哪种AOP进行切面处理 - JAVA代理或AspectJ;
    • proxyTargetClass - 指定应该使用哪种代理类 - CGLIB或JDK;此属性只有当mode设置成AdviceMode.PROXY才会产生效果。
    • order - 设置AsyncAnnotationBeanPostProcessor执行顺序(生命周期有关);默认情况下会最后一个执行,所以这样就能顾及到所有已存在的代理。
    @Configuration
    @EnableAsync
    public class AsyncConfig {
        
        @Async
        public void doSomethind() {
            System.out.println("begin async");
        }
    
        @Async
        Future<String> returnSomething(int i) {
            // this will be executed asynchronously
        }  
    }
    

    XML中开启异步的话,是通过如下:

    <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
    <task:executor id="myExecutor" pool-size="5"/>
    <task:scheduler id="myScheduler" pool-size="10"/>
    

    默认情况下,当@Async使用于方法上时,将使用annotation-driven提供的executor执行器,如果要指定相应的executor,可以使用@Async唯一的一个参数value来指定。

    @Async("otherExecutor")
    void doSomething(String s) {
        // this will be executed asynchronously by "otherExecutor"
    }
    

    有关Async和Scheduled相关的官网文档地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/scheduling.html#scheduling-annotation-support

    20. Scheduled Schedules 注解

      @Scheduled相信我们都用过,用来执行定时任务相关的操作,同样是方法级别的注解。使用该注解修饰的方法必须没有参数,并且返回类型通常是void类型的。该注解使用ScheduledAnnotationBeanPostProcessor来执行,同样,使用该注解前要首先要开启定时,XML中使用<task:annotation-driven/>,而注解的话则是通过@EnableScheduling来完成。
      另外,该注解可以作为元注解,从而自定义我们的定时任务相关的注解。接下来我们来简单看下它的参数:

    1. cron,这个应该是我们使用最多的参数了,定时任务时间设置的表达式,比如每隔1天执行一次,每隔半小时执行,这个设置网上的介绍实在太多了,用的时候再去查询就可以了;
    2. zone,cron表达式解析的时区,默认情况下,该属性是空字符串(也就是服务器的本地时区),接收的是TimeZone.getTimeZone(String)的id;
    3. fixedDelay,每次执行任务之后间隔多久再次执行任务,以上次任务结束时间和下此任务开始时间之间的毫秒为单位来执行;比如说间隔时间是5秒,任务执行的时间是8秒,那么8秒执行后,下此就是8+5秒的时候在执行,再下此就是8+5+8+5的时候再执行,就是以结束时间为准;
    4. fixedDelayString,和上面fixedDelay一样,只不过参数类型是字符串形式,可以通过外部来定义,如 fixedDelayString= "${job.fixed.string}";;
    5. fixedRate,固定频率的间隔时间,也就是每隔多久就执行,不管任务是否完成;
    6. fixedRateString,和fixedRate一样,只不过参数类型是字符串形式,可以通过外部来定义,如 fixedRateString = "${job.fixed.rate}";
    7. initialDelay,该参数表示第一次执行定时任务之前需要等待多长时间,也就是第一次延时多长时间后执行,单位同样是毫秒;

    有关fixedDelayfixedRatecron,再稍微简单说下:

    fixedDelay比较简单,就是根据上次任务结束时间计算的,而cron,和fixedDelay则有点不太一样,比如间隔时间是5S,如果执行时间是8S,则下此执行的时间将从10S开始,会跳过一个间隔时间,如果执行时间是10S,则下此执行时间从15S开始;而如果执行时间是3S,那下此执行时间就将从5S开始,每隔5S执行;

    至于fixedRate,它的使用和上面两种都不太相同,如果上次任务执行时间超过了间隔时间,那么超出的这部分时间会被计入下一次任务的执行时间中,至于问题的细节,我们可以参考这篇文章:
    https://yanbin.blog/understand-spring-schedule-fixedrate-fixeddelay/

    在网上看到两幅图描述fixedDelayfixedRate,描述的很形容:

    fixedDelay和fixedRate.png
    图片转自:https://blog.csdn.net/applebomb/article/details/52400154

    如果我们没有配置TaskScheduler 和 ScheduledExecutorService 的话,那么Scheduled默认是采用单线程来进行循环执行定时任务的,我们可以通过Thread.currentThread()来查看当前运行的线程,默认定时任务的线程是Executors.defaultThreadFactory() 产生的,线程名称是 "pool-NUMBER-thread-..."。

    至于Schedules 注解,则是多个Scheduled组成的数组,就不说了。

    参考:Spring 定时任务(Schedule) 和线程

    相关文章

      网友评论

        本文标题:[spring注解]Spring相关注解(四)

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