JavaWeb第二天

tlias案例实践

  1. 如果请求路径都包含同一个前缀,可以将其单独提取,在类外注解

image-20240426234626802

  1. 使用@RequestParam(defaultValue = "1")注解参数可为形参设置默认值
    /**
     * 分页查询
     * @param page 页号
     * @param pageSize 页记录数
     * @return
     */
    @GetMapping("/emps")
    public Result list(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize){
        PageBean pageBean = empService.list(page,pageSize);

        return Result.success(pageBean);
    }
  1. 在类上使用注解@Slf4j可以直接调用log对象来进行日志记录
log.info("hello");
log.info("hello {},{}", value1, value2);	//通过{}占位符输出变量
  1. 使用pagehelper插件进行分页查询
        <!--pageHelper分页插件坐标-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.6</version>
        </dependency>
//Mapper层
    /**
     * 分页查询emp表记录并返回
     * @return
     */
    @Select("select * from emp")
    List<Emp> list();

//service层
    @Override
    public PageBean list(Integer page, Integer pageSize) {
        /*使用pageHelper插件实现*/
        // 1.初始化PageHelper api, 传入页号和每页记录数
        PageHelper.startPage(page, pageSize);
        // 2.进行正常sql查询, 并将empList封装到Page对象,pagehelper插件会自动进行分页操作
        List<Emp> empList = empMapper.list();
        Page<Emp> ps = (Page<Emp>) empList;
        // 3.获取分页查询结果并返回
        return new PageBean(ps.getTotal(), ps.getResult());
    }
  1. 文件上传
  • 前端文件上传
    <!-- 前端上传文件是通过form表单来提交的,要上传文件,有三个必选项:
		 1.请求方式method="post"
		 2.编码方式enctype="multipart/form-data"
		 3.<input type="file" name="image">设置类型为"file"
	-->
	<form action="/upload" method="post" enctype="multipart/form-data">
        姓名: <input type="text" name="username"><br>
        年龄: <input type="text" name="age"><br>
        头像: <input type="file" name="image"><br>
        <input type="submit" value="提交">
    </form>
  • 服务端接收文件(MultipartFile对象
@Slf4j
@RestController
public class UploadController {
    @PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image){	//接收file类型文件时,使用MultipartFile接口来接收
        log.info("文件上传:{},{},{}",username,age,image);

        return Result.success();
    }
}
  1. 文件保存到本地(不推荐)
    @PostMapping("/upload")
    public Result upload(String username, Integer age, MultipartFile image) throws IOException {
        log.info("文件上传:{},{},{}",username,age,image);

        String originalFilename = image.getOriginalFilename();	//获取源文件名字(带扩展名)
        String extname = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));	//拼接uuid+源文件扩展名
        image.transferTo(new File("D:\\"+extname));	//将文件转存到本地指定路径

        return Result.success();
    }
  1. 文件保存到阿里云OSS

文件保存步骤:

image-20240428002819098

  1. 参数配置化

将一些服务的配置参数,以key=value的形式配置在application.properties配置文件中,然后在使用该配置参数的类中声明处,为每个参数变量添加一个注解@Value("${key}"),在${}中添加key名,这样在运行时会自动将配置文件中的值注入给当前变量

/*1. 配置文件*/
# 阿里云OSS默认参数配置信息
aliyun.oss.endpoint=https://oss-cn-shenzhen.aliyuncs.com
aliyun.oss.accessKeyId=LTAI5tDGy4TmiwW56WRuZzBa
aliyun.oss.accessKeySecret=xtIOICx7ryxBbed8g8Lmr9alsvGbLY
aliyun.oss.bucketName=sping-code

/*2. 类中变量声明处*/
    @Value("${aliyun.oss.endpoint}")
    private String endpoint ;
    @Value("${aliyun.oss.accessKeyId}")
    private String accessKeyId ;
    @Value("${aliyun.oss.accessKeySecret}")
    private String accessKeySecret ;
    @Value("${aliyun.oss.bucketName}")
    private String bucketName ;
  1. yml格式配置文件(推荐)

yml格式配置文件相对于xmlproperties更加简洁,层次结构更加分明,在springboot中添加yml文件需命名为application.yml

image-20240428140122839

  • yml基本数据类型配置
#配置对象/Map
user:
 name: zs
 age: 18
 address: shenzhen
 
#配置数组/List/Set
hobby:
 - java
 - c
 - python
 - c++
  1. ConfigurationProperties注解

@ConfigurationProperties(prefix = "") 注解用于批量注入配置文件中的key-value

prefix值为配置属性的统一前缀,使用方法为:

  • 添加一个实体类,类中属性声明为与prefix.key同名属性
  • 在类上添加@Data、@Component和@ConfigurationProperties(prefix = "")注解,目的是提供get/set方法,提供IOC容器管理,以及注入批量同级keyvalue
  • 在需要value值时,调用bean对象,通过getter获取对应同名的属性值即可
/*声明同名属性类*/
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
}
/*在别处获取value*/
public class example{
    ...
    @Autowired
    private AliOSSProperties aliOSSProperties;

    public String upload(MultipartFile file) throws IOException {
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();
        ...
    }
    ...
}

  1. 配置文件优先级

image-20240430195739136

登录校验

image-20240428153526292

image-20240428154014730

1, Cookie技术——存储在客户端

image-20240428155010972

优点:

  • HTTP协议中支持的技术,由浏览器自动解析和携带

缺点:

  • 移动端无法使用Cookie
  • 用户可自定义禁用Cookie
  • 不可跨域(跨域:协议/IP/端口不同)

2, Session技术——存储在服务端

image-20240428160127649

优点:

  • 存储在服务端,安全

缺点:

  • 服务器集群环境无法直接使用Session
  • Cookie的所有缺点,因为其底层是基于Cookie的

3, 令牌Token技术

JWT(JSON Web Token)令牌

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

base64: 基于64个打印字符(A-Z、a-z、0-9 + /)来表示二进制数据的编码方式

  • JWT组成
  1. Header(请求头)
  2. Payload(有效载荷)
  3. Signature(签名)
  • JWT令牌的生成和校验
  1. 引入JWT依赖:
        <!--jwt令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
  1. 生成一个jwt令牌:
    @Test
    public void testGenJwt(){
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("name","tom");
		/*生成一个jwt令牌*/
        String jwt = Jwts.builder()
                .setClaims(claims)	//自定义载荷
                .signWith(SignatureAlgorithm.HS256, "poemcode")	//签名算法和秘钥
                .setExpiration(new Date(System.currentTimeMillis() + 10*1000))	//有效期
                .compact();
        System.out.println(jwt);
    }
  1. jwt令牌校验:
/*
校验失败通常有两种原因:
	1. JWT令牌被篡改
	2. JWT令牌过期
*/
	@Test
    public void testParseJwt(){
        Claims claims = Jwts.parser()
                .setSigningKey("poemcode")  //指定签名秘钥
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcxNDI5NTkzMH0.8bvfhvXFAYxQAJ-1QQ7MPZnhiCSwXmd8LBPBZ2R4S7E") //解析令牌
                .getBody(); //获取载荷数据
        System.out.println(claims);
    }

4, 过滤器Filter

Filter是JavaWeb三大(Servlet、Filter、Listener)组件之一

过滤器可以把对资源的请求拦截下来,从而实现一些特殊的处理,通常为登录校验、统一编码处理、敏感字符等

https://www.cnblogs.com/huanzi-qch/p/11239167.html

image-20240428222245679

定义过滤器

定义一个过滤器只需要两步:

  1. 创建一个类,实现标准的Filter接口,同时重写init(), doFilter(), destroy()三个方法,init和destroy可以不重写,Filter接口有默认实现,doFilter方法就是我们拦截请求或响应时的处理方法
  2. 在过滤器上添加@WebFilter(urlPatterns = "")注解,urlPatterns 指要拦截的请求路径;同时,由于Filter是javaweb的三大组件,并不属于springboot,因此还需在springboot启动类上添加一个@ServletComponentScan注解来开启Servlet组件支持

注:@WebFilter不能与@Component注解共用,不然会导致Spring加载两次,导致默认拦截所有路径

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
    @Override //初始化方法, 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    @Override //拦截到请求之后调用, 调用多次
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("拦截到了请求...放行前逻辑");
        //放行,调用chain对象
        chain.doFilter(request,response);

        System.out.println("拦截到了请求...放行后逻辑");
    }

    @Override //销毁方法, 只调用一次
    public void destroy() {
        System.out.println("destroy 销毁方法执行了");
    }
}
  • 过滤器执行流程

请求——>放行前操作——>放行——>下一个过滤器/资源(如果是最后一个过滤器)——>放行后操作

Filter拦截路径

image-20240428225958755

过滤器链

一个Web应用中,可以创建多个过滤器,多个过滤器就形成一条有顺序的过滤器链

顺序:注解配置的Filter,默认按照类名进行优先级排序

5、拦截器interceptor

image-20240429163912789

image-20240429164050781

image-20240429164522858

image-20240429164714514

6、全局异常处理器

定义全局异常处理器:

  1. 需要创建一个类并且在类上添加@RestControllerAdvice注解
  2. 定义一个方法,形参为要捕获的异常,接着为方法添加一个注解@ExceptionHandler(异常类型.class),这样就可以自动捕获全局的异常了

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Result catchEx(Exception e){
        e.printStackTrace();
        return Result.error("操作失败,信息有误!");
    }
}

7、Spring事务管理

image-20240429185213510

事务进阶——事务属性

  • 1- rollbackFor

@Transactional默认只有RunTimeException异常才会执行回滚,使用rollbackFor属性可以指定要执行回滚的异常类型

@Transactional(rollbackFor = Exception.class)
//todo
  • 2- propagation

事务传播行为:当前事务在被另一个事务调用时,这个事务方法应该如何进行事务控制

image-20240429192649770

AOP——面向切面编程/面向特定方法编程

AOP,全称Aspect Oriented Programming,是一种编程思想

动态代理技术就是面向切面编程最主流的实现,SpringAOP技术底层就是基于动态代理技术,就是对特定的类/接口/方法进行编程,从而在方法执行的前后分别执行特定的操作

SpringAOP快速入门

  • 1、在pom.xml中导入AOP的依赖
        <!--aop-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  • 2、创建一个AOP类
@Component	//将当前类加入IOC容器
@Aspect	//指明当前类是一个AOP类
public class TimeAspect {
}
  • 3、新增一个面向方法编程的方法,用来在原始方法执行前后添加一些逻辑处理代码
@Component
@Slf4j
@Aspect
/*
	统计service层方法执行的时间
*/
public class TimeAspect {
    @Around("execution(* com.codepoem.service.*.*(..))")    //Around注解用来标识要执行哪些方法
    //ProceedingJoinPoint--类型的形参用来封装要执行的"原始方法"
    Object runTime(ProceedingJoinPoint joinPoint) throws Throwable {
        //原始方法调用前
        long begin = System.currentTimeMillis();
        //执行原始方法
        Object result = joinPoint.proceed();	//proceed()返回原始方法的返回值,类型为Object
        //原始方法调用后
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature() + "执行时间为:" + (end - begin) + "ms");
        return result;
    }
}

AOP的核心概念

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指要对接入点执行的共性的操作,最终体现为一个方法
  • 切入点:PointCut,匹配连接点的条件,通知仅在切入点匹配连接点时执行
  • 切面:Aspect,描述切入点与通知的对应关系(切面 = 切入点 + 通知)
  • 目标对象:Target,通知所应用的对象

通知的类型

image-20240430001512457

  • 提取公共切入点

image-20240430001544416

image-20240430002236022

切入点表达式

问号处可以省略

  1. execution

image-20240430151154960

  1. @annotation

@annotation()中传入匹配注解的全类名

image-20240430152442451

连接点

可以获取目标方法运行时的相关信息

image-20240430153247731

bean的管理

单例bean的获取

默认情况下,Spring项目启动时,会把bean都创建好放在IOC容器中(只针对默认的单例非延迟加载的bean),如果想要主动获取这些bean,可以通过IOC容器对象对象的方法来获取

  • 上下文对象
@Autowired
private ApplicationContext applicationContext;
  • 通过name获取bean
Object getBean(String name)
  • 通过类型获取bean
<T> T getBean(Class<T> requiredType)
  • 通过name类型获取bean
<T> T getBean(String name, Class<T> requiredType)

bean的作用域和延迟加载

image-20240430201728161

第三方bean

如果要管理的bean对象来自第三方,使用@Component及其衍生注解声明bean的还需要指定扫描,这时能使用@Bean注解,bean注解作用于方法上,通过声明一个返回第三方类对象的方法 + @Bean注解的方式就可以将第三方类加入IOC容器管理中

@Configuration	//声明一个配置类,专门管理Bean
public class CommonConfig{
    /**
    * 默认下,@Bean管理的第三方bean对象名字为'方法名'
    * 如果该第三方bean要注入其他bean,直接在bean定义方法中设置形参,容器会根据类型'自动装配'
    */
    @Bean
    public TestClass testClass(DeptController dept){	
        return new TestClass();	//返回第三方bean对象
    }
}
  • Component及其衍生注解和Bean的使用场景

项目中定义的,使用@Component及其衍生注解

项目中引用第三方的,使用@Bean注解

Springboot原理

一、起步依赖

基于maven的依赖传递特性,Springboot的起步依赖就变得更加简洁了

二、自动配置(面试高频)

Springboot的自动配置就是当Spring容器启动后,自动将一些配置类bean对象存入到IOC容器中,不用我们再去手动声明,极大简化了开发

方案一:使用@ComponentScan扫描组件 (不推荐)

当引入第三方依赖时,即使第三方依赖声明了@Component等注解,在当前项目仍不会加入IOC容器,因为Spring的扫描组件并没有去扫描第三方依赖的包,因此需要显示的声明@ComponentScan注解来扫描第三方包和当前项目包

//在springboot启动类上添加@ComponentScan注解
@ComponentScan({"com.path1","com.path2"})
@SpringBootApplication
public class SpringbootTliasQuickstartApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootTliasQuickstartApplication.class, args);
    }
}

方案二:使用@Import注解导入

方案一太过繁琐,并且占用资源高。使用@Import导入的类会被Spring加载到IOC容器中,一般声明在启动类

导入方式主要有4种:

  1. 导入普通类
@Import({SimpleClass.class, ...})
  1. 导入配置类

原理是通过配置类+@Bean的方式声明多个第三方bean对象,然后用@Import导入,IOC容器会自动将该配置类下的所用Bean都引入

@Import({ConfigClass.class, ...})
  1. 导入ImportSelector接口实现类

ImportSelector接口定义了一个返回值为String[]selectImports方法,通过该方法来返回我们所要引入的第三方类全类名,左右与前面两种类似

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{UserDaoImpl2.class.getName()};
    }
}
- - - - - - - - - - - - - - - - - - - - - - - - - - 分 隔 线 - - - - - - - - - - - - - - - - - - - - - - - - 
@Import({MyImportSelector.class, ...})
  1. @EnableXxxx注解,封装@Import注解——(Springboot默认实现方式)

由第三方定义@EnableXxxx注解,在其中封装@Import注解导入要成为bean的类。如果我们要导入这些bean,只需在类上声明该@EnableXxxx注解

Bean的条件装配

image-20240501153847451

自动配置原理

image-20240501153437094

自定义starter

image-20240501154532795

Web后端开发主要内容

image-20240502132913502

Maven高级

一、分模块设计与开发

将项目按照功能分成若干个子模块,方便项目的维护、拓展,也方便模块间相互调用、资源共享

二、Maven的继承与聚合

继承

1.概念:

继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承,子工程会继承父工程的依赖

2.作用:

简化依赖配置,统一依赖管理

3.实现

<parent></parent>标签

4.打包方式

image-20240502140505596

  • 继承结构

image-20240502141716500

  • 继承关系实现

image-20240502141803037

<!------------------------------------------父工程pom.xml------------------------------------------->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.0</version>
    <relativePath/>	<!--父工程的相对路径,默认从本地仓库或远程仓库查找-->
</parent>

<groupId>com.myproj</groupId>
<artifactId>proj-parent</artifactId>
<version>1.1.1</version>
<packaging>pom</packaging>	<!--指定父工程打包方式为pom-->

<dependencies>
	<!--将子工程公共的依赖提取到父工程,若子工程与父工程配置了不同版本的同一依赖,默认以子工程为准-->
</dependencies>

<!------------------------------------------子工程pom.xml------------------------------------------->
<parent>
    <groupId>com.myproj</groupId>
    <artifactId>proj-parent</artifactId>
    <version>1.1.1</version>
    <relativePath>"父工程的相对路径"</relativePath>	<!--父工程的相对路径-->
</parent>

<groupId>com.myproj</groupId>	<!--默认子工程会继承父工程声明依赖的groupId,可以不用再声明-->
<artifactId>proj-child1</artifactId>
<version>0.0.1</version>

三、Maven的版本锁定

在Maven特性中,可以在父工程的pom.xml文件中对所有子工程进行依赖的版本管理,使用<dependencyManagement>标签

<dependencyManagement><dependencies>的区别

  • <dependencies>是直接依赖,如果直接配置的是<dependencies>,子工程会自动继承父工程的依赖

  • <dependencyManagement>是统一管理依赖版本,使用时用<dependencyManagement>来包裹<dependencies>,这样该<dependencies>中配置的依赖不会直接被子工程继承,还需子工程自己引入,但无需指定版本,统一使用父工程中的版本

    • version管理方式1
    <dependencyManagement>
    	<dependencies>
        	<dependency>
            	<groupId>xxxx</groupId>
                <artifactId>xxxx</artifactId>
                <version>1.1.1</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    • version管理方式2
    <!--自定义属性-->
    <properties>
    	<xxxx.version>1.1.1</xxxx.version>
    </properties>
    
    <dependencyManagement>
    	<dependencies>
        	<dependency>
            	<groupId>xxxx</groupId>
                <artifactId>xxxx</artifactId>
                <version>${xxxx.version}</version>	<!--读取属性值${xxxx.version}-->
            </dependency>
        </dependencies>
    </dependencyManagement>
    

四、Maven聚合

使用聚合可以实现一键构建项目,一键打包,通常在父工程的pom.xml中添加<modules><module>标签来聚合其他模块

<modules>
	<module>聚合模块的相对路径</module>
</modules>

image-20240502165330303

五、Maven私服

image-20240502170824374

posted @ 2024-07-25 15:40  Arthur-Morgan  阅读(2)  评论(0编辑  收藏  举报