JavaWeb合集08-项目开发实战

devtools/2024/10/20 13:19:12/

八、项目开发

1、项目搭建

在这里插入图片描述

1.1 配置配置文件

application.properties文件

spring.application.name=mybatis_testspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/homepage
spring.datasource.username=root
spring.datasource.password=123456#打开Mybatis日志信息
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl#开启mybatis的驼峰命名自动映射开关
mybatis.configuration.map-underscore-to-camel-case=true

2、接口设计规范-Restful

Restful开发规范:REST (REpresentational State Transfer), 表述性状态转换,它是一种软件架构风格。

在这里插入图片描述

在这里插入图片描述

注意:

  1. REST是风格,是约定方式,约定不是规定,可以打破。
  2. 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如: users、books等。

3、日志小技巧

使用日志框架(Slf4j) 来写日志,直接在类上加上注解:@Slf4j 即可使用自动定义的 log对象中的方法来记录日志。

4、分页查询

分页查询思路:①要返回分页查询的数据;②要返回总记录数据;所以要执行两条sql,这时可以使用一个实体类来封装这两个返回数据,再一起返回给前端。

SQL分页查询命令:select * from user limit 起始页页码 , 每页总数;

SQL总记录命令:select count(*) from user;

起始页页码=(要查询的某页-1)* 每页总数

java">//封装数据的实体类
@Data
@NoArgsConstructor  //无参构造器
@AllArgsConstructor  //全参构造器
public class PageBean {private List total; //总记录数private List rows;// 当前页数据列表 
}

查询思路:

在这里插入图片描述

4.1 不使用插件手动编写分页查询
  1. Controller层

    java">@RestController
    @RequestMapping("/users")
    public class UserController {@Autowiredprivate UserService userService;//分页查询//@RequestParam的属性defaultValue可以来设置参数的默认值。@GetMapping("/{startNum}/{totalPage}")public Result getAllUserPage(@PathVariable Integer startNum,@PathVariable Integer totalPage){return new Result(true,"分页查询",userService.getAllUserPage(startNum,totalPage));}
    }
    
  2. Service层

    java">@Slf4j
    @Service
    public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic PageBean getAllUserPage(Integer startNum, Integer totalPage) {//1、查询总记录数Long  count =userDao.count();//2、查询每页的数据:查询起始页页码=(要查询的页码-1)*每页总数List<User> list=userDao.getAllUserPage((startNum-1)*totalPage,totalPage);//3、封装查询出来的两个数据PageBean pageBean=new PageBean(count,list);//4、返回查询出来的数据return  pageBean;}}
  3. Mapping层

    @Component
    @Mapper
    public interface UserDao {//1、查询总记录数@Select("select count(*) from tb_user")Long count();//2、查询每页的数据@Select("select * from tb_user limit #{startNum},#{totalPage}")List<User> getAllUserPage(Integer startNum, Integer totalPage);
    }
    
  4. 返回结果

    {"flag": true,"msg": "分页查询","data": {"total": 5,"rows": [{"uid": 2,"email": "333@qq.com","password": "55","nickName": "昵称",},{"uid": 54,"email": "1234@qq.com","password": "123456","nickName": "永恒之月",}]}
    }
    
4.2 使用PageHelper插件编写分页查询
  1. 在pom.xml 中下载PageHelper依赖

     <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version>
    </dependency>
    
  2. Controller层

    java">@RestController
    @RequestMapping("/users")
    public class UserController {@Autowiredprivate UserService userService;//分页查询//@RequestParam的属性defaultValue可以来设置参数的默认值。@GetMapping("/{startNum}/{totalPage}")public Result getAllUserPage(@PathVariable Integer startNum,@PathVariable Integer totalPage){return new Result(true,"分页查询",userService.getAllUserPage(startNum,totalPage));}
    }
    
  3. Service层

    java">@Slf4j
    @Service
    public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic PageBean getAllUserPage(Integer startNum, Integer totalPage) {log.info("开始页码:"+startNum+"  每页数量"+totalPage);//1、设置设置分页参数(将传递过来的分页参数作为参数)PageHelper.startPage(startNum,totalPage);//2、执行查询,将查询结果强制转换为Page类型List<User> userList= userDao.getAllUserPage();Page<User> p= (Page<User>) userList;//3、封装查询出来的两个数据(通过Page对象的两个静态方法来获取这两个结果)PageBean pageBean=new PageBean(p.getTotal(),p.getResult());//4、返回查询出来的数据return  pageBean;}}
  4. Mapping层

    @Component
    @Mapper
    public interface UserDao {//直接写上查询全部信息方法,其它交给PageHelper完成sql拼接@Select("select * from tb_user")List<User> getAllUserPage();
    }
    

5、条件分页查询

分页查询实现思路

在这里插入图片描述

  1. Controller层

    java">@RestController
    @RequestMapping("/users")
    public class UserController {@Autowiredprivate UserService userService;//@RequestParam通过该注解来设置前端传递过来的表单参数,如果参数为空可以通过defaultValue 来设置默认值;required=false是当前端没有传值过来是默认为空@GetMappingpublic Result getUserByInfo(@RequestParam(defaultValue ="1") Integer page,@RequestParam(defaultValue = "10") Integer pageSize,@RequestParam(required=false) String email,@RequestParam(required=false) String nickName,@RequestParam(required=false) String sex,@RequestParam(required = false) Integer admin){return  new Result(true,"条件分页查询",userService.getUserByInfo(page,pageSize,email,nickName,sex,admin));}
    }
  2. Service层

    java">@Slf4j
    @Service
    public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic PageBean getUserByInfo(Integer page,Integer pageSize,String email,String nickName,String sex,Integer admin) {//1、设置设置分页参数(将传递过来的分页参数作为参数)PageHelper.startPage(page,pageSize);//2、执行查询,将查询结果强制转换为Page类型List<User> userList= userDao.getUserByInfo(email,nickName,sex,admin);Page<User> p= (Page<User>) userList;//3、封装查询出来的两个数据(通过Page对象的两个静态方法来获取这两个结果)PageBean pageBean=new PageBean(p.getTotal(),p.getResult());//4、返回查询出来的数据return  pageBean;}}
    
  3. Mapper层与xml映射

    java">@Component
    @Mapper
    public interface UserDao {List<User> getUserByInfo(String email,String nickName,String sex,Integer admin);
    }
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yhzyai.dao.UserDao"><select id="getUserByInfo" resultType="com.yhzyai.pojo.User">select * from tb_user<where><if test="email != null">email like concat('%',#{email},'%')</if><if test="nickName != null">and nick_name like concat('%',#{nickName},'%')</if><if test="sex != null">and   sex=#{sex}</if><if test="admin != null">and admin=#{admin}</if></where>order by email</select></mapper>

6、批量删除数据

批量删除执行思路

在这里插入图片描述

  1. Controller层

    java">@RestController
    @RequestMapping("/users")
    public class UserController {@Autowiredprivate UserService userService;//批量删除用户@DeleteMapping("/{emails}")public Result delUsersById(@PathVariable List<String> emails){return new Result(userService.delUsersById(emails),"批量删除用户",null);}
    }
    
  2. Service层

    java">@Slf4j
    @Service
    public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;//批量删除用户@Overridepublic boolean delUsersById(List<String> emails) {int listSize=emails.size();int delRows= userDao.delUsersById(emails);log.info("集合长度:"+listSize+" ,成功删除数量:"+delRows);return listSize==delRows;}}
    
  3. Mapper层和xml映射文件

    java">@Component
    @Mapper
    public interface UserDao {int delUsersById(List<String> emails);
    }
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yhzyai.dao.UserDao"><!--  delete from tb_user where email in ("123@qq.com","234@qq.com");--><delete id="delUsersById">delete from tb_user where email in <foreach collection="emails" item="email" separator="," open="(" close=")">#{email}</foreach></delete></mapper>
  4. 前端接口请求

    在这里插入图片描述

7、新增数据

新增数据实现思路

在这里插入图片描述

  1. Controller层

    java">@RestController
    @RequestMapping("/users")
    public class UserController {@Autowiredprivate UserService userService;//新增用户@PostMappingpublic Result addUser(@RequestBody User user){return new Result(userService.addUser(user),"新增用户");}
    }
    
  2. Service层

    java">@Slf4j
    @Service
    public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;//新增用户@Overridepublic boolean addUser(User user) {return userDao.addUser(user)==1;}}
    
  3. Mapper层和xml映射文件

    java">@Component
    @Mapper
    public interface UserDao {int addUser(User user);
    }
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yhzyai.dao.UserDao"><insert id="addUser">insert into tb_user<trim prefix="(" suffix=")" suffixOverrides=","><if test="email != null ">email,</if><if test="password != null ">password,</if><if test="nickName != null ">`nick_name`,</if><if test="fase != null ">fase,</if><if test="admin != null ">admin,</if><if test="userStatus != null ">`user_status`,</if><if test="sex != null ">sex,</if><if test="birthday != null ">birthday</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="email != null ">#{email},</if><if test="password != null ">#{password},</if><if test="nickName != null ">#{nickName},</if><if test="fase != null ">#{fase},</if><if test="admin != null ">#{admin},</if><if test="userStatus != null ">#{userStatus},</if><if test="sex != null ">#{sex},</if><if test="birthday != null ">#{birthday}</if></trim></insert></mapper>

8、文件上传

文件上传,是指将本地图片、视频、音频等文件,上传到服务器,供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发微博、发微信朋友圈都用到了文件.上传功能。

前端页面三要素:①使用file类型的输入框;②必须使用POST上传方式;③enctype=multipart/form-data,表示表单的编码格式为二进制格式。

后端接收使用:MultipartFile类型,并且参数名要与前端的表单名保持一致。

在这里插入图片描述

8.1 文件存储-文件本地存储

文件存储分为:本地文件存储和云存储(阿里云OSS)

文件本地存储:就是将文件保存到服务器的本地磁盘中。

常用的MultipartFile对象方法

java">String getOriginalFilename(); //获取原始文件名void transferTo(File dest); //将接收的文件转存到磁盘文件中心long getSize(); //获取文件的大小,单位:字节byte[] getBytes(); //获取文件内容的字节数组InputStream getInputStream(); //获取接收到的文件内容的输入流

注意:在SpringBoot中,文件上传,默认单个文件允许最大大小为1M。如果需要上传大文件,可配置

#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB
  1. Controller层

    java">@RestController
    @RequestMapping("/resources")
    public class ResourceController {@Autowiredprivate ResourceService resourceService;@PostMappingpublic Result uploads(@RequestParam String tag, @RequestParam String type,@RequestParam  String email,@RequestParam  MultipartFile file) throws Exception {//将数据封装到对象中Resource resource=new Resource();resource.setTag(tag);resource.setEmail(email);resource.setType(type);return new Result(resourceService.upload(resource,file),"文件上传");
    }
    }
  2. Service层

    java">@Service
    public class ResourceServiceImpl implements ResourceService {@Autowiredprivate ResourceDao resourceDao;@Overridepublic boolean upload(Resource resource, MultipartFile file) throws Exception {//1、获取上传文件的名字(全名+后缀),获取后缀名String fileFullName=file.getOriginalFilename();int spotIndex=fileFullName.lastIndexOf('.'); //获取最有一个"."的下标位置String suffixName=fileFullName.substring(spotIndex);  //获取后缀名//2、生成UUID,作为文件名保存String uuid=UUID.randomUUID().toString();String fileName=uuid+suffixName;//3、获取到要上传到服务器的目录ApplicationHome applicationHome=new ApplicationHome(this.getClass()); //获取到项目本身的目录String pre=applicationHome.getDir().getParentFile().getParentFile()+"\\src\\main\\resources\\static\\wallpaper\\";//4、将文件保存在服务器端目录下(要抛异常)file.transferTo(new File(pre+fileName));//5、生成文件的在线访问链接,添加到对象属性中resource.setRUrl("http://locahost:8080/resources/static/wallpaper"+fileName);resource.setRid(uuid);    //添加idresource.setUpDate(LocalDateTime.now()); //添加上传时间//6、将对象传给Mapping中的方法,插入到的数据库中return resourceDao.upload(resource)==1;}
    }
    
  3. Mapping层

    java">@Mapper
    @Component
    public interface ResourceDao {@Insert("insert into tb_resource(rid, tag, type, r_url, email, up_date) values (#{rid},#{tag},#{type},#{rUrl}," +"#{email},#{upDate})")public int  upload(Resource resource);
    }
    
8.2 文件存储-阿里云OSS

阿里云对象存储OSS ( Object Storage Service),是一款海量、安全、低成本、高可靠的云存储服务。使用OSS,您可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种文件。

第三方服务-通用思路:①准备工作(账户注册等);②参照官方SDK参照官方软件开发工具包编写入门程序;③集成使用。

SDK: Software Development Kit的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包) 、 代码示例等,都可以叫做SDK。

Bucket:存储空间是用户用于存储对象(Object, 就是文件)的容器,所有的对象都必须隶属于某个存储空间。

准备工作:

①注册并实名认证阿里云账户

②小容量无需充值

③直接搜索OSS,找到对象存储OSS,并开通其服务。

④进入OSS中管理面板中,创建bucket,自需要选择地区,以及设置为公共读取设置。

⑤鼠标放到头像的地方,选择AccessKey管理,创建AccessKey,保存对应的AccessKey和AccessKeySecret

⑥参照阿里云提供的SDK文档,来编写入门程序。

⑦将OSS集成到项目中来使用,将OSS作为一个工具类来使用。

在这里插入图片描述

8.2.1 阿里云OSS-集成

集成OSS实现思路

在这里插入图片描述

下面的代码是在插入数据的同时,上传文件(携带参数上传文件),但是这样有个弊端,就是如果参数比较多时比较繁琐,需要一个一个的编写对应的形参,并且在修改数据时,又要重新时间上传文件的接口,用户可能不修改头像,只修改基本信息,这时就会导致空指针异常,比较繁琐。所以推荐将上传文件单独设计成一个接口来使用。

  1. 创建阿里云OSS工具类

    java">package com.yhzyai.util;import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;
    import java.util.UUID;//将类交给IOC容器管理(这样就用于去创建对象来调用其方法了)
    @Component
    //通过样例将OSS打造为一个工具类
    public class AliyunOSS {// Endpoint以华北2(北京)为例,其它Region请按实际情况填写。 概括->外网访问private String endpoint = "https://oss-cn-beijing.aliyuncs.com";// 填写Bucket名称,例如examplebucket。private String bucketName = "yhzy-resource";//文件上方法(返回上传后文件的访问路径)public  String upload(MultipartFile file) throws Exception {// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();//1、获取上传文件的输入流InputStream inputStream=file.getInputStream();//2、避免覆盖,生成UUID作为文件名String fileFullName=file.getOriginalFilename();String fileName= UUID.randomUUID().toString()+fileFullName.substring(fileFullName.lastIndexOf("."));// 上传到OSSOSS ossClient=new OSSClientBuilder().build(endpoint,credentialsProvider);ossClient.putObject(bucketName,fileName,inputStream);//获取文件上传后的路径:https://bucket的名字.地区的路径/文件名字String url=endpoint.split("//")[0]+"//"+bucketName+"."+endpoint.split("//")[1]+"/"+fileName;//关闭 ossClientossClient.shutdown();//返回文件访问的路径return url;}}
    
  2. Controller层

    java">@RestController
    @RequestMapping("/resources")
    public class ResourceController {@Autowiredprivate ResourceService resourceService;@PostMappingpublic Result insert(@RequestParam String tag, @RequestParam String type,@RequestParam  String email,@RequestParam  MultipartFile file) throws Exception {//将数据封装到对象中Resource resource=new Resource();resource.setTag(tag);resource.setEmail(email);resource.setType(type);return new Result(resourceService.insert(resource,file),"文件上传");
    }}
    
  3. Service层

    java">@Service
    public class ResourceServiceImpl implements ResourceService {@Autowiredprivate ResourceDao resourceDao;//注入阿里云OSS工具类@Autowiredprivate AliyunOSS aliyunOSS;@Overridepublic boolean insert(Resource resource, MultipartFile file) throws Exception {//1、通过ICO容器来调用工具类的方法,传入文件。String fileUrl= aliyunOSS.upload(file);//2、将信息添加的对象中resource.setRUrl(fileUrl);resource.setRid(fileUrl.substring(fileUrl.lastIndexOf("/")));    //添加idresource.setUpDate(LocalDateTime.now()); //添加上传时间//3、将对象传给Mapping中的方法,插入到的数据库中return resourceDao.insert(resource)==1;}
    }
  4. Mapping层

    java">@Mapper
    @Component
    public interface ResourceDao {@Insert("insert into tb_resource(rid, tag, type, r_url, email, up_date) values (#{rid},#{tag},#{type},#{rUrl}," +"#{email},#{upDate})")public int  insert(Resource resource);
    }
    

9、修改数据

实现思路:通过ID查询出对应数据显示出来,再对数据进行修改。

①前端通过点击编辑按钮,调用根据ID查询数据的接口,查询出数据,并展示。

②用户编辑基本信息,点击提交按钮,调用修改基本信息接口。

③用户点击上传文件按钮,选择文件后确定,自动调用文件上传接口,将文件上传到阿里云OSS中,并返回访问文件访问链接,就是原链接没有变化,就是将文件进行了替换。

在这里插入图片描述

  1. 阿里云OSS-工具类

    java">package com.yhzyai.util;import com.aliyun.oss.*;
    import com.aliyun.oss.common.auth.CredentialsProviderFactory;
    import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;
    import java.util.UUID;//将类交给IOC容器管理(这样就用于去创建对象来调用其方法了)
    @Component
    //通过样例将OSS打造为一个工具类
    public class AliyunOSS {// Endpoint以华北2(北京)为例,其它Region请按实际情况填写。 概括->外网访问private String endpoint = "https://oss-cn-beijing.aliyuncs.com";// 填写Bucket名称,例如examplebucket。private String bucketName = "yhzy-resource";//文件上方法(返回上传后文件的访问路径)public  String upload(MultipartFile file) throws Exception {// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();//1、获取上传文件的输入流InputStream inputStream=file.getInputStream();//2、避免覆盖,生成UUID作为文件名String fileFullName=file.getOriginalFilename();String fileName= UUID.randomUUID().toString()+fileFullName.substring(fileFullName.lastIndexOf("."));// 上传到OSSOSS ossClient=new OSSClientBuilder().build(endpoint,credentialsProvider);ossClient.putObject(bucketName,fileName,inputStream);//获取文件上传后的路径:https://bucket的名字.地区的路径/文件名字String url=endpoint.split("//")[0]+"//"+bucketName+"."+endpoint.split("//")[1]+"/"+fileName;//关闭 ossClientossClient.shutdown();//返回文件访问的路径return url;}//文件上方法(返回上传后文件的访问路径)public  String update(MultipartFile file,String filePath) throws Exception {// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();//1、获取上传文件的输入流InputStream inputStream=file.getInputStream();//2、从文件路径中截取出文件名,使其覆盖远有我文件,达到修改的效果。String fileName =filePath.substring(filePath.lastIndexOf("/")+1);// 上传到OSSOSS ossClient=new OSSClientBuilder().build(endpoint,credentialsProvider);ossClient.putObject(bucketName,fileName,inputStream);//关闭 ossClientossClient.shutdown();//返回文件访问的路径return fileName;}
    }
  2. Controller层

    java">@RestController
    @RequestMapping("/resources")
    public class ResourceController {@Autowiredprivate ResourceService resourceService;@Autowired
    private AliyunOSS aliyunOSS;//根据ID查询@GetMapping("/{rid}")
    public Result  getResourceById(@PathVariable String rid){return new Result(true,"根据Id查询",resourceService.getResourceById(rid));}//文件上传,文件修改(只要Contrller层就行)
    @PostMapping("/uploadUpdate")
    public Result uploadUpdate(MultipartFile file,String filePath) throws Exception {//直接调用工具类String fileNewPath=aliyunOSS.update(file,filePath);//返回文件访问路径return new Result(true,"单文件上传",fileNewPath);
    }//只修改基本信息@PutMappingpublic Result update(@RequestBody Resource resource){System.out.println(resource.toString());return new Result(resourceService.update(resource),"修改基本信息");}
    }
  3. Service层

    java">@Service
    public class ResourceServiceImpl implements ResourceService {@Autowiredprivate ResourceDao resourceDao;//注入阿里云OSS工具类@Autowiredprivate AliyunOSS aliyunOSS;@Overridepublic boolean upload(Resource resource, MultipartFile file) throws Exception {//1、通过ICO容器来调用工具类的方法,传入文件。String fileUrl= aliyunOSS.upload(file);//2、将信息添加的对象中resource.setRUrl(fileUrl);resource.setRid(fileUrl.substring(fileUrl.lastIndexOf("/")));    //添加idresource.setUpDate(LocalDateTime.now()); //添加上传时间//6、将对象传给Mapping中的方法,插入到的数据库中return resourceDao.upload(resource)==1;}@Overridepublic Resource getResourceById(String rid) {return resourceDao.getResourceById(rid);}@Overridepublic boolean update(Resource resource) {resource.setUpDate(LocalDateTime.now());return resourceDao.update(resource)==1;}
    }
    
  4. Mapping层和对应的映射文件

    java">@Mapper
    @Component
    public interface ResourceDao {@Insert("insert into tb_resource(rid, tag, type, r_url, email, up_date) values (#{rid},#{tag},#{type},#{rUrl},#{email},#{upDate})")int  upload(Resource resource);@Select("select * from tb_resource where rid=#{rid}")Resource getResourceById(String rid);int update(Resource resource);
    }
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yhzyai.dao.ResourceDao"><update id="update">update tb_resource<set><if test="tag != null and tag != ''">tag=#{tag},</if><if test="type != null and type != ''">type=#{type},</if><if test="rUrl != null and rUrl != ''">r_url=#{rUrl},</if><if test="email != null and email != ''">email=#{email},</if><if test="upDate != null">up_date=#{upDate}</if></set>where rid=#{rid}</update></mapper>

10、配置文件

10.1 参数配置化

就是将一些常用,切重复的配置项,定义到配置文件中,通过@value来进行注入调用。

由于之前定义的阿里云OSS工具类中有一些特殊的变量,如果多个工具类都要用到相同的配置,或是要改变时,需要找到对应的工具类来进行修改,太过繁琐,这时可以将对应的变量定义到配置文件中(application.properties),通过@Value来引用配置的变量。

@Value注解:通常用于外部配置的属性注入,具体用法为: @Value(“${配置文件中的key}”)

#application.properties
#名字可以自己定义(尽量定义的有意义)
#阿里云OSS
aliyun.oss.endpoint=https://oss-cn-beijing.aliyuncs.com
aliyun.oss.bucketName=yhzy-resource
java">//将类交给IOC容器管理(这样就用于去创建对象来调用其方法了)
@Component
//通过样例将OSS打造为一个工具类
public class AliyunOSS {// Endpoint以华北2(北京)为例,其它Region请按实际情况填写。 概括->外网访问@Value("${aliyun.oss.endpoint}")private String endpoint;// 填写Bucket名称,例如examplebucket。@Value("${aliyun.oss.bucketName}")private String bucketName;//文件上方法(返回上传后文件的访问路径)public  String upload(MultipartFile file) throws Exception {// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();//1、获取上传文件的输入流InputStream inputStream=file.getInputStream();//2、避免覆盖,生成UUID作为文件名String fileFullName=file.getOriginalFilename();String fileName= UUID.randomUUID().toString()+fileFullName.substring(fileFullName.lastIndexOf("."));// 上传到OSSOSS ossClient=new OSSClientBuilder().build(endpoint,credentialsProvider);ossClient.putObject(bucketName,fileName,inputStream);//获取文件上传后的路径:https://bucket的名字.地区的路径/文件名字String url=endpoint.split("//")[0]+"//"+bucketName+"."+endpoint.split("//")[1]+"/"+fileName;//关闭 ossClientossClient.shutdown();//返回文件访问的路径return url;}//文件上方法(返回上传后文件的访问路径)public  String update(MultipartFile file,String filePath) throws Exception {// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();//1、获取上传文件的输入流InputStream inputStream=file.getInputStream();//2、从文件路径中截取出文件名,使其覆盖远有我文件,达到修改的效果。String fileName =filePath.substring(filePath.lastIndexOf("/")+1);// 上传到OSSOSS ossClient=new OSSClientBuilder().build(endpoint,credentialsProvider);ossClient.putObject(bucketName,fileName,inputStream);//关闭 ossClientossClient.shutdown();//返回文件访问的路径return fileName;}
}
10.2 yml 配置文件

基本语法如下

  1. 大小写敏感。

  2. 数值前边必须有空格,作为分隔符。

  3. 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)。

  4. 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。

  5. #表示注释,从这个字符一直到行尾,都会被解析器忽略。

#yml配置文件两种常用格式如下:#定义对象/Map集合
user :name: Tomage: 20address: beijing#定义数组/List/Set集合
hobby:- java- C- game- sport

将以前的配置内容通过yml文件来代替:

server:port: 8080spring:#数据库配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://locahost:3306/homepageusername: rootpassword: 123456#文件上传大小配置servlet:multipart:max-file-size: 50MBmax-request-size: 100MB#Mybatis配置(日志和驼峰命令)
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true#阿里云OSS配置
aliyun:oss:endpoint: https://oss-cn-beijing.aliyuncs.combucketName: yhzy-resource
10.3 @ConfigurationProperties注解

将多个配置项注入对应的类中。

操作步骤:

①在yml配置文件中自定义配置项;

②将这些配置项属性封装为一个实体类,加上@Data注解,加上@Compnent注解使其添加到IOC容器中;

③在需要引用的地方加上@ConfigurationProperties(prefix=“引用配置项的固定路径”)注解;

④使用@Autowrite注解,引入实体类对象。

⑤通过实体类对象来获取对应的配置项。

@ConfigurationProperties与@Value的相同与不同点

  • 相同点:都是用来注入外部配置的属性的。
  • 不同点:@Value注解只能一个一个的进行外部属性的注入。@ConfigurationPropertiesi可以批量的将外部的属性配置注入到bean对象的属性中。

11、用户登录技术

11.1 会话技术

会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一-方断开连接, 会话结束。在一-次会话中可以包含多次请求和响应。

会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。

会话跟踪方案:①客户端会话跟踪技术: Cookie;②服务端会话跟踪技术: Session;③令牌技术

  1. Cookie

    通过在服务端设置Cookie,第一次通过HTTP请求在响应头的set-cookie里可找到服务端项浏览器端设置的cookie,往后每一次请求,浏览器端都会携带请求头里的Cookie值。(响应头里:set-cookie,获取cookie;请求头里:cookie,用来验证请求)

    java">@S1f4j
    @RestController
    public class SessionController {
    //设置Cookie
    @GetMapping ("/c1")
    public Result cookie1 (HttpServletResponse response) {
    response.addCookie (new Cookie( name: "login_ username",value: "11111")); // 设置Cookie/响应Cookie
    return Result. success();
    }//获取Cookie
    @GetMapping ("/c2")
    public Result cookie2 (HttpServletRequest request) {
    Cookie[] cookies = request .getCookies(); // 获取所有的Cookie
    for (Cookie cookie : cookies) {
    if (cookie.getName().equals ("login_ _username")){ //输出name为login_ username 的cookie
    System. out.println("login_ _username: "+cookie.getValue());
    }
    return Result. success() ;}}
    

    优点: HTTP协议中支持的技术
    缺点:①移动端APP无法使用ookie;②不安全,用户可以自己禁用Cookie;③Cookie不能跨域

  2. Session

    也是通过响应头和请求头的set-cookie和cookie来传输session

    java">@S1f4j
    @RestController
    public class SessionController {
    @GetMapping ("/s1")
    public Result sessionl (HttpSession session) {
    log.info ("HttpSession-s1: {}", session.hashCode());
    session.setAttribute ( name: "loginUser", value: "tom"); // 往session中存储数据
    return Result. success();
    }  //从HttpSession中获取值
    @GetMapping ("/s2")
    public Result session2 (HttpServletRequest request) {
    HttpSession session = request.getSession();
    log.info("HttpSession-s2: {}", session.hashCode());
    Object loginUser = session.getAttribute ( name: "loginUser"); // 从session中获取数据
    log.info ("loginUser: {}", loginUser) ;
    return Result. success (loginUser) ;
    }
    }
    

    优点:存储在服务端,安全
    缺点:①服务器集群环境下无法直接使用Session;②Cookie的缺点

  3. 令牌技术

优点:支持PC端、移动端;②解决集群环境下的认证问题;③减轻服务器端存储压力

缺点:需要自己实现

11.2 JWT 令牌技术

全称:JSON Web Token (https:/ /jwt.io/)

定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

Base64:是一种基于64个可打印字符(A-Z a-z0-9 +. /)来表示二进制数据的编码方式。

三个组成部分:

第一部分: Header(头) ,记录令牌类型、签名算法等。例如: {“alg”:“HS256”,“type’”:“WT”}

第二部分: Payload(有效载荷),携带一些自定义信息、 默认信息等。例如: {“id”:“1”,“username”:“Tom”}

第三部分: Signature(签名),防止Token被篡改、确保安全性。将header. payload, 并加入指定秘钥,通过指定签名算法计算而来。

在这里插入图片描述

在这里插入图片描述

11.2.1 JWT -生成与校验
  1. 引入JWT依赖

          <!--JWT令牌--><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version></dependency>
    
  2. 生成和校验JWT

    java">@SpringBootTest
    class MybatisTestApplicationTests {@Test//创建JWTvoid getJwt() {Map<String, Object> claims = new HashMap<>();claims.put("name", "小明");claims.put("sex", "男");// 生成一个安全的密钥byte[] keyBytes = generateSecureKey();// 将字节数组转换为Base64编码的字符串以便打印和存储String encodedKey = Base64.getEncoder().encodeToString(keyBytes);// 创建JWTKey key = Keys.hmacShaKeyFor(keyBytes);  //创建一个密钥对象,参数为字节数组String jwt = Jwts.builder().setClaims(claims)   // 自定义内容(载荷).signWith(key,SignatureAlgorithm.HS256)   // 设置密钥 和 设置加密算法为HS256.setExpiration(new Date(System.currentTimeMillis() + 2 * 3600 * 1000))  // 设置过期时间为2小时,时间过期会报错.compact();System.out.println("字节数组的密钥:"+Arrays.toString(keyBytes));System.out.println("Base64的密钥: " + encodedKey);System.out.println("生成的JWT: " + jwt);//调用解密的方法(传入字节数组密钥和JWt)Claims claimsParse=ParseJwt(keyBytes,jwt);System.out.println("解密结果:"+claimsParse);}//生成32位的字节数组作为密钥private byte[] generateSecureKey() {// 生成一个安全的随机字节数组作为密钥SecureRandom secureRandom = new SecureRandom();byte[] keyBytes = new byte[32]; // 256 bits / 32 bytessecureRandom.nextBytes(keyBytes);return keyBytes;}//JWT解密(校验)private  Claims ParseJwt(byte[] keyBytes ,String jwt){// 创建JWT解析器Key key = Keys.hmacShaKeyFor(keyBytes);  //创建一个密钥对象,参数为字节数组Claims claims=Jwts.parserBuilder().setSigningKey(key)   //指定签名密钥(要与生成的密钥相同).build().parseClaimsJws(jwt)  //解析令牌.getBody();return claims;}}
    
11.2.2 JWT -登录实现工具类

令牌生成:登录成功后,生成JWT令牌,并返回给前端。

令牌校验:在请求到达服务端后,对令牌进行统一拦截、校验。

java">package com.yhzyai.util;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Date;
import java.util.Map;public class JwtUtil {//密钥字节数组private static byte[] keyBytes={42, -81, 2, -42, -74, 119, -17, -74, 23, 88, 22, 94, 37, -52, 95, 87, 39, -8, 103, -68, 62, 79, -14, 24, -32, -13, -97, 106, 6, -43, 109, 32};//过期时间private static Long expire=12*3600*1000L;//生成JWTpublic static String generateJwt(Map<String,Object> claims){String encodedKey= Base64.getEncoder().encodeToString(keyBytes);//生成JWTKey key= Keys.hmacShaKeyFor(keyBytes); //创建一个密钥对象,参数为字节数组String jwt= Jwts.builder().setClaims(claims).signWith(key, SignatureAlgorithm.HS256).setExpiration(new Date(System.currentTimeMillis()+expire)).compact();return jwt;}public static Claims parseJWT(String jwt){Key key=Keys.hmacShaKeyFor(keyBytes);  //创建一个密钥对象,参数为字节数组Claims claims=Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwt).getBody();return claims;}//生成32位的字节数组作为密钥private byte[] generateSecureKey() {// 生成一个安全的随机字节数组作为密钥SecureRandom secureRandom = new SecureRandom();byte[] keyBytes = new byte[32]; // 256 bits / 32 bytessecureRandom.nextBytes(keyBytes);return keyBytes;}
}
11.3 过滤器 Filter

概念: Filter 过滤器,是JavaWeb三大组件(Servlet、Filter. Listener)之一 。

过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。

过滤器一般完成一 些通用的操作,比如:登录校验、统一编码处理、 敏感字符处理等。

11.3.1 快速入门
  1. 定义Filter: 定义- -个类,实现Filter接口,并重写其所有方法。
  2. 配置Filter: Filter类.上加@WebFilter注解,配置拦截资源的路径。引导类.上加@ServletComponentScan开启Servlet组件支持。

在这里插入图片描述

11.3.2 Filter执行流程

Filter执行流程:浏览器发起请求,Filter进行请求拦截,执行放行前的逻辑(jwt验证等);放行请求操作数据库;执行放行后的逻辑。

注意:操作完数据库后,会回到Filter,执行放行后的逻辑代码。

java">chain.doFilter (request, response); //放行代码

在这里插入图片描述

11.3.3 Filter 拦截路径

Filter可以根据需求,配置不同的拦截资源路径:

java">@WebFilter (urlPatterns ="/*"){    //*代表拦截全部路径
public class DemoFilter  implements Filter
}
拦截路径urlPatters值说明
拦截具体的路径/login只有访问/login路径时,才会被拦截
拦截目录/emps/*访问/emps下及其本身的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截
11.3.4 Filter 过滤器链

介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。

当第一个过滤器放行后,它会放行到下一个过滤器,直到最后一个过滤器放行,才放行到web资源中。

在这里插入图片描述

注意:过滤器的执行顺序与Filter的类名有关,按照类名的字母顺序进行执行过滤器。

11.3.5 Filter 登录校验

不是所有的请求都要进行拦截JWT校验,有一 个例外,就是登录请求。

拦截到请求后,有令牌, 且令牌校验通过(合法) ;否则都返回未登录错误结果

登录校验执行流程:

  1. 获取请求url。
  2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
  3. 获取请求头中的令牌( token)。
  4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
  5. 解析token,如果解析失败。返回错误结果(未登录)。
  6. 放行。

在这里插入图片描述

Filter工具类:

java">@Slf4j
//拦截所有请求
@WebFilter(urlPatterns = "/*")
public class FilterUtil implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//先将servletRequest 和 servletResponse对象强制转换为HttpServletRequest和HttpServletResponse对象(要获取链接和token)HttpServletRequest request= (HttpServletRequest) servletRequest;HttpServletResponse response= (HttpServletResponse) servletResponse;//        1. 获取请求url。String url=request.getRequestURL().toString();log.info("请求的URL为:"+url);
//        2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行。if(url.contains("/login")){filterChain.doFilter(request,response);return;   //放行后就不执行Filter的后面代码了}//        3. 获取请求头中的令牌( token)。String token=request.getHeader("token");//        4. 判断令牌是否存在,如果不存在,返回错误结果(未登录),判断token是否有长度,有true。if(!StringUtils.hasLength(token)){//没有长度,响应前端错误信息Result result=new Result(false,"Not Login");//手动的将对象信息,转换为JSON字符串返回,使用阿里巴巴的fastJSON/* <dependency> <groupId>com.alibaba</groupId><artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency>*/String errorMsg=JSONObject.toJSONString(result);//通过响应体里的输出流来将信息响应给前端response.getWriter().write(errorMsg);return;}//        5. 解析token,如果解析失败。返回错误结果(未登录)。(校验成功不报错,校验失败会报错)try {//调用JWT工具类进行jwt校验JwtUtil.parseJWT(token);} catch (Exception e) {//校验失败,返回错误信息Result result=new Result(false,"Not Login");//手动的将对象信息,转换为JSON字符串返回,使用阿里巴巴的fastJSONString errorMsg=JSONObject.toJSONString(result);//通过响应体里的输出流来将信息响应给前端response.getWriter().write(errorMsg);return;}//        6. 放行(到这里说明没有报错)。filterChain.doFilter(request,response);}
}
11.4 拦截器 Interceptor

概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。

作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

11.4.1 快速入门
  1. 定义拦截器,实现HandlerInterceptor接口, 并重写其所有方法(ctrl+O)。
  2. 注册拦截器

在这里插入图片描述

11.4.2 拦截器-拦截路径

拦截路径可以根据需求配置

在这里插入图片描述

拦截路径含义例子
/*一级路径能匹配/depts, /emps, /login, 不能匹配/depts/1
/**任意路径能匹配/depts, /depts/1, /depts/1/2
/depts/*/depts 下的一级路径能匹配/depts/1,不能匹配/depts/1/2, /depts
/depts/**/depts下的任意级路径能匹配/ depts, /depts/1, /depts/1/2, 不能匹配/emps/1

拦截器执行流程

在这里插入图片描述

Filter与Interceptor

接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。

拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只 会拦截Spring环境中的资源。

11.5 异常处理

程序开发过程中不可避免的会遇到异常现象,要如何规范的响应错误信息给前端。

当操作Mapping层发送错误时,会逐层向上抛异常,这时我们可以定义一个全局异常处理器。

在这里插入图片描述

@RestControllerAdvice = @ControllerAdvice + @ResponseBody,所有返回结果会自动转换为JSON格式。

java">//全局异常处理器(工具类)
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)  //捕获全部的异常public Result  ex(Exception exception){exception.printStackTrace();return new Result(false,"操作错误,请联系管理员");}
}

http://www.ppmy.cn/devtools/127293.html

相关文章

【报错处理】MR/Spark 使用 BulkLoad 方式传输到 HBase 发生报错: NullPointerException

博主希望能够得到大家的点赞收藏支持&#xff01;非常感谢 点赞&#xff0c;收藏是情分&#xff0c;不点是本分。祝你身体健康&#xff0c;事事顺心&#xff01; Spark 通过 BulkLoad 方式传输到 HBase&#xff0c;我发现会出现空指针异常。简单写下如何解决的。 原理&#xf…

ubuntu2404下搭建Odoo18开发环境

Odoo在windows下也可以开发&#xff0c;不过执行速度比较慢&#xff0c;经过我测试&#xff0c;重启一下服务&#xff0c;windows下需要十几秒甚至几十秒&#xff0c;而mac或者ubuntu只需要几秒钟。 另外&#xff0c;官方推荐的生产环境也是ubuntu&#xff0c;所以&#xff0c…

Olap数据处理

一、OLAP 是什么 1. OLAP的定义 OLAP&#xff08;Online Analytical Processing&#xff0c;联机分析处理&#xff09;是一种软件技术&#xff0c;它主要专注于复杂的分析操作&#xff0c;帮助分析人员、管理人员或执行人员从多角度对信息进行快速、一致、交互地存取&#xf…

SpringBoot车辆管理系统:设计与实施细节

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了车辆管理系统的开发全过程。通过分析车辆管理系统管理的不足&#xff0c;创建了一个计算机管理车辆管理系统的方案。文章介绍了车辆管理系统的系统分析部分&…

【微服务】全面构建微服务监控体系:确保系统稳定与性能优化的关键

目录 引言一、微服务监控概述1.1 微服务监控的定义1.2 微服务监控的重要性1.3 监控的核心目标1.4 微服务监控的关键指标1.5 监控的策略 二、微服务监控的架构2.1 监控架构图2.2 架构组件2.3 监控架构示意图 三、微服务监控的工具3.1 工具概述3.2 Prometheus3.3 Grafana3.4 ELK …

CEEMDAN +组合预测模型(Transformer - BiLSTM + ARIMA)

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较 全是干货 | 数据集、学习资料、建模资源分享&#xff01; EMD、EEMD、FEEMD、CEEMD、CEEMDAN的区别、原理和Python实现&#xff08;一&#xff09;EMD-CSDN博客 EMD、EEM…

LeetCode 面试题 02.07. 链表相交

题目描述 给你两个单链表的头节点 headA 和 headB&#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点&#xff0c;返回 null。 例如&#xff1a; 输入&#xff1a;headA [4,1,8,4,5], headB [5,6,1,8,4,5] 输出&#xff1a;Intersected at 8解题思…

Redis7 数据类型

Redis7 数据类型 文章目录 Redis7 数据类型1. Redis键&#xff08;Key&#xff09;2. Redis字符串&#xff08;String&#xff09;3. Redis列表&#xff08;List&#xff09;4. Redis哈希表&#xff08;Hash&#xff09;5. Redis集合&#xff08;Set&#xff09;5.1 常用操作5.…