SpringBoot开源项目个人博客——(3)构建框架

经过前面的设计,搭建好数据库后,就进入到开发过程。

前端页面直接在oneStar老哥的博客中下载,导入即可。前期不建议直接用有模板引擎的完整前端代码,可以先使用没有thymeleaf的静态页面,当需要编写哪个页面的业务时,再逐个引入(不然在没有数据的又访问了页面的情况会报错)。

一、构建springboot框架

1. 使用idea搭建springboot项目,引入相应模块

使用idea新建springboot项目

在这里插入图片描述

选择springboot版本,目前先引入如下依赖,后续还会不断添加。
在这里插入图片描述

创建好项目后,初始结构如下,笔者使用的是yml配置文件,所以先把application.properties改成application.yml,然后就可以进行相关配置。

在这里插入图片描述

2.配置springboot配置文件

跟oneStar博客的操作一样,配置了开发(application-dev.yml)部署(application-pro.yml)两个不同生产环境的配置文件,最后也可以在application.yml这个总的配置中就进行切换。

  • application.yml

其实上新版thymeleaf引入后,可以不需要配置也能够使用。

默认的服务端口为 8080(可以自定义)。

# 公共配置和表明当前配置文件
spring:
  profiles:
    active: dev
#thymeleaf新版不需要配什么
  thymeleaf:
    prefix: classpath:/templates/  #prefix:指定模板所在的目录
    check-template-location: true  #check-tempate-location: 检查模板路径是否存在
    cache: false  #cache: 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器,线上设置为true,可以提高性能。
    suffix: .html
    encoding: UTF-8
    #content-type: text/html

#配置mybatis
mybatis:
  #别名(后续除了entity,dto也需要配置别名,多个包需要这样分隔 ,;)
  type-aliases-package: com.hxj.entity ,;com.hxj.queryvo
  #mapper地址(自动在resources下找)
  mapper-locations: classpath:mybatis/mapper/*.xml
  #受不了经常配resultmape,直接驼峰自动转换
  configuration:
    map-underscore-to-camel-case: true

然后配置mybatis配置,需要注意这里的mapper-locations路径,用户可以自定义,笔者使用这样的路径。

在这里插入图片描述


在开发和部署环境中,配置数据库连接和日志

不同的环境可能需要不同的 端口号,数据库,日志的不同级别…。

  • application-dev.yml
#配置数据库
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/myblog?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

#配置日志级别(整个项目/可以分开来设置不同的级别)
logging:
  level:
    root: info
    com.hxj : debug
  #日志文件的目录(指定文件夹下生成日志),默认超过多大就切分(也可以做个性化配置)
  file:
    path: log/blog-dev.log
  • application-pro.yml
#配置数据库
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/myblog?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456

#配置日志级别(整个项目/可以分开来设置不同的级别)
logging:
  level:
    root: warn
    # 不让它打印那么多信息
    com.hxj : info
  #日志文件的目录(指定文件夹下生成日志),默认超过多大就切分
  # (也可以做个性化配置,重写springboot日志配置 logback-spring.xml)
  file:
    path: log/blog-pro.log

server:
  port: 8081
  • 重写springboot日志配置

先在application.yml同理目录下创建 这样的文件logback-spring.xml,就可以进行配置(笔者直接粘贴使用):

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--包含Spring boot对logback日志的默认配置-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <!--上面是springboot默认集成的就有这些,我们需要拿过来覆盖,下面再写自定义的-->

    <!--重写了Spring Boot框架 org/springframework/boot/logging/logback/file-appender.xml 配置-->
    <appender name="TIME_FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i</fileNamePattern>
            <!--切分日志的方式 之后的命名格式-->
            <!--保留历史日志一个月的时间-->
            <maxHistory>30</maxHistory>
            <!--
            Spring Boot默认情况下,日志文件10M时,会切分日志文件,这样设置日志文件会在10M时切分日志
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>

        </rollingPolicy>
    </appender>

    <!--也是默认集成的配置-->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <!--输出到 控制台-->
        <appender-ref ref="TIME_FILE"/>
        <!-- 输出到 日志文件  -->
    </root>

</configuration>
        <!--
            1、继承Spring boot logback设置(可以在appliaction.yml或者application.properties设置logging.*属性)
            2、重写了默认配置,设置日志文件大小在10MB时,按日期切分日志 (也可以不用这配置文件,默认用大小切分)
        -->

随着项目的运行,可以看到在dev和pro配置下打印的log日志以及在项目中自动生成一个log文件夹,存放dev和pro日志文件。运行哪个环境后的日志,就存到对应的文件夹中。
在这里插入图片描述

后续开发使用dev环境,可以看到更多的信息。


二、异常处理

1.定义错误页面

  • 404.html
  • 500.html
  • error.html(自定义错误)

错误页面的实现在springboot里面很简单,在template下,写一个error目录(什么错误,里面写对应错误代码的html

springboot帮我们做了很多静态资源的映射。正常情况, 我们会把页面 放到 template 文件夹中,但是template不是静态资源目录, 只能通过controller去映射,访问这个目录下需要模板引擎.(这就是需要Thymeleaf模板引擎的原因)

在这里插入图片描述

定义一个跳转首页的controller,对跳转首页进行测试。

@Controller
public class IndexController {
    //通过get方式请求路径
    @GetMapping("/")
    public String index(){
        return "index";
    }
}
  • 404测试,访问一个无效的后缀路径 localhost:8080/asdaf,访问就会发现跳转到404错误页面。
  • 500测试,(在服务器中)在controller中写一段错误代码int a=1/0; 让服务器出错,在访问的localhost:8080时,运行到错误代码时,就会跳转到500页面。(注释掉这段代码,再次访问会发现正常)

对于自定义的错误(我们想当发生一些错误时能进入自定义的一个错误页面),但springboot是不会有对应的错误代码,所以去找也找不到自定义页面。我们此时就需要做一个全局的异常处理,拦截所有的异常,再做一个统一的分发。


2.全局异常处理

com.xx包下创建一个handler包,创建ControllerExceptionHandler错误页面拦截器,通过定义这个类来拦截所有的异常,代码如下:

@ControllerAdvice //拦截所有有 @controller注解的 controller控制器
public class ControllerExceptionHandler {
    //这里是应用切面处理异常,并且将处理的内容加入到日志里
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    //获取日志,记录我们的异常

    //回顾spring mvc的视图解析器
    @ExceptionHandler(Exception.class) //表示这个方法可以做拦截器,而且只要是exception级别的都可以拦截
    public ModelAndView exceptionHandler(HttpServletRequest request,Exception e) throws Exception {

        //记录error信息, 请求的url,异常信息 {}是占位
        logger.error("Request URL : {}, Exception : {}",request.getRequestURI(),e);


        //当标识了状态码的时候就不拦截(这样能让它不拦截404 500等)
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }

        //返回到错误页面(回顾mvc 返回页面)
        ModelAndView mv = new ModelAndView();
        mv.addObject("url", request.getRequestURI());
        mv.addObject("exception", e);
        mv.setViewName("error/error"); //统一拦截所有的exception,返回到error页面

        return mv;
    }
}

为了不把404和500错误也转入到自定义错误页面(error),需要做一些判断,当是这些错误时(即有错误代码时),不转入error错误页面。

通过判断标识了状态码的 异常就不会进行拦截, 该去哪里还是去哪.

这样我们这个统一拦截的页面处理类, 就可以 即能处理 自定义错误 也能处理 404和500错误.


3.自定义异常(资源找不到

对于资源找不到异常,一般也是要跳转到404页面的,这里就需要自定义一个异常类,专门用来应对资源找不到。

com.xxx包下面新建NotFoundException类。

(当我们想让一些错误发生时,不走error页面,可以抛出一个404状态码的异常,走404页面)

@ResponseStatus(HttpStatus.NOT_FOUND) //想要返回404页面,就要指定对应的返回状态码
public class NotFoundException extends RuntimeException{
    public NotFoundException() {
    }

    public NotFoundException(String message) {
        super(message);
    }

    public NotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

疑惑

无论看视频还是在博客里面的解释,自己看的时候,对于为什么要把【资源找不到】也要做一个自定义异常类也感到不解?

原本资源找不到不就算是404么,为什么还要自定义。即使上面已经定义了全局异常处理还做了筛选,难道不就够了吗?

(在做完了项目后)实验了一下,果不其然,如果访问一个不存在的资源(访问了一个不存在的博客http://localhost:8080/blog/1000),不需要抛出自定义错误,单靠全局异常的拦截器也能够成功找到404。

然后又在完成后的首页控制器中抛出一个不是404和500状态码标识的异常,这样它也会走到自定义错误页面。

在这里插入图片描述

在这里插入图片描述

在此以我的理解,虽然做了自定义错误类,但不仅是用到资源找不到的情况,可以对其他可能出现的情况,抛出这个异常走404页面,才有了这个“自定义的404标识的异常类“,把这种情况下的错误转到404页面,而不是error页面。

但是目前是没有太大用,用全局异常处理已经能完成绝大部分了。

但还是按正常流程写下记录,上面我的废话可以不用看(仅供参考


三、日志处理

采用SpringBoot中的AOP进行日志处理,AOP可以以切面的形式拦截,将日志内容记录下来,这里记录以下日志信息:

  • 访问的URL
  • 访问者的IP
  • 访问时调用的方法
  • 访问时传递的参数
  • 访问时返回的内容

spring AOP处理

能以切面的方式拦截,所有的web控制器的请求的流,包括从前端返回的请求到业务处理… (就像一个一样)。

aop 的使用就在这线一样 的 上 切一刀

  1. 在pom.xml添加AOP的依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 记录日志类

这里使用自定义类配合注解来实现AOP 【切面定义】。

com.xxx包下新建aspect包,新建切面LogAspect类。

在访问页面(controller)之前,拦截请求的URL、IP、调用的方法、传递的参数、返回的内容,并记录到日志中。

@Aspect
@Component
public class LogAspect {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* com.hxj.controller.*.*(..))") //这里改为用户自己的路径
    // 拦截所有(所有的控制器,任何的"类"里面 的 任何参数的"方法" 任何返回值 都拦截了),然后就可以围绕切面做处理
    public void log(){}

    @Before("log()") //在切面(请求方法)之前
    public void doBefore(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //这样就可以拿到http参数了, ip url
        HttpServletRequest request = attributes.getRequest();
        String url = request.getRequestURI();
        String ip = request.getRemoteAddr();//远程地址

        //获取方法和参数需要用到切面的参数
        String classMethod = joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        RequestLog requestLog = new RequestLog(url, ip, classMethod, args);
        logger.info("Request:{}",requestLog);
    }

    @After("log()") //切面之后
    public void doAfter(){
        //logger.info("-------doAfter------------");
    }

    @AfterReturning(returning = "result",pointcut = "log()") //方法执行完 返回值之后,拦截
    public  void doAfterReturn(Object result){ //返回的结果
        logger.info("Result : {}",result);
    }

    //    封装请求参数为一个内部类
    private  class RequestLog{
        private String url;
        private String ip;
        private String classMethod;
        private Object[] args;

        public RequestLog(String url, String ip, String classMethod, Object[] args) {
            this.url = url;
            this.ip = ip;
            this.classMethod = classMethod;
            this.args = args;
        }

        @Override
        public String toString() {
            return "RequestLog{" +
                    "url='" + url + '\'' +
                    ", ip='" + ip + '\'' +
                    ", classMethod='" + classMethod + '\'' +
                    ", args=" + Arrays.toString(args) +
                    '}';
        }
    }

}

Aop中execution的语法

在这里插入图片描述


至此项目的基础框架搭建完成。(启动访问测试无误,日志输出没有问题后即可进入下一个阶段)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值