1. 程式人生 > >SpringBoot4-spring高階話題-spring aware,多執行緒,計劃任務,條件註解@Conditional

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();
    }
}


  執行結果如下: