2018年8月27日 星期一

@ComponentScan 排除或包括 @Component (Spring 3.x 二十六)


AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Apple.class);
for (String s : app.getBeanDefinitionNames()) {
    System.out.println(s);
}
app.close();

※Apple.class 不管有沒有加 annotation 一定會有



※Apple.class

// @ComponentScan(excludeFilters = { @Filter(type = FilterType.ANNOTATION, classes = Component.class) })
// @ComponentScan(useDefaultFilters = false, includeFilters = { @Filter(type = FilterType.ANNOTATION, classes = Component.class) })
    
// @ComponentScan(excludeFilters = { @Filter(type = FilterType.ANNOTATION, classes = Repository.class) })
// @ComponentScan(useDefaultFilters = false, includeFilters = { @Filter(type = FilterType.ANNOTATION, classes = Controller.class) })
// @ComponentScan(useDefaultFilters = false, includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Chicken.class) })
@ComponentScan(useDefaultFilters = false, includeFilters = { @Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class) })
// @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class) })

※如果使用 includeFilters 時,一定要將 useDefaultFilters 設為 false,否則無效,表示只包括什麼

※第一個和第二個是有問題的,使用 excludeFilters 和 Component,結果是什麼都沒有;
使用 includeFilters 和 Component,結果是什麼都有



public class MyTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        // System.out.println(metadataReader.getAnnotationMetadata().getAnnotationTypes());
        // System.out.println(metadataReader.getClassMetadata().getClassName());//
        // package.class
        // System.out.println(metadataReader.getResource().getFilename());// name.class
        // System.out.println(metadataReaderFactory.getMetadataReader(metadataReader.getResource()).getAnnotationMetadata().getSuperClassName());
    
        Set<String> set = metadataReader.getAnnotationMetadata().getAnnotationTypes();
        if (new ArrayList<String>(set).contains("org.springframework.stereotype.Component")) {
            return false;
        }
        return true;
    }
}

※使用這個 class,配合上面的 FilterType.CUSTOM,可以排除或包括 @Component

2018年8月25日 星期六

Quartz 2.x 整合 SpringBoot 2.x


buildscript {
    ext {
        springBootVersion = '2.0.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
    
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
    
repositories {
    mavenCentral()
}
    
ext {
    springVersion = '4.3.18.RELEASE'
    springBootVersion = '2.0.3.RELEASE'
    quartzVersion = '2.2.1'
}
    
dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
    testCompile 'org.springframework.boot:spring-boot-starter-test'
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile group: 'org.springframework', name: 'spring-context-support', version: springVersion
    compile group: 'org.springframework', name: 'spring-tx', version: springVersion
    compile group: 'org.quartz-scheduler', name: 'quartz', version: quartzVersion
    compile group: 'org.quartz-scheduler', name: 'quartz-jobs', version: quartzVersion
}




@SpringBootApplication
@ComponentScan("annotation")
public class Library {
    public static void main(String[] args) {
        SpringApplication.run(Library.class, args);
    }
}

※@ComponentScan 的程式碼可看我的另一篇,最下面的 annotation

2018年8月22日 星期三

Quartz 2.x 整合 Spring 3/4

※整合 Spring

官網連結

※build.gradle

ext {
    springVersion = '4.3.18.RELEASE'
    quartzVersion = '2.2.1'
}
    
dependencies {
    compile("org.springframework:spring-context:$springVersion")
    compile group: 'org.springframework', name: 'spring-context-support', version: springVersion
    compile group: 'org.springframework', name: 'spring-tx', version: springVersion
    compile group: 'org.quartz-scheduler', name: 'quartz', version: quartzVersion
    compile group: 'org.quartz-scheduler', name: 'quartz-jobs', version: quartzVersion
}

※一定要有 quartz 的 jar 包,否則編譯時居然是報找不到 spring jar 包的編譯錯誤,很奇怪


官網有說明,提供了 JobDetailFactoryBean 和 MethodInvokingJobDetailFactoryBean
而每一個又分成使用 SimpleTriggerFactoryBean 和 SchedulerFactoryBean


※XML設定


※使用 JobDetailFactoryBean

public class MyJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
        System.out.println("xxxxxxxxxxxxxxxxxxxxx");
        // ctx.getJobDetail().getJobDataMap().get("aaa");
    }
}




※quartz.xml 之 SimpleTriggerFactoryBean

<bean id="jobDetail"
    class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="job.MyJob" />
    <!-- <property name="durability" value="true" /> -->
    <property name="jobDataAsMap">
        <map>
            <entry key="aaa" value="111"/>
        </map>
    </property>
</bean>
    
<bean id="simpleTrigger"
    class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="startDelay" value="10" />
    <property name="repeatInterval" value="2000" />
</bean>
    
<bean
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="simpleTrigger" />
        </list>
    </property>
</bean>

※durability 預設為 false,Spring 3.x 要為 true 才可以,否則會出「Jobs added with no trigger must be durable」的錯
原文是 Specify the job's durability, i.e. whether it should remain stored in the job store even if no triggers point to it anymore.
大概是說不管有沒有 trigger 指向它,是否應該保留在 job store 裡,反正 3.x 不是 true 就會報這個錯
關於 jobStore,可看官網第九篇,有三種


※quartz.xml 之 SchedulerFactoryBean

<bean id="jobDetail"
    class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="job.MyJob" />
</bean>
    
<bean id="cronTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="cronExpression" value="0/2 0 0 * * ?" />
</bean>
    
<bean
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger" />
        </list>
    </property>
</bean>





※MethodInvokingJobDetailFactoryBean

package job;
    
public class MyJob {
    public void getXxx() {
        System.out.println("xxxxxxxxxxxxxxxxxxxxx");
    }
}

※使用 JobDetailFactoryBean 要繼承;使用 MethodInvokingJobDetailFactoryBean 不用


※quartz.xml 之 SimpleTriggerFactoryBean

<bean id="myJob" class="job.MyJob" />
    
<bean id="jobDetail"
    class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="myJob" />
    <property name="targetMethod" value="getXxx" />
    <!-- <property name="concurrent" value="false"/> -->
</bean>
    
<bean id="simpleTrigger"
    class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="startDelay" value="10" />
    <property name="repeatInterval" value="2000" />
</bean>
    
<bean
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="simpleTrigger" />
        </list>
    </property>
</bean>

※concurrent 預設為 true,如果改成 false,和 @DisallowConcurrentExecution 是一樣的意思,可在 MyJob 加這個註解,和 concurrent 不寫或寫 true 比較看看,程式碼可看這篇


※quartz.xml 之 SchedulerFactoryBean

<bean id="myJob" class="job.MyJob" />
    
<bean id="jobDetail"
    class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="myJob" />
    <property name="targetMethod" value="getXxx" />
    <!-- <property name="concurrent" value="false" /> -->
</bean>
    
<bean id="cronTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="cronExpression" value="0/2 0 0 * * ?" />
</bean>
    
<bean
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger" />
        </list>
    </property>
</bean>




※測試類

new ClassPathXmlApplicationContext("quartz.xml");





※整合 Spring Web

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/quartz.xml</param-value>
</context-param>
    
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

※gradle 增加 compile("org.springframework:spring-web:$springVersion"),伺服器一啟動就開始跑了



※整合 SpringWebMVC

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/quartz.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
    
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

※gradle 增加 compile("org.springframework:spring-webmvc:$springVersion"),伺服器一啟動就開始跑了




※annotation 設定



@Component
public class MyAnnotationJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
        System.out.println("xxxxxxxxxxxxxxxxxxxxx");
    }
    
    // public void getXxx() {
    //     System.out.println("xxxxxxxxxxxxxxxxxxxxx");
    // }
}




@Configuration
@ComponentScan("annotation")
public class QuartzConfiguration {
    @Autowired
    private MyAnnotationJob myJob;
    
    // @Bean
    // public JobDetailFactoryBean jobDetail() {
    // JobDetailFactoryBean job = new JobDetailFactoryBean();
    // job.setJobClass(MyAnnotationJob.class);
    // job.setDurability(true);
    // return job;
    // }
    
    @Bean
    public MethodInvokingJobDetailFactoryBean jobDetail() {
        MethodInvokingJobDetailFactoryBean job = new MethodInvokingJobDetailFactoryBean();
        job.setTargetObject(myJob);
        job.setTargetMethod("getXxx");
        return job;
    }
    
    @Bean
    public SimpleTriggerFactoryBean simpleTrigger() {
        SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
        trigger.setJobDetail(jobDetail().getObject());
        trigger.setStartDelay(10);
        trigger.setRepeatInterval(2000);
        return trigger;
    }
    
    // @Bean
    // public CronTriggerFactoryBean cronTriggerBean() {
    // CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
    // cronTriggerFactoryBean.setJobDetail(jobDetail().getObject());
    // cronTriggerFactoryBean.setCronExpression("*/2 * * * * ?");
    // return cronTriggerFactoryBean;
    // }
    
    @Bean
    public SchedulerFactoryBean schedulerFactory() {
        SchedulerFactoryBean schedule = new SchedulerFactoryBean();
        schedule.setTriggers(simpleTrigger().getObject());
        return schedule;
    }
}




※spring 測試

new AnnotationConfigApplicationContext(annotation.QuartzConfiguration.class);



※SpringWeb的 web.xml 設定

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>annotation.QuartzConfiguration</param-value>
</context-param>
    
<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
    
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>




※SpringWebMVC的 web.xml 設定

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>annotation.QuartzConfiguration</param-value>
    </init-param>
    
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
    
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>



2018年8月20日 星期一

Hello Quartz (Quartz 2.x 一)

※文檔

官網教學連結
官網設定教學連結

W3Cschool Quartz 官網

好心網友翻譯的連結:
第一課 使用 Quartz
第二課 Quartz API,Jobs 和 Triggers 介紹
第三課 關於更多的 Jobs和 JobDetails
第四課 關於更多的 Triggers
第五課 SimpleTriggers
第六課 CronTriggers
第七課 TriggerListeners & JobListeners
第八課 SchedulerListeners
第九課 JobStores
第十課 Configuration, Resource Usage和SchedulerFactory
第十一課 進階(企業)功能
第十二課 Quartz 其他功能


※Hello Quartz

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
    
public class TestQuartz {
    public static void main(String[] args) {
        JobDetail job = JobBuilder.newJob(MyJob.class)
            .withIdentity("myJob1", "group1")
            .build();
    
        Trigger trigger = TriggerBuilder
            .newTrigger()
            .withIdentity("myTrigger1", "group2")
            .startNow()
            .withSchedule(SimpleScheduleBuilder
                .simpleSchedule()
                .withIntervalInSeconds(3)
                .repeatForever())
            .build();
    
        System.out.println(job.getKey()); // group1.myJob1
        System.out.println(trigger.getKey());// group2.myTrigger1
    
        try {
            SchedulerFactory schedFact = new StdSchedulerFactory();
            Scheduler sched = schedFact.getScheduler();
            System.out.println(trigger.getJobKey()); // null
            sched.scheduleJob(job, trigger);
            System.out.println(trigger.getJobKey()); // 註冊之後才能取得 group1.myJob1
            sched.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

※注意官網有靜態 import,如下:
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
之後的代碼,我也會用這種方式

※假設 SchedulerFactory 和 Scheduler 寫在最上面,然後馬上就 start(),最後在呼叫 JobDetail、
Trigger並註冊 scheduleJob,也是可以運行的


public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("xxxxxxxxxxxxx");
    }
}

※Job 就是時間到後要執行的方法



※JobDataMap

JobDetail job = newJob(MyJob.class)
    .withIdentity("myJob", "group1")
    .usingJobData("aaa", "111")
    .usingJobData("bbb", 222)
    .build();
    
Trigger trigger = newTrigger()
    .withIdentity("myTrigger1", "group2")
    .startNow()
    .withSchedule(simpleSchedule()
    .withIntervalInSeconds(3)
    .repeatForever())
    .build();
    
try {
    SchedulerFactory schedFact = new StdSchedulerFactory();
    Scheduler sched = schedFact.getScheduler();
    sched.scheduleJob(job, trigger);
    sched.start();
} catch (SchedulerException e) {
    e.printStackTrace();
}

※使用 JobDataMap 可以塞值



public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey key = context.getJobDetail().getKey();
    
        // JobDataMap dataMap = context.getMergedJobDataMap();
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String a = dataMap.getString("aaa");
        int b = dataMap.getInt("bbb");
    
        System.out.println("key=" + key); // group1.myJob
        System.out.println("a=" + a); // 111
        System.out.println("b=" + b);// 222
    }
}

※取值時,可以使用 getMergedJobDataMap() 或 getJobDetail().getJobDataMap(),但還是有區別的,看下面的 @PersistJobDataAfterExecution



※@DisallowConcurrentExecution

JobDetail job = newJob(MyJob.class).withIdentity("myJob", "group1").build();
Trigger trigger = newTrigger()
    .withIdentity("myTrigger1", "group2")
    .startNow()
    .withSchedule(simpleSchedule()
        .withIntervalInSeconds(3)
        .repeatForever())
    .build();
    
try {
    SchedulerFactory schedFact = new StdSchedulerFactory();
    Scheduler sched = schedFact.getScheduler();
    sched.scheduleJob(job, trigger);
    sched.start();
} catch (SchedulerException e) {
    e.printStackTrace();
}




@DisallowConcurrentExecution
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            System.out.println("前=" + new Date());
            Thread.sleep(5000);
            System.out.println("後=" + new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

※我設定每3秒執行一次,但 Job 裡會睡 5 秒,所以我還沒加這個註解前會是如下的結果:
前=Tue Aug 21 14:17:44 SGT 2018
前=Tue Aug 21 14:17:47 SGT 2018
後=Tue Aug 21 14:17:49 SGT 2018
前=Tue Aug 21 14:17:50 SGT 2018
後=Tue Aug 21 14:17:52 SGT 2018
前=Tue Aug 21 14:17:53 SGT 2018
後=Tue Aug 21 14:17:55 SGT 2018
前=Tue Aug 21 14:17:56 SGT 2018
後=Tue Aug 21 14:17:58 SGT 2018
前=Tue Aug 21 14:17:59 SGT 2018
後=Tue Aug 21 14:18:01 SGT 2018
前=Tue Aug 21 14:18:02 SGT 2018
後=Tue Aug 21 14:18:04 SGT 2018

※44~49 才是一組,中間會有下一次的執行,但如果加上這個註解,結果如下:

前=Tue Aug 21 14:19:12 SGT 2018
後=Tue Aug 21 14:19:17 SGT 2018
前=Tue Aug 21 14:19:17 SGT 2018
後=Tue Aug 21 14:19:22 SGT 2018
前=Tue Aug 21 14:19:22 SGT 2018
後=Tue Aug 21 14:19:27 SGT 2018
前=Tue Aug 21 14:19:27 SGT 2018
後=Tue Aug 21 14:19:32 SGT 2018
前=Tue Aug 21 14:19:32 SGT 2018
後=Tue Aug 21 14:19:37 SGT 2018
前=Tue Aug 21 14:19:37 SGT 2018

※此時中間沒有插入下一次的執行,是以執行完為主的



※@PersistJobDataAfterExecution

JobDetail job = newJob(MyJob.class)
    .withIdentity("myJob", "group1")
    .usingJobData("aaa", "111")
    .usingJobData("bbb", 222)
    .build();
    
Trigger trigger = newTrigger()
    .withIdentity("myTrigger1", "group2")
    .startNow()
    .withSchedule(simpleSchedule()
        .withIntervalInSeconds(3)
        .repeatForever())
    .build();
    
try {
    SchedulerFactory schedFact = new StdSchedulerFactory();
    Scheduler sched = schedFact.getScheduler();
    sched.scheduleJob(job, trigger);
    sched.start();
} catch (SchedulerException e) {
    e.printStackTrace();
}




@PersistJobDataAfterExecution
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // JobDataMap dataMap = context.getMergedJobDataMap(); // 不會更新的
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        System.out.println("a=" + dataMap.getString("aaa")); // 111|333
        System.out.println("b=" + dataMap.getInt("bbb"));// 222|444
    
        dataMap.put("aaa", "333");
        dataMap.put("bbb", 444);
    }
}

※註解的意思是執行後可保存資料,如範例的 333 和 444,有加上這個註解以及使用的是 getJobDetail().getJobDataMap() 才可以,使用 getMergedJobDataMap() 不會

※第一次執行後是 111 和 222,之後就都會是 333 和 444 了

※官網有說明加上這個註解後,要考慮是不是也要加上 @DisallowConcurrentExecution

2018年8月14日 星期二

JUnit 4 整合 SpringBoot 2.x

※build.gradle

compile 'org.springframework.boot:spring-boot-starter:2.0.3.RELEASE'
testCompile 'org.springframework.boot:spring-boot-starter-test:2.0.3.RELEASE'




※啟動類

@SpringBootApplication
@ComponentScan("ooo.xxx")
public class Xxx {
    public static void main(String[] args) {
        SpringApplication.run(Xxx.class, args);
    }
}




※測試類

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Xxx.class })
public class LibraryTest {
    @Autowired
    Animal animal;
    
    @Autowired
    Bird bird;
    
    @Test
    public void testXxx() {
        System.out.println(animal);
        System.out.println(bird);
    }
}

※Animal、Bird 類別在 ooo.xxx 的 package,如果和 LibraryTest 不同 package,那就要在啟動類加 @ComponentScan

2018年8月12日 星期日

Task (Gradle 4.x 六)

※三種宣告

task 'xxx' { // 「'」可省略
    println 'xxxxx'
}
    
tasks.create('zzz') {
    println 'zzzzz'
}
    
tasks.create(name: 'ooo') {
    println 'ooooo'
}
    
task findTask {
    // println tasks.findByPath('xxx')?.name
    try {
        println tasks.getByPath('xxx').name // 有Exception時,? 無效
    } catch (UnknownTaskException e) {
        println '我錯了'
    }
}

※findByPath 找不到回傳 null; getByPath 找不到拋例外


※task 群組

task setInfo1(group: 'aaa', description: 'I am 1') {
    println tasks.findByPath('setInfo1')?.group // group 即可抓到,但 this.group 是抓這個專案的
    println tasks.findByPath('setInfo1')?.description
}
    
task setInfo2 {
    setGroup('aaa')
    setDescription('I am 2')
    println tasks.findByPath('setInfo2')?.group
    println tasks.findByPath('setInfo2')?.description
}

※gradle tasks 會看到,參數可看 Task


※task 的執行順序

task taskOrder1() {
    println 'taskOrder1'
    
    doLast {
        println 'a'
    }
    
    doFirst {
        println '1'
    }
    
    doLast {
        println 'b'
    }
    
    doFirst {
        println '2'
    }
}
    
    
task taskOrder2 {
    println 'taskOrder2'
    
    doLast {
        println 'c'
    }
    
    doFirst {
        println '3'
    }
    
    doLast {
        println 'd'
    }
    
    doFirst {
        println '4'
    }
}

※doFirst、doLast 都是執行階段才會執行,所以下 gradle clean 或 gradle task 名稱只會執行設定階段,並不會只有執行指定的 task,所有 task 都會執行
要 run 執行階段,要下 gradle task 名稱,但要有 doFirst 或 doLast,且只會執行指定的 task

※task taskOrder1(dependsOn: 'taskOrder2') // 只針對執行階段

※taskOrder1.dependsOn('taskOrder2'),也可用這種方式,寫在括號裡是被依賴的,被依賴的會先執行

※也可以 mustRunAfter 方法


※動態依賴

task taskOrder1 {
    dependsOn tasks.findAll {
        it.name.startsWith('xxx')
    }
}

※xxx 開頭的 task 才會被依賴


※task 的其他寫法

task taskOrder1 {
    doFirst {
        println '1'
    }
    
    doFirst {
        println '2'
    }
}
    
taskOrder1.doFirst {
    println '3'
}




※doLast 等同 <<

task taskOrder2 << {
    println 'taskOrder2'
}
    
task taskOrder2 {
    println 'taskOrder2'
    
    doLast {
        println '3'
    }
    
    doFirst {
        println '2'
    }
} << {
    println '4'
}

※最後的 << 只能一次

2018年8月11日 星期六

Executors

※基本的執行緒池

// ExecutorService es = Executors.newFixedThreadPool(3);
// ExecutorService es = Executors.newCachedThreadPool();
ExecutorService es = Executors.newSingleThreadExecutor();
    
for (int i = 1; i <= 7; i++) {
    es.execute(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int j = 1; j <= 5; j++) {
            System.out.println(Thread.currentThread().getName() + "->" + j);
        }
    });
}
es.shutdown();

※newFixedThreadPool:固定大小的執行緒
newCachedThreadPool:無限大小的執行緒
newSingleThreadExecutor:單一的執行緒

※shutdown():不增加新的執行緒,當前執行緒不會關閉
shutdownNow():不增加新的執行緒,當前執行緒也會關閉


※排程

ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
ses.schedule(() -> {
    System.out.println("yeah");
}, 2L, TimeUnit.SECONDS);
ses.shutdown();
    
ScheduledExecutorService sess = Executors.newScheduledThreadPool(3);
sess.scheduleAtFixedRate(() -> {
    System.out.println("xxx");
}, 2L, 1L, TimeUnit.SECONDS);
try {
    Thread.sleep(5000L);
    sess.shutdown();
} catch (InterruptedException e) {
    e.printStackTrace();
}
    
ScheduledExecutorService seas = Executors.newSingleThreadScheduledExecutor();
seas.scheduleAtFixedRate(() -> {
    System.out.println("xxx");
}, 2L, 1L, TimeUnit.SECONDS);
try {
    Thread.sleep(5000L);
    seas.shutdown();
} catch (InterruptedException e) {
    e.printStackTrace();
}

※newScheduledThreadPool、newSingleThreadScheduledExecutor 回傳 ScheduledExecutorService,裡面有執行一次的 schedule() 和重覆執行的 scheduleAtFixedRate()

※重覆執行要注意太早 shutdown() 就不執行了



※1.8 的 newWorkStealingPool

Executors.newWorkStealingPool();

※底層第一行就是 new ForkJoinPool,ForkJoinPool 是 1.7 增加的類別,但這個執行緒池是 1.8 才新增的,可參考這裡


※ThreadPoolExecutor

Executors 底層使用的就是這個,共有7個參數
new ThreadPoolExecutor(5,
20,
10,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());

表示一開始就有 5 個執行緒可以用,最多20個
假設有30個 request 進來,5個先執行,然後25個會放在第5個參數的 queue 裡
再來15個執行緒去 queue 抓來執行,最後10個看第7個參數的放棄策略
執行完的15個執行緒,過10毫秒 (第3和4的參數) 就關閉
第六個參數是將 Runnable 包裝成 Thread

以下是第 5~7 可用的參數
BlockingQueue
ArrayBlockingQueue
DelayedWorkQueue 基於 heap 的資料結構,按照時間順序將每個任務進行排序
BlockingDeque
LinkedBlockingDeque 預設是 Integer.MAX_VALUE,所以有 OOM 的風險
SynchronousQueue 一生產就消費,如沒有消費者就阻塞,所以沒有容量大小
DelayQueue 只能消費已過期的
TransferQueue
LinkedTransferQueue 很像 SynchronousQueue,但有容量,消費者沒取到就會阻塞
LinkedBlockingQueue 預設是 Integer.MAX_VALUE,所以有 OOM 的風險
PriorityBlockingQueue 按照自定義的優先級消費
ThreadFactory
DaemonThreadFactory 創建背景執行的執行緒,之後出來的執行緒都是背景執行緒
DefaultThreadFactory 預設的執行緒工廠
PrivilegedThreadFactory 創建與當前執行緒具有相同權限的新執行緒
RejectedExecutionHandler
DiscardOldestPolicy 丟棄最老的任務,就是在 queue 裡面的
AbortPolicy 新來的任務直接丟棄並報異常
CallerRunsPolicy 同步調用,start() 調用後執行 run(),但這個策略是直接調用 run()
DiscardPolicy 新來的任務直接丟棄但不報異常