软件开发的流程
角色分工
软件环境
开发环境的搭建
数据库环境
maven环境
1.创建完成后,需要检查一下编码、maven仓库、jdk等
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
</parent>
//注意:3.0版本以下使用的java版本是11,
2.导入pom
<dependencies>
<!--sb的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--sb的测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--springboot的web使用依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<!--mybatisplus的起步依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--json数据包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--数据库的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.33</version>
</dependency>
<!--数据源的依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<!--阿里云的短信服务-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
1.Filter-过滤器
作用:用于登录校验,未登录禁止访问
@WebFilter(filterName = "EmployeeLoginFilter",urlPatterns = "/*"),申明过滤器,名称自取,拦截路径为所有路径
想要使用过滤器,需要在启动类添加 @ServletComponentScan 注解
实现逻辑
- 创建一个类,实现Filter
- 重写doFilter方法
- 定义放行的路径数组
String[] url=new String[]
{ "/employee/login","/employee/logout","/common/**","/user/sendMsg","/user/login","/backend/**","/front/**"
};
//"/employee/login","/employee/logout"这是controller中的路径
//"/backend/**","/common/**",静态资源路径,即前端页面代码,路径从resources第一个包开始
4.获取前端请求路径,与以上的放行路径进行对比
HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse= (HttpServletResponse) servletResponse;
String requestURI = httpServletRequest.getRequestURI();//获取前端请求路径
5.遍历对比信息
public static final AntPathMatcher antPathMatcher=new AntPathMatcher();//路径对比器
public boolean pathmatch(String[] url,String requesturl)//自定义对比方法
{
for (String path:url
) {
boolean match = antPathMatcher.match(path, requesturl);
if(match)
{
return true;
}
}
return false;
}
6.调用自定义的方法,boolean pathmatch = this.pathmatch(url, requestURI);如果为true,则匹配成功,放行, filterChain.doFilter(httpServletRequest,httpServletResponse);这两个参数为doFilter的参数
7.如果没有进行前后端分离的话,在跳转到登录命令的controller后,需要设置一个session属性,用于判断是否登录
//Longincontroller,方法中需要自定义一个HttpServletRequest对象
request.getSession().setAttribute("employee",one.getId());
8.判断session是否存在,不存在则表示未登录
Object employee = httpServletRequest.getSession().getAttribute(“employee”);
2.MybatisPlus的分页查询-Pageconfig
作用:自定义分页,无需手动构造分页内容
@Configuration
public class PageConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor()
{
MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor( new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
//通过Page<Object> page=new Page<>(起始页,每页条数)可直接使用
3.公共字段填充-MetaObjectHander
作用:数据表发生变化时,自动添加某些字段
实现逻辑
1.自定义类实现MetaObjectHandler类
2.重写insertFill方法/updateFill方法
3.添加公共参数
metaObject.setValue("updateTime", LocalDateTime.now())//第一个参数为数据表字段名称;
4.MybatisPlus
作用:mybatis的升级,不用手动编写sql
注意:使用mybatisplus,数据层与业务层有所不同
`
//数据层
@Mapper
public interface Mapper extends BaseMapper<实体类名>
//业务层接口
public interface Service extends IService<实体类名> {}
//业务层实现类
@Service
public class ServiceImpl extends ServiceImpl<Mapper,实体类名> implements Service {}`
想要使用mybatisplus自带的命令
1.构造一个条件对象
LambdaQueryWrapper<实体类> queryWrapper=new LambdaQueryWrapper<>();
查找条件
LambdaUpdateWrapper<实体类> updateWrapper=new LambdaUpdateWrapper<>();
条件更新
2.通过条件对象进行sql操作
5.数据传输对象-Dto
作用:将多个表单对象封装到一起,用于前端展示
步骤
1.创建表单类A、B
2.自定义一个类,继承A类
3.在自定义类中定义其他属性(集合/字符串/整形等)
4.集合中的参数为表单类B
@Data
public class DishDto extends Dish {
private List<DishFlavor> flavors = new ArrayList<>();
private String categoryName;
private Integer copies;
}
6.后端响应结果集
将后端的发送的数据进行封装,在前端页面展示
//结果类包含的内容
private Integer code;//用于表示后端处理成功或失败
private String msg;//用于对后端处理结果的描述
private T data;//定义一个泛型,用于将后端处理后的数据传到前端
自定义泛型类
public class R<T> {
// 类成员和方法的定义
}
泛型类的构造方法,返回值是一个泛型
public static <T>R<T>success(T obj)
{
R<T> r=new R<T>();
r.data=obj;
r.code=1;
return r;
}
public static <T>R<T>error(String msg)
{
R r=new R();
r.msg=msg;
r.code=0;
return r;
}
7.自定义异常类
作用:捕获后端异常,当后端发生异常时,能够及时通知到前端
1.自定义异常类
//根据需要,继承想要展示的异常
public class CustomException extends RuntimeException{
public CustomException(String msg)
{
super(msg); //调用父类的构造方法
}
}
2.定义异常处理类-ExceptionHandler
//@RestControllerAdvice通常与ExceptionHandler注解连用
@RestControllerAdvice(annotations = RestController.class)//=@Responsebody+@ControllerAdvice,参数为被拦截的错误来源
@Slf4j
public class ExceptionHandler {
@org.springframework.web.bind.annotation.ExceptionHandler(CustomException.class)//处理异常类
//SQLIntegrityConstraintViolationException e
public R<String> ex(SQLIntegrityConstraintViolationException e)
{
log.info("异常处理");
if(e.getMessage().contains("Duplicate entry"))//e.getmessage,就能获取到自定义错误的信息
{
System.out.println(e.getMessage());
String[] errstr= e.getMessage().split(" ");
for (String s:errstr
) {
System.out.println(s);
}
String msg=errstr[2]+"已存在";
log.info(msg);
return R.error(msg);
}
return R.error("失败了");
}
8.多线程-ThreadLocal
作用
1.跟踪请求处理过程中的数据:如将用户的标识(比如用户ID)存储在 threadLocal 中,这样在整个请求处理过程中都可以方便地获取和使用它
2.线程隔离的数据存储:一些数据在多线程环境中是线程私有的,并且不同线程之间的数据不应该相互干扰,可以使用 ThreadLocal 来实现数据的线程隔离。
//一般在校验登录的时候就为其赋值,将初始化的session赋给threadLocal,对象.方法调用即可
public class BaseContext {
private static ThreadLocal<Long> threadLocal=new ThreadLocal<>();//通过这个方法就能随时获取sessionid,不用构建HttpServletRequest参数对象
public static void setThreadLocal(Long integer)
{
threadLocal.set(integer);
}
public static Long getThreadLocal()
{
return threadLocal.get();
}
}
10.文件上传下载-文件存储与本地,数据库只存储文件名
文件上传
1.获取上传的文件后缀名称
2.生成唯一的文件名称
3.校验存储位置的文件夹是否存在
4.转存到对应的文件夹
@Value("${reggie.path}")//配置文件自定义文件夹路径
private String path;
@PostMapping("/upload")
public R<String> upload(MultipartFile file) throws IOException //上传的文件对象参数名,必须与前端的input框的name名字一致,且文件对象必须是MultipartFile
{
//上传的file文件是一个临时文件,需要转存,不然本次操作后,该文件就会销毁消失
String originalFilename = file.getOriginalFilename(); //获取文件上传时的名称
log.info(originalFilename);
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));//获取上传文件的后缀
log.info(file+"这是前端的对象信息");
String filename = UUID.randomUUID().toString()+suffix;//UUID生成一个唯一的名字
log.info(filename);
/**
* 判断配置文件 @Value("${reggie.path}")的目录是否存在,没有就创建
*/
File dir=new File(path);
log.info("dir"+dir);
if(!dir.exists())
{
log.info("创建文件");
dir.mkdirs();//创建@Value("${reggie.path}")的文件夹
}
file.transferTo(new File(path+"/"+filename)); //将临时文件转存到该路径
return R.success(filename);
}
文件下载
@GetMapping("/download")
public void download(String name, HttpServletResponse response)//name为图片存储地址
{
//下载文件需要经过两个步骤,读数据流,写数据流
try {
FileInputStream fileInputStream=new FileInputStream(new File(path+"/"+name));//输入流,将文件地址写入
ServletOutputStream outputStream = response.getOutputStream();//输出流,因为这里需要将数据流显示到前端,所以用HttpServletResponse
response.setContentType("image/jpeg");//设置传输到前端的数据格式
int len=0;
byte[] bytes=new byte[1024];
while((len=fileInputStream.read(bytes))!=-1)//len是每次读取的字节数,bytes是存储读出字节的内容,fileInputStream.read是读取输入流,当读取完最后一个数时,len=-1
{
outputStream.write(bytes,0,len);//输出流,outputStream = response.getOutputStream(),写入bytes数组中的内容,从0开始写入,长度是len
outputStream.flush();//刷新数据流
}
outputStream.close();//关闭输出流
fileInputStream.close();//关闭输入流
} catch (Exception e) {
throw new RuntimeException(e);
}
}