# Spring

# Flag

是时候给大家介绍 Spring Boot/Cloud 背后豪华的研发团队了 (opens new window)

webmvc与webflux

展开查看示例结构
  • webmvc是servlet stack based,基于同步阻塞的IO模型
    • org.springframework.web包下
    • artifactIdspring-boot-starter-web
    • RestTemplate 阻塞式客户端,默认使用HttpURLConnection实现
  • webflux是reactive stack based,一个完全的reactive并且非阻塞的web框架,API公开了Reactor FluxMono类型
    • org.springframework.web.reactive包下
    • artifactIdspring-boot-starter-webflux
    • WebClient 非阻塞式客户端,默认使用Reactor Netty实现

注解生成Bean默认命名规则

在使用@Component@Repository@Service@Controller等注解创建bean时,如果不指定bean名称,默认类名的首字母小写

如果类名前两个及以上字母都是大写,那么bean名称与类名一样,如: RBACAuthorityService - RBACAuthorityService

启动错误

NoClassDefFoundError: Could not initialize class org.springframework.beans.factory.BeanCreationException

可能是内存大小不够,加参数:-Xms1024M -Xmx2048M -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=2048m -Xss5120k

# 事务

/*
Propagation.REQUIRED	如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
Propagation.REQUIRES_NEW	创建一个新的事务,如果当前存在事务,则把当前事务挂起。
Propagation.SUPPORTS	如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
Propagation.NOT_SUPPORTED	以非事务方式运行,如果当前存在事务,则把当前事务挂起。
Propagation.NEVER	以非事务方式运行,如果当前存在事务,则抛出异常。
Propagation.MANDATORY	如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
Propagation.NESTED	如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于Propagation.REQUIRED
*/
// REQUIRES_NEW 与 NESTED 前者是内层异常影响外层,外层不影响内层;后者正好相反,内层加try catch后 异常不影响外层,外层会影响内层
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)

@Autowired
private TransactionTemplate transactionTemplate;


// 手动管理事务
@Autowired
private DataSourceTransactionManager transactionManager;
/*@Autowired
private PlatformTransactionManager platformTransactionManager;*/
/*@Autowired
private TransactionDefinition transactionDefinition;*/

// 设置事务隔离级别,开启新事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

// foreach start

// 获得事务状态
TransactionStatus status = transactionManager.getTransaction(def);
try {
    
} catch (Exception e) {
    if(!TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()){ // 获取当前最大事务
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 标记事务回滚
    }
    if(!status.isRollbackOnly()){
        status.setRollbackOnly(); // 标记事务回滚
    }
    // https://www.cnblogs.com/yaohuiqin/p/9486975.html
    transactionManager.rollback(status); // 回滚事务,设置completed为完成状态,清理事务资源
} finally {
    if (status !=null && status.isNewTransaction() && !status.isCompleted() && !status.isRollbackOnly()){
        transactionManager.commit(status); // 如果rollBackOnly状态被设置将回滚,否则执行正常的事务提交操作
    }
}
// foreach end

# 依赖注入

# Spring Boot

约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式, 旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。

开发人员仅需规定应用中不符合约定的部分,在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想

有哪些约定

  • Maven的目录结构

    • 默认有resources文件夹,存放资源配置文件。src-main-resources,src-main-java
    • 默认编译生成的类都在targe文件夹下面
  • 项目默认的配置文件必须是

    • application前缀命名的yml文件
    • application前缀命名的properties文件

# 默认依赖管理

使用默认依赖管理的目的是:常用的包依赖可以省去version标签。如下:

<dependencies>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
</dependencies>
  • 方式一
<parent>
    <!-- spring-boot-starter-parent 是一个特殊的starter,它用来提供相关的Maven默认依赖 -->
    <!--继承spring-boot-dependencies依赖管理,指定了JDK版本,多了编译配置-->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/>
</parent>
  • 方式二
<dependencyManagement>
    <dependencies>
        <!--引入spring-boot依赖管理,其中包含多个依赖,如slf4j、logback-->
        <!--要使用property的形式覆盖原始的依赖项(升级依赖版本),则需要在此之前添加-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

# 配置方式

Spring Boot Starter

  • 需要在pom.xml中引入为starter的依赖
  • 然后在application.yml或者application.properties中配置,无需在其他任何地方配置(如xmlBean

Java代码配置Bean

  • 需要在pom.xml中引入不为starter的依赖
  • 创建一个配置类,然后使用注解@Configuration在类上,@bean配置方法,方法名是相当于xml配置的id, 返回结果相当于将哪个类注入到bean容器中

加载自定义xml配置

  • 需要在pom.xml中引入不为starter的依赖
  • 在启动类上使用注解@ImportResource(locations={"classpath:配置文件路径}),把自定义的xml配置加载进来

使用注解获取配置文件中的配置

  • @Value("${配置中的属性名}")用在Bean的域(属性)上,获取默认配置文件中的属性值
  • @ConfigurationProperties(prefix="配置中的属性名")用在类名上,获取默认配置文件中的属性值,用@Resource引入到其他类

必须要让Spring 扫描到该类才能使用,有三种方法:加@Component;创建配置类加@Configuration, 再加@EnableConfigurationProperties(类名.class)或者创建构造器加@Bean

  • @PropertySource("classpath:配置文件路径")用在类名上,获取默认自定义properties文件中的属性值,用@Resource引入到其他类

# Starters

application starters

Spring Boot 所有应用程序级的 Starters

Starter 名称 Starter 描述
spring-boot-starter 核心 Starter,包括自动配置、日志及 YAML 支持等
spring-boot-starter-activemq 集成 Apache ActiveMQ,基于 JMS 的消息队列
spring-boot-starter-artemis 集成 Apache Artemis,基于 JMS 的消息队列
spring-boot-starter-amqp 集成 Spring AMQP 和 Rabbit MQ 的消息队列
spring-boot-starter-aop 集成 Spring AOP 和 AspectJ 面向切面编程
spring-boot-starter-batch 集成 Spring Batch(批处理)
spring-boot-starter-cache 集成 Spring Cache(缓存)
spring-boot-starter-data-cassandra 集成 Cassandra(分布式数据库) 和 Spring Data Cassandra
spring-boot-starter-data-cassandra-reactive 集成 Cassandra(分布式数据库) 和 Spring Data Cassandra Reactive
spring-boot-starter-data-couchbase 集成 Couchbase(文档型数据库) 和 Spring Data Couchbase
spring-boot-starter-data-couchbase-reactive 集成 Couchbase(文档型数据库) 和 Spring Data Couchbase Reactive
spring-boot-starter-data-elasticsearch 集成 Elasticsearch(搜索引擎)和 Spring Data Elasticsearch
spring-boot-starter-data-solr 集成 Apache Solr(搜索引擎)结合 Spring Data Solr
spring-boot-starter-data-jdbc 集成 Spring Data JDBC
spring-boot-starter-data-jpa 集成 Spring Data JPA 结合 Hibernate
spring-boot-starter-data-ldap 集成 Spring Data LDAP
spring-boot-starter-data-mongodb 集成 MongoDB(文档型数据库)和 Spring Data MongoDB
spring-boot-starter-data-mongodb-reactive 集成 MongoDB(文档型数据库)和 Spring Data MongoDB Reactive
spring-boot-starter-data-neo4j 集成 Neo4j(图形数据库)和 Spring Data Neo4j
spring-boot-starter-data-r2dbc 集成 Spring Data R2DBC
spring-boot-starter-data-redis 集成 Redis(内存数据库)结合 Spring Data Redis 和 Lettuce 客户端
spring-boot-starter-data-redis-reactive 集成 Redis(内存数据库)结合 Spring Data Redis reactive 和 Lettuce 客户端
spring-boot-starter-data-rest 集成 Spring Data REST 暴露 Spring Data repositories 输出 REST 资源
spring-boot-starter-thymeleaf 集成 Thymeleaf 视图构建 MVC web 应用
spring-boot-starter-freemarker 集成 FreeMarker 视图构建 MVC web 应用
spring-boot-starter-groovy-templates 集成 Groovy 模板视图构建 MVC web 应用
spring-boot-starter-hateoas 集成 Spring MVC 和 Spring HATEOAS 构建超媒体 RESTful Web 应用程序
spring-boot-starter-integration 集成 Spring Integration
spring-boot-starter-jdbc 集成 JDBC 结合 HikariCP 连接池
spring-boot-starter-jersey 集成 JAX-RS 和 Jersey 构建 RESTful web 应用,是 spring-boot-starter-web 的一个替代 Starter
spring-boot-starter-jooq 集成 jOOQ 访问 SQL 数据库,是 spring-boot-starter-data-jpa 或者 spring-boot-starter-jdbc 的替代 Starter
spring-boot-starter-json 用于读写 JSON
spring-boot-starter-jta-atomikos 集成 Atomikos 实现 JTA 事务
spring-boot-starter-jta-bitronix 集成 Bitronix 实现 JTA 事务( 从 2.3.0 开始标识为 Deprecated)
spring-boot-starter-mail 集成 Java Mail 和 Spring 框架的邮件发送功能
spring-boot-starter-mustache 集成 Mustache 视图构建 web 应用
spring-boot-starter-security 集成 Spring Security
spring-boot-starter-oauth2-client 集成 Spring Security’s OAuth2/OpenID 连接客户端功能
spring-boot-starter-oauth2-resource-server 集成 Spring Security’s OAuth2 资源服务器功能
spring-boot-starter-quartz 集成 Quartz 任务调度
spring-boot-starter-rsocket 构建 RSocket 客户端和服务端
spring-boot-starter-test 集成 JUnit Jupiter, Hamcrest 和 Mockito 测试 Spring Boot 应用和类库
spring-boot-starter-validation 集成 Java Bean Validation 结合 Hibernate Validator
spring-boot-starter-web 集成 Spring MVC 构建 RESTful web 应用,使用 Tomcat 作为默认内嵌容器
spring-boot-starter-web-services 集成 Spring Web Services
spring-boot-starter-webflux 集成 Spring Reactive Web 构建 WebFlux 应用
spring-boot-starter-websocket 集成 Spring WebSocket 构建 WebSocket 应用

production starters

生产级 Starters 能被用于线上/生产功能,这个意味着和任何技术、任何业务没关系,也不是只有生产才能使用,只是在生产环境使用更能体现它的意义。

Starter 名称 Starter 描述
spring-boot-starter-actuator 集成 Spring Boot Actuator,提供生产功能以帮助监控和管理应用程序

technical starters

技术类 Starters,用于帮助你排除或者替换指定的框架或技术

Starter 名称 Starter 描述
spring-boot-starter-jetty 集成 Jetty 作为内嵌的 servlet 容器,可用于替代 spring-boot-starter-tomcat
spring-boot-starter-log4j2 集成 Log4j2 日志框架,可用于替代 spring-boot-starter-logging
spring-boot-starter-logging 集成 Logback 日志框架,这个也是默认的日志 Starter
spring-boot-starter-reactor-netty 集成 Netty 作为内嵌的响应式 HTTP 服务器
spring-boot-starter-tomcat 集成 Tomcat 作为内嵌的 servlet 容器,这也是默认的 servlet 容器 starter 被集成 spring-boot-starter-web 里面
spring-boot-starter-undertow 集成 Undertow 作为内嵌的 servlet 容器,可用于替代 spring-boot-starter-tomcat

# Spring Cloud

微服务就是单个应用程序拆分成许多个小型服务的一种开发方法

bootstrap.yml是被一个父级的Spring ApplicationContext加载的。这个父级的Spring ApplicationContext是先加载的, 在加载application.ymlApplicationContext之前。

  • application.yml(.properties) 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等
组件 作用 替代项目 说明
Alibaba Cloud SchedulerX 分布式任务调度 elastic-job、xxl-job 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务(商业组件)。
Alibaba RocketMQ 一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
Alibaba Seata
ElasticSearch+LogStash+Kibana 分布式日志收集 logstash(收集)、elasticsearch(存储+搜索)、kibana(展示),我们将这三个组合起来的技术称之为ELK
Feign 声明式HTTP客户端 Retrofit 基于动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
Netflix Archaius 配置管理 提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。可以实现动态获取配置
Netflix Eureka 服务发现 Consul、Zookeeper、Alibaba Nacos 各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
Netflix Hystrix 断路器 Resilience4j、Alibaba Sentinel 提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
Netflix Zuul API网关 Spring Cloud Gateway 如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务
Ribbon 负载均衡器 Spring Cloud Loadbalancer 服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
Spring Cloud Bus 消息总线 将分布式的节点用轻量的消息代理连接起来。它可以用于广播配置文件的更改或者服务之间的通讯,也可以用于监控
Spring Cloud Cluster 取代Spring Integration。提供在分布式系统中的集群所需要的基础功能支持,如:选举、集群的状态一致性、全局锁、tokens等常见状态模式的抽象和实现。
Spring Cloud Config 配置管理 Ctrip Apollo、Spring Cloud Consul、Zookeeper、Alibaba Nacos 解决分布式系统的配置管理方案。Server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,Client通过接口获取数据、并依据此数据初始化自己的应用。
Spring Cloud Connectors 简化了连接到服务的过程和从云平台获取操作的过程,有很强的扩展性,可以利用Spring Cloud Connectors来构建你自己的云平台。
Spring Cloud for Cloud Foundry 开源PaaS云平台 支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展
Spring Cloud Security 安全框架 添加安全控制
Spring Cloud Sleuth 分布式链路跟踪 日志收集工具包,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,为SpringCloud应用实现了一种分布式追踪解决方案。
Spring Cloud Starters Spring Boot式的启动项目,为Spring Cloud提供开箱即用的依赖管理。
Spring Cloud Stream 数据流 创建消息驱动微服务应用的框架,使用spring integration提供与消息代理之间的连接。数据流操作开发包,任务之间通过事件触发
Spring Cloud Task 批量任务 主要解决短命微服务的任务管理,任务调度的工作,比如说某些定时任务晚上就跑一次,或者某项数据分析临时就跑几次。

# Spring Security

拦截器和过滤器区别

  • 拦截器(Interceptor):依赖于web框架,在实现上,基于Java的反射机制,拦截的是action,说白了拦截的是访问路径
  • 过滤器(Filter):依赖于servlet容器。在实现上,基于函数回调,可以几乎过滤掉所有的东西

拦截器与过滤器的执行顺序:过滤前 -> 拦截前 -> action执行 -> 拦截后 -> 过滤后

  • 过滤器可以修改request,拦截器只能对action请求起作用
  • 过滤器需要在servlet容器中实现,拦截器可以适用于javaEE,javaSE等各种环境
  • 拦截器可以调用IOC容器中的各种依赖,而过滤器不能
  • 过滤器只能在请求的前后使用,而拦截器可以详细到每个方法

# Spring TaskScheduler

  • Spring Scheduling Tasks Spring 3.0 版本之后自带的一个定时任务。其所属Spring的资源包为:spring-context-support

可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。默认单线程串行执行任务,多任务时若某个任务执行时间过长, 后续任务会无法及时执行;抛出异常后,同一个任务后续不再触发

  • Spring Quartz Spring集成整合Quartz,主要Bean: JobDetailTrigger以及SchedulerFactoryJobDataMap数据传递

需要继承org.springframework.scheduling.quartz.QuartzJobBean或者实现org.quartz.Job。采用多线程,下一个调度时间到达时, 会另起一个线程执行调度,不会发生阻塞问题,但调度过多时可能导致数据处理异常,抛出异常后,同一个任务后续仍然会触发

  • TBSchedule

# Spring MVC

redirect重定向

redirect重定向可以跳转到任意服务器地址,传递时要对中文编码进行处理

@RequestMapping(value="/test", method = { RequestMethod.POST, RequestMethod.GET })
public ModelAndView testredirect(HttpServletResponse response){
    response.sendRedirect("/index");// 参数可以直接拼接在url上
    return null;
}
@RequestMapping("/testredirect")
public String testredirect(Model model, RedirectAttributes attr) {
	attr.addAttribute("test", "51gjie");// 跳转地址带参数
    attr.addFlashAttribute("u2", "51gjie");// 跳转地址不带参数,只存在body中
	return "redirect:/user/users";// 参数可以直接拼接在url上
}
@RequestMapping(value="/toredirect",method = { RequestMethod.POST, RequestMethod.GET })
public  ModelAndView toredirect(String userName){
    ModelAndView  model = new ModelAndView("/main/index");// 参数可以直接拼接在url上
    // 把userName参数带入到controller的RedirectAttributes中
    model.addObject("userName", userName);
    return model;
}

# Spring AOP原理

  1. AOP: 其实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类
    1. 其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;静态代理分为:编译时织入(特殊编译器实现)、类加载时织入(特殊的类加载器实现)。静态代理的代表为AspectJ;
    2. 而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。动态代理分为:JDK动态代理(基于接口来实现)、CGLib(基于类实现)。而动态代理则以Spring AOP为代表。
  2. Spring AOP:只支持动态代理,通过两种方式进行实现:
    1. JDK动态代理,通过反射实现,只支持对实现接口的类进行代理
    2. CGLib动态字节码注入方式实现代理。

# JDK动态代理:

JDK中的动态代理是通过反射类Proxy反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvocationHandler回调接口实现的,但是JDK中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率也不高

# Cglib

cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

动态生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑

ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为

# Lombok原理

  1. 定义编译期的注解 @Retention(RetentionPolicy.SOURCE)
  2. 利用JSR269 api(Pluggable Annotation Processing API )编译期的注解处理器 (AbstractProcessor在编译时指定一个processor类来对编译阶段的注解进行干预,Lombok的注解处理器:AnnotationProcessor)
  3. 利用tools.jarjavac api处理AST(抽象语法树),将功能注册进jar包