JavaWeb一些开发问题

一、Restful

image-20240303173010252

package com.example.crudtest1.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;//响应码,1 代表成功; 0 代表失败
    private String msg;  //响应信息 描述字符串
    private Object data; //返回的数据

    //增删改 成功响应
    public static Result success(){
        return new Result(1,"success",null);
    }
    //查询 成功响应
    public static Result success(Object data){
        return new Result(1,"success",data);
    }
    //失败响应
    public static Result error(String msg){
        return new Result(0,msg,null);
    }
}

image-20240303173615490

二、日志输出

项目开发中尽量避免使用sout输出,使用日志输出

private static Logger log = LoggerFactory.getLogger(DeptController.class);

//或者使用注解
@Slf4j

三、分页查询

注意sql中limit的第一个参数即可

例如查询第二页,每页展示10条数据

selcet * from emp limit 10,10

3.1、分页查询插件PageHelper

image-20240304105200327

四、文件上传

4.1、前端

前端,必须提供form表单,提交方式为post 必须要有个input框设置为file

image-20240305100101851

enctype 属性值 如果需要文件上传,必须使用multipart/form-data

4.2、后端

后端在spring boot中使用 MultpartFile 且表单项名字前后端一致

image-20240305101123891

4.3、本地存储

前置知识

1、

这是pom.xml配置文件:

Spring Boot 默认的文件上传大小限制是 1MB。如果上传文件过大,可以自行配置:

#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大大小的限制(一次请求中是可以上传多个文件)
spring.servlet.multipart.max-request-size=10MB

2、由于上传的图片可能会出现同名问题,我们需要给一个唯一的标识,这里推荐使用UUID,拼接原始的上传文件名

Controller层

    /*
    * 文件上传
    * */
    @PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image) throws Exception {
       log.info("文件上传,{},{},{}",username,age,image);

       //获取原始文件名
        String originalFilename = image.getOriginalFilename();

        //构造唯一的文件名(不能重复)--uuid(通用唯一识别码) a758f47d-8807-4d50-af22-5b4b3208f193
        int index = originalFilename.lastIndexOf(".");
        String extname = originalFilename.substring(index);
        String newFileName = UUID.randomUUID().toString()+extname;
        log.info("新的文件名:{}", newFileName);

        //将文件存储在服务器磁盘下
        image.transferTo(new File("G:\\笔记\\Java\\project\\" +
                "springboot-mybatis-crudTest\\crudTest1\\image\\"+newFileName));
        return Result.success();
    }

4.4、阿里云OSS存储

流程

image-20240305105327660

image-20240305105356731

  • 登录阿里云->控制台->oss基础服务

image-20240305105704801

  • 点击Bucket列表->创建bucket->输入名称,创建
  • 获取密钥,点击头像AccessKey管理-创建一个账号
  • 回到oss服务,拉到最下面找到SDK下载,就可以看到官方文档

image-20240305110622701

https://help.aliyun.com/zh/oss/developer-reference/overview-21

引入依赖

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.15.1</version>
</dependency>
    
    //java9以上
    <dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.3</version>
</dependency>

引入工具类

package com.example.crudtest1.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;

/**
 * 阿里云 OSS 工具类
 */
public class AliOSSUtils {

    private String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    private String accessKeyId = "LTAI4GCH1vX6DKqJWxd6nEuW";
    private String accessKeySecret = "yBshYweHOpqDuhCArrVHwIiBKpyqSL";
    private String bucketName = "web-tlias";

    /**
     * 实现上传图片到OSS
     */
    public String upload(MultipartFile file) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}

五、配置文件

优化阿里云配置文件,利用@ConfigurationProperties(prefix = “aliyun.oss”)

image-20240306141945833

image-20240306142118929

这里的属性必须要与yml配置文件里面同名

在AliOSSUtils中利用注解注入

package com.example.crudtest1.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.UUID;

/**
 * 阿里云 OSS 工具类
 */

@Component

public class AliOSSUtils {
    @Autowired
private AliOSSProperties aliOSSProperties;

    /**
     * 实现上传图片到OSS
     */

    public String upload(MultipartFile file) throws IOException {
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();
        // 获取上传的文件的输入流
        InputStream inputStream = file.getInputStream();

        // 避免文件覆盖
        String originalFilename = file.getOriginalFilename();
        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        //上传文件到 OSS
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, inputStream);

        //文件访问路径
        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
        // 关闭ossClient
        ossClient.shutdown();
        return url;// 把上传到oss的路径返回
    }

}

5.1、yml配置文件

基本语法

  • 大小写敏感
  • 数值前边必须有空格,作为分隔符
  • 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略
server:
  port: 9000

#定义对象/map集合
user:
  name:Tom
  age:18

#定义Liset/set集合
hobby:
  -java
  -java2


spring:
  #数据库连接
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/tlias
    username:
    password:
  #文件上传
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

#mybatis
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

#阿里云OSS
aliyun:
  oss:
    endpoint : "";
    accessKeyId : "";
    accessKeySecret : "";
    bucketName : "";

六、登录校验

会话技术

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

会话跟踪

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

  • 会话跟踪方案:

    客户端会话跟踪技术:Cookie
    • 服务端会话跟踪技术:Session
    • 令牌技术

image-20240306150426478

6.1、JWT令牌

由三个部分组成,由Base64进行编码

  • header(头)
  • Payload(有效载荷)
  • Signature(签名)

image-20240307101015470


在登录成功后,生成令牌,后续每个请求都需要携带令牌,系统在处理请求时,需要先校验令牌

6.1.1、依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

工具类

package com.example.crudtest1.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;

public class JwtUtils {

    private static String signKey = "zyyy";
    //设置生成时间为12小时
    private static Long expire = 43200000L;

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

七、过滤与拦截

过滤器链,由多个过滤器组成,由类文件名A-Z执行

image-20240308220507398

7.2、拦截器

定义拦截器,实现HandlerInterceptor接口,并重写其所有方法

package com.example.crudtest1.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override //目标资源方法前运行, 返回true 放行,返回false 不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("目标资源方法前运行");
//        return HandlerInterceptor.super.preHandle(request, response, handler);
        return  true;
    }

    @Override //目标资源方法后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        System.out.println("目标资源方法后运行");
    }

    @Override //视图渲染完后执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        System.out.println("视图渲染完后执行");
    }
}

注册拦截器

package com.example.crudtest1.config;

import com.example.crudtest1.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration //配置类
public class WebConfig  implements WebMvcConfigurer {

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //要拦截哪些资源 addPathPatterns 
//excludePathPatterns 放行
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");;
    }
}

image-20240311094631631

执行流程

image-20240311094924883

image-20240311095035214

八、异常处理

全局异常处理器 RestControllerAdvice

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)//捕获全部异常
    public Result ex(Exception e){
        e.printStackTrace();
        return Result.error("操作失败,请联系管理员");
    }
}

九、事务管理

事务:一组操作集合,不可分割,要么同时成功,要么失败

image-20240311100858289

注解@Transactional

注意 默认情况下只会出现RuntimeException才回滚异常

可以设置rollbackFor 来指定出现何种异常回滚

#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

9.1、传播行为

事务传播行为:指的是一个事务方法被另一个事务调用

image-20240311114041102

10、AOP

AOP:面向切面编程,就是面向特定的方法编程

通过底层的动态代理机制,对特定的方法进行编程

  • 引入依赖
<dependency>
            <groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

获取每个方法的运行时长

package com.example.crudtest1.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@Aspect
public class TimeAspect {
    @Around("execution(* com.example.crudtest1.service..*.*(..))")
    public Object  recordTime(ProceedingJoinPoint joinPoint ) throws Throwable {
        //1.记录开始时间
        long begin = System.currentTimeMillis();

        //调用原始方法运行
        Object result = joinPoint.proceed();

        //记录结束时间,计算方法执行耗时
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature()+ "方法耗时,{}",end-begin);


        return  result;
    }
}

10.1、核心概念

  • 连接点:JoinPoint,可以被AOP控制的方法

image-20240312102046747

  • 通知:Advice,指重复的逻辑,也就是共性功能

image-20240312102055529

  • 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用

image-20240312102232612

这里表现的是只有list这一个功能点运行时,才会被通知

  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)

image-20240312102418987

  • 目标对象:Target,通知所应用的对象

执行流程

image-20240312104429791

10.2、通知类 image-20240312104719894

@Pointcut 切入点,可以将重复的片段抽取起来统一使用

    @Pointcut("execution(* com.example.crudtest1.service.impl.DeptServiceImpl.*(..))")
    public  void pt(){};

    @Before("pt()")
    public void before(){
        log.info("before ...");
    }

不同切面类中,默认按照切面类的类名字母排序:
目标方法前的通知方法:字母排名靠前的先执行
目标方法后的通知方法:字母排名靠前的后执行

可以使用@Order(数字)控制顺序

目标方法前的通知方法:数字越小前的先执行
目标方法后的通知方法:数字越小的后执行

10.3、切入点表达式

  • execution():根据方法的签名来进行匹配

image-20240312123103279

image-20240312123145823书写建议
所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是find开头,更新类方法都是update开头。

描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性。

在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用…使用*匹配单个包。

  • @annotation():根据注解匹配

image-20240312123354070

10.4、连接点

JoinPoint抽象了连接点,可以用它获得方法执行时的相关信息,如目标类、方法名、方法参数

注意点:

  • @Around通知,获取连接点信息只能使用ProceedingJoinPoint

  • 其他四种通知,获取连接点信息只能使用JoinPoint

    它是ProceedingJoinPoint的父类型

        //1、获取目标对象的类名
        String className = proceedingJoinPoint.getClass().getName();
        log.info("目标对象的类名,{}",className);

        //2.获取目标方法的方法名
        String methodName = proceedingJoinPoint.getSignature().getName();
        log.info("目标对象的类名,{}",methodName);

        //3.获取目标运行时传入的参数
        Object[] args = proceedingJoinPoint.getArgs();
        log.info("目标对象的类名,{}", Arrays.toString(args));

        //4.放行 目标方法执行
        Object result = proceedingJoinPoint.proceed();

11.springBoot原理

配置优先级问题

image-20240314170332600

第三方Bean对象

  • 如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解声明bean的,就需要用到@Bean注解。
  • 若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过@Configuration注解声明一个配置类。

image-20240314172803395

11.1、起步依赖

spring boot 中引入项目依赖不需要那么繁琐,主要靠依赖传递,保证引入项目需要的依赖

11.2、自动配置

SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

  • 原理实现

    • @import
    • @EnableXXX注解,封装了@Import(推荐)
  • 源码

    • @SpringBootApplication

      • @SpringBootConfiguration

        • 和configuration注解作用相同,声明当前也是一个配置类
      • @EnableAutoConfiguration

        • 组件扫描,默认扫描当前引导类所在包及其子类
      • @ComponentScan

        • 实现自动化配置的核心注解

条件装配注解

  • Conditional注解
    • 按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring IOC容器中
  • 常用Conditional注解
    • @ConditionalOnClass:判断是否有对应的字节码文件
    • @ConditionalOnMissingBean:判断环境中没有对应的bean才会注入
    • @ConditionalOnProperty:判断配置文件中对应属性和值,才注册bean到IOC容器

11.3、案例(自动starter)

自定义起步依赖-制作阿里云工具类的自动装配

image-20240315120801264

12、maven

12.1、继承与聚合

作用
聚合用于快速构建项目
继承用于简化依赖配置、统一管理依赖

相同点:
聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中聚合与继承均属于设计型模块,并无实际的模块内容

不同点:
聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己

12.2、私服

私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。

image-20240316172159637

image-20240316172448902

  • Release:发行版本,功能趋于稳定,当前停止更新
  • snapshot:快照版本,功能不稳定、尚且 处于开发中的版本

步骤

  • 配置本地仓库访问私服的权限,修改maven的conf/settings.xml配置文件,指定访问私服的用户名/密码
<server>
    <id>releases</id>
    <username>admin</username>
    #这里是私服设置的密码
    <password>123456</password>
</server>
<server>
    <id>snapshots</id>
    <username>admin</username>
    #这里是私服设置的密码
    <password>123456</password>
</server>
  • IDEA中配置要上传项目中的发布管理,修改pom.xml,指定上传的位置
<distributionManagement>
      <repository>
            <id>releases</id>
            <url>http://localhost:8081/repository/maven-releases/</url>
      </repository> 
      <snapshotRepository>
            <id>snapshots</id>
            <url>http://localhost:8081/repository/maven-snapshots/</url>
      </snapshotRepository> 
</distributionManagement>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周粥粥ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值