皆非的万事屋

Spring和Spring Boot知识总结

文章内容整理自JavaGudie

Spring

简介

Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。

一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。

比如说 Spring 自带 IoC(Inverse of Control:控制反转)AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。

Spring 最核心的思想就是不重新造轮子,开箱即用!

Spring Core

核心模块, Spring 其他所有的功能基本都需要依赖于该类库,主要提供 IoC 依赖注入功能的支持。

Spring Aspects

该模块为与 AspectJ 的集成提供支持。

Spring AOP

提供了面向切面的编程实现。

Spring Data Access/Integration :

Spring Data Access/Integration 由 5 个模块组成:

Spring Web

Spring Web 由 4 个模块组成:

Spring Test

Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。

Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好。

IOC AOP

IOC

IoC(Inverse of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。

IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理

不过, IoC 并非 Spirng 特有,在其他语言中也有应用。

将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 大大增加了项目的可维护性且降低了开发难度。

在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。

三级缓存

哪三级:

作用:
spring ioc的三级缓存解决引用循环依赖,不解决构造方法注入的循环依赖,不解决非单例的循环引用(因为非单例对象都没有放到这三级缓存中)

获取流程:

AOP

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP 就是基于动态代理

如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象

而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:

也可以使用 AspectJ。Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。

Spring AOP 和 AspectJ AOP

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强

Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单

当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多

Bean

概念

bean 代指的就是那些被 IoC 容器所管理的对象

我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。

配置元数据可以是 XML 文件注解或者 Java 配置类

作用域

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

单例 bean 的线程安全问题

@Component 和 @Bean

下面这个例子是通过 @Component 无法实现的。

@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}

声明为bean的注解

@Component,@Repository,@Service,@Controller

生命周期

注入的三种方式

@Resource与@Autowired、@Qualifier的用法与区别

SpringMVC

MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码

MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。

Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。

原理

使用到的设计模式

事务

管理事务的方式

传播行为

事务传播行为是为了解决业务层方法之间互相调用的事务问题。

REQUIRED

TransactionDefinition.PROPAGATION_REQUIRED
使用的最多的一个事务传播行为,我们平时经常使用的@Transactional注解默认使用就是这个事务传播行为。

如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

有则加入,没有则创建

REQUIRES_NEW

TransactionDefinition.PROPAGATION_REQUIRES_NEW

创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

挂起当前事务,开启新事务

NESTED

TransactionDefinition.PROPAGATION_NESTED

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED

有则创建嵌套,没有则创建新事务

MANDATORY

TransactionDefinition.PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

有则加入,没有则抛异常

以下 3 种事务传播行为,事务将不会发生回滚:

隔离级别

和事务传播行为这块一样,为了方便使用,Spring 也相应地定义了一个枚举类:Isolation

@Transactional(rollbackFor = Exception.class)

Exception 分为运行时异常 RuntimeException 和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

@Transactional 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

@Transactional 注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上 rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。

失效情况

Spring Boot

概念

Spring 是重量级企业开发框架 Enterprise JavaBean(EJB) 的替代品,Spring 为企业级 Java 开发提供了一种相对简单的方法,通过 依赖注入面向切面编程 ,用简单的 Java 对象(Plain Old Java Object,POJO) 实现了 EJB 的功能

虽然 Spring 的组件代码是轻量级的,但它的配置却是重量级的(需要大量 XML 配置)

从本质上来说,Spring Boot 就是 Spring,它做了那些没有它你自己也会去做的 Spring Bean 配置。

Spring Framework 旨在简化 J2EE 企业应用程序开发。Spring Boot Framework 旨在简化 Spring 开发。

优点:

读取配置文件

@Value

@Value("${wuhan2020}")
String wuhan2020;

@ConfigurationProperties

通过@ConfigurationProperties读取并与 bean 绑定

@Component
@ConfigurationProperties(prefix = "xxx")
@Data
class LibraryProperties {
    private String location;
    private List<Book> books;
    static class Book {
        String name;
        String description;
    }
}

@PropertySource

@PropertySource读取指定 properties 文件

@Component
@PropertySource("classpath:website.properties")
@Data
class WebSite {
    @Value("${url}")
    private String url;
}

Spring加载配置文件的优先级

Filter过滤器

Filter 过滤器主要是用来过滤用户请求的,它允许我们对用户请求进行前置处理后置处理

比如实现 URL 级别的权限控制、过滤非法请求等等。

Filter 过滤器是面向切面编程——AOP 的具体实现

自定义 Filter 只需要实现 javax.Servlet.Filter 接口,然后重写里面的 3 个方法即可

public interface Filter {
   //初始化过滤器后执行的操作
    default void init(FilterConfig filterConfig) throws ServletException {
    }
   // 对请求进行过滤
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
   // 销毁过滤器后执行的操作,主要用户对某些资源的回收
    default void destroy() {
    }
}

Interceptor拦截器

拦截器(Interceptor)同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)

可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置

在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或更多)

过滤器和拦截器的区别

自动装配

[scode type="share"]SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。/scode]

没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。

引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。

自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。

可以把 @SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制

@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类

@ComponentScan: 扫描被 @Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除 TypeExcludeFilterAutoConfigurationExcludeFilter

@EnableAutoConfiguration

@EnableAutoConfiguration 是实现自动装配的重要注解

@EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

AutoConfigurationImportSelector

AutoConfigurationImportSelector 类的继承体系如下:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

}
public interface DeferredImportSelector extends ImportSelector {

}
public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

可以看出,AutoConfigurationImportSelector 类实现了 ImportSelector接口,也就实现了这个接口中的 selectImports方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。

private static final String[] NO_IMPORTS = new String[0];
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // <1>.判断自动装配开关是否打开
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
          //<2>.获取所有需要装配的bean
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

这里我们需要重点关注一下getAutoConfigurationEntry()方法,这个方法主要负责加载自动配置类的。

getAutoConfigurationEntry()

  1. 判断自动装配开关是否打开
    默认spring.boot.enableautoconfiguration=true,可在 application.properties 或 application.yml 中设置
  2. 用于获取EnableAutoConfiguration注解中的 exclude 和 excludeName
  3. 获取需要自动装配的所有配置类,读取META-INF/spring.factories
  4. 经历了一遍筛选,@ConditionalOnXXX 中的所有条件都满足,该类才会生效

总结

Spring Boot 通过@EnableAutoConfiguration开启自动装配,通过 SpringFactoriesLoader 最终加载META-INF/spring.factories中的自动配置类实现自动装配,自动配置类其实就是通过@Conditional按需加载的配置类,想要其生效必须引入spring-boot-starter-xxx包实现起步依赖

tomcat调优

可通过org.springframework.boot.autoconfigure.web.ServerProperties查看,其中包括属性tomcatjettyundertow三种服务器的设置,默认启用tomcat。

# tomcat 8
server:
  tomcat:
    accept-count: 100 # 最大连接等待数,默认100 
    max-connections: 10000 #最大连接数,默认为10000 
    max-threads: 200  #最大工作线程数,默认200
    min-spare-threads: 10 #最小工作线程数,默认10
# tomcat 9
server:
  tomcat:
    max-connections: 8192
    accept-count: 100
    threads:
      max: 200
      min-spare: 10

最大连接数=max-connections + accept-count
最大并发数=max-threads

max-threads线程数的经验值为:
1核2g内存,线程数经验值200;
4核8g内存,线程数经验值800。

4核8g内存,建议值:

server:
  tomcat:
    accept-count: 1000
    max-connections: 10000 
    max-threads: 800 
    min-spare-threads: 100

4核8G内存单进程调度线程数800-1000,超过这个并发数之后,将会花费巨大的时间在cpu调度上。
等待队列长度:队列做缓冲池用,但也不能无限长,消耗内存,出入队列也耗cpu。

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »