SpringBoot 基础篇
欢迎访问我的博客 02的博客 (lulyqqqq.github.io)
Restful风格
REST开发
REST,表现形式状态切换
优点:
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
@RestController:将当前控制器类设置为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
SpringMVC 常用注解 - 云+社区 - 腾讯云 (tencent.com)
将请求参数放入请求路径中,再由注解**@PathVariable**(“对应请求参数的名称”) 从而获得相对应的值,有多个参数就多个使用
RESTful API 一种流行的 API 设计风格
Restful原则:行为操作(资源的访问形式)
- 增:post请求 @PostMapping
- 删:delete请求 @DeleteMapping
- 改:put请求 @PutMapping
- 查:get请求 @GetMapping
一般来说请求路径不要出现动词
分页,排序等操作,不需要使用斜杠传参,一般传的参数不是数据库表的字段,可以不采用斜杠
@RequestBody @RequestParam @PathVariable
- 区别
- @RequestParam 用于接收url地址传参或者表单传参
- @RequestBody用于接收json数据
- @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
- 应用
- 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody 应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
- 采用RESTful进行开发,当参数较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
springboot应用
springboot是简化Spring应用的初始搭建和开发过程 springboot简化了依赖的配置
Spring Boot Web 开发@Controller @RestController 使用教程 - fishpro - 博客园 (cnblogs.com)
将spring的配置文件和spring-mvc配置简化了
springboot的配置文件中 pom文件的继承父类的文件,父类的文件里规定了dependency坐标依赖的最好版本 自动配置
starter和parent
- starter
- springboot中常见的项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
- parent
- 所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的
- spring-boot-starter-parent各版本间存在着诸多坐标版本不同
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/>
</parent>
- 实际开发 GAV(G:groupId A:artifactId V:version)
- 使用任意坐标时,仅写GAV中的G和A,V由SpringBoot提供,除非SpringBoot为提供对应版本V
- 如果发生坐标错误,则需要手动指定Version(但要小心版本冲突)
引导类
SpringBoot的引导类是Boot工厂的执行入口,通过mian方法就可以启动项目
加载spring容器 默认扫描对应包及其子包的内容 实例bean 放入spring容器
@SpringBootApplication
public class SpringbootQuickApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootQuickApplication.class, args);
}
}
内嵌tomcat(springboot支持切换web服务器)
在pom文件里引入的spring-boot-starter-web里自动加载tomcat服务器
tomcat的本质是用java语言编写的执行过程 现在SpringBoot将tomcat的执行过程封装成一个对象放入spring容器里,需要使用web程序直接引用tomcat对象,从而在tomcat上执行,所以SpringBoot不用配置tomcat服务器也可以使用tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.6.7</version>
<scope>compile</scope>
</dependency>
变更内嵌服务器:是将现有的服务器去除,添加全新的服务器
SpringBoot可以支持切换内置服务器有三个
- tomcat(默认) apache出品,应用面广,负载若干较重的组件
- jetty 更轻量级,可扩展性更高,负载性能远不及tomcat
- undertow 负载性能勉强跑赢tomcat
配置文件
复制工程(快速新建模块)
在工作空间里复制对应工程,兵修改过程名称
删除与Idea相关的配置文件,仅保留src目录和pom文件
修改pom文件中的artifactId与新工程/模块名相同
保留备份后期使用
springboot配置文件(三种):
- application.properties (主导)使用key-value的格式
server.port=80
server:
? port: 80
不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
yml配置文件里属性
使用@Value注解读取单个属性
@Value("${country}")
private String country1;
使用@Autowired获得全部属性
@Autowired
private Environment env;
env.getProperty("user.name")
加载实体的get set方法使用lombok里注解**@Data自动加载,需要在pom文件加入lombok**的坐标
将一个类转换为bean用spring注解:@Component将其转换为bean并放入spring容器里
获得yml指定区域数据:@ConfigurationProperties(prefix=“字段名”)
springboot整合junit
测试类需要在启动类的包或子包中,可以省略启动类的设置,也就是省略calsses的设定
否则需要使用**@SpringBootTest(calsses = 启动类名)**注解来查找
SpringBoot整合junit相当于spring整合junit
spring整合junit是 使用@RunWith()+ContextConfiguration(calsses = 启动类名)
**而springboot是使用@SpringBootTest(calsses = 启动类名)**注解
@SpringBootTest(classes = Springboot03JunitApplication.class)
class Springboot03JunitApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
bookDao.save();
}
}
springboot整合mybatis
在yml配置文件里添加配置数据库的相对应信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: nian0209
创建实体类domain和dao实现操作数据库的方法 注意:实体类名应该和数据库的字段名一致
@SpringBootTest
class Springboot04MybatisApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
System.out.println(bookDao.getById(2));
}
}
至于在controller层上使用的service的接口而不是实现类
因为通过**@Autowired的对象是通过接口的方式会使用jdk的动态代理**,jdk的动态代理针对接口产生代理,动态的产生实现类的对象,在注入到spring容器里也是实现类的对象 这样使用jdk代理的方式动态的生成接口的实现类,还可以实现对实现类的增强从而做到增强类
一个接口里有多个实现类
Spring中如Service有多个实现类,它怎么知道该注入哪个ServiceImpl类?_Java知音_的博客-CSDN博客
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用
@Autowired
@Qualifier(“personDaoBean”) 存在多个实例配合使用
在service层使用@Service(名称)来将实现方法带名称的注入到spring容器里
使用@Autowired自动注入,
@Qualifier(“beanId”):beanId是指对应实现类的类名称且字母开头小写
- 方法一: Controller中注入service的时候使用**@Autowired**自动注入,
@Qualifier("beanId") 来指定注入哪一个。 - 方法二: Controller中注入service的时候使用
@Resource(type = 类名.class) 来指定注入哪一个。 - 方法三:
- 每个service的impl都可以指定名称(使用
@Service(“名称”) ) - Controller中注入service的时候使用名称来指定注入哪一个(使用
@Resource(name="名称") )
@Autowired注解的意思就是:
当Spring发现@Autowired 注解时,将自动在代码上下文中找到和其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。
@Resource 的作用相当于@Autowired 。
@Autowired和@Resource两个注解的区别:
1.@Autowired 是Spring的注解,@Resource 是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了。
2.@Autowired 默认按照byType(方法类型)方式进行bean匹配,@Resource 默认按照byName(方法名称)方式进行bean匹配。
3.@Autowired默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) 。
springboot整合MyBatis-Plus
手动导入MP坐标
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
springboot整合Druid
导入Druid对应的starter
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
在yml配置文件中配置druid数据源
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: nian0209
SSMP整合案例
基于springboot
- 案列方案分析
- 实体类开发—使用lombok快速制作实体类
- Dao开发—整合MyBatisPlus,制作数据层测试类
- Service开发—基于MyBatisPlus进行增量开发,制作业务层测试类
- Controller开发—基于Restful开发,使用PostMan进行接口测试
- Controller开发—前后端开发协议制作(前后端数据交换形式)
- 页面开发—基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
- 项目异常处理
- 按条件查询—页面功能调整,Controller修正功能,Service修正功能
准备步骤
勾选springweb 和 mysql对应坐标
修改配置文件为yml格式并设置端口并设置自动增长的属性 开启日志
mybatis-plus:
global-config:
db-config:
id-type: auto
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
数据库
分页
使用MP快速的查看分页的数据,但是需要指定spring拦截器 在使用语句前也需要传入分页对应的参数----(第几页,几条数据)
IPage page = new Page(1,3);
bookDao.selectPage(page,null);
//打印当前页内数据
System.out.println(page.getRecords());
@Configuration
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
条件查询
使用QueryWrapper则需要自己去写对应匹配查询的字段,使用LambdaQueryWrapper则直接get就行了
like方法里可以添加判断条件:不为空的时候才执行条件查询
@Test
void testBy() {
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.like(true,"name","学");
bookDao.selectList(queryWrapper);
}
@Test
void testBy2() {
String name = null;
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(name!=null,Book::getName,name);
bookDao.selectList(lambdaQueryWrapper);
}
业务层-快速开发
实现Service接口和实现类
使用MyBatisPlus提供业务层通用接口(IService)与业务层通用实现类(ServiceImpl<M,T>)
在通用类基础上做功能重载或追加
注意重载时不要覆盖原始操作,避免原始提供的功能丢失-----可以在方法名上添加@Override查看有无重名
public interface IBookService extends IService<Book> {
}
@Service
public class IBookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
}
表现层展示
接收参数:
实体数据:@RequestBody
路径变量:@PathVariable
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
//查
@GetMapping
public List<Book> getAll(){
return bookService.list();
}
//增
@PostMapping
public Boolean save(@RequestBody Book book){
return bookService.save(book);
}
//改
@PutMapping
public Boolean update(@RequestBody Book book){
return bookService.modify(book);
}
//删
@DeleteMapping("{id}")
public Boolean delete(@PathVariable int id){
return bookService.delete(id);
}
//查
@GetMapping("{id}")
public Book getById(@PathVariable int id){
return bookService.getById(id);
}
}
表现层信息一致性处理
设计表现层返回结果的模型类,用于后端和前端进行数据格式统一,也称为前后端数据协议
flag:代表查询是否成功
data:存放数据
同意表现层数据
@Data
public class R {
private boolean flag;
private Object data;
R(){
}
public R(Boolean flag){
this.flag =flag;
}
public R(Boolean flag,Object data){
this.data = data;
this.flag = flag;
}
public R(Boolean flag,String msg){
this.flag = flag;
this.msg = msg;
}
public R(String msg){
this.flag = false;
this.msg = msg;
}
}
前后端 页面
前后端协议联调
- 前后端分离结构设计在页面归属前端服务器
- 单体工程页面在resoures目录下的static目录中(建议执行clean)
发送异步请求,调用后端接口
axios.get(“/books”).then((res)=>{
? 数据连接
});
异常处理
自定义springmvc的异常处理类来处理异常 在类上添加@RestControllerAdvice定义restful风格异常处理类且返回的数据给前端也需要和
前后端数据协议一致
@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public R doException(Exception ex){
ex.printStackTrace();
return new R("服务器故障,请稍后再试!");
}
}
条件查询
分页部分的条件查询是根据v-model动态模型,在分页的基础上动态的添加查询数据,一起传到后端
getAll() {
param = "?type="+this.pagination.type;
param +="&name="+this.pagination.name;
param +="&description="+this.pagination.description;
axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize+param).then((res)=>{
this.pagination.pageSize = res.data.data.size;
this.pagination.currentPage = res.data.data.current;
this.pagination.total = res.data.data.total;
this.dataList = res.data.data.records;
});
},
service层判断条件查询有无值
@Override
public IPage<Book> getPage(int currentPage, int pageSize, Book book) {
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Strings.isNotEmpty(book.getType()),Book::getType,book.getType());
lqw.like(Strings.isNotEmpty(book.getName()),Book::getName,book.getName());
lqw.like(Strings.isNotEmpty(book.getDescription()),Book::getDescription,book.getDescription());
IPage page = new Page(currentPage,pageSize);
bookDao.selectPage(page,lqw);
return page;
}
elementUI
分页插件
分页bug:一页中只有一个数据的时候。删除这个数据后当前页不会有信息展示,例如:原来有三页,删除了一页中只有一个数据的时候,系统还是在查删除完了那页,所以不会显示数据
解决方法:一般是在后台判断当前页是不是比总页数大,为true则返回最大页数数据 -----------但这也还是有bug
较好的解决方法是直接返回第一页
根本:根据需求修改
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize,Book book){
IPage<Book> page = bookService.getPage(currentPage, pageSize,book);
if( currentPage > page.getPages()){
page = bookService.getPage((int)page.getPages(), pageSize,book);
}
return new R(true, page);
}
注意事项
在对应的实体类中,如果数据库和实体类的字段是驼峰命名MP会将其转化为下划线形式,如果需要对应有三种办法
实体类有主键的需要在实体类主键字段头添加@TableId(type = IdType.AUTO(自增长类型))
|