SpringBoot學習-第三章 Spring高級應用-

Spring Aware

通常@Component標識的Bean由容器所管理,但它自身是不能和容器交互的(解耦),你只能通過@Autowired引入並使用。而Aware接口爲Bean提供了與容器交互的能力。

  • BeanNameAware:獲取容器中Bean的名稱
  • BeanFactoryAware:獲取當前Bean的Factory,從而調用容器的服務
  • MessagerSourceAware:獲取文本信息
  • ApplicationEventPublisherAware:應用事件發佈器,也可以通過AbstractApplicationContext的publish方法發佈
  • ResourceLoaderAware:獲取資源加載器,可以獲取外部資源
  • ApplicationContextAware:獲取上下文,包含了MessagerSource、EventPublisher、ResourceLoader
@Service
public class AwareService implements BeanNameAware, ResourceLoaderAware {
    String beanName;
    ResourceLoader resourceLoader;

    @Override
    public void setBeanName(String s) {
        this.beanName = s;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public void outputInfo() {
        System.out.println("bean在容器中的名稱是:"+beanName);
        Resource resource = resourceLoader.getResource("classpath:aware/test.txt");
        try {
            System.out.println("資源文件的內容是:"+IOUtils.toString(resource.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

多線程

  • 註解需要異步執行(多線程)的方法
@Service
@Async //註解表明該方法是一個異步方法,註解在類上則表示類中所有方法都是異步的.
public class AsyncTaskService {
    public void executeAsyncTask(Integer i) {
        System.out.println("執行異步任務:" + i);
    }

    public void executeAsyncTaskPlus(Integer i) {
        System.out.println("執行異步任務+1:" + (i + 1));
    }
}
  • 開啓異步執行的方法,並定義異步執行器
@Configuration
@ComponentScan("demo.springboot.taskexecutor")
@EnableAsync //使用@EnableAsync開啓異步任務,實現異步配置類返回一個基於線程池的執行器
public class ExecutorConfig 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;
    }

    public static void main(String[] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfig.class);
        AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class);
        /**
         * 調用異步方法時,Spring會自動獲取內部的Async執行器來執行
         * 執行的結果是異步的(通過多線程執行),而不是0-9順序的執行
         */
        for (int i=0;i<10;i++){
            asyncTaskService.executeAsyncTask(i);
            asyncTaskService.executeAsyncTaskPlus(i);
        }
        context.close();
    }
}

結果展示

執行異步任務+1:1
執行異步任務:1
執行異步任務+1:2
執行異步任務:3
執行異步任務+1:3
執行異步任務:0
執行異步任務:2
執行異步任務:5
執行異步任務+1:5
執行異步任務:4
執行異步任務+1:4
執行異步任務:7
執行異步任務+1:7
執行異步任務:6
執行異步任務+1:6
執行異步任務:9
執行異步任務+1:9
執行異步任務:8
執行異步任務+1:8
執行異步任務+1:10

計劃任務

現在使用註解就可以使用Spring的計劃任務模塊, 代替之前複雜繁瑣的quartz整合

@Service
public class SchedulerService {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

    @Scheduled(fixedRate = 5000)
    public void reportCurrentTime(){System.out.println("每5秒執行,當前時間:"+sdf.format(new Date()));}

    @Scheduled(cron = "0 08 17 ? * *")
    public void fixedTimeExecution(){System.out.println("定時執行:"+sdf.format(new Date()));}
}
@Configuration
@ComponentScan("demo.springboot.taskscheduler")
@EnableScheduling //註解開啓計劃任務
public class SchedulerConfig {
    public static void main(String[] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SchedulerConfig.class);
    }
}

條件註解

根據條件操控Bean, 比profile更通用, 可以定義在程序內方便理解
* 設置條件, 實現條件實現的函數

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }
}

public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }
}
  • 設置Bean的不同實現
public class WindowsServiceImpl implements ListService {
    @Override
    public String showListCmd() { return "dir"; }
}
public class LinuxServiceImpl implements ListService {
    @Override
    public String showListCmd() { return "ls"; }
}
  • 設置Bean的加載條件
@Configuration
public class ConditionalConfig {

    @Bean
    @Conditional(WindowsCondition.class)
    public ListService windowsList() { return new WindowsServiceImpl(); }

    @Bean
    @Conditional(LinuxCondition.class)
    public ListService linuxList() { return new LinuxServiceImpl(); }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionalConfig.class);
        ListService listService = context.getBean(ListService.class);
        System.out.println("查看系統列表的命令是:" + listService.showListCmd());
    }
}

組合註解和元註解

自定義註解組合常用的註解(類似 繼承/組合)

@Target(ElementType.TYPE)/* Class, interface (including annotation type), or enum declaration */
@Retention(RetentionPolicy.RUNTIME)/* 保留至運行時 ,運行時可以檢測到這個註解 */
@Documented/* 被javadoc記錄 */
 /* 組合註解類似繼承 ,可以使用同名函數(屬性) 覆蓋 被組合的註解中的函數(屬性) */
@Configuration
@ComponentScan
public @interface CommonConfiguration {
    String[] value() default {};
}

使用組合註解

@CommonConfiguration("demo.springboot.annotation")
public class DemoConfig {
    //...
}

@Enable*註解的原理

常用的註解

@org.springframework.context.annotation.EnableAspectJAutoProxy //aspectj支持
@org.springframework.scheduling.annotation.EnableAsync //開啓異步方法
@org.springframework.web.servlet.config.annotation.EnableWebMvc //開啓Mvc配置
@org.springframework.boot.context.properties.EnableConfigurationProperties //開啓註解配置bean
@org.springframework.cache.annotation.EnableCaching //開啓註解式緩存
@org.springframework.scheduling.annotation.EnableScheduling //開啓計劃任務

@EnableJpaRepositories //開啓JPA支持
@org.springframework.transaction.annotation.EnableTransactionManagement //開啓註解事務

原理解析
* 引入配置類@EnableScheduling

//註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)/* 引入配置類 */
@Documented
public @interface EnableScheduling {

}

// 配置類 ,定義了一個bean(等效於 xml中的<bean .../>)
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}
  • 依據條件選擇配置類@EnableAsync
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
    //...
}

//配置類
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    //...
    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] { ProxyAsyncConfiguration.class.getName() };
            case ASPECTJ:
                return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
            default:
                return null;
        }
    }
     //...
}
  • 動態註冊Bean@EnableAspectJAutoProxy
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    //...
}

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata,  // 獲取配置類的註解
            BeanDefinitionRegistry registry //註冊bean
    ) {
        //...
        }
}

測試

手動導入spring-test或者spring-boot-starter-test

@RunWith(SpringJUnit4ClassRunner.class) //包含需要啓動的spring容器
@ContextConfiguration(classes = {DemoConfig.class}) //需要包含的配置

@ActiveProfiles("dev") //配合profile使用
public class DemoBeansIntegrationTests {
    @Autowired
    private DemoBean demoBean;

    @Test //測試用例
    public void contentTest() {
        String content = demoBean.getContent();
        Assert.assertEquals(content, "register from dev");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章