# Spring
- 简化开发:Spring 是分层的 JavaSE/EE 应用 full-stack 轻量级开源框架,以 IoC:Inverse of Control(反转控制)和 AOP(Aspect Oriented Programming)面向切面编程为内核。以及由此演化出来的事务处理。
- 框架整合:MyBatis,Struts,Hibernate 等等
- 全家桶:Spring Framework、Spring Boot、Spring Cloud 等等
# Spring-Boot 注解类型
- @SpringBootApplication:来标注一个主程序类,说明这是一个 Spring Boot 应用;
- @SpringBootConfiguration:Spring Boot 的配置类,标注在某个类上,表示这是一个 Spring Boot 的配置类;
- @Configuration 配置类上来标注这个注解,创建一个class配置文件;配置类 - 配置文件;配置类也是容器中的一个组件;
- @Component 表示一个类是由 Spring 管理的组件,通用型注解;@Component("idxxx")可以指定id;
- @EnableAutoConfiguration 开启自动配置功能;以前我们需要配置的东西,Spring Boot 帮我们自动配置;告诉 Spring Boot 开启自动配置功能;这样自动配置才能生效;
- @Controller:表示一个类是 Spring MVC 控制器;
- @Autowired:用于 bean 的自动依赖注入;
- @Service:表示一个类是一个 Spring 服务;
- @Repository:用来表示一个类是 Spring 的存储库;被标注在DAO层;
- @RequestMapping:用于将 URL 请求映射到控制器方法;
- @PathVariable:用于从 URL 路径中提取一个变量;
- @RequestParam:用于从查询字符串或表单数据中提取一个变量;
- @ResponseBody:用来表示一个方法应该直接返回响应体;
- @ExceptionHandler:用于处理由控制器方法抛出的异常;
- @Transactional:用来表示一个方法应该在一个事务中被执行;
- @Bean:用于表示一个方法产生一个由 Spring 管理的 Bean;
- @Value:用于从属性文件或环境变量中注入值;
- @Profile:用来激活一个特定的 Spring 配置文件;
- @ComponentScan:开启包扫描,不用再在每个Bean上加注解,Bean即data class上需要加@Component注解;
# 项目结构
这 4 个部分是指 Java Spring MVC 项目的典型结构,它是一种用于构建 Web 应用的架构模式。
- controller:这个 package 包含处理传入的 HTTP 请求的类,并将它们路由到适当的服务方法。
- model/bean/dto:这个 package 包含定义应用程序的数据模型的类。这包括实体、数据传输对象(DTO)和其他特定领域的对象。
- dao/repository(jpa/jdbc)/mapper(mybatis):这个 package 包含处理应用程序中数据持久性的类。这包括数据库访问、查询和其他与数据相关的操作。
- dao的实现一般放在impl包下,即dao一般是些interface,而具体的sql语句则写在impl类中,比如基于myBatis实现DAO。
- service:这个 package 包含实现应用程序的业务逻辑的类。这包括处理数据、应用规则和其他特定于应用程序领域的操作。
- eg.
/src/main/java:放置项目Java源代码
...
|_annotation:放置项目自定义注解
|_aspect:放置切面代码
|_config:放置配置类
|_constant:放置常量、枚举等定义
|__consist:存放常量定义
|__enums:存放枚举定义
|_controller:放置控制器代码
|_filter:放置一些过滤、拦截相关的代码
|_mapper:放置数据访问层代码接口
|_model:放置数据模型代码
|__entity:放置数据库实体对象定义
|__dto:存放数据传输对象定义
|__vo:存放显示层对象定义
|_service:放置具体的业务逻辑代码(*接口*和*实现*分离)
|__intf:存放业务逻辑接口定义
|__impl:存放业务逻辑实际实现
|_utils:放置工具类和辅助代码
...
/src/main/resources:放置项目静态资源和配置文件
...
|_mapper:存放mybatis的XML映射文件(如果是mybatis项目)
|_static:存放网页静态资源,比如下面的js/css/img
|__js:
|__css:
|__img:
|__font:
|__等等
|_template:存放网页模板,比如thymeleaf/freemarker模板等
|__header
|__sidebar
|__bottom
|__XXX.html等等
|_application.yml 基本配置文件
|_application-dev.yml 开发环境配置文件
|_application-test.yml 测试环境配置文件
|_application-prod.yml 生产环境配置文件
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# IoC 控制反转
使用对象时,由主动 new 产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。Spring 技术对 IoC 思想进行了实现:
- Spring 提供了一个容器,称为 IoC 容器,用来充当 IoC 思想中的「外部」
- IoC 容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在 IoC 容器中统称为 Bean
- 依赖注入 DI:在容器中建立 bean 与 bean 之间的依赖关系的整个过程,称为依赖注入
# 使用
在项目
resources -> applicationContext.xml
中配置。导入 Spring 的坐标 spring-context。
配置 bean:
- bean 标签表示配置 bean
- id 属性表示给 bean 起名字
- class 属性表示给 bean 定义类型
获取 IoC 容器:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
获取 bean:
BookDao bookDao = (BookDao) ctx.getBean("bookDao");// id
即可调用
bookDao.save();
配置 Dao,业务代码中解耦 new 关键字,可以写一个 setter 方法
在
applicationContext.xml
中的 bean 标签中配置 server 与 dao 的关系- property 标签表示配置当前 bean 的属性
- name 属性表示配置哪个具体的属性
- ref 属性表示参照哪一个 bean
# bean 基础配置
- name 属性:可以定义多个 bean 的别名用逗号分号空格分隔
- scope 属性:Spring 默认创建的实例是单例模式,即 scope 属性是 singleton!非单例模式创建方法:设置 scope 为 prototype
- 实例化:bean 本质是对象,创建 bean 使用构造方法完成,调用的是无参构造方法,可以通过反射访问私有构造方法
- 构造方法
- 静态工厂方法:用得少,bean 中增加
factory-method
属性告诉 Spring 调哪个方法获得实例,当然也要自己先实现工厂类 - 实例工厂方法:先添加 bean 使用实例工厂实例化 bean,然后再在原来的 bean 中移除 class,增加
factory-method
属性设置为新增 bean 的 id,并修改factory-method
属性为 class 中的实际方法。 - 3 的改进型:不需要增加一个 bean。先在工厂方法类中实现
FactoryBean<要返回的实例的类>
接口,重写两个 get 方法,getObjectType()->UserDao.class
类似这样。 - Bean的别名,生成的是同一个Bean实例。
- 报错从最后几行往前看
- 生命周期:
- 初始化容器:创建对象(内存分配),执行构造方法,执行属性注入(set 操作),执行 bean 初始化方法。init-method、destroy-method 等。
- 使用 bean:执行业务操作
- 关闭/销毁容器:执行 bean 销毁方法
- 关闭容器的方式:
- 手动关闭:ConfigurableApplicationContext 接口 close()操作
- 注册关闭钩子,在虚拟机退出前先关闭容器,再退出虚拟机:ConfigurableApplicationContext 接口 registerShutdownHook()操作
# 依赖注入
往类中传递数据的方式有几种?普通方法(set 方法)、构造方法。
- setter 注入:
- 简单类型:先在 bean 中定义引用类型属性并提供可供访问的 set 方法,然后在
bean
->property
中设置 value 进行传递 - 引用类型:同上
- 简单类型:先在 bean 中定义引用类型属性并提供可供访问的 set 方法,然后在
- 构造器注入:
- 简单类型:先在 bean 中定义构造方法,然后在
bean
->constructor-arg
中设置 name 和 ref/value 进行传递 - 引用类型:同上
- 简单类型:先在 bean 中定义构造方法,然后在
- 自己开发的模块用 setter 注入
- 依赖自动装配:
- bean 中添加
autowire="byType/byName"
字段,配合 setter - 只用于引用类型,不能对简单类型操作
- 使用按类型装配时必须保障容器中相同类型的 bean 唯一,推荐使用
- 使用按名称装配时必须保障容器中具有指定名称的 bean,因变量名与配置耦合,不推荐
- 自动装配优先级低于 setter 注入与构造器注入,同时出现时自动装配失效
- bean 中添加
- 集合类型注入:
- array/list/set/map 等:在 ApplicationContext.xml 中,新建一个 bean,然后通过 property 标签配合 name 属性通过 value 或者 ref 来配置
- 注入null:
- 注入的时候创建内部Bean:
# Bean的作用域
# Singleton作用域
在一个ApplicationContext上下文环境中,只创建一个Bean实例,<bean id="xx" ... scope="singleton" ></bean>
。
# prototype作用域
创建多个Bean实例,<bean id="xx" ... scope="prototype" ></bean>
。
有如下场景:
此时需要通过方法注入的方式实现:
- 在Bean1中增加抽象方法createBean2()用于生成Bean2的实例,
- 在spring.xml中:通过
<lookup-method name="createBean2" bean="bean2"></lookup-method>
来实现
# Web环境作用域
- request作用域
- session作用域
- application作用域
- websocket作用域
# 自定义作用域
SimpleThreadScope作用域
- 自定义作用域
- SimpleThreadScope
# Bean的懒加载
默认的情况下,Singleton作用域下,Bean会在Context之前就被创建完成。即Spring容器会在创建容器时提前初始化Singleton作用域下的Bean。如果想要在使用时才创建Bean,可以使用懒加载。但是如果Bean被标注了lazy-init="true"
,则该Bean只有在其被需要时才会被初始化
- 如果把
<beans ... default-lazy-init="true"></beans>
,则该标签下的Bean都开启懒加载 - 节省资源,但可能会增加某些资源的的响应时间
# Bean初始化及销毁逻辑处理
- 初始化:
<bean ... init-method="onInit"></bean>
- 实现InitializingBean接口,重写afterPropertiesSet()方法
- 销毁:
<bean ... destroy-method="onDestroy"></bean>
- 实现DisposableBean接口,重写destroy()方法
- 为所有的Bean设定默认的初始化方法和销毁方法:
<beans ... default-init-method="onInit" default-destroy-method="onDestroy"></beans>
# Bean属性继承
场景一:Child类继承了Parent类:
- 使用
<bean id="parent" class="com.example.Parent" abstract="true"><property name="attr1" value="val1"/></bean>
定义一个抽象的父类,通过abstract="true"
告诉Spring这个Bean不需要实例化。 - 使用
<bean id="child1" class="com.example.Child1" parent="parent"><property name="attrC1" value="valC1"/></bean>
定义一个子类,继承父类 - 使用
<bean id="childN" class="com.example.ChildN" parent="parent"><property name="attrCN" value="valCN"/></bean>
定义一个子类,继承父类 - 子类会继承父类的属性,但不会继承父类的构造方法和初始化方法
- 子类可以重写父类的属性,但不能重写父类的构造方法和初始化方法
场景二:Child类没有继承Parent类,但是多个Child类中有相同的属性和值,此时Parent基类Bean上删除class="com.example.Parent"
即可。
Bean的别名:只能在@Configuration中使用,不能在@Component中使用。
# Bean的依赖注入
- 构造器注入:在构造器中注入依赖,使用
<bean id="xx" class="com.example.Xx" constructor-arg ref="yy"></bean>
- setter注入:在 setter 方法中注入依赖,使用
<bean id="xx" class="com.example.Xx"><property name="yy" ref="yy"></property></bean>
- 自动装配:使用
<bean id="xx" class="com.example.Xx" autowire="byType"></bean>
,byName、byType、constructor、no - 使用
@Autowired
注解,可以自动装配,可以标注在构造器、setter 方法、字段上 - 使用
@Qualifier
注解,可以指定具体的 bean,可以标注在构造器、setter 方法、字段上 - ,通过order指定顺序。
# Bean的生命周期
- 实例化:通过构造器或工厂方法创建 bean 实例
- 属性赋值:通过 setter 方法或工厂方法设置 bean 属性
- 初始化:通过 init-method 方法或 InitializingBean 接口的 afterPropertiesSet 方法进行初始化
- 使用:bean 可以被使用
- 销毁:通过 destroy-method 方法或 DisposableBean 接口的 destroy 方法进行销毁
# 通过注解设置Bean的作用域
- 通过@Scope注解设置作用域及名称
# 通过注解设置Bean的懒加载
@Lazy注解,通过@Lazy注解设置懒加载,可以标注在类上,也可以标注在方法上。
# 通过注解实现Bean的初始化及销毁
使用@PostConstruct 注解,方法在依赖注入完成后执行,可以标注在方法上。
使用@PreDestroy 注解,方法在 bean 销毁前执行,可以标注在方法上。
使用 InitializingBean 接口的 afterPropertiesSet 方法进行初始化,可以实现 InitializingBean 接口。
使用 DisposableBean 接口的 destroy 方法进行销毁,可以实现 DisposableBean 接口。
在Bean实例上使用
@Bean(initMethod="onInit", destroyMethod="onDestroy")
注解,可以指定初始化和销毁方法。
# 加载 properties 配置文件
一些数据库用户名密码啥的不应该写在 pom.xml 里,应该单独管理 jdbc.properties
把配置写入 jdbc.properties 文件,比如:
jdbc.username=xxx jdbc.password=xxx
开 context 命名空间
- jdbc.properties 中,beans 里面增加一行
xmlns:context="http://www.springframework.org/schema/context"
,再增加 2 行http:.../context
,http:.../context/spring-context.xsd
。大部分是这么操作 - 使用 context 空间加载 properties 配置文件:
<context:property-placeholder location="jdbc.properties"/>
,通过system-properties-mode="NEVER"
设置不去加载系统属性,通过 location 属性配置使用不同的 jdbc.properties 文件。 - 在 bean 中使用属性占位符
${}
读取 properties 配置文件中的属性。
- jdbc.properties 中,beans 里面增加一行
不加载系统属性:
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER" />
加载多个:
<context:property-placeholder location="jdbc.properties,jdbc2.properties" />
加载所有:
<context:property-placeholder location="*.properties" />
加载 properties 文件标准格式:
<context:property-placeholder location="classpath:*.properties" />
从类路径或加载系统属性:
<context:property-placeholder location="classpath*:*.properties" />
# IOC 容器
- 容器初始化方式一:加载类路径下的配置文件:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 容器初始化方式二:从文件系统下加载配置文件:
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
- 容器初始化加载多个配置文件:
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
- 获取 bean 方式一:使用 bean 名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
- 获取 bean 方式二:使用 bean 名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
- 获取 bean 方式三:使用 bean 类型获取
BookDao bookDao = ctx.getBean(BookDao.class);
,此时系统中该类只能有一个。 - 上古方法:BeanFactory 接口是 IoC 容器的顶层接口,初始化 BeanFactory 对象时,延迟加载 Bean。
- ApplicationContext 是 Spring 容器的核心接口,初始化时立即加载 Bean。该接口提供基础的 bean 操作相关方法,通过其它接口扩展器功能。
- bean 相关:
2
# 注解模式开发
定义 bean
@Component
- @Controller
- @Service
- @Repository
<context:component-scan/>
纯注解开发
- @Configuration
- @ComponentScan,多个值用数组
- @AnnotationConfigApplicationContext
bean 生命周期
- 作用范围@Scope
- 使用@PostConstruct、@PreDestroy 定义 bean 生命周期
@Repository
@Scope("singleton") // 单例模式,prototype:非单例
public class BookDaoImpl implements BookDao{
public BookDaoImpl(){...}
@PostConstruct
public void init(){...}
@PreDestroy
public void destroy(){...}
}
2
3
4
5
6
7
8
9
自动装配:无同名 bean 时直接使用@AutoWired,按类型装配,使用反射里面的暴力反射简化代码书写。有同名 bean 时,1 可以通过按名称装配,在 bean 中使用@Repository("xx")注解的形式区分;2 是使用@Qualifier("beanName")注解的形式开启指定名称装配 bean,直接使用。
- 注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供 setter 方法
- 注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应的构造方法,请提供唯一的构造方法
- 注意:@Qualifier 注解无法单独使用,必须配合@AutoWired 注解使用
- 使用@Value 实现简单类型/值类型的注入
加载外部 properties 文件
- 使用@PropertySource 注解加载 properties 文件,不支持
*
文件名 - 注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符
*
- 使用@PropertySource 注解加载 properties 文件,不支持
第三方 bean 管理
- 使用@Bean 定义一个返回这 bean 实例的方法
- 将独立的配置类加入核心配置
- 方式一:导入式,使用@Import 注解手动加入配置类到核心配置,此注解只能添加一次,多个数据使用数组格式。推荐
- 方式二:扫描式,使用@ComponentScan({"xx.config"})注解扫描配置类所在的包,加载对应的配置类信息。不推荐
第三方 bean 的依赖注入
- 简单类型:直接使用@Value 注解,相当于变量的形式
- 引用类型:在@Bean 下定义的方法中定义形参,然后就能直接使用,会自动按类型检测。容器会根据类型自动装配对象。
# AOP
- 基础:
- 面向切面编程,一种编程范式,指导开发者如何组织程序结构。作用是在不惊动原始设计的基础上为其进行功能增强。
- Spring 倡导无侵入式编程
- 连接点:程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
- 在 SpringAOP 中,理解为方法的执行
- 切入点:匹配连接点的式子。在 SpringAOP 中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
- 一个具体方法:某个包下某个接口中的无形参无返回值的某个方法
- 匹配多个方法:所有的 save 方法,所有的 get 开头的方法,所有的以 Dao 结尾的接口中的任意方法。。
- 通知:在切入点处执行的操作,也就是共性功能
- 在 SpringAOP 中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面:描述通知与切入点的对应关系
- 实战:
- 导包:
org.springframework.spring-context
,org.aspectj.aspectjweaver
- 保持原来的接口和实现类不变
- Spring 的执行程序(main 方法)也不变
- SpringConfig 中,增加
@EnableAspectJAutoProxy
注解,告诉程序我们要用注解开发 AOP - AOP 通知类(自己创建)上,增加
@Component
注解,告诉 Spring 来加载。增加@Aspect
注解,进行关联通知。 - 在通知类中,新增一个私有的空方法
pt()
做通知,名字任意,无参无返回值无实际逻辑,使用@Pointcut
注解,并传入:"execution(void com.xxx.BookDao.xx())"
- 把切入点(抽出来的函数)与通知绑定,通过
@Before("pt()")
的形式 - SpringAOP 的核心模式就是代理模式
- 切入点:要进行增强的方法
- 切入点表达式:要进行增强的方法的描述方式,有两种描述方式:
- 执行 com.xxx.dao 包下的 BookDao 接口中的无参数 update 方法
- 执行 com.xxx.dao.impl 包下的 BookDaoImpl 类中的无参数 update 方法
- 切入点表达式标准格式:
动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
- 动作关键字:描述切入点的行为动作。例如 execution 表示执行到指定切入点
- 访问修饰符:public,private 等可以省略
- 返回值
- 包名
- 类/接口名
- 方法名
- 参数
- 异常名:方法定义中抛出指定异常,可以省略
- 可以使用通配符描述切入点,快速描述
*
:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现,@Pointcut("execution(public * com.xxx.*.BookDao.find* (*))")
..
:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写,execution(public User com..BookDao.findById (..))
+
:专用于匹配子类类型,execution(* *..*Service+.*(..))
- 终极写法:
execution(* *..*(..))
,不常用
-
- 关键步骤如下:
@Around()
Object
Throwable
ProceedingJoinPoint pjp
pjp.proceed()
return ret
- 然后填充要做的操作逻辑
Signature sig = pjp.getSignature(); String xx = sig.getDeclaringTypeName()/getName();
获取具体执行的切入点的信息- AOP 通知获取参数数据:
- JoinPoint 对象描述了连接点方法的运行状态。可以获取到原始方法的调用参数,用于除了
@Around
以外的 - ProceedJointPoint 是 JointPoint 的子类:用于
@Around
- JoinPoint 对象描述了连接点方法的运行状态。可以获取到原始方法的调用参数,用于除了
- AOP 通知获取返回值数据:
- 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接受对应的异常对象。
@AfterReturning(value="pt()",returning="ret")
- 环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值。
- 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接受对应的异常对象。
- AOP 通知获取异常数据(了解)
- 抛出异常:
@AfterThrowing(value="pt()",throwing="t")
,通过形参Throwable t
接收并使用对应的异常对象 - 抛出异常后通知可以获取切入点方法运行的异常信息,使用形参可以接受运行时抛出的的异常对象,使用
try{}catch(Throwable t){...}
接受并使用
- 抛出异常:
- 大量的相同的有共性的可以提取出来的方法,可以用 AOP,可以简化大量操作。
# Spring 事务
- 事务的作用:在数据层保障一系列的数据库操作同成功同失败。
- Spring 事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败。
interface PlatformTransactionManager
、class DataSourceTransactionManager
- 使用步骤 3 步:
- 在业务层接口上添加 Spring 事务管理:
@Transactional
,通常添加在业务层接口而不是业务层实现类中,降低耦合,注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务 - 设置事务管理器:通过
PlatformTransactionManager
新建一个 bean,bean 中返回DataSourceTransactionManager
的实例对象。事务管理器要根据实现技术进行选择,MyBatis 框架使用的是 JDBC 事务。 - 开启注解式事务驱动:
@EnableTransactionManagement
- 在业务层接口上添加 Spring 事务管理:
- Spring 事务角色:
- 事务管理员:发起事务方,在 Spring 中通常指代业务层开启事务的方法
- 事务协调员:加入事务方,在 Spring 中通常指代数据层方法,也可以是业务层方法
- 事务相关配置:一般只在 Error 错误或运行时异常时才会导致事务回滚。有些异常是不会导致事务回滚的,比如 IOException。
- 如果要加上这种异常,需在
@Transactional(rollbackFor={IOException.class})
这样配置
- 如果要加上这种异常,需在
- 事务传播行为:事务协调员对事务管理员所携带事务的处理态度。。。
- 如果某个接口或方法需要单独开启事务,需在
@Transactional(propagation=Propagation.REQUIRE_NEW)
这样配置
- 如果某个接口或方法需要单独开启事务,需在
# SpringMVC
SpringMVC 技术与 Servlet 技术功能等同,均属于 web 层开发技术。SpringMVC 是一种基于 Java 实现 MVC 模型的轻量级 Web 框架。使用简单,开发便捷(相较于 Servlet)。灵活性强。
# 使用步骤
- 先导入
SpringMVC(spring-webmvc)
与Servlet(javax.servlet-api)
的坐标(依赖) - 创建 SpringMVC 控制器类(等同于 Servlet 功能)
- 定义访问路径:
@RequestMapping("/xxx")
- 定义返回:
@ResponseBody
- 定义访问路径:
- 初始化 SpringMVC 环境(同 Spring 环境)设定 SpringMVC 加载对应的 bean
- 启动 Tomcat 服务器的时候要保证能加载到服务器的配置,初始化 Servlet 容器。加载 SpringMVC 环境,并设置 SpringMVC 技术处理的请求。
- 创建一个类,继承
AbstractDispatcherServletInitializer
并实现它的几个抽象方法。 - 初始化一个
AnnotationConfigWebApplicationContext
并注册 SpringMvcConfig.class - 在 getServletMappings 方法中设置把所有进入 Tomcat 的请求都交由 SpringMVC 来处理。固定格式
return new String[]{"/"};
- 创建一个类,继承
- 乱码处理:重写
getServletFilters()
,return new Filter[]{new CharacterEncodingFilter().setEncoding("UTF-8")};
- get 请求:在@Controller 中
- 注解
@RequestMapping("函数名,也是请求的 path")
- 注解
@ResponseBody
- 编写响应函数体,通过形参接收参数,通过
(@RequestParam("name") String username, ... )
设置参数别名,return 返回响应字符串
- 注解
- post 请求:
- 通过
x-www-form-urlencoded
发送时,Java 代码和 get 请求一样
- 通过
- 参数传递:
- 普通参数如上
- POJO 参数:通过实体类来接收,使用时直接以形参注入
- 嵌套 POJO 参数一样,请求时嵌套属性以 AA.bb 的形式请求
- 数组参数:多个相同的属性会自动组成数组
- 集合参数:通过
@RequestParam List<String> lists
来接收 - 各种 JSON 数据(包括 POJO,POJO 集合)对象数组等:
- 添加依赖
jackson-databind
- SpringMvcConfig 中添加
@EnableWebMvc
注解 - 通过
@RequestBody List<String> lists
来接收
- 添加依赖
@RequestBody
与@RequestParam
的区别:@RequestParam
用于接收 url 地址传参,表单传参application/x-www-form-urlencoded
@RequestBody
用于接收 json 数据application/json
@PathVariable
用于接收路径参数,使用{参数名称}描述路径参数- 开发中,发送 json 格式数据为主,
@RequestBody
应用较广 - 如果发送非 json 数据,选用
@RequestParam
接收请求参数 - 采用 restful 进行开发,当参数数量较少时,例如 1 个,可以采用
@PathVariable
接收请求路径变量,通常用于传递 id 值
- 日期型参数接收:提前使用
@DateFormat(pattern="yyyy-MM-dd") Date date
进行格式约定,否则默认只能接收yyyy/MM/dd
格式。底层是通过 Converter 接口重写的 convert 方法实现的转化。 - 响应:
- 页面:直接在处理函数中返回页面的相对路径即可,不需要
@ResponseBody
- 数据:
- 字符串:需要
@ResponseBody
- JSON:需要
@ResponseBody
--设置当前控制器返回值作为响应体,返回一个 POJO 对应的对象即可,是 jackson 实现的。是 HttpMessageConverter 接口转的。
- 字符串:需要
- 页面:直接在处理函数中返回页面的相对路径即可,不需要
# RESTful 风格
- 首先要遵循 restful 风格定义接口
- 然后借助注解
@RequestMapping("函数名,也是请求的 path", method=RequestMethod.DELETE)
的第二个参数,定义接口行为 - 对于路由参数,使用注解
@PathVariable
接收,同时要做@RequestMapping(value="/xxx/{id}")
配置,跟前端路由参数一样。 - 公共路径前缀通过在函数上添加注解
@RequestMapping("/commonPath")
实现简化。 - 同上
@ResponseBody
注解也可以提取到函数外面,前提是所有响应方法都需要这个注解。 - 在上面 4+5 中,可以用
@RestController
代替@Controller
和@ResponseBody
。 @RequestMapping(value="", method=RequestMethod.POST)
===@PostMapping(...)
,其他几种都有类似简化书写方式。- 设置对静态资源的访问放行:
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport{
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry){
// 当访问/pages/???的时候,走/pages目录下的内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
2
3
4
5
6
7
8
9
10
11
# SSM 整合
# 学透 Spring
# sdkman
管理 Java 版本,类似 nvm?也可以管理 Maven、groovy 等。
curl -s "https://get.sdkman.io" | bash
: 安装 sdkmansource "$HOME/.sdkman/bin/sdkman-init.sh"
: 刷新环境变量,可以直接加到你的 shell 配置文件(比如 ~/.bash_profile、~/.zshrc 等)中,以便每次打开新终端时自动初始化 sdkman。sdk version
sdk list java
: 获取当前操作系统可用的 JDK 信息sdk install java 11.0.18
: 安装特定版本 JDKsdk install java 14.0.1 /Library/Java/JavaVirtualMachines/jdk-14.0.1.jdk/Contents/Home
: 关联本地已安装 JDKsdk default java 11.0.18
: 设置默认版本(感觉并不管用,还得去改.zshrc 才会生效)sdk use java 11.0.18
: 使用某个 jdk 版本sdk uninstall java 8.0.252-open
: 移除 SDKMAN 中的某个特定版本sudo rm -rf /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk
: 移除本地 jdk 版本ls /Library/Java/JavaVirtualMachines/
: Mac 上通过 .dmg 文件安装的 JDK 一般会在这个目录下sudo rm -rf /Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk
: 使用 rm 命令来删除对应的 JDK 目录sdk install maven
: 安装 Maven,安装前要确保已正确安装 JDK- Maven 镜像仓库,阿里云:
https://maven.aliyun.com/
,按使用指南修改 Maven 安装目录下的conf/settings.xml
文件;也可以在用户目录的.m2
子目录中创建settings.xml
文件,并设置相应配置内容。 /Users/eric/.sdkman/candidates/maven/3.9.1/conf/settings.xml
: 配置举例brew cask install intellij-idea-ce
: 安装 idea 社区版或直接去官网下载IDEA->Help->Edit Custom VW Options
,在打开的 idea.vmoptions 文件中调整-Xms 和-Xmx 等 JVM 相关参数,让 ide 流畅运行- 通过Spring Initializr (opens new window)创建工程,打开网页,填写项目信息,点击生成,下载 helloworld.zip 压缩包
- Maven 设置 sdkman 的 settings.xml 为全局公用配置:
- 在
.zshrc
中设置环境变量:一般来说 sdkman 默认安装的位置都是一样的
export SDKMAN_DIR=$HOME/.sdkman export M2_HOME=$SDKMAN_DIR/candidates/maven/current
1
2mkdir -p $M2_HOME/conf
,如果没有权限则sudo chown -R $USER $M2_HOME
cp /path/to/your/settings.xml $M2_HOME/conf/
- 这样当你切换 Maven 版本之后,也依然会使用公用的 settings
- 在
# 快速新建一个 SpringBoot 项目
- 一个web开发框架
- 在这个网站 (opens new window)选择配置,作用可以理解为前端的 vue-cli、create-react-app 等脚手架,选好配置依赖之后下载;
- idea 中打开刚才下载的项目
- 项目结构:
src/main/java/com/example/demo/DemoApplication.java
:主类,启动类src/main/java/com/example/demo/controller/HelloController.java
:控制器类src/main/resources/application.properties
:配置文件src/main/resources/static
:静态资源目录src/main/resources/templates
:模板目录
DemoApplication.java
:package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
1
2
3
4
5
6
7
8
9
10
11HelloController.java
:package com.example.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/hello") public class HelloController { @GetMapping public String sayHello() { return "Hello, World!"; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14application.properties
:server.port=8080
1- 运行项目,访问
http://localhost:8080/hello
,会看到Hello, World!
# SpringBoot 常用注解
@SpringBootApplication
:启动类注解,包含@Configuration
、@EnableAutoConfiguration
、@ComponentScan
@Configuration
:配置类注解,用于定义配置类@EnableAutoConfiguration
:自动配置类注解,用于启用自动配置@ComponentScan
:组件扫描类注解,用于指定扫描组件的包路径@RestController
:组合注解,用于定义控制器类,包含@Controller
和@ResponseBody
@RequestMapping
:请求映射注解,用于定义请求路径和请求方法@GetMapping
:组合注解,用于定义 GET 请求路径@PostMapping
:组合注解,用于定义 POST 请求路径@PutMapping
:组合注解,用于定义 PUT 请求路径@DeleteMapping
:组合注解,用于定义 DELETE 请求路径@PathVariable
:路径变量注解,用于获取路径变量值
# 依赖管理
- 在
pom.xml
中添加依赖:<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
1
2
3
4
5
6 - 在
pom.xml
中添加插件:<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
1
2
3
4
5
6
7
8 - 在
pom.xml
中添加仓库:<repositories> <repository> <id>aliyun</id> <url>https://maven.aliyun.com/repository/public</url> </repository> </repositories>
1
2
3
4
5
6 - 在
pom.xml
中添加属性:<properties> <java.version>11</java.version> </properties>
1
2
3
# SpringBoot 配置文件
application.properties
:用于配置 SpringBoot 应用程序的属性application.yml
:用于配置 SpringBoot 应用程序的属性,使用 YAML 格式- 配置文件的优先级:
application.properties
>application.yml
>application-{profile}.properties
>application-{profile}.yml
- application.yml 示例:
spring:
profiles:
# 默认激活 dev 环境
active: dev
# jackson:
# # 设置后台返参,若字段值为 null, 则不返回
# default-property-inclusion: non_null
# # 设置日期字段格式
# date-format: yyyy-MM-dd HH:mm:ss
datasource: # 配置数据库连接
# p6spy组件的数据库驱动
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/weblog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
# JDBC的数据库驱动
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://127.0.0.1:3306/weblog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
username: root
password: xxx
hikari:
minimum-idle: 5
maximum-pool-size: 20
auto-commit: true
idle-timeout: 30000
pool-name: Weblog-HikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
security:
# 自定义登录用户名、密码
user:
name: admin # 登录用户名
password: xxx # 登录密码
jwt:
# 过期时间
expiration: 86400
# 签名算法
algorithm: HS512
# 签发人 瞎写的
issuer: ericyang
# 秘钥
secret: xxx
logging:
level:
root: info
com.example.demo: debug # trace级别更低,比debug输出的信息更多?
pattern:
console: '%p%m%n'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# SpringBoot 自动配置
- SpringBoot 会根据类路径中的依赖自动配置应用程序
- 自动配置类位于
spring-boot-autoconfigure
模块中 - 自动配置类使用
@Conditional
注解来决定是否应用配置
# SpringBoot 测试
- 在
pom.xml
中添加测试依赖:<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
1
2
3
4
5
6
7 - 使用
@SpringBootTest
注解来启动测试应用程序 - 使用
@Autowired
注解来注入测试所需的依赖 - 使用
@Test
注解来定义测试方法
# SpringBoot 部署
- 使用
mvn clean package
命令打包 SpringBoot 应用程序 - 构建:
mvn install
- 构建跳过测试类
mvn install -Dmaven.test.skip=true
- 生成的 JAR 文件位于
target
目录下 - 使用
java -jar your-application.jar
命令启动应用程序 - 使用
nohup java -jar your-application.jar &
命令在后台启动应用程序 - 使用
systemd
或init.d
脚本来管理应用程序的启动和停止
# SpringBoot 安全
- 使用
spring-boot-starter-security
依赖来添加安全功能 - 配置安全规则,例如使用
@EnableWebSecurity
注解来启用安全配置 - 使用
@Override
方法来重写安全配置,例如配置登录页面、权限等 - 使用
@PreAuthorize
、@PostAuthorize
、@Secured
等注解来控制方法的访问权限
# SpringBoot 日志
- 使用
spring-boot-starter-logging
依赖来添加日志功能 - 配置日志级别,例如使用
logging.level.root=INFO
来设置根日志级别 - 配置日志文件,例如使用
logging.file.name=app.log
来设置日志文件名 - 使用
@Slf4j
注解来简化日志记录
# SpringBoot 配置中心
- 使用
spring-cloud-config
依赖来添加配置中心功能 - 配置配置中心的地址,例如使用
spring.cloud.config.uri=http://localhost:8888
来设置配置中心地址 - 使用
@Value
注解来注入配置中心的配置 - 使用
@ConfigurationProperties
注解来注入配置中心的配置到配置类中
# SpringBoot 微服务
- 使用
spring-cloud-starter-netflix-eureka-client
依赖来添加 Eureka 客户端功能 - 配置 Eureka 客户端的地址,例如使用
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
来设置 Eureka 客户端地址 - 使用
@EnableEurekaClient
注解来启用 Eureka 客户端 - 使用
@EnableDiscoveryClient
注解来启用服务发现功能
# SpringBoot 任务调度
- 使用
spring-boot-starter-quartz
依赖来添加 Quartz 任务调度功能 - 配置 Quartz
- 任务调度类使用
@Component
注解来定义 - 任务调度方法使用
@Scheduled
注解来定义
# SpringBoot 数据库
- 使用
spring-boot-starter-data-jpa
依赖来添加 JPA 数据库访问功能 - 配置数据库连接信息,例如使用
spring.datasource.url=jdbc:mysql://localhost:3306/your_database
来设置数据库连接信息 - 使用
@Entity
注解来定义实体类 - 使用
@Repository
注解来定义数据访问层
# SpringBoot 消息队列
- 使用
spring-boot-starter-activemq
依赖来添加 ActiveMQ 消息队列功能 - 配置 ActiveMQ 连接信息,例如使用
spring.activemq.broker-url=tcp://localhost:61616
来设置 ActiveMQ 连接信息 - 使用
@JmsListener
注解来监听消息队列中的消息 - 使用
JmsTemplate
来发送消息
# SpringBoot 文件上传
- 使用
spring-boot-starter-web
依赖来添加 Web 功能 - 配置文件上传大小限制,例如使用
spring.servlet.multipart.max-file-size=10MB
来设置文件上传大小限制 - 使用
MultipartFile
来接收文件上传 - 使用
File
来保存上传的文件
# SpringBoot 文件下载
- 使用
spring-boot-starter-web
依赖来添加 Web 功能 - 使用
ResponseEntity
来设置响应头和响应体 - 使用
InputStreamResource
来设置响应体 - 使用
HttpServletResponse
来设置响应头和响应体
# SpringBoot 文件压缩
- 使用
spring-boot-starter-web
依赖来添加 Web 功能 - 使用
ZipOutputStream
来压缩文件 - 使用
ZipInputStream
来解压缩文件
# SpringBoot 文件解压缩
- 使用
spring-boot-starter-web
依赖来添加 Web 功能 - 使用
ZipOutputStream
来压缩文件 - 使用
ZipInputStream
来解压缩文件
# SpringBoot 文件加密
- 使用
spring-boot-starter-web
依赖来添加 Web 功能 - 使用
Cipher
类来加密和解密文件