读 Spring Boot实战笔记--day002

接上一章:day001
Spring Aware

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

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

多线程

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

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

package com.example.springboot.schedule;

import cn.hutool.core.date.DateUtil;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @Author: hyh
 * @Date: 2021/11/2 18:50
 **/
@Component
public class ScheduleTask {

    // 每隔5秒执行一次
    @Scheduled(fixedRate = 5000)
    public void schedule1(){
        System.out.println("时间:" + DateUtil.now() + "执行");
    }

    // 指定时间执行
    @Scheduled(cron = "0 01 19 ? * * ")
    public void schedule2(){
        System.out.println("时间:" + DateUtil.now() + "执行");
    }
}
package com.example.springboot;

import cn.hutool.core.date.DateUtil;
import com.example.springboot.async.AsyncTaskService;
import com.example.springboot.event.MyPublish;
import com.example.springboot.schedule.ScheduleTask;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * @Author: hyh
 * @Date: 2021/10/6 15:12
 **/
@Configuration
@ComponentScan("com.example.springboot.schedule")
@EnableScheduling
public class Test {


    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
        		AnnotationConfigApplicationContext(Test.class);
    }
}
输出:
时间:2021-11-02 19:01:47执行
时间:2021-11-02 19:01:52执行
时间:2021-11-02 19:01:57执行
指定时间:2021-11-02 19:02:00执行
时间:2021-11-02 19:02:02执行
时间:2021-11-02 19:02:07执行
时间:2021-11-02 19:02:12执行
时间:2021-11-02 19:02:17执行

条件注解
@Conditional根据满足某一个特定条件创建一个特定的Bean。比方说,当某一个jar包在一个类路径下的时候,自动配置一个或多个Bean;或者只有某个 Bean被创建才会创建另外一个Bean。总的来说,就是根据特定条件来控制 Bean 的创建行为,这样我们可以利用这个特性进行一些自动的配置。
下面的示例将以不同的操作系统作为条件,我们将通过实现 Condition 接口,并重写其 matches方法来构造判断条件。若在 Windows系统下运行程序,则输出列表命令为dir;若在Linux操作系统下运行程序,则输出列表命令为 ls。

package com.example.springboot.conditional;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @Author: hyh  配置 windows 条件
 * @Date: 2021/11/3 8:56
 **/
public class WindowCon implements Condition {

    @Override
    public boolean matches(ConditionContext context, 
    						AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }
}
package com.example.springboot.conditional;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @Author: hyh  配置 linux 条件
 * @Date: 2021/11/3 8:58
 **/
public class LinuxCon implements Condition {
    @Override
    public boolean matches(ConditionContext context, 
    						AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }
}
package com.example.springboot.conditional;

/**
 * @Author: hyh
 * @Date: 2021/11/3 9:35
 **/
public interface ListService {

    String show();
}
// 编写一个 linux 实例
class LinService implements ListService {

    @Override
    public String show() {
        return "ls";
    }
}
//  编写一个 window 实例
class WinService implements ListService {

    @Override
    public String show() {
        return "dir";
    }
}
package com.example.springboot.conditional;

import org.springframework.context.annotation.*;

/**
 * @Author: hyh
 * @Date: 2021/11/3 9:28
 **/
@Configuration
@ComponentScan("com.example.springboot.conditional")
public class ConditionConfig {
    
    // 根据 windows 条件 来生产bean 如符合则实例化
    @Bean
    @Conditional(WindowCon.class)
    public ListService win(){
        return new WinService();
    }
    // 根据 linux 条件 来生产bean 如符合则实例化
    @Bean
    @Conditional(LinuxCon.class)
    public ListService lin(){
        return new LinService();
    }


    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
        				AnnotationConfigApplicationContext(ConditionConfig.class);

        System.out.println(context.getEnvironment().getProperty("os.name"));
        ListService bean1 = context.getBean(ListService.class);
        System.out.println("系统命令:"+bean1.show());
    }
}
输出:
Windows 10
系统命令:dir

组合注解与原注解
从 Spring 2开始,Spring开始大量加入注解来替代xml配置。Spring 的注解主要用来配置注入 Bean,切面相关配置(@Transactional)。随着注解的大量使用,尤其相同的多个注解用到各个类中,会相当啰嗦。这就是所谓的模板代码,是Spring`设计原则中要消除的代码。
所谓元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解、是可能有点拗口,体会含义最重要),组合注解具备元注解的功能。Spring 的很多注解都可以作为元注解,而且 Spring 本身已经有很多组合注解,如@Configuration 就是一个组合@Component注解,表明这个类其实也是一个 Bean。
我们前面的章节里大量使用@Configuration 和@ComponentScan注解到配置类上,如果你跟着本书一直在敲代码的话是不是觉得已经有点麻烦了呢?下面我将这两个元注解组成一个组合注解,这样我们只需写一个注解就可以表示两个注解。

package com.example.springboot.annotation;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.lang.annotation.*;

/**
 * @Author: hyh  注解上添加注解 ->组合注解
 * @Date: 2021/11/3 11:17
 **/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ComponentScan
@Configuration
public @interface MetaAnnotation {

    String[] value() default {};
}
package com.example.springboot.annotation;

import org.springframework.stereotype.Service;

/**
 * @Author: hyh
 * @Date: 2021/11/3 11:20
 **/
@Service
public class AnnotationService {

    public void print(){
        System.out.println("来自于组合注解");
    }
}
package com.example.springboot.annotation;

import com.example.springboot.conditional.ConditionConfig;
import com.example.springboot.conditional.ListService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author: hyh
 * @Date: 2021/11/3 11:21
 **/
@MetaAnnotation("com.example.springboot.annotation")
public class AnnotationConfig {


    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
        			AnnotationConfigApplicationContext(AnnotationConfig.class);
        // 从组合注解中获取bean
        AnnotationService bean1 = context.getBean(AnnotationService.class);
        bean1.print();
    }
}
输出:
来自于组合注解

Enable** 注解
@EnableAspectJAutoProxy开启对AspectJ自动代理的支持。
@EnableAsync开启异步方法的支持。
@EnableScheduling开启计划任务的支持。
@EnableWebMvc开启Web MVC的配置支持。
@EnableConfigurationProperties开启对@ConfigurationProperties注解配置Bean的支
@EnableJpaRepositories开启对Spring Data JPA Repository的支持。
@Enable TransactionManagement开启注解式事务的支持。
@EnableCaching开启注解式的缓存支持。
通过简单的@Enable来开启一项功能的支持,从而避免自己配置大量的代码,大大降低使用难度。通过观察这些@Enable注解的源码,我们发现所有的注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入的配置方式主要分为以下三种类型。
第一类:直接导入配置类


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}
 //直接导入配置类SchedulingConfiguration, 
 //这个类注解了@Configuration,且注册了一个 scheduledAnnotationProcessor的Bean,源码如下:
package org.springframework.scheduling.annotation;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

@Configuration(
    proxyBeanMethods = false
)
@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }

    @Bean(
        name = 
        {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

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

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;

@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 2147483647;
}
//AsyncConfigurationSelector 通过条件来选择需要导入的配置类,
//AsyncConfigurationSelector的根接口为ImportSelector,
//这个接口需重写selectImports 方法,在此方法内进行事先条件判断。此例中,若adviceMode 为PORXY, 
//则返回 ProxyAsyncConfiguration这个配置类;若activeMode为 金ASPECTJ,则返回
//AspectJAsyncConfiguration配置类,源码如下:
   
package org.springframework.scheduling.annotation;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.lang.Nullable;

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = 
    "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    public AsyncConfigurationSelector() {
    }

    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{ProxyAsyncConfiguration.class.getName()};
        case ASPECTJ:
            return new 
            String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
        default:
            return null;
        }
    }
}
 

第三类:动态注册 Bean

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 切面代理
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

package org.springframework.context.annotation;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
//AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar 接口,
//ImportBeanDefinitionRegistrar的作用是在运行时自动添加 Bean 到已有的配置类,通过重写方法:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }
	//其中,AnnotationMetadata参数用来获得当前配置类上的注解;
	//BeanDefinitionRegistry 参数用来注册 Bean。源码如下:
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, 
    											BeanDefinitionRegistry registry) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy = 
        	AnnotationConfigUtils.attributesFor(importingClassMetadata,
        										 EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }

            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }

    }
}

你可能感兴趣的:(Spring,spring,boot,spring,java)