SpringBoot_第六章(知识点总结)

目录

1:拦截器(Interceptor)

1.1:拦截器代码实现

1.2:拦截器源码分析和流程总结

2:过滤器(Filter)、自定义(Servlet)、监听器(Listener)

3:文件上传

3.1:文件上传代码实现

3.2:文件上传源码分析

4:整合druid数据源

4.1:整合德鲁伊

4.2:德鲁伊监控页面

5:Spingboot指标监控

6:整合mybatis、mybatis-plus

6.1:整合mybatis

6.2:整合mybatis-plus

7:整合redis集群

8:错误处理

8.1:请求不存在的controller

8.2:请求存在但是代码错误的controller

8.3:前后端未分离

8.4: 前后端分析,全局异常


1:拦截器(Interceptor)

1.1:拦截器代码实现

拦截器是Spring容器进行管理的,对请求路径进行动态的拦截,可以进行各种权限日志等等骚操作。

代码实现第一步:自己实现HandlerInterceptor接口

/**
 * 登录检查 拦截器
 *
 * 1:写拦截器代码逻辑
 * 2  配置拦截器拦截 那些请求
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * controller方法 执行之前
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        log.info("拦截请求{}"+request.getRequestURI());
        User user = (User) request.getSession().getAttribute("loginUser");
        if (user == null) {
            System.out.println("拦截器重定向");
            //如果session不存在登录信息,重定向到登录页面
            request.setAttribute("msg","拦截器拦截未登录");
            request.getRequestDispatcher("/").forward(request,response);
            return false;
        }
        return true;//返回true 程序接着执行
    }


    /**
     * controller方法执行之后,在页面渲染之前
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           @Nullable ModelAndView modelAndView) throws Exception {
        log.info("controller方法执行之后{}",modelAndView);
    }
    /**
     * 页面渲染之后
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                @Nullable Exception ex) throws Exception {
        log.info("页面渲染之后{}",ex);
    }

}

代码实现第二步:自定义拦截器拦截路径和放行路径

/**
 * 实现WebMvcConfigurer接口 改变MVC行为
 * 使用addInterceptors 接口添加拦截器
 */
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/*") //  /**拦截所有请求,包含静态页面  /*:只拦截后面一级 /**:拦截内容包含多个层级
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/*","/js/**");//过滤掉登录请求和静态资源
    }
}

1.2:拦截器源码分析和流程总结

SpringBoot_第六章(知识点总结)_第1张图片

所以真正的执行步骤如下:

SpringBoot_第六章(知识点总结)_第2张图片

2:过滤器(Filter)、自定义(Servlet)、监听器(Listener)

Filter、servlet是Servlet规范,拦截的是自定义的请求。因为DispatcherServlet拦截的是/

但是Filter、servlet可以自定义拦截请求,根据匹配规则,他们会自己处理请求。

Filter代码实现:

/**
 * 继承Filter
 * 不会经过拦截器
 * 拦截/*所有请求 不管get post
 * 
 * 使用次注解生效
 * @ServletComponentScan(basePackages = "com.example.springboot2_web03")
 */
@WebFilter(urlPatterns = "/*",filterName = "MyFilter")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String name = request.getParameter("name");
        System.out.println("执行MyFilter无论什么get、post方法:"+name);
        chain.doFilter(request,response);//调用链
    }
}

Servlet代码实现: 

/**
 * 继承HttpServlet类,可以重写各种get post put 等等方法
 *
 * 使用次注解生效
 * @ServletComponentScan(basePackages = "com.example.springboot2_web03")
 *
 * servlet 拦截/s1(精度高 优先匹配)  就不会进入DispatcherServlet拦截/
 */
@WebServlet(urlPatterns = "/s1",name = "MyServlet")
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        System.out.println("执行MyServlet请求get:"+name);
        resp.getWriter().write("get请求MyServlet拦截/s1请求:"+name);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        System.out.println("执行MyServlet请求post:"+name);
        resp.getWriter().write("post请求MyServlet拦截/s1请求:"+name);
    }
}

监听器代码实现:

/**
 * 继承ServletContextListener
 * 容器启动的时候生效
 * 
 * 使用次注解生效
 * @ServletComponentScan(basePackages = "com.example.springboot2_web03")
 */
@WebListener(value = "MyListener")
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("MyListener监听ServletContext初始化,容器启动的时候输出");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("MyListener监听ServletContext销毁");
    }
}


3:文件上传

3.1:文件上传代码实现

第一步:html文件


第二步Java代码实现:

 /**
     * 文件上传
     * @param files
     * @return
     */
    @PostMapping(value = "/fileUpload")
    public String fileUpload(@RequestParam(value = "username") String username,
                             @RequestParam(value = "email")String email,
                             @RequestParam(value = "headerImg") MultipartFile headerImg,
                             @RequestParam(value = "photos") MultipartFile[] photos,
                             Files files) throws IOException {
        if (!headerImg.isEmpty()){
            System.out.println("username:"+username);
            System.out.println("email:"+email);
            System.out.println("files:"+files);
            //headerImg 单文件
            System.out.println("headerImg名字:"+ headerImg.getOriginalFilename());
            System.out.println("headerImg大小:"+ headerImg.getSize());
            System.out.println("headerImg文件类型:"+ headerImg.getContentType());
            String newFileHeaderImg="A"+headerImg.getOriginalFilename();
            headerImg.transferTo(new File("/Users/huyiju/Desktop/upload/"+newFileHeaderImg));
        }

        if (photos.length>0){
            //photos多文件用MultipartFile[]数组
            for (MultipartFile photo : photos) {
                File file=new File("","");
                String newFilePhotos="B"+photo.getOriginalFilename();
                photo.transferTo(new File("/Users/huyiju/Desktop/upload/"+newFilePhotos));

            }
        }

        return "/form/form_layouts";
    }

 第三步配置文件设置文件上传大小

#单个文件上限
spring.servlet.multipart.max-file-size=10MB
#多个文件请求上限
spring.servlet.multipart.max-request-size=100MB

3.2:文件上传源码分析

首先查看源码MultipartAutoConfiguration的自动装配

SpringBoot_第六章(知识点总结)_第3张图片

有了文件上传解析器,我们在配置文件中设置上传文件大小配置,默认是1MB(单个文件)和10MB(最大请求)

然后查看源码

//for循环将controller的参数逐个绑定
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);


//根据请求multipartRequest获取MultipartFile 并将返回值 
//赋值给 controller中的MultipartFile 类型的参数 
//
List files = multipartRequest.getFiles(name);
			return (!files.isEmpty() ? files.toArray(new MultipartFile[0]) : null);

4:整合druid数据源

4.1:整合德鲁伊

1:导入依赖

         
            mysql
            mysql-connector-java
        

        
        
            com.alibaba
            druid-spring-boot-starter
            1.2.16
        

2:配置数据库信息和连接池

#德鲁伊数据库连接
spring.datasource.url=jdbc:mysql://localhost:3306/W1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#连接池信息
spring.datasource.type= com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.max-active=20
spring.datasource.druid.min-idle=5
spring.datasource.druid.initial-size=5
spring.datasource.druid.max-wait=60000

3:代码测试(未使用mybatis,所以这里使用了jdbcTemplate)

 @Autowired
    JdbcTemplate jdbcTemplate;
    /**
     * druid测试
     */
    @Test
    void starter_jdbc() {
        String sql="select * from t1";
        List> maps = jdbcTemplate.queryForList(sql);
        maps.forEach(map -> System.out.println(map));
    }

4:结果查看(图片里边初始化的德鲁伊连接池)

SpringBoot_第六章(知识点总结)_第4张图片

4.2:德鲁伊监控页面

德鲁伊的依赖包含监控页面功能,只是没有开启我们只需要配置文件开启监控页面就可以

1:配置监控页面信息

# StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
#是否启用StatViewServlet(监控页面)默认值为false(考虑到安全问题默认并未启动,如需启用建议设置密码或白名单以保障安全)
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.reset-enable=false
spring.datasource.druid.stat-view-servlet.login-username=root
spring.datasource.druid.stat-view-servlet.login-password=123456

### WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.session-stat-enable=true
spring.datasource.druid.web-stat-filter.session-stat-max-count=1000
spring.datasource.druid.filters=stat,wall
spring.datasource.druid.filter.stat.slow-sql-millis=1000
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.enabled=true

2:页面验证

登录页面,输入账户密码root、123456

http://localhost:8080/druid/login.html

SpringBoot_第六章(知识点总结)_第5张图片

5:Spingboot指标监控

6:整合mybatis、mybatis-plus

6.1:整合mybatis

1:导入依赖

 
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.3
        

2:配置mapper映射位置

#mybatis配置 config-location配置mybatis文件地址  mapper-locations mapper地址
mybatis.config-location=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mapper/*.xml

3:接口

package com.example.springboot2_web03.mapper;

import com.example.springboot2_web03.entity.T1;

@Mapper
public interface T1Mapper {
     List selectAll();
     T1 selectById(@Param(value = "id") int id);

     @Select("select * from t1 where id=#{id}")
     T1 selectById1(@Param(value = "id") int id);
}

4:mapper配置文件




    
    

    
    

代码测试:

 @Autowired
    T1Mapper t1Mapper;
    /**
     * mybatis测试
     */
    @Test
    void mybatis_test() {
        List t1s = t1Mapper.selectAll();
        for (T1 t1 : t1s) {
            System.out.println("查询数据:"+t1);
        }

        T1 t1 = t1Mapper.selectById1(1);
        System.out.println("根据id查询:"+t1);
    }

6.2:整合mybatis-plus

1:导入依赖

  
            mysql
            mysql-connector-java
        

        
        
            com.alibaba
            druid-spring-boot-starter
            1.2.16
        

        
        
        
        
        
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.5.3.1
        

        
        
            com.baomidou
            mybatis-plus-extension
            3.5.3.1
            compile
        

2:配置文件

#mybatisPlus配置 config-location配置mybatis文件地址  mapper-locations mapper地址
#mybatis-plus.config-location=classpath:mybatis-config.xml
mybatis-plus.mapper-locations=classpath*:mapper/*.xml
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

3:添加分页拦截器

@Configuration
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInnerInterceptor.setOverflow(true);//最后一页的下一页调回首页
        paginationInnerInterceptor.setMaxLimit(500L);//-1不受限制 这里限制500条
        interceptor.addInnerInterceptor(paginationInnerInterceptor);//如果配置多个插件,切记分页最后添加

        //interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
        return interceptor;
    }

4:代码测试

//mapper代码
@Mapper
public interface TestMapper extends BaseMapper {
    //自定义分页查询
    Page selectOrderByAgePage(@Param(value = "page") Page page);

}


//接口和实现类代码
public interface TestService extends IService {
     Page selectOrderByAgePage(Page page);
}

@Service
public class TestServiceImpl extends ServiceImpl implements TestService{

    @Autowired
    TestMapper testMapper;

    /**
     * 查询数据 分页显示
     * @return
     */
    public Page selectOrderByAgePage(Page page){
       return testMapper.selectOrderByAgePage(page);
    }
}


//controller 代码
 @GetMapping(value = "/dynamic_table")
    public String dynamic_table(@RequestParam(name = "pageIndex",defaultValue = "1",required = false)Integer pageIndex,
                                            Model model){

        System.out.println("执行dynamic_table方法:默认值是第一页");

        //开始页和条数
        Page page=new Page<>(pageIndex,3);
        Page pages = testService.selectOrderByAgePage(page);
        System.out.println("当前页:"+pages.getCurrent());
        System.out.println("总页数:"+pages.getPages());
        System.out.println("总行数:"+pages.getTotal());
        System.out.println("当前页数据:"+pages.getRecords());

        model.addAttribute("pages",pages);
        return "/table/dynamic_table";
    }

7:整合redis集群

集群搭建过程见其他文章

1:导入依赖

  
        
        

        
            org.springframework.boot
            spring-boot-starter-data-redis
        

2:配置集群信息

#配置redis 集群
#集群密码
spring.redis.password=123456
#集群节点81 - 86
spring.redis.cluster.nodes=127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384,127.0.0.1:6385,127.0.0.1:6386
#最大重定向次数
spring.redis.cluster.max-redirects=5
#连接池最大连接量
spring.redis.lettuce.pool.max-active=8
#连接最大阻塞时间 毫秒
spring.redis.lettuce.pool.max-wait=1s
#空闲的最大数量
spring.redis.lettuce.pool.max-idle=8
#空闲的最小数量
spring.redis.lettuce.pool.min-idle=0

3:测试代码


//redistemplate配置
@Configuration
public class RedisConfig集群 {

    @Bean("redisTemplate")
    public RedisTemplate redisTemplateJQ(
            @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
                    RedisConnectionFactory factory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }

}


//测试代码
 @Autowired
   RedisTemplate redisTemplate;
    //StringRedisTemplate redisTemplate;

    @Test
    void t1() {
        redisTemplate.opsForValue().set("代", "测试", 10, TimeUnit.MINUTES);
        Set keys = redisTemplate.keys("*");
        for (String key : keys) {
            System.out.println("key:"+key);
        }
        System.out.println(keys);
        System.out.println("返回值a:" + redisTemplate.opsForValue().get("代"));
    }

8:错误处理

8.1:请求不存在的controller

1:浏览器请求没有服务的controller,返回html

SpringBoot_第六章(知识点总结)_第6张图片

2:postMan请求没有服务的controller,返回json

SpringBoot_第六章(知识点总结)_第7张图片

源码分析:

当请求不存在的时候,找不到处理方法报错

服务器转发了一个http://localhost:8080/error 请求,该请求会被自动装配的basicErrorController拦截

1:在ErrorMvcAutoConfiguration声明了一个bean是basicErrorController这个controller会拦截/error请求的错误。

SpringBoot_第六章(知识点总结)_第8张图片

2:这个controller拦截/error请求,在源码中可以看到这连个拦截错误请求

SpringBoot_第六章(知识点总结)_第9张图片​ 

 3:根据不同的请求方式浏览器和postMan程序来决定返回html和json

SpringBoot_第六章(知识点总结)_第10张图片

8.2:请求存在但是代码错误的controller

controller代码

 @GetMapping(value = "/basic_table")
    public String basic_table(){
        //自定义错误 抛出 java.lang.ArithmeticException: / by zero
        int a=10/0;
        return "/table/basic_table";
    }

源码分析:

1:执行controller代码报错,DispatcherServlet拦截到异常。

SpringBoot_第六章(知识点总结)_第11张图片

2:异常处理

SpringBoot_第六章(知识点总结)_第12张图片

3:系统默认的异常解析器处理异常,结果都处理不了,回到上边用baseErrorController来处理

SpringBoot_第六章(知识点总结)_第13张图片

8.3:前后端未分离

当我们配置了4XX和5XX错误的html页面的时候

我们配置在项目中配置错误页,baseErrorController就会在浏览器请求错误的情况下,跳转到这些错误页面。我们在错误页面中取出异常信息

SpringBoot_第六章(知识点总结)_第14张图片

html页面

    

请求路径

状态

错误消息

时间

 浏览器页面显示

SpringBoot_第六章(知识点总结)_第15张图片

8.4: 前后端分析,全局异常

首先我们定义两个Controller

@Controller
public class 异常Controller {


    /**
     * 前后端分离Controller 直接返回json
     */
    @GetMapping(value = "/basic_table1")
    @ResponseBody
    public CommonReturnType basic_table1(@RequestParam(value = "a",required = false) int b ){

        //1:重点:没有自定义全局异常,默认的异常解析器解析
        //这里出现异常没有返回值 会被DispatcherServlet拦截,捕获异常 发送/error
        // 会进入baseErrorController 返回html

        //2:重点:自定义全局异常,拦截指定异常
        // 会根据自定异常返回值 是走ModelAndView 还是直接返回json
        // 会进入baseErrorController 返回html

        int a=10/0; //报错 后边执行
        List users = Arrays.asList(new User("张三1","000"),
                new User("张三1","111"),
                new User("张三2","222"),
                new User("张三3","333"));
        return CommonReturnType.crateCommonReturnType(users);
    }

    /**
     * 前后端不分离Controller 返回视图
     */
    @GetMapping(value = "/basic_table")
    public String basic_table(@RequestParam(value = "a") int b ){
        //1:重点:没有自定义全局异常,默认的异常解析器解析
        //这里出现异常没有返回值 会被DispatcherServlet拦截,捕获异常 发送/error
        // 会进入baseErrorController 返回html

        //2:重点:自定义全局异常,拦截指定异常
        // 会根据自定异常返回值 是走ModelAndView 还是直接返回json
        // 会进入baseErrorController 返回html
        
        //自定义错误 抛出 java.lang.ArithmeticException: / by zero
        int a=10/0; //报错 后边不执行
        // 这个异常ExceptionHandler 直接返回视图  return "login";//返回视图
        return "/table/basic_table";
    }
}

自定义全局异常

@Slf4j
@ControllerAdvice //底层Component
public class GlobalExceptionHandler {

    //自定义异常CommonReturnType {
    //    "status": "fail",
    //    "data": {
    //        "errCode": "300",
    //        "errMsg": "参数绑定错误"
    //    }
    //}

    //第一个自定义异常
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonReturnType GlobalControllerExceptions1(Exception exception){
        log.info("异常是{}:",exception);
        Map map=new HashMap();
        if (exception instanceof ServletRequestBindingException) {
            map.put("errCode", "300");
            map.put("errMsg", "参数绑定错误");
        } else if (exception instanceof NoHandlerFoundException) {
            map.put("errCode", "404");
            map.put("errMsg", "404错误");
        } else {
            map.put("errCode", "8888");
            map.put("errMsg",exception.getMessage());
        }
        return CommonReturnType.crateCommonReturnType("fail", map);//返回指定异常
    }


    //前提:没有配置全局异常解析器 出现异常会被抛出,没有异常解析器处理
    //然后发送/error请求 被baseErrorController拦截 返回标准页面

    //拦截指定异常,会返回ModelAndView 不加@ResponseBody java.lang.ArithmeticException: / by zero
    @ExceptionHandler(value ={NullPointerException.class,ArithmeticException.class})
    //@ResponseBody 异常直接返回页面
    public String GlobalControllerExceptions(Exception exception) {
        log.info("异常是{}:",exception);
        return "login";//返回视图
    }


}

你可能感兴趣的:(spring,boot,java,后端)