从 Spring 到 SpringBoot

从 Spring 到 SpringBoot

Spring

Spring 是什么?

【官网介绍】Spring框架为**任何类型的部署平台上的基于Java的现代企业应用程序提供了全面的编程和配置模型**。

Spring的一个关键元素是在应用程序级别的基础架构支持:Spring专注于企业应用程序的“管道”,以便团队可以专注于应用程序级别的业务逻辑,而不必与特定的部署环境建立不必要的联系

  • Spring框架是一个为Java应用程序的开发提供了综合、广泛的基础性支持的Java平台
  • Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发
  • Spring框架本身亦是按照设计模式精心打造,这使得我们可以在开发环境中安心的集成Spring框架,不必担心Spring是如何在后台进行工作的。
  • Spring框架至今已集成了20多个模块。这些模块主要被分为核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。

Spring 模块结构图

spring结构

Spring 的出现解决的问题

之前存在的问题 不用Spring的解决方案 Spring IoC的解决方案
依赖对象频繁创建 单例、工厂、创建对象、反射 beanFactory、singletonObjects
依赖关系复杂 外部传入(构造器传入、方法传参、属性反射) 自动装配(byType/byName/…)、DI(依赖注入)

Spring 的优点

  1. 方便解耦,便于开发(Spring就是一个大工厂,可以将所有对象的创建和依赖关系维护都交给spring管理)

  2. spring支持aop编程(spring提供面向切面编程,可以很方便的实现对程序进行权限拦截和运行监控等功能)

  3. 声明式事务的支持(通过配置就完成对事务的支持,不需要手动编程)

  4. 方便程序的测试,spring 对 junit4 支持,可以通过注解方便的测试 spring 程序

  5. 方便集成各种优秀的框架

  6. 降低javaEE API的使用难度(Spring 对 javaEE 开发中非常难用的一些 API 例如JDBC,javaMail,远程调用等,都提供了封装,是这些API应用难度大大降低)

Spring IoC 的理解与实现

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了,归spring进行管理和创建

IoC:把对象的创建、初始化、销毁交给spring来管理,而不是由开发者控制,实现控制反转。【Spring来对对象进行创建、管理、装配】

spring作用

Spring中的org.springframework.beans 包和 org.springframework.context构成了Spring框架IoC容器的基础。

  • org.springframework.beans.factory.BeanFactory 是 Spring IoC容器的具体实现,用来包装和管理前面提到的各种bean。BeanFactory 接口提供了一个先进的配置机制,使得任何类型的对象的配置成为可能。
  • BeanFactory 接口是 Spring IoC 容器的核心接口。
  • ApplicationContext接口对BeanFactory(是一个子接口)进行了扩展,在BeanFactory的基础上添加了其他功能,比如与 Spring的AOP 更容易集成,也提供了处理 message resource的机制 (用于国际化)、事件传播以及应用层的特别配置,比如针对Web应用的WebApplicationContext

依赖注入

依赖注入:在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件,DI是Spring创建对象的方法和途径

在Java中依赖注入有以下三种实现方式

  • 构造器注入
  • Setter方法注入
  • 接口注入

自动装配模式

自动装配是为了**将依赖注入“自动化”**的一个简化配置的操作,是针对对象的属性的

在Spring框架中共有5种自动装配;

  1. no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。
  2. byName:该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
  3. byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
  4. constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
  5. autodetect:该模式自动探测使用构造器自动装配或者byType自动装配。首先,首先会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe的自动装配方式。

Spring AOP 的理解与实现

  • AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
  • 在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。
  • AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
  • AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
  • 使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

AOP 相关概念

  1. 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
  2. Aspect(切面):通常是一个类,里面可以定义切入点和通知。
  3. JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  4. Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置)、after(后置)、afterReturning(最终)、afterThrowing(异常)、around(环绕)。
  5. Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式。
  6. weave(织入):将切面应用到目标对象并导致代理对象创建的过程。
  7. introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。
  8. AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。
  9. 目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象,POJO。

Spring AOP的原理——动态代理

目的:将我们的关注点代码与业务代码分开

使用:可以通过切入点(PointCut)表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

  • 当被代理的对象为接口时,采用JDK动态代理。【生成的代理类,已经继承了Proxy类,Java只支持单继承,但可以实现多个接口】

原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)

  • 通过实现InvocationHandler接口创建自己的调用处理器IvocationHandler handler = new InvocationHandlerImpl(…);
  • 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
  • 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
  • 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
  • 当被代理对象不为接口时,采用CGLIB动态代理。

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

使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码。

AOP使用场景

Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

SpringBoot

SpringBoot 的起源

多年来,随着新功能的增加,spring 变得越来越复杂。只需访问 https://spring.io/projects 页面,我们就会看到可以在我们的应用程序中使用的所有 Spring 项目的不同功能。如果必须启动一个新的 Spring 项目,我们必须添加构建路径或添加 Maven 依赖关系,配置应用程序服务器,添加 spring 配置。因此,开始一个新的 spring 项目需要很多努力,因为我们现在必须从头开始做所有事情。

Spring Boot 是解决这个问题的方法。Spring Boot 已经建立在现有 spring 框架之上。使用 spring 启动,我们避免了之前我们必须做的所有样板代码和配置。因此,Spring Boot 可以帮助我们以最少的工作量,更加健壮地使用现有的 Spring 功能。

Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平了道路。

SpringBoot中的一些特征

  1. 创建独立的Spring应用。
  2. 嵌入式TomcatJettyUndertow容器(无需部署war文件)
  3. 提供的starters 简化构建配置
  4. 尽可能自动配置spring应用
  5. 提供生产指标,例如指标、健壮检查和外部化配置
  6. 完全没有代码生成和XML配置要求(约定大于配置)

SpringBoot为不同的Spring模块提供了许多依赖项。一些最常用的是:

spring-boot-starter-data-jpa
spring-boot-starter-security
spring-boot-starter-test
spring-boot-starter-web
spring-boot-starter-thymeleaf

SpringBoot Starter

作用

SpringBoot可以省略众多的繁琐配置,它的众多starter可以说是功不可没。例如SpringBoot中集成redis,只需要pom.xml中引入spring-boot-starter-data-redis,配置文件中加入spring.redis.database等几个关键配置项即可,常用的starter还有spring-boot-starter-webspring-boot-starter-testspring-boot-starter-jdbc,相比于传统的xml配置可以说是大大减少了集成的工作量。

SpringBoot-starter是一个集成接合器,完成两件事:

  • 引入模块所需的相关jar包
  • 自动配置各自模块所需的属性

原理

利用starter实现自动化配置只需要两个条件——maven依赖、配置文件

这里简单介绍下starter实现自动化配置的流程。此处以mybatis为例

springboot的诸多配置

引入maven实质上就是导入jar包,mybatis-spring-boot-starter帮我们自动依赖了Mybatis所需jar包。

starter目录

其中有一个负责自动配置的mybatis-spring-boot-autoconfigure.jar

autoconfigure

META-INF/spring-configuration-metadata.json中便是Mybatis在SpringBoot中的所有配置属性和介绍。

SpringBoot-starter自动配置bean

接下来该考虑Mybatis所需的bean(如必需的sqlSessionFactory、sqlSessionTemplate等)是如何被自动加载的?

SpringBoot启动的时候会找到starter jar包中的resources/META-INF/spring.factories文件,根据spring.factories文件中的配置,找到需要自动配置的类,如下:

1
2
3
4
5
6
7
8
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
//....
}

这是一个mybatis-spring-boot-autoconfigure中的自动配置类。简单说明一下其中的注解:

  • @Configuration:表明是一个配置类,被注解的类将成为一个bean配置类,作用等同于xml配置,里面有被@Bean注解的方法,也等同于xml配置的各种<bean>

  • @ConditionalOnClass/@ConditionalOnBean自动配置条件注解,用于在某一部分配置中,将另一模块部分的配置自动加载进来,因为随着系统越来越大,配置内容越来越多,我们应当将Mybatis的配置放在一处,将log4j的配置放在一处,将SpringBoot自身的配置放在一处,当他们需要互相依赖时,可通过这类注解进行自动配置,如下:

    1
    2
    3
    4
    5
    6
    7
    @ConditionalOnClass @ConditionalOnMissingClass
    @ConditionalOnBean @ConditionalOnMissingBean
    @ConditionalOnProperty
    @ConditionalOnResource
    @ConditionalOnWebApplication @ConditionalOnNotWebApplication
    @ConditionalOnExpression
    @AutoConfigureAfter @AutoConfigureBefore @AutoConfigureOrder(指定顺序)
  • @EnableConfigurationProperties:启用对@ConfigurationProperties注解的bean的支持,这里对应了配置属性类MybatisProperties,它里面定义了Mybatis的所有配置

  • @AutoConfigureAfter:应在其他指定的自动配置类之后应用自动配置。即org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration被自动配置后,才会接着自动配置MybatisAutoConfiguration。这里也解释了为什么我们在application.xml中只配置了数据源,而没有配置Mybatis,但是Mybatis可以正常查库的原因,就是因为它们配置之间的依赖关系