SpringBoot4-spring高階話題-spring aware,多執行緒,計劃任務,條件註解@Conditional
一:Spring Aware
1,點睛
spring的依賴注入的最大亮點就是你所有的Bean對Spring容器的存在是沒有意識的,即你可以將你的容器替換成別的容器,如Google Guice,這時Bean之間的耦合度很低。
但是在實際專案中,你不可避免的要用到Spring容器本身的功能資源,這時你的Bean必須意識到Spring容器的的存在,才能呼叫Spring所提供的資源,這就是所謂的Spring Aware。其實Spring Aware本來就是Spring設計用來框架內容使用的,若使用了Spring Aware,你的Bean將會和Spring框架耦合。
Spring提供的Aware介面如下:
BeanNameAware:獲得容器中的bean名稱
BeanFactoryAware:獲得當前bean factory,這樣就可以呼叫容器的服務
ApplicationContextAwate*:當前application context,這樣可以呼叫容器的服務
MessageSourceAware:獲得message source,這樣可以獲得文字資訊
ApplicationEventPublisherAware:應用時間釋出器,可以釋出事件
ResourceLoaderAware:獲得資源載入器,可以獲得外部資原始檔
Spring Aware的目的是為了讓Bean獲得Spring容器的服務。因為ApplicationContext介面集成了MessageSource介面,ApplicationEventPublisher介面和ResourceLoader介面,所以Bean繼承ApplicationContextAware可以獲得Spring容器的所有服務,但原則上我們還是用到什麼介面,就實現什麼介面。
示例如下:
在jack.ch3.aware包下新建一個test.txt,內容隨意,給下面的外部資源載入使用。
Spring Aware演示Bean:
package jack.ch3.aware; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.BeanNameAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Service; /** * Created by jack on 2017/7/11. */ //實現BeanNameAware,ResourceLoaderAware介面,獲得Bean名稱和資源載入的服務 @Service public class AwareService implements BeanNameAware,ResourceLoaderAware{ private String beanName; private ResourceLoader loader; //實現BeanNameAware需重寫setBeanName @Override public void setBeanName(String name) { this.beanName = name; } //實現ResourceLoaderAware需重寫setResourceLoader @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.loader = resourceLoader; } public void outputResult(){ System.out.println("Bean的名稱為:"+beanName); Resource resource = loader.getResource("classpath:jack/ch3/aware/test.txt"); try { System.out.println("ResourceLoader載入檔案內容為: "+ IOUtils.toString(resource.getInputStream())); }catch (Exception e){ e.printStackTrace(); } } }
配置類:
package jack.ch3.aware;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Created by jack on 2017/7/11.
*/
@Configuration
@ComponentScan("jack.ch3.aware")
public class AwareConfig {
}
測試程式碼如下:
package jack.ch3.aware;
import jack.ch2.event.DemoPublisher;
import jack.ch2.event.EventConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Created by jack on 2017/7/11.
*/
public class MainTest9 {
public static void main(String [] args){
//AnnotationConfigApplicationContext作為spring容器,接受一個配置類作為引數
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class);
AwareService awareService = context.getBean(AwareService.class);
awareService.outputResult();
context.close();
}
}
執行測試程式,結果如下:
二:多執行緒
spring通過任務執行器(TaskExecutor)來實現多執行緒和併發程式設計。使用ThreadPoolTaskExecutor可實現一個基於執行緒池的TaskExecutor。而實際開發中任務一般是非阻礙的,即非同步的,所以我們要在配置類中通過@EnableAsync開啟對非同步任務的支援,並通過在實際執行的Bean的方法中使用@Async註解宣告其是一個非同步任務。 下面是示例:
配置類:
package jack.ch3.taskexecutor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* Created by jack on 2017/7/15.
*/
@Configuration
@ComponentScan("jack.ch3.taskexecutor")
@EnableAsync//利用@EnableAsync註解開啟非同步任務支援
/**
* 配置類實現AsyncConfigurer介面,並重寫getAsyncExecutor方法,並返回一個ThreadPoolTaskExecutor,這樣我們就獲得一個基於
* 執行緒池TaskExecutor
*/
public class TaskExecutorConfig implements AsyncConfigurer{
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
任務執行類:
package jack.ch3.taskexecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* Created by jack on 2017/7/15.
*/
@Service
public class AsyncTaskService {
//通過@Async註解表明該方法是一個非同步方法,如果註解在類級別上,則表明該類所有的方法都是非同步方法,
//而這裡的方法自動被注入使用ThreadPoolTaskExecutor作為TaskExecutor
@Async
public void executeAysncTask(Integer i){
System.out.println("執行非同步任務:"+i);
}
@Async
public void executeAsyncTaskPlus(Integer i){
System.out.println("執行非同步任務+1:"+(i+1));
}
}
測試程式碼如下:
package jack.ch3.taskexecutor;
import jack.ch2.event.DemoPublisher;
import jack.ch2.event.EventConfig;
import jack.ch3.aware.AwareService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.scheduling.annotation.AsyncConfigurer;
/**
* Created by jack on 2017/7/15.
*/
public class MainTest9 {
public static void main(String [] args){
//AnnotationConfigApplicationContext作為spring容器,接受一個配置類作為引數
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig.class);
AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class);
for (int i=0;i<10;i++){
asyncTaskService.executeAysncTask(i);
asyncTaskService.executeAsyncTaskPlus(i);
}
context.close();
}
}
執行程式,結果是併發的而不是順序執行的,如下:
三:計劃任務
從spring3.1開始,計劃任務在spring中的實現變得異常的簡單,首先通過在配置類註解@EnableScheduling來開啟對計劃任務的支援,然後在要執行計劃任務的方法上註解@Scheduled,宣告這是一個計劃任務。spring通過@Scheduled支援多種型別的計劃任務,保護cron,fixDelay,fixRate等。
示例如下:
計劃任務執行類:
package jack.ch3.taskscheduler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.SimpleFormatter;
/**
* Created by jack on 2017/7/15.
*/
@Service
public class ScheduledTaskService {
private static final SimpleDateFormat dateFormate = new SimpleDateFormat("HH:mm:ss");
//通過@Scheduled宣告該方法是計劃任務,使用fixedRate屬性每隔固定時間執行
@Scheduled(fixedRate = 5000)
public void reportCurrentTime(){
System.out.println("每隔五秒執行一次:"+dateFormate.format(new Date()));
}
//使用cron屬性可按照指定時間執行,本例指定在11點25分執行;cron是UNIX和類UNIX(Linux)系統下的定時任務
@Scheduled(cron = "0 25 11 ? * *")
public void fixTimeExecutor(){
System.out.println("在指定時間:"+dateFormate.format(new Date())+" 執行");
}
}
計劃任務配置類:
package jack.ch3.taskscheduler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* Created by jack on 2017/7/15.
*/
@Configuration
@ComponentScan("jack.ch3.taskscheduler")
@EnableScheduling //通過@EnableScheduling 註解開啟對計劃任務的支援
public class TaskSchedulerConfig {
}
測試程式碼:
package jack.ch3.taskscheduler;
import jack.ch3.taskexecutor.AsyncTaskService;
import jack.ch3.taskexecutor.TaskExecutorConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Created by jack on 2017/7/15.
*/
public class MainTest10 {
public static void main(String [] args){
//AnnotationConfigApplicationContext作為spring容器,接受一個配置類作為引數
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerConfig.class);
//context.close();計劃任務這裡不能關閉,需要一直在後臺執行
}
}
執行測試程式,結果如下:
通過輸出結果可以看出,一個是每隔5秒執行的,一個是定時執行的。
四:條件註解@Conditional
前面舉例了活動的profile,我們可以獲得不同的Bean。spring4提供了一個更通用的基於條件的bean的建立,即使用@Conditional註解@Conditional根據滿足某一個特定條件建立一個特定的Bean。比方說,當某一個jar包在一個類路徑下的時候,自動配置一個;或者只有某個bean被建立才會建立另外一個bean。總的來說,就是根據特定條件來控制bean的建立行為,這樣我們就可以利用這個特性進行一些自動化的配置。
在spring boot中將會大量應用到條件註解。
下面的示例將以不同的作業系統作為條件,我們將通過實行Condition介面,並重寫matches方法來構造判斷條件。若在Window系統下執行程式,則輸出列表命令dir;若在Linux系統下執行程式,則輸出列表命令ls。
示例如下:
判定Windows的條件:
package jack.ch3.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Created by jack on 2017/7/15.
*/
//判斷Window的條件
public class WindowsConditional implements Condition{
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
}
}
判定Linux的條件:
package jack.ch3.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Created by jack on 2017/7/15.
*/
//判斷Linux的條件
public class LinuxConditional implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Linux");
}
}
下面是一個介面:
package jack.ch3.conditional;
/**
* Created by jack on 2017/7/15.
* 介面
*/
public interface ListServer {
public String showListCmd();
}
Windows下所要建立的Bean的類:
package jack.ch3.conditional;
/**
* Created by jack on 2017/7/15.
*/
//Windows下所建立的Bean的類
public class WindowsListService implements ListServer {
@Override
public String showListCmd() {
return "dir";
}
}
Linux下所要建立的Bean類:
package jack.ch3.conditional;
/**
* Created by jack on 2017/7/15.
*/
//Linux下所要建立的Bean類
public class LinuxListService implements ListServer {
@Override
public String showListCmd() {
return "ls";
}
}
配置類:
package jack.ch3.conditional;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Created by jack on 2017/7/15.
*/
@Configuration
public class ConditionConfig {
/**
* 通過@Conditional註解,符合Windows條件則例項化windowsListService
* @return
*/
@Bean
@Conditional(WindowsConditional.class)
public ListServer windowsListService(){
return new WindowsListService();
}
/**
* 通過@Conditional註解,符合Linux條件則例項化linuxListService
* @return
*/
@Bean
@Conditional(LinuxConditional.class)
public ListServer linuxListService(){
return new LinuxListService();
}
}
測試程式碼如下:
package jack.ch3.conditional;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Created by jack on 2017/7/15.
*/
@Configuration
public class ConditionConfig {
/**
* 通過@Conditional註解,符合Windows條件則例項化windowsListService
* @return
*/
@Bean
@Conditional(WindowsConditional.class)
public ListServer windowsListService(){
return new WindowsListService();
}
/**
* 通過@Conditional註解,符合Linux條件則例項化linuxListService
* @return
*/
@Bean
@Conditional(LinuxConditional.class)
public ListServer linuxListService(){
return new LinuxListService();
}
}
執行結果如下: