JavaWeb合集08-项目开发实战

八、项目开发

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)* 每页总数

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

查询思路:

在这里插入图片描述

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

    @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层

    @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层

    @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层

    @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层

    @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层

    @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映射

    @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层

    @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层

    @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映射文件

    @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层

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

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

    @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对象方法

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层

    @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层

    @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层

    @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工具类

    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层

    @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层

    @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层

    @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-工具类

    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层

    @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层

    @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层和对应的映射文件

    @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
//将类交给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,用来验证请求)

    @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

    @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

    @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令牌,并返回给前端。

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

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,执行放行后的逻辑代码。

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

在这里插入图片描述

11.3.3 Filter 拦截路径

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

@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工具类:

@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格式。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/56929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

闪迪sd卡如何恢复删除的内容?这3种方法很实用

在数字时代&#xff0c;闪迪SD卡因其便携性、高容量和稳定性&#xff0c;成为我们日常生活中不可或缺的数据存储设备。然而&#xff0c;数据丢失的风险也如影随形&#xff0c;一个不小心的误删除、格式化或是其他意外情况&#xff0c;都可能让我们珍贵的照片、视频、文档等重要…

Vue详细入门(语法【一】)

今天滴学习目标&#xff01;&#xff01;&#xff01; Vue1. 什么是Vue.js2. Vue是怎么使用的嘞3. MVVM是什么嘞&#xff1f;4. Vue有哪些指令1. v-bind2.v-model3.v-if 和 v-else4. v-for5. v-show6. v-on 咱们先了解一下什么是Vue&#xff0c;Vue是怎么使用的嘞&#xff0c;V…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-19

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-19 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-19目录1. What makes your model a low-empathy or warmth person: Exploring theOrigins of Personality in LLMs摘要研究背景算…

小公司团队管理:8个你需要知道的要点

小公司要想管理好团队&#xff0c;关键在于明确的目标与愿景、有效的沟通机制、灵活的激励制度、培养团队协作精神、领导者以身作则。其中&#xff0c;有效的沟通机制尤为重要。建立畅通的沟通渠道&#xff0c;不仅可以提高工作效率&#xff0c;还能及时发现并解决问题&#xf…

MatLab Desired port was :31515解决方案

前言&#xff1a;使用的MatLabR2022b今天突然出现了错误&#xff0c;在程序中打不开文件。后尝试了下面的方法&#xff0c;可以解决。 解决方法一&#xff1a; 搜索栏输入&#xff1a;firewall.cpl 找到相关项&#xff0c;右键属性&#xff0c;设置为允许。 之后就可以了…

【个人同步与备份】电脑(Windows)与手机/平板(Android)之间文件同步

文章目录 1. syncthing软件下载2. syncthing的使用2.1. 添加设备2.1.1. syncthing具备设备发现功能&#xff0c;因此安装好软件&#xff0c;只需确认设备信息是否对应即可2.1.2. 如果没有发现到&#xff0c;可以通过设备ID连接2.1.3. 设置GUI身份验证用户&#xff0c;让无关设备…

FloodFill 算法(DFS)

文章目录 FloodFill 算法&#xff08;DFS&#xff09;图像渲染岛屿数量岛屿的最大面积被围绕的区域太平洋大西洋水流问题扫雷游戏衣橱整理 FloodFill 算法&#xff08;DFS&#xff09; 漫水填充(Flood Fi)算法是一种图像处理算法&#xff0c;在计算机图形学和计算机视觉中被广泛…

阿里开源Multi-(llm)Agent应用框架: Agentscope探究与学习

1. 架构图 2. 核心概念 Core Concepts 2.1 Message&#xff08;消息&#xff09; 是信息的载体&#xff08;例如指令、多模态数据和对话内容&#xff09;。在AgentScope中&#xff0c;消息是Python字典的子类&#xff0c; 具有name和content作为必要字段&#xff0c;url作为可…

[项目详解][boost搜索引擎#1] 概述 | 去标签 | 数据清洗 | scp

目录 一、前言 二、项目的相关背景 三、搜索引擎的宏观原理 四、搜索引擎技术栈和项目环境 五、正排索引 VS 倒排索引--原理 正排索引 分词 倒排索引 六、编写数据去除标签和数据清洗模块 Parser 1.数据准备 parser 编码 1.枚举文件 EnumFile 2.去标签ParseHtml(…

【CSS in Depth 2 精译_048】7.2 CSS 响应式设计中的媒体查询原则(中):页面断点(breakpoint)样式的添加

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第七章 响应式设计】&#xff08;概述&#xff09; 7.1 移动端优先设计原则&#xff08;上篇&#xff09; 7.1.1 创建移动端菜单&#xff08;下篇&#xff09;7.1.2 给视口添加 meta 标签&#xf…

「数学::快速幂」矩阵快速幂运算|快速斐波那契数列 / LeetCode 509(C++)

目录 概述 思路 算法过程 复杂度 Code 概述 LeeCode 509&#xff1a; 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff…

linux--库指令

ldd ldd 可执行文件路径 显示依赖的库的查找路径以及是否查找到了。

Request2:Post请求和Json

百度翻译拿到自己想看的数据&#xff0c;下图查看请求到数据的请求 preview提前看下 取出对应的RequestUrl &#xff0c;看出来要使用的话得用post请求 #!/usr/bin/env python # -*- coding:utf-8 -*- import requests import json if __name__ "__main__":#1.指定…

SAP物料凭证报表字段调整

业务场景&#xff1a; 报表MB51的输入和输出字段调整&#xff1a; 输入&#xff08;选择界面&#xff09; 输出界面 可以看到在这是没有布局调整的 后台路径&#xff1a; SPRO-物料管理-库存管理和实际库存-报表-定义物料凭证列表的字段选择 事务码&#xff1a;SM30-V_MMI…

FPGA学习(6)-基础语法参数化设计阻塞与非阻塞

目录 1.两种参数化不改变源文件&#xff0c;只改仿真文件的值 2.参数化设计实现模块的重用 2.1不用参数化方法 2.1.1源文件 2.1.2仿真文件 2.1.3仿真波形及实验 2.2 用参数方法 2.2.1调用之前写的led灯闪烁模块&#xff0c;在本源函数中&#xff0c;例化4次调用之前的模…

史上最烂 spring transaction 原理分析

史上最烂 spring transaction 原理分析 事务定义、事务作用、事务特性、生命周期、数据库事务三种运行模式、数据库事务控制、并发事务问题、隔离级别、数据库事务实现原理、spring 事务传播行为、spring 事务核心组件、spring boot 事务相关组件、事务嵌套原理、编程式事务与声…

51单片机的万年历【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块时钟模块按键蜂鸣器等模块构成。适用于电子万年历、数字时钟万年历等相似项目。 可实现功能: 1、LCD1602实时显示年月日星期和北京时间&#xff0c;具备闰年判断功能 2、按键可设置闹钟时间 3、按键可修改当前时…

[Halcon矩阵] 通过手眼标定矩阵计算相机旋转角度

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

06.队列介绍+实现

目录 一、队列的概念 二、队列的实现 1、头文件定义 2、功能函数实现 3、主函数测试 一、队列的概念 队列就像吃饭排队类似&#xff0c;先来先吃&#xff0c;先进先出。 队头&#xff1a;队列的头部。 队尾&#xff1a;队列的尾部。 入队&#xff1a;在队尾操作。 出队&…

STM32—BKP备份寄存器RTC实时时钟

1.BKP简介 BKP(Backup Registers)备份寄存器BKP可用于存储用户应用程序数据。当VDD&#xff08;2.0~3.6V&#xff09;电源被切断&#xff0c;他们仍然由VBAT(1.8~3.6V)维持供电。当系统在待机模式下被唤醒&#xff0c;或系统复位或电源复位时&#xff0c;他们也不会被复位TAMP…