spring
Spring
1 | <dependency> |
1 | jdbc.driver=com.mysql.cj.jdbc.Driver |
bean
bean基础配置
bean基础配置
**名称:**bean
**类型:**标签
**所属:**beans标签
**功能:**定义
格式:
1
2
3
4<beans>
<bean/>
<bean></bean>
</beans>属性列表:
- **id:**bean的id,使用容易可以通过id值获取对应的bean,在一个容器中id值唯一
- **class:**bean的类型,即配置的bean的全部路径类名
范例:
1
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
配置两者关系:
1
2
3
4
5
6
7
8public BookDaoImpl{
...
}
public BookServiceImpl{
BookDao book_dao;
...
}
此时BookServiceImpl包含BookDao1
2
3
4
5<bean id="bookDao_id" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookServie" class="com.itheima.service.impl.BookServiceImpl">
<property name="book_Dao" ref="bookDao_id">
</bean>
bean别名配置
1 | <bean id="bookDao_id" name="other_name other_name2..." class="com.itheima.dao.impl.BookDaoImpl"/> |
name后面起别名,如需多个可以用空格,逗号,分号
分割
bean作用范围配置
1 | <bean id="bookServie" class="com.itheima.service.impl.BookServiceImpl" scope=""/> |
scope可选范围:
- singleton:单例(默认)
- prototype:非单例
bean作用范围说明
适合交给容器进行管理的bean
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
不适合交给容器进行管理的bean
- 封装实体的域对象
bean实例化
构造方法
- bean本质是就是对象,创建bean使用构造方法完成
如果无参构造方法不存在则抛出异常
静态工厂
1 | public class OrderDaoFactory{ |
1 | <bean id="orderDao" class="路径" factory-method="所需要调用的该工厂中的方法名"/> |
实例工厂与FactoryBean
方法一(了解)
1 | public class UserDaoFactory{ |
1 | <bean id="userFactory" class="路径"/> |
方法二
先创建一个类去接入FactoryBean
1 | public class UserDaoFactoryBean implements FactoryBean<对象类型>{ |
1 | <bean id="userDao" class="路径"/> |
bean的生命周期
生命周期:从从创建到销毁的整体过程
初始化容器
创建对象(内存分配)
执行构造方法
执行属性注入(set操作)
执行bean初始化方法
1
<bean id="userFactory" class="路径" init-method="初始化执行方法名称" destroy-method="销毁方法名称"/>
使用bean
- 执行业务操作
关闭/销毁容器
- 执行bean销毁方法:设置”钩子”或者关闭容器
bean销毁时机
容器关闭前触发bean的销毁
容器关闭方式:
手工关闭容器
1
ConfigurableApplicationContext接口close()操作
注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
1
2ConfigurableApplicationContext接口registerShutDownHook()操作
该操作可以写在任何地方
依赖注入
setter注入(推荐)
1 | <bean id="userDao" class="org.example.dao.impl.UserDaoImpl"/> |
构造器注入(耦合度较高)
1 | <bean id="userDao" class="org.example.dao.impl.UserDaoImpl"/> |
解决参数类型重复问题,使用位置解决参数匹配
1 | <bean id="userDao" class="org.example.dao.impl.UserDaoImpl"/> |
依赖注入方式选择
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 可选依赖使用setter注入进行,灵活性强
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化(严谨)
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 自己开发的模块推荐使用setter注入
自动装配
1 | <bean id="userDao" class="org.example.dao.impl.UserDaoImpl"/> |
autowire可选:byType或者byName
不推荐使用byname,耦合度高
集合注入
1 | <bean id="userDao" class="org.example.dao.impl.UserDaoImpl"> |
加载properties文件
- 开启context命名空间
1 |
|
- 使用context命名空间,加载指定properties文件
1 | <context:property-placeholder location="名字.properties"/> |
- 使用${}读取加载的属性值
1 | jdbc.name=value |
1 | <property name="username" value="${jdbc.name}"/> |
注解开发
注解开发定义bean
扫描组件
方案一
1 | 写在xml里 |
方案二
配置结构转换为**@Configuration**
配置文件中的扫描换成**@ComponentScan(“路径”)**
如果要添加多个路径需要数组形式,即:
@ComponentScan(“{路径1, 路径1…}”)
1 | 写配置类代替配置文件 |
此时加载配置文件(换接口),其他都一样
1 | ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.calss); |
定义bean
- spring提供@Component注解的三个衍生注解(用法一样,只是为了便于区分)
- @Controller:用于表现层bean定义
- @Service:用于业务层bean定义
- @Respository:用于数据层定义
1 |
|
bean的作用范围
@Scope用来说明是否为单例
1 |
|
生命周期
@PostConstruct构造方法后
@Destroy销毁前
1 |
|
自动装配
非简单类型
@Autowired使用反射中的暴力反射,setter可以不用写,可以写在任意位置
1 |
|
如果有相同bean,支持按名称注入,但是不推荐一般使用:
**@Qualifier(名称)**指定加载
1 |
|
简单类型
@Value可以进行简单类型的注入
1 |
|
**@PropertySource(“文件名”)**写入配置类可以实现加载文件
1 | jdbc.name=value |
1 |
|
1 |
|
如果要传入多个文件用数组形式:
@PropertySource(“{文件1, 文件2, …}”)
此处不允许使用*****号
第三方bean管理
没有配置只能编程写
方法一
@Bean把方法返回值作为bean
1 |
|
方法二
**@Import(名称)**导入配置
如果有多个需要用数组来进行导入
@Import(“{名称1, 名称2, …}”)
1 | 不需要 |
1 |
|
或者可以使用扫描配置**@ComponentScan进行,但是不推荐**
第三方bean依赖注入
引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象
AOP
简介
- AOP面向切面编程,一种编程范式,知道开发者如何组织程序结构
- OOP面向对象编程
- 作用:在不惊动原始设计基础上为其进行功能增强
- spring理念:无侵入式
核心理念
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等
- 在SpringAOP中,理解为方法的执行
- 切入点(PointCut):匹配连接点的式子
- 在Spring中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
- 一个具体方法:com.test.dao包下的BookDao接口中的无形参无返回值的save方法
- 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
- 在Spring中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
- 通知(Advice):在切入点处执行的操作,也就是共性功能
- 在SpringAOP中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面(Aspect):描述通知与切入点的对应关系
示例
dao
1 | package org.example.dao.impl; |
通知类
@Pointcut(“execution(返回值类型 目标方法路径”)
1 | package org.example.app; |
配置类
1 | package org.example.config; |
AOP工作流程
工作流程
- Spring容器启动
- 读取所有切面配置中的切入点(配置的切入点(配置并使用))
- 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
jdk的代理:增强
Spring内部AOP通过代理实现
- 获取bean执行方法
- 获取bean,调用方法并执行,完成操作
- 获取的bean是代理对象时,根据代理对象的运行模式运行源石方法与增强的内容,完成操作
核心概念
- 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成工作的
- 代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
SpringAOP本质
SpringAOP本质是代理模式
AOP切入点表达式
- 切入点:要进行增强的方法
- 切入点表达式:要精心增强的方法的描述方式
语法格式
切入点表达式标准格式:动作关键字(访问修饰符 返回值 报名.类/接口名.方法名(参数) 异常名)
1
execution(public User org.example.dao.service.UserService.findById(int))
- 动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
- 访问修饰符:public,private等,可以省略
- 返回值
- 包名
- 类/接口名
- 方法名
- 参数
- 异常名:方法定义中抛出指定异常,可以省略
通配符
可以使用通配符描述切入点,快速描述(可以参考正则表达式)
*
:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现1
2*出现在参数代表必有一个参数
execution(public * org.example.*.service.UserService.find*(*))匹配org.example包下的任意包中的UserService类接口中所有find开头的带有一个参数的方法
..
:多个连续的任意符号,裸可以独立出现,常用于简化包名与参数的书写1
2..出现在参数代表参数可有可无,可以有多个参数
execution(public User com..UserService.findById(..))匹配com包下的任意包汇总的UserService类或接口中所有名称为findById的方法
+
:专用于匹配子类类型1
execution(* *..*Service+.*(..))
书写技巧
- 所有代码按照标准规范开发,否则一下技巧全部失效
- 描述切入点通常描述接口,而不实现描述类
- 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
- 返回值类型对于增删改类使用精确类型加速匹配,对于查询类使用
*
通配快速描述 - 包名书写尽量不使用
.
匹配,效率过低,常用*
做单个包描述匹配,或精确匹配 - 接口名/类名书写名称与模块相关的采用
*
匹配,例如UserService书写成*Service,绑定业务层接口名 - 方法名书写以动词进行精准匹配,名次采用
*
匹配,例如getById书写成getBy*,selectAll写成selectAll - 参数规则较为复杂,根据业务方法灵活调整
- 通常不使用异常作为匹配规则
AOP通知类型
- AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
- AOP通知共分为了五种类型
- 前置通知
- 后置通知
- 环绕通知(重点)
- 返回后通知(了解)
- 抛出异常后通知(了解)
前置通知
@Before
后置通知
@After
环绕通知(重要)
@Around
需要在方法中加入ProceedingJoinPoint 变量名,用这个变量使用**.proceed()方法进行指定位置,同时需要抛出Throwable异常**,因为原始操作不确定有无异常
1 |
|
原始方法有返回值
1 | 返回值类型必须要改成Object,用临时变量存储原始方法返回值结果并返回,如有需要可以强转 |
注意事项
- 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
- 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过对原始方法的执行
- 对原始方法的调用可以不接受返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
- 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
- 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
ProceedingJoinPoint
ProceedingJoinPoint.getSignature();获取签名信息
1 | Signature signature = ProceedingJoinPoint.getSignature(); |
返回后通知
@AfterReturning
只有原始方法不抛出异常的时候才运行该通知
抛出异常后通知
@AfterThrowing
只有原始方法抛出异常的时候才运行该通知
AOP获取通知数据
数据有三种:
- 原始操作的参数
获取切入点方法的参数:- JoinPoint:适用于前置,后置,返回后,抛出异常后通知
- ProceedingJoinPoint:适用于环绕通知
- 原始操作的返回值
获取切入点方法返回值- 返回后通知
- 环绕通知
- 原始操作的异常
获取切入点方法运行异常信息- 抛出异常后通知
- 环绕通知
获取参数
使用JoinPoint里面的**.getArgs()方法,返回值类型为Object类型数组**
可以用Arrays.toString(args)来把里面的类型都转换为字符串
1 |
|
ProceedingJoinPoint也有一样的方法
ProceedingJoinPoint里面的**.proceed()方法里面可以传一个Object数组**,即getArgs()方法获得的Object数组,如果将同类型数组放入.proceed()方法,即.proceed(args)
,那么就会更改传入原始方法中的参数
获取返回值
正常情况下接收参数直接返回
特殊:@AfterReturning
1 | 需要先定义一个Object放入参数中,表示要用这个Object接收返回值 |
如果参数中同时包含JoinPoint,那么JoinPoint必须在前
1 |
|
获取异常
正常情况下把抛出异常改为内部的try..catch…环绕即可
特殊:@AfterThrowing
1 |
|
Spring事务
Spring事务简介
- 事务作用:在数据层保障一系列的数据库操作同成功同失败
- Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
Spring事务角色
- 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
- 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法
在方法上加上**@Transactional**即可保证方法中所有事务同成功同失败,如果其中一步出现了错误则全部会回滚
@Transactional被称为事务管理员,内部事务被称为事务协调员
1 |
|
事务相关配置
配置属性
在**@Transactional**里面写属性,例如@Transactional(XXX)
属性 | 作用 | 示例 |
---|---|---|
readOnly | 设置是否为只读事务 | readOnly=true |
timeout | 设置事务超时时间 | timeout=-1(永不超时) |
rollbackFor | 设置事务回滚异常(class) | rollbackFor={NullPointException.class} |
rollbackForClassName | 设置事务回滚异常(String) | 同上格式为字符串 |
noRollbackFor | 设置事务不回滚异常(class) | noRollbackFor={NullPointException.class} |
noRollbackForClassName | 设置事务不回滚异常(String) | 同上格式为字符串 |
propagation | 设置事务传播行为 | … |
事务传播行为
事务传播行为:事务协调员对事务管理员所携带事务的处理态度
1 |
|
1 | public interface LogService { |
- REQUIREO:
- 开始T:加入T
- 无:新建T2
- REQUIRES_NEW:
- 开启T:新建T2
- 无:新建T2
- SUPPORTS:
- 开启T:加入T
- 无:无
- NOT_SUPPORTS:
- 开启T:无
- 无:无
- MANDATORY:
- 开启T:加入T
- 无:ERROR
- NEVER:
- 开启T:ERROR
- 无:无
- NESTED:
设置savePoint,一旦事务回滚,事务将回滚到savePoint处,交由客户响应提交/回滚
SpringMVC
SpringMVC概述
SpringMVC技术与Servlet技术功能等同,均属于web层开发技术,开发比Servlet更简单
- SpringMVC是一种表现层框架技术
- SpringMVC用于进行表现层功能开发
- SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
- 优点
- 使用简单,开发便捷(相比于Servlet)
- 灵活性强
导包
1 | <!-- SpringMVC --> |
1 | <!-- build --> |
入门案例
创建SpringMVC控制器类(等同于Servlet)
1
2
3
4
5
6
7
8
9
10
11
12// 使用Controller定义bean
public class UserController {
// 设置当前操作的访问路径
// 设置当前操作的返回值类型
public String save(){
System.out.println("user save ...");
return "{'key': 'value'}";
}
}初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
1
2
3
4
5
6// 这里千万不能@Import(Servlet容器)!!!
public class SpringMvcConfig {
}初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 注意这里继承了AbstractDispatcherServletInitializer
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
// 注意这里因为是Web环境,所以是AnnotationConfigWebApplicationContext
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 注册
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getServletMappings() {
// 所有请求都交由SpringMVC处理
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
总结
SpringMVC入门程序开发总结(1+N)
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
- 多次工作
- 定义处理请求的控制类
- 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
- 一次性工作
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个Web容器范围
1
2
3
4
5protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理
1
2
3protected String[] getServletMappings() {
return new String[]{"/"};
}createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方法同createRootApplicationContext()
1
2
3protected WebApplicationContext createRootApplicationContext() {
return null;
}
@Controller
名称:@Controller
类型:类注解
位置:SpringMVC控制器类定义上方
作用:设定SpringMVC的核心控制器bean
范例:
1
2
3
public class UserController {
}
@RequestMapping
名称:@RequestMapping
类型:方法注解
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法请求访问路径
范例:
1
2
3
4
public void save(){
System.out.println("user save ...");
}相关属性
- value(默认):请求访问路径
@ResponseBody
名称:@ResponseBody
类型:方法注解
位置:SpringMVC控制器类定义上方
作用:设置当前控制器方法相应内容为当前返回值,无需解析
范例:
1
2
3
4
5
6
public String save(){
System.out.println("user save ...");
return "{'key': 'value'}";
}
bean加载控制
不能让Spring控制的bean加载到SpringMVC的bean
解决方法:
- Spring加载的bean设定扫描范围为com.mvc,排除掉controller内的bean
- Spring加载的bean设定扫描范围为精准范围,例如service包,dao包等
- 不区分Spring与SpringMVC的环境,加载到同一个环境中
方法二@ComponentScan
includeFilters设定包含的过滤器
excludeFilters设定不包含的过滤器
1 |
方法三
1 | public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { |
简化
更改原来Web加载容器
1 | public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { |
请求与响应
请求映射路径
不能放相同路径
分块解决,增加请求模块前缀
1 |
|
请求方式
后台不区分GET请求和POST请求
后台直接传入参数即可
1 |
|
乱码处理
设定过滤器
1 | public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { |
请求参数
简单类型
如果发送名与接收名不匹配则接收不到,例如:
1 | localhost/user/save?otherName=test |
使用**@RequestParam(发送名)**
1 | User类 |
接收,只要User里面跟传入的key匹配即可自动装配
1 | localhost/user/save?name=haog&id=123&address.son=**&... |
1 |
|
如果要接收数组,只要传入的时候名字一样即可
1 | localhost/user/save?likes=**&likes=***&*** |
1 |
|
如果传入集合则需要加上**@RequestParam**,url传入和数组一样
1 |
|
Json数据参数传递
导包
1 | <dependency> |
json传递
SpringMvcConfig中添加**@EnableWebMvc**开启转为json为对象的功能
1 |
|
controller的请求体里加入**@RequestBody**
1 |
|
传入pojo,方法一样
1 |
|
日期类型
默认标准格式为yyyy/MM/dd
如果不是标准格式需要用**@DateTimeFormat(pattern = “yyyy-MM-dd”)**来指定格式
如果传过来的不跟指定格式相匹配则会报错,例如:
1 | 2022/9/8 -> yyyy-MM-dd |
1 |
|
类型与转换器
Converter接口
1
2
3
4public interface Converter<S, T> {
T Convert(S var1);
}- 请求参数年龄数据(String -> Interger)
- 日期格式转换(String -> Date)
@EnableWebMvc功能之一:根据类型匹配对应的类型转换器
如果发现没有转换则将此开启即可
响应
- 响应页面
- 响应数据
- 文本数据
- json数据
响应页面
直接返回页面名称即可
1 |
|
访问二级页面需要加上../页面
:
1 | http://localhost/user/save |
1 |
|
响应数据
@ResponseBody响应方式自定义
返回的时候自动转Json
1 |
|
- 类型:方法注解
- 位置:SpringMVC控制器方法定义上方
- 作用:设置当前控制器返回值作为响应体
- 范例:如上所示
REST风格
REST简介
REST,表现形式状态转换
传统风格资源描述形式
1
2http://localhost/user/getById?id=1
http://localhost/user/saveUserREST风格描述形式
1
2http://localhost/user/1
http://localhost/user
优点:
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
1
2
3
4
5
6
7
8
9
10http://localhost/users // 查询全部用户信息 GET(查询)
http://localhost/users/1 // 查询指定用户信息 GET(查询)
http://localhost/users // 添加用户信息 POST(新增/保存)
http://localhost/users // 修改用户信息 PUT(修改/更新)
http://localhost/users/1 // 删除用户信息 DELETE(删除)
注意事项:
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:
users,books,accounts...根据REST风格对资源进行访问称为RESTful
案例
用@RequestMapping中的methoh
1 |
|
传参需要设置路径变量**@PathVariable**同时需要在路径后面加上要传的参数,用{参数}
,名字要跟方法参数名一样
1 |
|
快速开发
1 |
|
可以将@ResponseBody和路径提取出来变成
1 |
|
可以把@Controller和@ResponseBody结合变成**@RestController**
method可以变成**@(请求名)Mapping(路径)**
1 |
|