前言
接上文人乓,接著學(xué)習(xí)Spring的注解勤篮。本篇博客所學(xué)習(xí)注解基本都是spring-context對(duì)應(yīng)jar包下的注解,Spring版本:4.3.14色罚,包含注解:
Component
碰缔,Controller
,Service
戳护,Repository
金抡,bean
,ComponentScan
腌且,ComponentScan.Filter
梗肝,ComponentScans
,Configuration
铺董,DependsOn
巫击,Description
,Import
精续,Lazy
坝锰,Primary
,Scope
重付,PropertySource
顷级,Conditional
,Profile
确垫,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ù):
value
和name
咙鞍,bean的名稱房官;autowire
,注入方式续滋,可以是ByName翰守,byType,也可也選擇NO疲酌,默認(rèn)是Autowire.NO蜡峰;initMethod
和destroyMethod
,實(shí)例化bean的時(shí)候的初始化方法朗恳,及銷毀前執(zhí)行的方法湿颅;
6. ComponentScan注解
該注解對(duì)應(yīng)于XML配置中的 <context:component-scan>
,用來(lái)掃描相應(yīng)的注解并自動(dòng)注入bean粥诫。該注解于XML配置有些許不同:
- 該注解沒有
annotation-config
對(duì)應(yīng)的屬性油航,因?yàn)槲覀兂绦驇缀跛械那闆r,都會(huì)掃描@Autowired
等這一類<context:annotation-config>
所對(duì)應(yīng)的注解怀浆,所以也就相當(dāng)于有這個(gè)參數(shù)谊囚,但這個(gè)參數(shù)一直是true;- 此外执赡,該注解多了
basePackageClasses
這個(gè)數(shù)組參數(shù)镰踏,表示指定特定的要掃描的類,注入的時(shí)候也只注入這些類搀玖;- 還有一個(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è)注解挎袜,用在ComponentScan
的includeFilters
和excludeFilters
顽聂,而這里的用法和配置文件中的context:exclude-filter
和context: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 ...
一般总处,該注解有以下幾種用法:
- 會(huì)配合ComponentScan注解進(jìn)行掃描,并且配合EnableWebMvc注解來(lái)開啟MVC:
@Configuration
@ComponentScan("com.acme.app.services")
@EnableWebMvc
public class AppConfig {
// various @Bean definitions ...
}
- 配合
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"));
}
}
- 配合@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);
}
}
- 配合@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());
}
}
- 配合@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() { ... }
}
- 配合@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);
}
}
- 使用@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();
}
}
}
- 單元測(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注解,需要注意下:
- 該注解修飾的必須是類客冈,不能是接口旭从;
- 配置類必須不能是final類型的;
- 配置類不能是匿名類;
- 嵌套的配置類必須是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ù):
-
scopeName
和value
,作用域類型的名稱纱耻; -
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ù):
value
,要加載的資源位置急但,數(shù)組格式澎媒,和XML中支持的功能一樣強(qiáng)大,包括classpath
波桩,file
戒努;但不支持類似的資源通配符:**/*.properties
,也就是.properties
必須要精確到具體位置镐躲;name
储玫,對(duì)應(yīng)屬性資源的名稱;encoding
匀油,字符編碼缘缚,比如UTF-8
格式;ignoreResourceNotFound
敌蚜,如果找不到對(duì)應(yīng)的屬性資源桥滨,是否忽略。默認(rèn)是false,這種情況下齐媒,如果找不到的話將會(huì)提示異常蒲每;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)注在類上的話惠毁,則表示該類的所有方法都將是異步的。
- 異步方法的返回值必須是void或者Future崎页,方法必須是public類型的鞠绰;
- 如果要異步的話,首先必須要在配置類上添加
@EnableAsync
注解來(lái)開啟異步飒焦;- @Async不能與生命周期函數(shù)一起使用蜈膨,必須使用一個(gè)單獨(dú)的初始化Spring bean來(lái)調(diào)用目標(biāo)上的@Async注釋方法;
- 在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ù):
cron
汇竭,這個(gè)應(yīng)該是我們使用最多的參數(shù)了闲延,定時(shí)任務(wù)時(shí)間設(shè)置的表達(dá)式,比如每隔1天執(zhí)行一次韩玩,每隔半小時(shí)執(zhí)行垒玲,這個(gè)設(shè)置網(wǎng)上的介紹實(shí)在太多了,用的時(shí)候再去查詢就可以了找颓;zone
合愈,cron表達(dá)式解析的時(shí)區(qū),默認(rèn)情況下击狮,該屬性是空字符串(也就是服務(wù)器的本地時(shí)區(qū))佛析,接收的是TimeZone.getTimeZone(String)的id;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);fixedDelayString
盐数,和上面fixedDelay
一樣棒拂,只不過參數(shù)類型是字符串形式,可以通過外部來(lái)定義玫氢,如 fixedDelayString= "${job.fixed.string}"帚屉;;fixedRate
漾峡,固定頻率的間隔時(shí)間攻旦,也就是每隔多久就執(zhí)行,不管任務(wù)是否完成灰殴;fixedRateString
敬特,和fixedRate
一樣,只不過參數(shù)類型是字符串形式牺陶,可以通過外部來(lái)定義伟阔,如 fixedRateString = "${job.fixed.rate}";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
,描述的很形容:
圖片轉(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ō)了厂捞。