Spring高级话题

  1. 三.Spring高级话题
    1. 1.spring aware
    2. 2.多线程
    3. 3.计划任务
    4. 4.条件注解@Conditional
    5. 5.组合注解与元注解
    6. 6.@Enable* 注解的工作原理
    7. 7.测试

三.Spring高级话题

1.spring aware

  Spring的依赖注入的最大亮点就是bean对spring容器的存在是没有意识的。

若你想用spring容器本身的资源,bean就要意识到spring容器的存在。

Spring提供的aware接口:

(1) beanNameAware : 获得到容器中bean名称。

(2) beanFactoryAware :获得当前bean factory,这样可以调用容器的服务。

(3) applicationContextAware:当前的application context.这样可以调用容器的服务。

(4) messageSourceAware :获得message source,这样可以获得文本信息。

(5) ApplicationEventPublisherAware:应用事件发布器,可以发布事件。

(6) ResourceLoaderAware:获得资源加载器,可以获得外部资源文件。

Spring aware 的目的是为了让bean获得spring容器的服务,因为applicationContext接口集成了messageSource接口、applicationEventPublisher接口和ResourceLoader接口。
所以bean继承applicationContextAware可以获取到spring容器的所有服务。

代码示例:

@Service
public class AwareService implements BeanNameAware,ResourceLoaderAware{
    private String beanName;
    private ResourceLoader loader;

    @Override
    public void setResourceLoader(ResourceLoader paramResourceLoader) {
        // TODO Auto-generated method stub
        this.loader = paramResourceLoader;
    }

    @Override
    public void setBeanName(String msg) {
        // TODO Auto-generated method stub
        this.beanName = msg;
    }

    public void outputResult(){
        System.out.println("bean的名称为:"+beanName);
        Resource resource = loader.getResource("classpath:cn/cw/study/springaware/test.txt");

        try {
            String string = IOUtils.toString(resource.getInputStream());
            System.out.println("接收的内容是:"+string);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }

}
---------------------------------------------------------------------
@Configuration
@ComponentScan("cn.cw.study.springaware")
public class AwareConfig {

}
---------------------------------------------------------------------
public class AppMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class);
        AwareService bean = context.getBean(AwareService.class);
        bean.outputResult();
        context.close();
    }
}

2.多线程

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

@Async注解:

①如果注解在方法级别,则表明该方法是异步方法,

②如果注解在类级别,则表明类所有的方法都是异步方法,

@EnableAsync注解:

开启对异步任务的支持。

代码示例:

@Configuration
@ComponentScan("cn.cw.study.ThreadPool")
@EnableAsync //开启异步任务支持
public class TaskExecutorConfig implements AsyncConfigurer{

    @Override
    public Executor getAsyncExecutor() {
        // TODO Auto-generated method stub
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);//核心线程数
        executor.setMaxPoolSize(10);//最大线程数
        executor.setQueueCapacity(25);//队列最大长度
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        // TODO Auto-generated method stub
        return null;
    }
}

@Service
public class AsyncTaskService {
    //无注解表明是同步方法,顺序执行
    public void executeTask(int i){
        System.out.println("执行同步任务:"+i);
    }
    //注解表明该方法是异步方法,如果注解在类级别,则表明该类所有的方法都是异步方法,而这里的方法自动被注解使用ThreadPoolTaskExecutor作为TaskExecutor
    @Async
    public void executeTaskTest(int i){
        System.out.println("执行异步任务+1:"+(i+1));
    }
}

public class AppMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig.class);
        AsyncTaskService bean = context.getBean(AsyncTaskService.class);
        for(int i=0;i<10;i++){
            bean.executeTask(i);
            bean.executeTaskTest(i);
        }
        context.close();
    }
}

3.计划任务

  从spring 3.1开始,计划任务在spring中实现变得异常简单,首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后再要执行计划任务的方法上注解@Sheduled,声明这是一个计划任务。

  Spring 通过@Scheduled支持多种类型的计划任务,包含cron、fixDelay、fixRate等。

代码示例:

@Service
public class ScheduleTaskService {

    private SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HH:mm:ss");


    @Scheduled(fixedRate=1000)
    public void reportFixedRate(){
        System.out.println("rate:"+format.format(new Date()));
    }
    @Scheduled(fixedDelay=4000)
    public void reportDelay(){
        System.out.println("delay:"+format.format(new Date()));
    }
    @Scheduled(cron="0 29 11 * * *")
    public void reportCron(){
        System.out.println("rate:"+format.format(new Date()));
    }
}

@Configuration
@ComponentScan("cn.cw.study.schedule")
@EnableScheduling  //开启对计划任务的支持
public class TaskConfig {

}

public class AppMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskConfig.class);
//        context.close();不能关闭context,关闭后 将不会执行定时任务
    }
}

4.条件注解@Conditional

Spring 通过活动创建一个特定bean。

spring4提供了一个更通用的基于条件的bean创建。使用注解@Conditional。
@Conditional根据满足某一个特定条件创建一个特定bean。

比如,当某一个jar包在一个类路径下的时候,会自动配置一个或者多个bean;或者只有某个bean被创建才会创建另一个bean;总的来说,就是根据特定条件来控制bean的创建行为,这样我们可以利用这个特性进行一些自动配置。

下面例子代码思想:
通过实现condition接口,并重写matches方法来构造条件。
若在windows系统下运行程序,则输出列表命令为dir;若在linux系统下运行程序,则输出列表命令ls

代码示例:

public class WindowsCondition implements Condition{

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata data) {
        System.out.println(context.getEnvironment().getProperty("os.name"));
        // TODO Auto-generated method stub
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }
}


public class LinuxCondition implements Condition{

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata data) {
        // TODO Auto-generated method stub
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }

}

public interface ListServer {
    public String showListCmd();
}

public class WindowsListService implements ListServer{

    @Override
    public String showListCmd() {
        // TODO Auto-generated method stub
        return "dir";
    }

}

public class LinuxListService implements ListServer{

    @Override
    public String showListCmd() {
        // TODO Auto-generated method stub
        return "ls";
    }

}

@Configuration
public class ConditionConfig {

    @Bean
    @Conditional(WindowsCondition.class)
    public ListServer windowsService(){
        return new WindowsListService();
    }

    @Bean
    @Conditional(LinuxCondition.class)
    public ListServer linuxService(){
        return new LinuxListService();
    }

}

public class AppMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
        ListServer listServer = context.getBean(ListServer.class);
        System.out.println(context.getEnvironment().getProperty("os.name")+"系统下的列表命名为:"+listServer.showListCmd());
    }
}

5.组合注解与元注解

  从spring2开始,为了响应jdk1.5推出的注解功能,spring开始大量加入注解来替代xml配置,spring的注解主要用来配置和注入bean,以及aop相关配置(@Transactional).

  随着注解的大量使用,尤其相同的多个注解用到各个类或方法中,会相当繁琐。

  所谓元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解,组合注解具备注解其上的元注解的功能。

  Spring的很多注解都可以作为元注解,而且spring 本身已经有很多组合注解,如@Configuration就是一个组合@Component注解,表明这个类其实也是一个bean.

代码示例:

将@Configuration和@ComponentScan注解组成一个组合注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@ComponentScan
public @interface WiselyConfiguration {
    String[] value() default{};//覆盖value参数
}

@Service
public class DemoService {
    public void outputResult(){
        System.out.println("组合注解可以获得bean");
    }

}

@WiselyConfiguration("cn.cw.study.compositeAnnotation")
public class DemoConfig {

}

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

6.@Enable* 注解的工作原理

(1).@EnableAspectJAutoProxy 开启对AspectJ 自动代理的支持。

(2).@EnableAsync 开启异步方法的支持。

(3).@EnableScheduling 开启计划任务的支持。

(4).@EnableWebMvc 开启web mvc的配置支持。

(5).@EnableConfigurationProperties 开启对@ConfigurationProperties注解配置的bean的支持

(6).@EnableJpaRepositories 开启对spring data JPA Repository的支持

(7).@EnableTransactionManagement 开启注解式事务的支持

(8).@EnableCaching 开启注解式缓存的支持

通过简单的@Enable*来开启一项功能的支持,从而避免配置大量代码,大大降低使用难度。

原理:

所有的@Enable*注解都有一个@import注解,@import是用来导入 配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的bean。

这些导入配置的方式主要分为以下三种类型:

第一类:直接导入配置类

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

}

编写上述代码类,可查看SchedulingConfiguration源码。

第二类:依据条件选择配置类(可在网上查看资料)

第三类:动态注册bean(可在网上查看资料)

7.测试

Maven依赖:

<dependency>
      <artifactId>spring-test</artifactId>
      <groupId>org.springframework</groupId>
      <version>5.1.5.RELEASE</version>
  </dependency>
  <dependency>
      <artifactId>junit</artifactId>
      <groupId>junit</groupId>
      <version>4.11</version>
  </dependency>

文章标题:Spring高级话题

发布时间:2019-11-14, 17:04:21

最后更新:2019-11-14, 17:04:22