文章目录
前言
本篇主要对最近在学的javaweb做个总结,从一个案例入手,笔记中内容均主要针对个人所想,但如果恰巧你也有所收获,我也感到非常荣幸,与君共勉。
一、前期准备
1.创建springboot工程
在创建工程的时候需要引入对应的起步依赖,包括
- lombok:一个实用的java类库,在项目开发中可以使用lombok提供的注解进行重复部分代码的快速生成,包括get,set方法,toString方法,equals方法,无参和全参构造方法。(如果需要在pom文件中引入lombok的依赖外还需要在idea安装lombok插件才能正常使用lombok注解的对应功能)
- springweb:可以快速的构建起一个web项目,其中包含了很多组件,包括springmvc、内嵌的tomcat服务器和一些其他的web相关依赖。
- Mybatis:持久层框架,可以简化JDBC的开发,通过使用Mybatis框架提供的注解可以很方便的实现java和数据库之间的连接和操作。
- MySQL驱动
2.引入Mybatis配置信息
#数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/anLi1
spring.datasource.username=root
spring.datasource.password=123456
#开启mybatis的日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启数据库表字段 到 实体类属性的驼峰映射(主要使用在表中字段和实体属性的映射:dept_id -> deptId)
mybatis.configuration.map-underscore-to-camel-case=true
3.建立三层结构和实体类
三层结构(Controller、Service、Dao)的位置放置在和启动类所在包下,启动类标有@SpringBootApplication注解,这个注解中集成了@ComponentScan注解,正是因为这个注解的存在,Springboot会自动的扫描启动类所在包及其子包中的类,并根据注解自动的生成Bean对象放入到IOC容器中进行管理。
在Mybatis框架中,也会将Dao层称为Mapper层。按照规范,包名一般都会小写(避免和类名或者接口名冲突,因为这些都是遵循驼峰命名法的)。实体类主要是与数据库中的表形成对应,例如我需要查询某个部门的信息,那么在程序中应该有一个实体类可以接收部门表中的记录,因为表中的每一条记录都可以看作是一个部门实体,其中存储着这个部门的相关信息。
实体类中的属性一般就和对应表中的字段保持一致。
实体类
dept表中字段:
对应实体类Dept:
//Lombok提供的注解
@Data //get,set,toString,equals,hashcode
@AllArgsConstructor //全参构造
@NoArgsConstructor //无参构造
public class Dept {
//这个类中的属性应该同
private Integer id;
private String name;
private LocalDateTime createTime; //根据驼峰命名法实现映射,表中create_time字段->实体类中createTime属性
private LocalDateTime updateTime;
}
在书写实体类的过程中,需要注意到外键的存在,外键的数据类型应该和其对应字段的数据类型保持一致。
案例最终结构如下图所示:
二、功能实现过程中的一些小点子
- 如果在Mapper接口的方法中传入了实体类对象,Mybatis会自动的将实体对象中的属性映射到SQL语句的参数占位符中(猜测应该也是通过反射实现的,先欠一下,后续学完javaweb后翻看Mybatis源码再补上)。
- @RequestMapping注解
可以在Controller类上指明公共的请求路径
@RestController
@RequestMapper("/depts")
public class DeptController{
@PostMapper("/...")
public Result add(){
...
}
}
@RequsetMapping注解可以看作是@PostMapping,@GetMappingh等注解的父注解,可以处理所有类型的HTTP请求(通过Action参数指定访问的URL,通过method指定访问方法)。将@RequsetMapping标注在类上,此时的完整路径请求 = 类上@RequestMapping的value属性 + 方法上的 @RequestMapping的value属性。
- @RequestParam注解
在这个案例的功能实现中,学习了@RequestParam注解的新的用途,可以用来给参数设定默认值
public void test(@RequestParam(defaultValue = "1")Integer num){} //nun参数默认值为1
- 带条件的分页查询
个人在这个功能中犯了很多的新手错误:
1.报异常:class java.util.ArrayList cannot be cast to class com.github.pagehelper.Page。
在网上查过资料后,原因在于pagehelper的版本太低,我所使用的是jdk21,所学资料中导入的pagehelper版本为1.4.2,在更新为1.4.7后不再报异常。
2.在编写动态SQL语句的时候,在含有参数占位符的情况下出了错误。
<if ...> name like concat('%',#{name},'%') </if> //此为正确写法
//错误写法
<if ...> name like concat('%','#{name}','%') </if> //此时#{name}会被是被为字符串,而不是?切记切记
3.在controller类方法中声明时间相关的参数时,一定要使用注解 @DateTimeFormat(pattern = “…”) 来指定时间参数格式
public void test(@DateTimeFormat(pattern = '...') LocalDate date){
...
}
- 关于批量操作
在使用批量操作的时候,会从前端传回包涵了多个参数的数组,可以考虑使用foreach标签来书写sql语句,简单回顾一下foreach标签的使用:
select * from emp
//foreach标签一般和in关键字配合使用
where id in
//ids:(15,16,17)
<foreach collection="ids" item="id" separator="," open="(" close=")">
//collection:数组或者集合名称,在本次案例中就是传入的集合参数
//item:数据或者集合中遍历得到元素的别名
//separator:数组或者集合元素之间的分隔符
//open,close:开始遍历和结束遍历的符号标志(不包含)
#{id}
</foreach>
三、文件上传
文件上传的使用具有很多场景,在进行文件上传的时候会涉及到前后端两个部分:
1.前端
//三点要求:1.传输方法必须使用post 2.编码方式使用multipart/form-data 3.标签类型为file
<form action="/upload" method="post" enctype="multipart/form-data">
姓名: <input type="text" name="username"><br>
年龄: <input type="text" name="age"><br>
头像: <input type="file" name="image"><br>
<input type="submit" value="提交">
</form>
2.后端
首先简单记录一下后端对于文件传输的处理
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String username, Integer age, MultipartFile image) {
log.info("文件上传:{},{},{}",username,age,image);
return Result.success();
}
}
我们在log处打下断点,当前端提交数据后,后端实际会再C盘user文件夹下的temp文件中创建前端提交数据的临时文件,这些临时文件中就存储着前端所提交的信息。但是这些临时文件在程序结束运行以后就会被自动的删除,所以要做的就是将这个临时文件在程序运行过程中拷贝出来放到某个为止,这样就实现了对前端传递数据的存储,也就是所谓的文件上传。
1.本次存储
将前端提交的文件数据存储到本地磁盘中,主要分为两个步骤:
1.在本地创建用于存储上传文件的目录
2.使用MultipartFile类提供的API方法,把临时文件转移到本地磁盘中
MultipartFile类常用方法:
- String getOriginalFilename(); //获取原始文件名
- void transferTo(File dest); //将接收的文件转存到磁盘文件中
- long getSize(); //获取文件的大小,单位:字节
- byte[] getBytes(); //获取文件内容的字节数组
- InputStream getInputStream(); //获取接收到的文件内容的输入流
这是一个简单的例子:
public void test(MultipartFile file){
String orgName = file.getOriginalFileName(); //获取文件的原始名字
String extName = orgName.substring(orgName.lastIndexof(".")); //获取文件的后缀名
String newFileName = UUID.randomUUID().toString() + extname; //使用UUID可以得到唯一的随机文件名,然后与文件的后缀名拼接得到唯一文件名
file.transferTo(new File("本地存储目录"+newFileName)); //将临时文件转存到本地磁盘中,同时进行命名
}
tips: SpringBoot中,文件上传时默认单个文件大小是1M,如果希望上传大文件,可以在application.properties配置文件中进行配置
#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB
2.使用云服务
四、配置文件
在spring框架中,默认的配置文件格式为properties,并且在构建spring工程时会自动的在 src/resources/ 目录下创建一个名称为application.properties的配置文件,并将其作为全局配置文件进行使用。
1.properties文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/anLi1
propertie配置文件中存储的文件配置信息是以键值对的形式进行存储的,中间使用 “=” 来进行连接。上方示例中其实都是在对数据源进行配置,但是properties文件中在存储配置信息时的结构并不清晰。
2.yml文件
个人在学习以后更加偏向于yml文件。
yml文件的一些基本语法:
- 大小写敏感
- 键值之间使用":“或者”-"来进行连接(依据yml文件数据格式确定)
- 数值前边必须有空格,作为分隔符(即在连接符":"和值之间需要插入一个空格作为分隔,否则无法正确识别)
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
#
表示注释,从这个字符一直到行尾,都会被解析器忽略
spring:
datasource:
driver-class-name : com.mysql.cj.jdbc.Driver
url : jdbc:mysql://localhost:3306/anLi1
在上述示例中,配置效果同properties文件,但是在yml文件中通过空格的缩进,可以清晰的表现出该文件配置项的结构。
另外是yml文件中常见的数据格式,这里记录两种最为常见的:
//对象、Map集合
user:
name: zhangsan
age: 18
password: 123456
//数组、List集合、Set集合
hobby:
- java //java前也有空格作为分隔符
- game
- sport
3.@ConfigurationProperties注解
在配置文件中设置好文件配置信息后,我们会在程序中对配置信息通过使用软编码的方法对配置信息进行引用,例如使用Value进行值注入:
public class Util{
@Value("${配置文件中的key值}")
public String endpoint;
@Value("${配置文件中的key值}")
public String accesskey;
}
//通过参数占位符的方式将配置文件的配置信息注入到当前工具类的属性当中
但是如果Properties文件中有很多的配置信息,则此时如果仍旧使用@Value注解逐个属性进行注入,配置工作会比较繁琐。而使用spring框架提供的@ConfigurationProperties注解就会简单很多。
- 创建所需对应的实现类,要求实现类中的属性名称和配置文件中的key值必须保持一致,且实现类需要提供对应的get、set方法
- 将实体类交给IOC容器进行管理,称为IOC容器中的bean对象
- 使用@ConfigurationProperties注解对实体类进行标注,并通过prefix属性来指定属性对应配置参数的前缀
@ConfigurationProperties(prefix="spring.datasource")
public class user{
String driver-class-name;
String url;
}
如果注入的属性非常多,并且希望做到复用,就可以定义一个bean对象。通过 @configurationproperties 批量的将外部的属性配置直接注入到 bean对象的属性当中。在其他的类当中,直接注入bean 对象,然后调用 get 方法,就可以获取到对应的属性值了
实体类示例:
/*阿里云OSS相关配置*/
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
//区域
private String endpoint;
//身份ID
private String accessKeyId ;
//身份密钥
private String accessKeySecret ;
//存储空间
private String bucketName;
}
使用bean对象实例:
@Component //当前类对象由Spring创建和管理
public class AliOSSUtils {
//注入配置参数实体类对象
@Autowired
private AliOSSProperties aliOSSProperties;
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile multipartFile) throws IOException {
// 获取上传的文件的输入流
InputStream inputStream = multipartFile.getInputStream();
// 避免文件覆盖
String originalFilename = multipartFile.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),
aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());
ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);
//文件访问路径
String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
其实还是将实现类的实例托付由IOC容器进行管理,然后再通过注入bean对象,调用对应的get来获取到配置文件中的配置信息来进行使用,只是使用@ConfigurationProperties注解可以简化实体类中属性值的注入操作。
总结
本文主要供个人学习使用,但如果恰好对你也有所帮助,小子深感荣幸,共勉。