SpringBoot实战读书笔记(一)

729 阅读5分钟

Spring 常用配置

2.1 Bean的scope

@Scope 注解,情况分以下几种:

  1. Singleton:单例,spring 的默认配置,全容器共享一个实例
  2. Prototype:每次调用新建一个bean实例,举例 @Scope("prototype")
  3. Request:web项目中,每一个 http request 新建一个bean实例
  4. Session:web项目中,每一个 http session 新建一个bean实例
  5. GlobalSession:仅用于portal应用,每一个global http session 新建一个bean实例

2.2 SpringEL和资源利用

  1. @Value注解

  2. #与$符号的区分

2.3 Bean的初始化和销毁

两种方式如下:

  1. Java配置方式:使用@Bean 的 initMethod 和 destoryMethod 方法,相当于 xml 配置的 init-method 和 destroy-method
  2. 注解方式:利用 JSR-250 的 @PostConstruct 和 @ PreDestroy 。需要另外引入 JSR-250 依赖。

备注:可深入了解一下 spring bean 的生命周期。

2.4 Profile

  1. 通过设定 Environment 的 ActiveProfiles 来设定当前 context 需要使用的配置环境。在开发中使用 @Profile 注解类或方法,达到在不同情况下选择实例化不同的bean。
  2. 通过设定jvm的 spring.profiles.active 参数来设置配置环境。
  3. Web项目设置在Servlet的 context parameter 中,注 servlet2.5 及以下与 servlet 3.0 及以上 设置方式有区别。

总结:使用哪种方式去区分项目部署环境需要视情况而定。实际项目中,第二种方式比较适合线上环境部署的场景,把环境判断抽离出代码层面,盲猜一波 spring-boot 自动配置的 profile 也是基于此模块,拭目以待。

2.5 事件(Application Event)

支持容器内 bean 与 bean 之间的消息通信,基本流程如下:

  1. 自定义事件,继承 ApplicationEvent
  2. 定义事件监听器,实现 ApplicationListener。指定监听事件类型,onApplicationEvent 对消息进行受理。
  3. 使用容器发布事件。spring 上下文 AppcalitionContext 调用 publishEvent 发布事件。

Spring高级话题

3.1 Spring Aware

google 翻译:春天察觉

实际意义是 spring 提供 bean 与 spring 框架耦合的一套机制,提供如下 Aware 接口(了解主要作用即可):

  1. BeanNameAware
  2. BeanFactoryAware
  3. ApplicationContextAware
  4. MessageSourceAware
  5. ApplicationEventPublishAware
  6. ResourceLoadAware

3.2 多线程

Spring 通过 TaskExecutor 来实现 多线程 和 并发编程。备注,ThreadPoolTaskExecutor 是基于 线程池 的 TaskExecutor。涉及注解:@EnableAsync 、 @Async。示例如下:

配置类实现 AsyncConfigurer 接口并重写 getAsyncExecutor 方法,返回 ThreadPoolTaskExecutor:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * @author akazc
 * @date 2020/8/23 14:58
 */
@EnableAsync
@Configuration
public class TaskExecutorConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

备注:需要补充一下线程池的知识点

3.3 计划任务

涉及注解:@EnableScheduling、@Scheduled

3.4 条件注解@Conditional

涉及注解:@Conditional。涉及接口:org.springframework.context.annotation.Condition

据说springboot中大量应用条件注解,拭目以待

3.5 组合注解与元注解

组合注解:@Configuration: @Component + @Bean , 差不多就这个意思吧

3.6 @Enable*注解的工作原理

常见 Enable*注解

@EnableAspectJAutoProxy 开启 AspectJ 自动代理的支持

@EnableAsync 开启异步方法支持

@EnableScheduling 开启计划任务的支持

@EnbaleWebMvc 开启 WebMVC 配置支持

@EnableConfigurationProperties 开启对 @ConfigurationProperties 注解配置 bean 支持

@EnbaleJpaRepositories 开启对 Spring Data JPA Repository 支持

@EnableTransactionManagement 开启注解式事务支持

@EnableCaching 开启注解式缓存支持

@Import注解导入配置方式的三种类型

直接导入配置类

直接注册一个bean,示例:

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

    @Bean(
        name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}
依据条件选择配置类

根据选择器来注册bean。即实现 ImportSelector 重写 selectImports 方法,示例:

@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;
}
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;
        }
    }
}
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
    public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";

    public AdviceModeImportSelector() {
    }

    protected String getAdviceModeAttributeName() {
        return "mode";
    }

    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Class<?> annType = GenericTypeResolver.resolveTypeArgument(this.getClass(), AdviceModeImportSelector.class);
        Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (attributes == null) {
            throw new IllegalArgumentException(String.format("@%s is not present on importing class '%s' as expected", annType.getSimpleName(), importingClassMetadata.getClassName()));
        } else {
            AdviceMode adviceMode = (AdviceMode)attributes.getEnum(this.getAdviceModeAttributeName());
            String[] imports = this.selectImports(adviceMode);
            if (imports == null) {
                throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
            } else {
                return imports;
            }
        }
    }

    @Nullable
    protected abstract String[] selectImports(AdviceMode var1);
}
动态注册bean

运行时自动添加bean到已有配置类。即实现 ImportBeanDefinitionRegistrar 接口,重写 registerBeanDefinitions 方法。示例:

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

    boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    AspectJAutoProxyRegistrar() {
    }

    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);
            }
        }

    }
}

3.7 测试

SpringMVC 基础

4.4 SpringMVC基本配置

核心

核心类:WebMvcConfigurerAdapter (5.X 版本已弃用继承该类,改为实现 WebMvcConfigurer 或继承 WebMvcConfigurationSupport,大同小异,可看下源码理解)

核心注解:@EnableWebMvc

理解 WebMvcConfigurer 源码:

public interface WebMvcConfigurer {
    // 路径匹配参数配置
    default void configurePathMatch(PathMatchConfigurer configurer) {
        // 示例:路径参数不可忽略 "."
        // configurer.setUseSuffixPatternMatch(false);
    }

    default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    }

    default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    }

    default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    }

    default void addFormatters(FormatterRegistry registry) {
    }

    // 注册拦截器
    default void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器,集成 HandlerInterceptorAdapter 注册即可
        // registry.addInterceptor(HandlerInterceptor.class);
    }

    // 添加静态资源映射
    default void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 对外暴露的访问路径
        // registry.addResourceHandler();
        // 指定静态文件放置目录
        // resgistry.addResourceLocations();
    }

    default void addCorsMappings(CorsRegistry registry) {
    }

    // 添加 view-controller,即快捷页面跳转
    default void addViewControllers(ViewControllerRegistry registry) {
        // 示例
        // registry.addViewController("/index").setViewName("/index");
    }

    default void configureViewResolvers(ViewResolverRegistry registry) {
    }

    default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    }

    default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
    }

    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }

    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    }

    @Nullable
    default Validator getValidator() {
        return null;
    }

    @Nullable
    default MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}
@ControllerAdivice

顾名思义是 controller 的功能加强,目标是对于控制器的全局配置放置在统一个位置,主要有以下功能注解:

@ExpectionHandler:用于全局处理控制器里的异常

@InitBinder:设置WebDataBinder,用来自动绑定前台请求参数到Model中

@ModelAttribute:本来的作用是绑定键值对到model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对

4.5 SpringMVC的高级配置

文件上传配置

主要bean:MultipartResolver

主要类:MultipartFile

HttpMessageConverter

HttpMessageConverter是用来处理 request 和 response 里的数据的。

  1. Spring内置的HttpMessageConverter:
  • StringHttpMessageConverter 负责字符串类型的数据的读取或者写出
  • ByteArrayHttpMessageConverter 负责二进制格式的读取或者写出
  • ResourceHttpMessageConverter 负责资源文件的读取或者写出
  • FormHttpMessageConverter 负责读取form提交的数据 application/x-www-form-urlencoded
  • MappingJacksonHttpMessageConverter 负责json格式数据的读取或者写出
  1. 自定义HttpMessageConverter

4.6 SpringMVC的测试