zoukankan      html  css  js  c++  java
  • 三、spring进阶

    3.1 Spring Aware

    Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的。
    但是在实际项目中,你不可避免的要用到Spring容器本身的功能资源,这时你的Bean必须要意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring Aware
    其实Spring Aware本来就是Spring设计用来框架内部使用的,若使用了Spring Aware,你的Bean将会和Spring框架耦合

    spring提供的Aware接口如下:

    接口 说明
    BeanNameAware 获得容器中Bean的名称
    BeanFactoryAware 获得当前Bean factory,这样可以调用容器的服务
    ApplicationContextAware 当前的application context,这样可以调用容器的服务
    MessageSourceAware 获得message source,这样可以获得文本信息
    ApplicationEventPublisherAware 应用时间发布器,可以发布事件
    ResourceLoaderAware 获得资源加载器,可以获得外部资源文件

    Spring Aware的目的是为了让Bean获得Spring容器的服务。因为ApplicationContext接口集成了MessageSource接口、ApplicationEventPublisher接口和ResourceLoader接口,所以Bean继承ApplicationContextAware可以获得Spring容器的所有服务,但原则上我们还是用到什么接口,就实现什么接口。


    示例

    (1)准备。在com.wisely.highlight_spring4.ch3.aware包下新建一个test.txt,内容随意,给下面的外部资源加载使用。
    (2)Spring Aware演示Bean

    package com.wisely.highlight_spring4.ch3.aware; 
    
    @Service 
    public class AwareService implements BeanNameAware,ResourceLoaderAware{//1 
        
        private String beanName; 
        private ResourceLoader loader; 
        
        @Override 
        public void setResourceLoader(ResourceLoader resourceLoader) {//2 
            this.loader = resourceLoader; 
        } 
      
        @Override 
        public void setBeanName(String name) {//3 
            this.beanName = name; 
        } 
        
        public void outputResult(){ 
            System.out.println("Bean的名称为:" + beanName); 
            
            Resource resource = loader.getResource("classpath:com/wisely/highlight_spring4/ch2/aware/test.txt"); 
            try{ 
                System.out.println("ResourceLoader加载的文件内容为: " + IOUtils.toString(resource.getInputStream())); 
            }catch(IOException e){ 
                e.printStackTrace(); 
               } 
            } 
    } 
    

    ①实现BeanNameAware、ResourceLoaderAware接口,获得Bean名称和资源加载的服务。
    ②实现ResourceLoaderAware需重写setResourceLoader。
    ③实现BeanNameAware需重写setBeanName方法。

    (3)配置类

    package com.wisely.highlight_spring4.ch3.aware; 
    
    @Configuration 
    @ComponentScan("com.wisely.highlight_spring4.ch3.aware") 
    public class AwareConfig { 
    } 
    

    (4)运行

    package com.wisely.highlight_spring4.ch3.aware; 
    
    public class Main { 
        public static void main(String[] args) { 
            
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class); 
            
            AwareService awareService = context.getBean(AwareService.class); 
            awareService.outputResult(); 
            context.close(); 
        } 
    } 
    

    (5)结果

    Bean的名称为:awareService
    ResourceLoader加载的文件内容为: 11111....//文件内容
    

    3.2 多线程

    Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。
    使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明其是一个异步任务。


    3.2.2 示例

    (1)配置类

    package com.wisely.highlight_spring4.ch3.taskexecutor; 
    
    @Configuration 
    @ComponentScan("com.wisely.highlight_spring4.ch3.taskexecutor") 
    @EnableAsync //1 
    public class TaskExecutorConfig implements AsyncConfigurer{//2 
      
        @Override 
        public Executor getAsyncExecutor() {//2 
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); 
            taskExecutor.setCorePoolSize(5); 
            taskExecutor.setMaxPoolSize(10); 
            taskExecutor.setQueueCapacity(25); 
            taskExecutor.initialize(); 
            return taskExecutor; 
        } 
      
        @Override 
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 
            return null; 
        } 
      
    } 
    

    ①利用@EnableAsync注解开启异步任务支持。
    ②配置类实现AsyncConfigurer接口并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,这样我们就获得了一个基于线程池TaskExecutor。

    (2)任务执行类

    @Service 
    public class AsyncTaskService { 
        @Async //1 
        public void executeAsyncTask(Integer i){ 
            System.out.println("执行异步任务: "+i); 
        } 
        @Async 
        public void executeAsyncTaskPlus(Integer i){ 
            System.out.println("执行异步任务+1: "+(i+1)); 
        } 
    } 
    

    ①通过@Async注解表明该方法是个异步方法,如果注解在类级别,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor。

    (3)运行

    package com.wisely.highlight_spring4.ch3.taskexecutor; 
    
    public class Main { 
        
        public static void main(String[] args) { 
             AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig. class); 
              
             AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class); 
              
             for(int i =0 ;i<10;i++){ 
                 asyncTaskService.executeAsyncTask(i); 
                 asyncTaskService.executeAsyncTaskPlus(i); 
                } 
                context.close(); 
        } 
    } 
    

    3.3 计划任务

    首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。
    Spring通过@Scheduled支持多种类型的计划任务,包含cron、fixDelay、fixRate等。


    3.3.1 示例

    (1)计划任务执行类

    package com.wisely.highlight_spring4.ch3.taskscheduler; 
    
    @Service 
    public class ScheduledTaskService { 
        
          private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); 
      
          @Scheduled(fixedRate = 5000) //1 
          public void reportCurrentTime() { 
               System.out.println("每隔五秒执行一次 " + dateFormat.format(new Date())); 
           } 
      
          @Scheduled(cron = "0 28 11 ? * *"  ) //2 
          public void fixTimeExecution(){ 
              System.out.println("在指定时间 " + dateFormat.format(new Date())+"执行"); 
          } 
    } 
    

    ①通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行。
    ②使用cron属性可按照指定时间执行,本例指的是每天11点28分执行;cron是UNIX和类UNIX(Linux)系统下的定时任务。

    (2)配置类

    package com.wisely.highlight_spring4.ch3.taskscheduler; 
    
    @Configuration 
    @ComponentScan("com.wisely.highlight_spring4.ch3.taskscheduler") 
    @EnableScheduling //1 
    public class TaskSchedulerConfig { 
    } 
    

    ①通过@EnableScheduling注解开启对计划任务的支持。

    (3)运行

    package com.wisely.highlight_spring4.ch3.taskscheduler; 
    
    public class Main { 
        public static void main(String[] args) { 
             AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerConfig.class); 
        } 
    } 
    

    3.4 条件注解

    Spring 4提供了一个更通用的基于条件的Bean的创建,即使用@Conditional注解。
    @Conditional根据满足某一个特定条件创建一个特定的Bean。
    在Spring Boot中将会大量应用到条件注解。

    3.4.1 示例

    下面的示例将以不同的操作系统作为条件,我们将通过实现Condition接口,并重写其matches方法来构造判断条件。若在Windows系统下运行程序,则输出列表命令为dir;若在Linux操作系统下运行程序,则输出列表命令为ls。


    1.判断条件定义
    (1)判断windows的条件

    package com.wisely.highlight_spring4.ch3.conditional; 
    
    public class WindowsCondition implements Condition { 
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
            return context.getEnvironment().getProperty("os.name").contains("Windows"); 
        } 
    } 
    

    (2)判定Linux的条件

    package com.wisely.highlight_spring4.ch3.conditional; 
    
    public class LinuxCondition implements Condition { 
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
            return context.getEnvironment().getProperty("os.name").contains("Linux");
        }
    }
    

    2.不同系统下Bean的类
    (1)接口

    package com.wisely.highlight_spring4.ch3.conditional; 
      
    public interface ListService { 
        public String showListCmd(); 
    } 
    

    (2)Windows下所要创建的Bean的类

    package com.wisely.highlight_spring4.ch3.conditional; 
      
    public class WindowsListService implements ListService { 
        @Override 
        public String showListCmd() { 
            return "dir"; 
        } 
    } 
    

    (3)Linux下所要创建的Bean的类

    package com.wisely.highlight_spring4.ch3.conditional; 
      
    public class LinuxListService implements ListService{ 
        @Override 
        public String showListCmd() { 
            return "ls"; 
        } 
    } 
    

    3.配置类

    package com.wisely.highlight_spring4.ch3.conditional;       
    
    @Configuration 
    public class ConditionConifg { 
        @Bean 
        @Conditional(WindowsCondition.class) //1 
        public ListService windowsListService() { 
            return new WindowsListService(); 
        } 
      
        @Bean 
        @Conditional(LinuxCondition.class) //2 
        public ListService linuxListService() { 
            return new LinuxListService(); 
        } 
    } 
    

    ①通过@Conditional注解,符合Windows条件则实例化windowsListService。
    ②通过@Conditional注解,符合Linux条件则实例化linuxListService。

    4.运行

    package com.wisely.highlight_spring4.ch3.conditional; 
    
    public class Main { 
      
        public static void main(String[] args) { 
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConifg.class); 
            
            ListService listService = context.getBean(ListService.class); 
            System.out.println(context.getEnvironment().getProperty("os.name")  + "系统下的列表命令为: "  + listService.showListCmd()); 
        } 
    } 
    

    3.5 组合注解与元注解

    Spring的注解主要用来配置注入Bean,切面相关配置(@Transactional)。随着注解的大量使用,尤其相同的多个注解用到各个类中,会相当啰嗦。这就是所谓的模板代码,是Spring设计原则中要消除的代码。
    元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解、是可能有点拗口,体会含义最重要),组合注解具备元注解的功能。Spring的很多注解都可以作为元注解,而且Spring本身已经有很多组合注解,如@Configuration就是一个组合@Component注解,表明这个类其实也是一个Bean。

    3.5.1 示例


    (1)示例组合注解

    package com.wisely.highlight_spring4.ch3.annotation; 
    
    @Target(ElementType.TYPE) 
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    @Configuration //1 
    @ComponentScan //2 
    public @interface WiselyConfiguration { 
        String[] value() default {}; //3 
    } 
    

    ①组合@Configuration元注解。
    ②组合@ComponentScan元注解。
    ③覆盖value参数。

    (2)演示服务Bean

    package com.wisely.highlight_spring4.ch3.annotation; 
      
    @Service 
    public class DemoService { 
        public void outputResult(){ 
            System.out.println("从组合注解配置照样获得的bean"); 
        } 
    } 
    

    (3)新的配置类

    package com.wisely.highlight_spring4.ch3.annotation; 
      
    @WiselyConfiguration("com.wisely.highlight_spring4.ch3.annotation")//1 
    public class DemoConfig { 
    } 
    

    ①使用@WiselyConfiguration组合注解替代@Configuration和@ComponentScan。

    (4)运行

    public class Main { 
        
        public static void main(String[] args) { 
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class); 
            
            DemoService demoService =  context.getBean(DemoService.class); 
            demoService.outputResult(); 
            context.close(); 
        } 
    } 
    

    (5)结果

    从组合注解配置赵阳获得的bean
    

    3.6 @Enable*注解的工作原理

    通过简单的@Enable* 来开启一项功能的支持,从而避免自己配置大量的代码,大大降低使用难度。
    通过观察这些@Enable*注解的源码,我们发现所有的注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入的配置方式主要分为以下三种类型。

    3.6.1 第一类:直接导入配置类

    @Target(ElementType.TYPE) 
    @Retention(RetentionPolicy.RUNTIME) 
    @Import(SchedulingConfiguration.class) 
    @Documented 
    public @interface EnableScheduling { 
    } 
    

    直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的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(); 
        } 
    } 
    

    3.6.2 第二类:依据条件选择配置类

    @Target(ElementType.TYPE) 
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    @Import(AsyncConfigurationSelector.class) 
    public @interface EnableAsync { 
    Class<? extends Annotation> annotation() default Annotation.class; 
        boolean proxyTargetClass() default false; 
        AdviceMode mode() default AdviceMode.PROXY; 
        int order() default Ordered.LOWEST_PRECEDENCE; 
    } 
    

    AsyncConfigurationSelector通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,这个接口需重写selectImports方法,在此方法内进行事先条件判断。此例中,若adviceMode为PORXY,则返回ProxyAsyncConfiguration这个配置类;若activeMode为ASPECTJ,则返回AspectJAsyncConfiguration配置类,源码如下:

    public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { 
        private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; 
      
        @Override 
        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; 
            } 
        } 
    } 
    

    3.6.3 第三类:动态注册Bean

    @Target(ElementType.TYPE) 
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    @Import(AspectJAutoProxyRegistrar.class) 
    public @interface EnableAspectJAutoProxy { 
        boolean proxyTargetClass() default false; 
    }
    

    AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBean DefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:

    registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
    
  • 相关阅读:
    cf 785#
    hdu 4920 Matrix multiplication
    poj 2443 Set Operation
    bzoj 3687: 简单题
    洛谷 三月月赛 C
    洛谷 三月月赛 B
    洛谷 三月月赛 A
    bzoj 3156: 防御准备
    bzoj 3437: 小P的牧场
    bzoj 3675: [Apio2014]序列分割
  • 原文地址:https://www.cnblogs.com/phtjzzj/p/7587354.html
Copyright © 2011-2022 走看看