SpringBoot入门(Web服务端笔记)

这篇博客是SpringBoot的入门教程,详细讲解了从基础到进阶的各个环节,包括Servlet、Filter、Listener、SSM框架、JUnit单元测试、Thymeleaf模板引擎、自定义全局异常处理、过滤器、拦截器、监听器、定时任务和异步任务的使用。还深入探讨了MyBatis的配置、异常处理、JDBC与ORM的差异、数据库操作、AOP和IOC原理,以及JWT的使用。最后,总结了SpringBoot学习过程和未来学习计划。
摘要由CSDN通过智能技术生成

SpringBoot

第一节课

1.图例

image-20201112121527582

2.三大组件

Servlet
  • 作用

    • 接受请求–>处理请求–>返回响应
  • 流程

    1. 客户端发送请求到服务端
    2. 服务端将请求消息发送给Servlet
    3. Servlet生成响应发送给服务器
    4. 服务器将响应发送给客户端

img

Filter
  • 作用

    • 过滤请求和响应
  • 流程

    • 1.请求进入Filter,执行相关操作
    • 2.判断通行,进入Servlet,执行完毕,再返回给Filter,最后返回请求方
    • 3.判断失败,直接返回失败结果

image-20201112121740592

Listener
  • 监听对象的状态
  • (类似观察者模式)

3.三大框架(SSM)

组成

Spring,SpringMVC,Mybatis

思考
  1. Spring和SpringMVC的区别
    • Spring是IOC和AOP的容器框架,SpringMVC是基于Spring功能之上添加的Web框架,想用SpringMVC必须先依赖Spring

image-20201118101510602

  1. IOC(控制反转)和AOP(面向切片编程)的实现原理

    • 配置文件,反射机制
  2. SpringBoot和SpringMVC的区别

    • SpringBoot实现了自动配置,降低了项目搭建的复杂度,是一套快速开发整合包,内嵌了常用的样板代码
    • Spring MVC提供了一种轻度耦合的方式来开发web应用
  3. 为什么不使用JDBC

    • 因为MyBatis ,只需要提供 SQL 语句就好了,其余的诸如:建立连接,操作 Statment,ResultSet,处理 JDBC 相关异常等等都可以交给 MyBatis 去处理,我们的关注点于是可以就此集中在 SQL 语句上,关注在增删改查这些操作层面上
MVC模式组成

有点类似于适配器模式

  • M(模型层,即数据)
  • V(视图层,展示模型的数据)
  • C(控制层,不同的model展现到不同的view)

课后作业

1.MVC设计模式与传统Web开发模式的区别

与传统Web的区别

  • 传统Web将显示层、控制层、数据层的操作全部交给 JSP 或者 JavaBean 来进行处理的缺点

    • 代码严重耦合,不利于扩展和维护
    • 代码难以复用
    • 工作模式同步,前端等待后端,后端等待前段
  • 传统Web水平划分视图和逻辑两层,MVC垂直划分3层

2.接口定义及其实现分开的好处
  • 有利于代码规范化

    • 接口相当于类的行为规范
  • 代码可维护和易扩展

    • 就拿最近学习设计模式的例子来说,商家卖红茶,直接new BlackTea(),需求变化,扩展业务,卖绿茶,就得添加new GreenTea(),需求再变,红茶不卖了,这时如果修改就得一个个去删,会显得很繁琐,但如果采用工厂模式的话,完全可以只去修改工厂中的接口实现时的类型,而在外的代码一直是new Factory()不会发生变化
  • 有利于代码安全和严密

    • 对接口的调用不需要关注接口内部的实现,保证了接口内部的严密
  • 丰富了继承的形式

    • java中没有多继承,但可以通过继承多个接口的方式变相实现多继承
  • 实现松耦合,方便注入

第二节课

1.JUnit单元测试

基本概念
  • 区分与人工测试,更加快捷方便和有保证
  • java单元测试框架
  • 测试驱动编程
用处
  • 测试代码逻辑的正确性(尤其是复杂工程)

  • 已知输入的先决条件,预期输出后置条件

    • TestCase断言
  • 正负检验

实践

首先需要在测试类配置如下的注解,特别注意@SpringBootTest后面跟着的是主程序入口类的class

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {
   Ex1Application.class})
@AutoConfigureMockMvc
前置理论
  • @Before,发生在测试之前

  • @Test,测试时

  • @After,测试之后

  • MockMvc类,主要用于模拟http请求

    • get

      MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/api/ex1/hello/list"))
                      .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
              int status = mvcResult.getResponse().getStatus();
              TestCase.assertEquals(200, status);
      
      • 执行MockMvcRequestBuilders请求,如果获取的状态码不是200(OK),抛出异常,正常就返回MvcResult对象
    • post

      MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/api/ex1/user/login")
                      .content(JSONArray.toJSON(user).toString())
                      .contentType(MediaType.APPLICATION_JSON))
                      .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
              JsonData jsonData = JSONArray.parseObject(mvcResult.getResponse().getContentAsString(UTF_8), JsonData.class);
              TestCase.assertEquals(jsonData.getCode(), 0);
      
      • post在http请求报文的body中有数据,所以还得传递一个json格式的对象user
  • 碰到的问题

    • post测试时传参时json对象和自定义对象的转化

      解决方法:http://www.mamicode.com/info-detail-2668986.html

  • 热部署

    • 编译器会根据修改的代码重新调整程序

2.Thymeleaf

轻量级引擎模板

静态模板放在templates

注意点:

  1. 需要添加xmlns:th=“http://www.thymeleaf.org”
  2. 具体用法参考文档,https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
public class ThymeleafProperties {
   
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
    private Charset encoding;
    private boolean cache;
    private Integer templateResolverOrder;
    private String[] viewNames;
    private String[] excludedViewNames;
    private boolean enableSpringElCompiler;
    private boolean renderHiddenMarkersBeforeCheckboxes;
    private boolean enabled;
    private final ThymeleafProperties.Servlet servlet;
    private final ThymeleafProperties.Reactive reactive;
  .....
}

上面是一部分的ThymeleafProperties的源码,我们可以看到,Thymeleaf默认是去"classpath:/templates/"里找后缀为.html的文件的

第三节课(2020-10-15/第四周)

1.自定义全局异常错误

如何配置全局自定义全局异常

image-20201118105853007

对应异常的处理方法上添加@ExceptionHandler(value = Exception.class)注解

类型
  • Json格式
@RestControllerAdvice
public class JsonUserHandler {
   
    @ExceptionHandler(value = Exception.class)//捕获什么异常
    JsonData handlerException(Exception ex, HttpServletRequest request) {
   
        return JsonData.buildError("服务端异常报错");
    }
}
  • 自定义页面,通过ModelAndView实现
@ControllerAdvice
public class ViewUserHandler {
   
    @ExceptionHandler(value = Exception.class)
    Object handlerException(Exception ex, HttpServletRequest request) {
   
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error.html");
        modelAndView.addObject("msg", ex.getMessage());
        return modelAndView;
    }
}

2.过滤器

作用

权限控制,用户状态控制

编码

实现Filter接口,并重写init,doFilter,destroy方法,添加@WebFilter注解,使启动类可以回调自定义的Filter

@WebFilter(urlPatterns = "/api/ch/pri/*", filterName = "LoginFilter")
public class LoginFilter implements Filter {
   
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
   
        System.out.println("init");
    }

    @Override
    public void destroy() {
   
        System.out.println("destroy");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
   
        System.out.println("doFilter");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token))
            token = request.getParameter("token");

        if (!StringUtils.isEmpty(token)) {
   
            User user = UserServiceImpl.sessionMap.get(token);
            if (user != null) {
   
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
   
                JsonData jsonData = JsonData.buildError("登录失败", -2);
                String jsonStr = objectMapper.writeValueAsString(jsonData);
                renderJson(response, jsonStr);
            }
        } else {
   
            JsonData jsonData = JsonData.buildError("未登录", -3);
            String jsonStr = objectMapper.writeValueAsString(jsonData);
            renderJson(response, jsonStr);
        }
    }

    public void renderJson(HttpServletResponse response, String json) {
   
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        try (PrintWriter writer = response.getWriter()) {
   
            writer.print(json);
        } catch (Exception ex) {
   
            ex.printStackTrace();
        }
    }
}

3.拦截器

作用

同过滤器

编码
public class LoginInterceptor implements HandlerInterceptor {
   
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   
        System.out.println("preHandle");
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token))
            token = request.getParameter("token");

        if (!StringUtils.isEmpty(token)) {
   
            User user = UserServiceImpl.sessionMap.get(token);
            if (user != null) {
   
                return true;
            } else {
   
                JsonData jsonData = JsonData.buildError("登录失败", -2);
                String jsonStr = objectMapper.writeValueAsString(jsonData);
                renderJson(response, jsonStr);
                return false;
            }
        } else {
   
            JsonData jsonData = JsonData.buildError("未登录", -3);
            String jsonStr = objectMapper.writeValueAsString(jsonData);
            renderJson(response, jsonStr);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
   
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
   
        System.out.println("afterCompletion");
    }

    public void renderJson(HttpServletResponse response, String json) {
   
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        try (PrintWriter writer = response.getWriter()) {
   
            writer.print(json);
        } catch (Exception ex) {
   
            ex.printStackTrace();
        }
    }
}
多个拦截器的执行过程
实践

如果请求一旦被某个拦截器拦截,那后面的拦截器就不会再执行,相当for-if-break

多个拦截器阻拦效果,首先需要配置拦截器的路由

@Configuration
public class UserWebMvcController implements WebMvcConfigurer {
   
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
        registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/api/ch/pri/**");
        registry.addInterceptor(new TwoInterceptor()).addPathPatterns("/api/ch/pri/**");
        registry.addInterceptor(new ThreeInterceptor()).addPathPatterns("/api/ch/pri/**");
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Bean
    public LoginInterceptor getLoginInterceptor() {
   
        return new LoginInterceptor();
    }
}
  • 部分放行(第二个拦截)

    image-20201118111224055

    第二个拦截器拦截:第一个拦截器preHandle和afterCompletion会被执行,但因为被第二个拦截器拦截,所以第一个postHandle不会被执行

  • (部分拦截)第三个拦截

    image-20201118111531644

    同理

  • 全部放行

    image-20201118111540462

    栈的既视感,先进后出

    全部放行:具体流程可以用下图解释

原理

image-20201118111023794

4.监听器(了解)

  • 应用启动监听
  • 会话监听
  • 请求监听

5.课后作业

过滤器和拦截器比较

1.编码
  • 过滤器
    • 实现Filter接口,重载init,destroy,doFilter(过滤逻辑实现的地方)方法,同时需要添加注解
    • @WebFilter(urlPatterns = “/api/ch/pri/*”, filterName = “LoginFilter”),访问/api/ch/pri/*就会触发过滤器
  • 拦截器
    • 实现HandlerInterceptor接口,重载preHandle(拦截器逻辑实现的地方),postHandle,afterCompletion,不需要添加注解,但需要配合自定义的config类,覆盖原来的WebMvcConfigure类
    • 重载addInterceptors方法添加对应的路由(注意添加@Configuration)
      registry.addInterceptor(getLoginInterceptor()).addPathPatterns("/api/ch/pri/**");
2.生命周期
  • 过滤器
    • init随程序启动被调用,init和destroy在整个Filter生命周期只会被调用一次,而doFilter在对应的每一次请求都被会调用
  • 拦截器
    • 在每一次对应请求都会执行一个完整的生命周期,即preHandle,postHandle,afterCompletion都会在一次请求中被执行
3.实现原理
  • 过滤器
    • 函数回调

filterChain.doFilter(servletRequest, servletResponse);

接下来我们重点分析这一句代码

我们先来看一下FilterChain这个类

public interface FilterChain {
   
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

通过上面的代码我们可以发现它是一个接口,那我们再来看一下它的应用级实现类,也就是ApplicationFilterChain这个类

image-20201118112640780

ApplicationFilterChain.this.internalDoFilter(req, res);

我截取了比较doFilter核心语句,所以我们再来扒一扒internalDoFilterf方法

if (this.pos < this.n) 
ApplicationFilterConfig filterConfig = this.filters[this.pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);

从上面的这几句中我们可以发现,这个方法其实是对过滤器的一个遍历,不断获取我们定义的过滤器,然后进行过滤操作,最后将结果回调,当不符合过滤条件就抛出异常

总结:ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。而每个xxxFilter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChaindoFilter() 方法,以此循环执行实现函数回调

img

  • 拦截器
    • 基于Java的反射机制,动态代理==(留坑)==
4.灵活度
  • 过滤器
    • 实现javax.servlet.Filter接口,依赖于Servlet,需要使用服务器容器,只能限于web程序中
  • 拦截器
    • spring的一个组件,使用范围包括application,web等等
5.触发时机

image-20201118123941989

过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用

6.参考文献

过滤器 和 拦截器的 6个区别(别再傻傻分不清了)

第四节课(2020-10-22/第五周)

1.JDBC(了解)

基本流程

  • 加载JDBC驱动
  • 创建数据库连接
  • 创建preparedStatement对象
  • 执行sql语句
  • 处理结果集
  • 关闭JDBC对象资源

2.ORM框架

理解

数据库的表和java对象做映射关系,比如User类中的username字段映射user表中的username字段

Mybatis(重点)
1.简介
Mybatis是什么?
  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • 存储地:apache——>google——>github(现在)
如何获得?
  • maven
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.4</version>
</dependency>
  • github:https://github.com/mybatis/mybatis-3/releases
  • 中文文档:https://mybatis.org/mybatis-3/zh/index.html
持久化

持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)

JDBC就是一种持久化机制。文件IO也是一种持久化机制。

2.流程
1.导入依赖

mysql,mybatis,log4j(可以不添加)

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.4</version>
</dependency>
2.环境配置
  • 配置连接数据库的信息
<environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"></transactionManager>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://127.0.0.1:3306/mailDB?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="XXXXX"/>
      </dataSource>
    </environment>
</environments>
  • 配置数据库操作的xml映射
<mappers>
    <mapper resource="mapper/UserMapper.xml"/>
    <mapper resource="mapper/MailMapper.xml"/>
</mappers>
  • 注意(xml需添加这一段,不然不会联想)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
3.java类和sql语句的映射
  • 定义数据表的实体类
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
   
    private int id;
    private String username;
    private String password;
    private String mobile;
}
  • 定义sql语句的接口
public interface UserMapper {
   

    User selectById(int useId);

    int add(User user);

    List<User> selectListByXML();

    List<User></
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值