[spring注解]Spring相關(guān)注解(四)

前言

接上文人乓,接著學(xué)習(xí)Spring的注解勤篮。本篇博客所學(xué)習(xí)注解基本都是spring-context對(duì)應(yīng)jar包下的注解,Spring版本:4.3.14色罚,包含注解:Component碰缔,ControllerService戳护,Repository 金抡,beanComponentScan腌且,ComponentScan.Filter梗肝,ComponentScansConfiguration铺董,DependsOn巫击,DescriptionImport精续,Lazy坝锰,PrimaryScope重付,PropertySource 顷级,ConditionalProfile 确垫,Async 弓颈,Scheduled 拣凹。該jar包下其他注解平時(shí)使用不多,如果哪天用到了恨豁,再添加進(jìn)來(lái)嚣镜。

一、Spring-Context相關(guān)注解

1. Component注解

??該注解是Spring的元注解橘蜜,意思是它可以用于標(biāo)注其他注解菊匿,被它標(biāo)注的注解和它具有相同或者相似的功能。Spring用它來(lái)標(biāo)注為Spring組件的bean计福,用它標(biāo)注的類跌捆,默認(rèn)情況下在Spring進(jìn)行掃描的時(shí)候,會(huì)自動(dòng)注冊(cè)為Spring的Bean象颖。而Spring利用它定義了一些我們常用的注解如:@Controller佩厚,@Service,@Repository等说订。
??我們可以簡(jiǎn)單看下Controller注解的定義:

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

??當(dāng)然抄瓦,我們也可以仿照Controller注解,自定義我們的注解陶冷,然后標(biāo)注到某個(gè)類上钙姊,這樣Spring在進(jìn)行掃描的時(shí)候就會(huì)自動(dòng)將該類注冊(cè)為Bean。該注解一般泛指各種通用的組件埂伦,比如說(shuō)我們的類既不屬于@Controller煞额,@Service,@Repository的時(shí)候沾谜,就可以使用該注解膊毁。

參數(shù)只有一個(gè)value,表示該bean的名稱基跑;

2. Controller注解

??這個(gè)注解應(yīng)該是我們使用最多的注解了婚温,在Spring MVC中作為控制器Controller,負(fù)責(zé)處理DispatcherServlet 分發(fā)的請(qǐng)求涩僻,接受到請(qǐng)求后解析處理數(shù)據(jù)缭召,然后再返回給瀏覽器。而在Spring MVC中逆日,定義Controller也是特別簡(jiǎn)單,只需要一個(gè)@Controller注解就可以標(biāo)記一個(gè)類是Controller萄凤,然后使用RequestMapping等注解來(lái)定義URL和Controller之間的映射室抽。
??該注解標(biāo)記于類上,但也只是定義了一個(gè)控制器類靡努,而使用@RequestMapping注解的方法才是真正處理請(qǐng)求的處理器坪圾,然后我們配置掃描路徑后晓折,就可以訪問這些具體的控制器方法了。

該注解只有一個(gè)參數(shù):

value兽泄,Spring掃描Controller后漓概,會(huì)生成對(duì)應(yīng)的bean,該參數(shù)用于定義bean的名稱病梢,如果不指定value胃珍,則Controller默認(rèn)的bean名稱是該類的類名,其中首字母小寫蜓陌。

3. Service注解

基于@Component注解觅彰,用于標(biāo)注某個(gè)類為服務(wù)組件。也就是說(shuō)用它標(biāo)注的類表示Service層的實(shí)現(xiàn)钮热,同樣參數(shù)只有value填抬,用于表示該bean的名稱,也就是在Spring環(huán)境中的id隧期。不過需要簡(jiǎn)單注意下:

在文章 spring-applicationContext.xml-context標(biāo)簽 的<context:component-scan>標(biāo)簽的 name-generator 這里飒责,我們已經(jīng)了解到,由于Spring默認(rèn)的bean的命名策咯的實(shí)現(xiàn)類是AnnotationBeanNameGenerator仆潮,根據(jù)源碼也就是說(shuō)读拆,默認(rèn)情況下如果我們不設(shè)置value屬性,那么默認(rèn)的bean名稱就是類名,第一個(gè)字母是小寫世澜;當(dāng)然還有一個(gè)特殊的處理奖唯,當(dāng)類的名字是以兩個(gè)或兩個(gè)以上的大寫字母開頭的話,那么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;
        }
    }
    // 處理默認(rèn)的bean名稱
    return buildDefaultBeanName(definition, registry);
}

然后,單步調(diào)試 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;
    }
    // 如果類的前兩個(gè)字母都是大寫芥喇,直接返回類名
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    // 將類的第一個(gè)字母轉(zhuǎn)成小寫
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

然后看一下注釋即對(duì)應(yīng)的實(shí)現(xiàn),就可以看到相應(yīng)的實(shí)現(xiàn)了凰萨。至于如何修改bean的命名策咯继控,前文已經(jīng)了解過,這里就不多講了胖眷。

4. Repository 注解

Repository注解和@Controller武通,@Service這兩個(gè)功能是類似的,@Controller作為Web層組件珊搀,@Service作為服務(wù)層組件冶忱,而@Repository是作為數(shù)據(jù)訪問層的組件,也就是境析,一般情況下囚枪,通過該bean對(duì)數(shù)據(jù)庫(kù)進(jìn)行各種操作派诬。其余和Service功能并無(wú)很大區(qū)別,不多說(shuō)链沼。

5. bean注解

??這里我們來(lái)學(xué)習(xí)bean相關(guān)的注解默赂,由于前文已經(jīng)學(xué)習(xí)了XML中bean的配置,所以有可能有重復(fù)的括勺,如果有重復(fù)的缆八,這里就一筆帶過。首先來(lái)看一下bean注解朝刊。
??@Bean注解是一個(gè)方法級(jí)別的注解耀里,和XML中配置<bean/>標(biāo)簽功能是一樣的。通常情況下是與@Configuration注解一起使用拾氓,并且通過和@Scope冯挎,@Lazy等注解一起來(lái)控制Bean的一些配置。

@Configuration
public class AppConfig {

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

等價(jià)于在Spring XML中的配置:

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

我們來(lái)簡(jiǎn)單看下@Bean的各個(gè)參數(shù):

  1. valuename咙鞍,bean的名稱房官;
  2. autowire,注入方式续滋,可以是ByName翰守,byType,也可也選擇NO疲酌,默認(rèn)是Autowire.NO蜡峰;
  3. initMethoddestroyMethod,實(shí)例化bean的時(shí)候的初始化方法朗恳,及銷毀前執(zhí)行的方法湿颅;

官網(wǎng)文檔:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-java-bean-annotation

6. ComponentScan注解

該注解對(duì)應(yīng)于XML配置中的 <context:component-scan>,用來(lái)掃描相應(yīng)的注解并自動(dòng)注入bean粥诫。該注解于XML配置有些許不同:

  1. 該注解沒有 annotation-config對(duì)應(yīng)的屬性油航,因?yàn)槲覀兂绦驇缀跛械那闆r,都會(huì)掃描@Autowired等這一類<context:annotation-config>所對(duì)應(yīng)的注解怀浆,所以也就相當(dāng)于有這個(gè)參數(shù)谊囚,但這個(gè)參數(shù)一直是true;
  2. 此外执赡,該注解多了basePackageClasses這個(gè)數(shù)組參數(shù)镰踏,表示指定特定的要掃描的類,注入的時(shí)候也只注入這些類搀玖;
  3. 還有一個(gè)參數(shù):lazyInit余境,表示掃描的時(shí)候根據(jù)bean是否配置了lazy來(lái)決定是否延遲初始化該bean。默認(rèn)是false灌诅,也就是說(shuō)無(wú)論bean是否配置了lazy屬性芳来,該bean被掃描到后都會(huì)立刻初始化,設(shè)置為true之后猜拾,如果bean配置了lazy屬性即舌,那么會(huì)根據(jù)bean的lazy屬性來(lái)選擇是否延遲初始化;
7. ComponentScan.Filter注解

??ComponentScan注解的內(nèi)部的一個(gè)注解挎袜,用在ComponentScanincludeFiltersexcludeFilters顽聂,而這里的用法和配置文件中的context:exclude-filtercontext:include-filter是一致的,目的就是為了掃描的時(shí)候過濾用的盯仪。至于Filter中的參數(shù)和我們前面學(xué)習(xí)的XML中的參數(shù)差不多紊搪,不多說(shuō)了。

@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注解

??這個(gè)注解是Spring4.3之后引入的全景,目的就是配置多個(gè)掃描器(當(dāng)然也可以在一個(gè)類上配置多個(gè)ComponentScan注解)耀石。學(xué)習(xí)過XML的配置我們知道,在XML中我們是可以配置多個(gè)<context:component-scan>標(biāo)簽的爸黄,同樣滞伟,使用該注解可以讓我們?cè)诔绦蚶锱渲枚鄠€(gè)ComponentScan注解。該注解參數(shù)只有一個(gè)炕贵,就是數(shù)組類型的ComponentScan梆奈。

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

??Spring的@Configuration注解修飾的類其實(shí)就相當(dāng)于XML中的一個(gè)配置文件,可以通過該注解聲明一個(gè)配置類來(lái)減少繁瑣的XML配置称开,然后使用@Bean注解標(biāo)記相應(yīng)的方法亩钟。在這里@Configuration就相當(dāng)于XML中的beans標(biāo)簽,而@Bean就相當(dāng)于bean標(biāo)簽鳖轰。
舉個(gè)簡(jiǎn)單的例子:

@Configuration
public class AppConfig {

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

??由于Component注解是Configuration的元注解清酥,所以也會(huì)被@ComponentScan所掃描到。而我們?cè)谑褂米⒔庹{(diào)用的時(shí)候脆霎,是通過AnnotationConfigApplicationContext或者web相關(guān)的AnnotationConfigWebApplicationContext

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

一般总处,該注解有以下幾種用法:

  1. 會(huì)配合ComponentScan注解進(jìn)行掃描,并且配合EnableWebMvc注解來(lái)開啟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注解獲取導(dǎo)入其他的配置類:
@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注解獲取當(dāng)前環(huán)境信息(開發(fā)睛蛛,測(cè)試鹦马,生產(chǎn)):
 @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注解導(dǎo)入對(duì)應(yīng)的配置文件忆肾,如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. 單元測(cè)試荸频,引入@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 ...
    }
}

關(guān)于Configuration注解,需要注意下:

    1. 該注解修飾的必須是類客冈,不能是接口旭从;
    1. 配置類必須不能是final類型的;
    1. 配置類不能是匿名類;
    1. 嵌套的配置類必須是static的和悦;

更多有關(guān)Configuration的內(nèi)容退疫,可以參考官方API:https://docs.spring.io/spring/docs/current/javadoc-api/
而有關(guān)Spring @Configuration 和 @Component 區(qū)別可以參考:
Spring @Configuration 和 @Component 區(qū)別

10. DependsOn注解

??DependsOn注解和bean的 depends-on方法類似,用于一個(gè)beanA初始化之前必須先初始化另一個(gè)beanB鸽素,由于前面已經(jīng)仔細(xì)分析過褒繁,這里就不多說(shuō),具體可參考:applicationContext.xml詳解一beans標(biāo)簽

11. Description注解

??同樣馍忽,和bean標(biāo)簽中的description屬性類似棒坏,用于對(duì)bean添加描述信息,同樣是直接或間接使用了@Component或者@Bean注解的類或方法上遭笋。

12. Import注解

??對(duì)應(yīng)于beans標(biāo)簽的import子標(biāo)簽坝冕,用于模塊化過程中引入其他的配置文件,而在對(duì)應(yīng)的注解中瓦呼,則是為了引入@Configuration注解所修飾的配置類喂窟。當(dāng)然,不但但可以導(dǎo)入配置類吵血,也可以導(dǎo)入普通的java類谎替,并將其聲明成一個(gè)bean。而如果需要導(dǎo)入XML或者其他非@Configuration所定義的資源蹋辅,可以通過注解@ImportResource 來(lái)實(shí)現(xiàn)钱贯。

參數(shù)value 數(shù)組,聲明用于導(dǎo)入的具體配置類的類型侦另;

可以看個(gè)簡(jiǎn)單的例子:

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標(biāo)簽的lazy-init屬性功能是相同的。同樣褒傅,可以在任何直接或間接的使用了@Component或者@Bean注解的類或方法上使用該注解弃锐。如果該注解沒有用于@Component或@Bean定義中,那么就會(huì)立即初始化殿托,而不會(huì)延遲霹菊。該注解只有一個(gè)參數(shù)value,默認(rèn)是true支竹,也就是延遲初始化旋廷。

如果該注解用于@Configuration修飾的配置類上,那么@Configuration中的所有的@Bean方法都會(huì)被延遲初始化礼搁。而如果@Configuration中的bean上已經(jīng)配置了Lazy注解饶碘,那么以Bean中的優(yōu)先。

14. Primary注解

??在前面學(xué)習(xí)Spring bean標(biāo)簽的時(shí)候馒吴,我們當(dāng)時(shí)已經(jīng)學(xué)習(xí)了bean的primary屬性扎运,其實(shí)它和@Primary注解功能是一樣的瑟曲。是說(shuō)在使用類型注入的時(shí)候,如果存在多個(gè)相同類型的bean豪治,那么可以通過使用primary屬性或者@Primary注解讓該bean成為優(yōu)先候選者洞拨,如果候選者之中只有一個(gè)primary修飾的bean,那么這個(gè)bean將會(huì)是自動(dòng)注入的bean鬼吵。

不過需要注意下扣甲,就是如果在XML中配置了bean的primary屬性篮赢,那么該bean對(duì)應(yīng)的@Primary注解將會(huì)被忽略掉齿椅,并且記得配置掃描路徑。

可以在任何直接或間接的使用了@Component或者@Bean注解的類或方法上使用該注解启泣。

15. Scope注解

Scope注解表示bean的作用域涣脚,默認(rèn)情況下我們創(chuàng)建的bean都是單例的。前文已經(jīng)說(shuō)過寥茫,這里就不多說(shuō)了遣蚀,看一下參數(shù):

  • scopeNamevalue,作用域類型的名稱纱耻;
  • proxyMode芭梯,作用域代理相關(guān)的配置,對(duì)應(yīng)的值是 ScopedProxyMode對(duì)象的值弄喘。
16. PropertySource 和 PropertySources注解

該注解是Spring提供的用于讀取properties文件的玖喘,通過配合Environment 類來(lái)實(shí)現(xiàn)該功能,該注解用于Configuration 所修飾的類蘑志。先來(lái)看一個(gè)例子累奈,其實(shí)上文已經(jīng)簡(jiǎn)單介紹過:

@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;
    }
}

再簡(jiǎn)單來(lái)看一下該注解的參數(shù):

  1. value,要加載的資源位置急但,數(shù)組格式澎媒,和XML中支持的功能一樣強(qiáng)大,包括classpath波桩,file戒努;但不支持類似的資源通配符:**/*.properties,也就是.properties必須要精確到具體位置镐躲;
  2. name储玫,對(duì)應(yīng)屬性資源的名稱;
  3. encoding匀油,字符編碼缘缚,比如 UTF-8 格式;
  4. ignoreResourceNotFound敌蚜,如果找不到對(duì)應(yīng)的屬性資源桥滨,是否忽略。默認(rèn)是false,這種情況下齐媒,如果找不到的話將會(huì)提示異常蒲每;
  5. factory,指定一個(gè)資源工廠對(duì)象PropertySourceFactory喻括;邀杏、

一般情況下,該注解還會(huì)配合@Value來(lái)一起使用唬血,這時(shí)候記得配置PropertySourcesPlaceholderConfigurer對(duì)象來(lái)解析占位符望蜡,在XML配置中我們可以通過<context:property-placeholder>,在注解中我們可以將該對(duì)象注冊(cè)為bean拷恨,配置如下:

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

而對(duì)于@PropertySources注解脖律,則是可以加載多個(gè)PropertySource,不多說(shuō)了腕侄。

17. Conditional 注解

??Spring在4.0之后引入的條件注解小泉,就是根據(jù)滿足某個(gè)特定的條件創(chuàng)建一個(gè)特定的bean,而我們實(shí)現(xiàn)的方式就是實(shí)現(xiàn)Condition接口冕杠,然后重寫matches方法來(lái)構(gòu)造判斷條件微姊,比如在Windows下和在Linux下執(zhí)行不同的操作,具體的例子可以參考:Spring 條件注解(@Conditional)

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

??可以用于類或方法上,當(dāng)用于@Configuration修飾的類上時(shí)噪舀,那么該類下所有bean都將受此條件限制魁淳。

有關(guān)更多可參考:Spring @Conditional Annotation

18. Profile 注解

@Profile是Spring用于獲取當(dāng)前運(yùn)行環(huán)境的注解,比如我們的開發(fā)与倡,測(cè)試界逛,UAT,生產(chǎn)等纺座,或者還有其他的幾套環(huán)境息拜,而我們就可以通過該注解來(lái)獲取當(dāng)前環(huán)境信息。
看一個(gè)簡(jiǎn)單的例子:

@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注解示例
官網(wǎng)地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-java

19. Async 注解

??@Async是Spring用于異步調(diào)用的注解少欺,該注解用于方法或類上。@Async標(biāo)注的方法稱為異步方法馋贤,這些方法將在執(zhí)行的時(shí)候赞别,將會(huì)在獨(dú)立的線程中被執(zhí)行,調(diào)用者無(wú)需等待它的完成配乓,即可繼續(xù)其他的操作仿滔。而@Async如果標(biāo)注在類上的話惠毁,則表示該類的所有方法都將是異步的。

  1. 異步方法的返回值必須是void或者Future崎页,方法必須是public類型的鞠绰;
  2. 如果要異步的話,首先必須要在配置類上添加@EnableAsync注解來(lái)開啟異步飒焦;
  3. @Async不能與生命周期函數(shù)一起使用蜈膨,必須使用一個(gè)單獨(dú)的初始化Spring bean來(lái)調(diào)用目標(biāo)上的@Async注釋方法;
  4. 在XML中沒有與@Async等價(jià)的標(biāo)簽牺荠;

如果EnableAsync滿足不了我們的需求的話翁巍,我們還可以配置EnableAsync的一些屬性:

  • annotation - 默認(rèn)情況下, @EnableAsync 會(huì)掃描使用了Spring @Async與EJB 3.1 javax.ejb.Asynchronous的方法;此選項(xiàng)也可以用來(lái)掃描其他的志电,如用戶自定義的注解類型曙咽;
  • mode - 指定應(yīng)該使用哪種AOP進(jìn)行切面處理 - JAVA代理或AspectJ;
  • proxyTargetClass - 指定應(yīng)該使用哪種代理類 - CGLIB或JDK挑辆;此屬性只有當(dāng)mode設(shè)置成AdviceMode.PROXY才會(huì)產(chǎn)生效果。
  • order - 設(shè)置AsyncAnnotationBeanPostProcessor執(zhí)行順序(生命周期有關(guān))孝情;默認(rèn)情況下會(huì)最后一個(gè)執(zhí)行鱼蝉,所以這樣就能顧及到所有已存在的代理。
@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"/>

默認(rèn)情況下魁亦,當(dāng)@Async使用于方法上時(shí),將使用annotation-driven提供的executor執(zhí)行器羔挡,如果要指定相應(yīng)的executor洁奈,可以使用@Async唯一的一個(gè)參數(shù)value來(lái)指定。

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

有關(guān)Async和Scheduled相關(guān)的官網(wǎng)文檔地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/scheduling.html#scheduling-annotation-support

20. Scheduled Schedules 注解

??@Scheduled相信我們都用過绞灼,用來(lái)執(zhí)行定時(shí)任務(wù)相關(guān)的操作利术,同樣是方法級(jí)別的注解。使用該注解修飾的方法必須沒有參數(shù)低矮,并且返回類型通常是void類型的印叁。該注解使用ScheduledAnnotationBeanPostProcessor來(lái)執(zhí)行,同樣军掂,使用該注解前要首先要開啟定時(shí)轮蜕,XML中使用<task:annotation-driven/>,而注解的話則是通過@EnableScheduling來(lái)完成蝗锥。
??另外跃洛,該注解可以作為元注解,從而自定義我們的定時(shí)任務(wù)相關(guān)的注解终议。接下來(lái)我們來(lái)簡(jiǎn)單看下它的參數(shù):

  1. cron汇竭,這個(gè)應(yīng)該是我們使用最多的參數(shù)了闲延,定時(shí)任務(wù)時(shí)間設(shè)置的表達(dá)式,比如每隔1天執(zhí)行一次韩玩,每隔半小時(shí)執(zhí)行垒玲,這個(gè)設(shè)置網(wǎng)上的介紹實(shí)在太多了,用的時(shí)候再去查詢就可以了找颓;
  2. zone合愈,cron表達(dá)式解析的時(shí)區(qū),默認(rèn)情況下击狮,該屬性是空字符串(也就是服務(wù)器的本地時(shí)區(qū))佛析,接收的是TimeZone.getTimeZone(String)的id;
  3. fixedDelay彪蓬,每次執(zhí)行任務(wù)之后間隔多久再次執(zhí)行任務(wù)寸莫,以上次任務(wù)結(jié)束時(shí)間和下此任務(wù)開始時(shí)間之間的毫秒為單位來(lái)執(zhí)行;比如說(shuō)間隔時(shí)間是5秒档冬,任務(wù)執(zhí)行的時(shí)間是8秒膘茎,那么8秒執(zhí)行后,下此就是8+5秒的時(shí)候在執(zhí)行酷誓,再下此就是8+5+8+5的時(shí)候再執(zhí)行披坏,就是以結(jié)束時(shí)間為準(zhǔn);
  4. fixedDelayString盐数,和上面fixedDelay一樣棒拂,只不過參數(shù)類型是字符串形式,可以通過外部來(lái)定義玫氢,如 fixedDelayString= "${job.fixed.string}"帚屉;;
  5. fixedRate漾峡,固定頻率的間隔時(shí)間攻旦,也就是每隔多久就執(zhí)行,不管任務(wù)是否完成灰殴;
  6. fixedRateString敬特,和fixedRate一樣,只不過參數(shù)類型是字符串形式牺陶,可以通過外部來(lái)定義伟阔,如 fixedRateString = "${job.fixed.rate}";
  7. initialDelay掰伸,該參數(shù)表示第一次執(zhí)行定時(shí)任務(wù)之前需要等待多長(zhǎng)時(shí)間皱炉,也就是第一次延時(shí)多長(zhǎng)時(shí)間后執(zhí)行,單位同樣是毫秒狮鸭;

有關(guān)fixedDelay合搅,fixedRate多搀,cron,再稍微簡(jiǎn)單說(shuō)下:

fixedDelay比較簡(jiǎn)單灾部,就是根據(jù)上次任務(wù)結(jié)束時(shí)間計(jì)算的康铭,而cron,和fixedDelay則有點(diǎn)不太一樣赌髓,比如間隔時(shí)間是5S从藤,如果執(zhí)行時(shí)間是8S,則下此執(zhí)行的時(shí)間將從10S開始锁蠕,會(huì)跳過一個(gè)間隔時(shí)間夷野,如果執(zhí)行時(shí)間是10S,則下此執(zhí)行時(shí)間從15S開始荣倾;而如果執(zhí)行時(shí)間是3S悯搔,那下此執(zhí)行時(shí)間就將從5S開始,每隔5S執(zhí)行舌仍;

至于fixedRate妒貌,它的使用和上面兩種都不太相同,如果上次任務(wù)執(zhí)行時(shí)間超過了間隔時(shí)間抡笼,那么超出的這部分時(shí)間會(huì)被計(jì)入下一次任務(wù)的執(zhí)行時(shí)間中苏揣,至于問題的細(xì)節(jié),我們可以參考這篇文章:
https://yanbin.blog/understand-spring-schedule-fixedrate-fixeddelay/

在網(wǎng)上看到兩幅圖描述fixedDelay推姻,fixedRate,描述的很形容:

fixedDelay和fixedRate.png

圖片轉(zhuǎn)自:https://blog.csdn.net/applebomb/article/details/52400154

如果我們沒有配置TaskScheduler 和 ScheduledExecutorService 的話框沟,那么Scheduled默認(rèn)是采用單線程來(lái)進(jìn)行循環(huán)執(zhí)行定時(shí)任務(wù)的藏古,我們可以通過Thread.currentThread()來(lái)查看當(dāng)前運(yùn)行的線程,默認(rèn)定時(shí)任務(wù)的線程是Executors.defaultThreadFactory() 產(chǎn)生的忍燥,線程名稱是 "pool-NUMBER-thread-..."拧晕。

至于Schedules 注解,則是多個(gè)Scheduled組成的數(shù)組梅垄,就不說(shuō)了厂捞。

參考:Spring 定時(shí)任務(wù)(Schedule) 和線程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市队丝,隨后出現(xiàn)的幾起案子靡馁,更是在濱河造成了極大的恐慌,老刑警劉巖机久,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臭墨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡膘盖,警方通過查閱死者的電腦和手機(jī)胧弛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門尤误,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人结缚,你說(shuō)我怎么就攤上這事损晤。” “怎么了红竭?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵尤勋,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我德崭,道長(zhǎng)斥黑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任眉厨,我火速辦了婚禮锌奴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘憾股。我一直安慰自己鹿蜀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布服球。 她就那樣靜靜地躺著茴恰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斩熊。 梳的紋絲不亂的頭發(fā)上往枣,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音粉渠,去河邊找鬼分冈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛霸株,可吹牛的內(nèi)容都是我干的雕沉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼去件,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼坡椒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起尤溜,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倔叼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后靴跛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缀雳,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年梢睛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肥印。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片识椰。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖深碱,靈堂內(nèi)的尸體忽然破棺而出腹鹉,到底是詐尸還是另有隱情,我是刑警寧澤敷硅,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布功咒,位于F島的核電站,受9級(jí)特大地震影響绞蹦,放射性物質(zhì)發(fā)生泄漏力奋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一幽七、第九天 我趴在偏房一處隱蔽的房頂上張望景殷。 院中可真熱鬧,春花似錦澡屡、人聲如沸猿挚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)绩蜻。三九已至,卻和暖如春室埋,著一層夾襖步出監(jiān)牢的瞬間办绝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工姚淆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留八秃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓肉盹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疹尾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子上忍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容