十分钟掌握springboot微服务架构的基本使用

 

        前言: SSM的框架的搭建比起传统的SSH已经优化了不少,但是配置时还是挺繁琐复杂,特别是各种xml的配置,让人让人莫名所以,这也违背了我们程序开发的原则。你说如果你接手一个大型项目,光看配置都要花好久时间,那还咋玩。所以呢,微服务的出现还是很有道理的。因为每一个大型项目都可以拆分成小的项目,每个小的项目只负责各自单一的功能,每个项目可以使用不同的技术,连接不同的数据库,部署到不同的服务器,项目之间通过restful接口互相访问即可。自从用上了微服务架构,感觉程序开发真的是越来越简便了。厉害厉害。说了一分钟废话了,下面用九分钟说一下springboot的基本使用。

解释一下概念:springboot不等于微服务,它只是一套开源框架,跟ssm差不多,只是基于springboot来开发微服务相当方便,所以这两个词一般都是成对出现的。当我们的服务越来越多时,就可以通过springcloud来统一管理这些服务了,springcloud才算是真正的微服务框架。可以认为 springboot ≈ ssm,springcloud = 多个 springboot

项目源码连接:springboot基本使用项目源码

一,生成springboot项目并提供接口

1.1 访问start.spring.io 输入包名项目名,添加基本依赖web (选Build web, including RESTful, application...) 生成工程。

  • 注1:以上的配图是比较早版本的样子,新版有一些改变,但是二者内容还是不变的。
  • 注2:如果使用IDEA开发的,可以在File -> new -> project -> Spring Initializr -> next 输入包名、项目名及依赖信息后直接在工程中生成springboot项目,很方便。

1.2 导入工程将下载后的项目解压,使用idea/eclipse的导入 import -> exist maven project. IEDA下是自带maven插件的,eclipse的话得自己配置一下。自己配置的方法也很简单,此处不做介绍。

 

1.3 编写接口。建包controller,写TestRestController类

 

1.4启动项目

运行主类:项目名+Application的main方法。

前台输入:localhost:8080/test即可显示:call success!

 

        总结:没错,第一个springboot的demo就这么完成了。是不是很6很简单很暴力。分分钟我们就开发了一个项目,提供了一个接口。别人也可以通过访问这个接口获取到我们提供的数据了。比起之前的springmvc项目开始前的还需要配置各种xml的是不是爽多了。

 

二,springboot访问html页面

2.1 springboot推荐前台使用thymeleaf模板。pom.xml文件中引入thymeleaf依赖:

(ps通过: mvnrepository.comsearch.maven.org 可以找到所需依赖的dependency)

<!-- thymeleaf 模板相关 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2.2 在自带的application.properties中对thymeleaf进行配置。

### server config
server.ip=127.0.0.1
server.port=8080
server.servlet.context-path: /sbDemo
​
### themeleaf
spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false

 

2.3 后台与html交互

    2.3.1建controller类,在Controller写前往html的接口,并提供数据

 

    2.3.2 写前台hello.html,接收并显示数据。thymeleaf的用法跟jsp差不多。能完成所有jsp能完成的事。

 

    2.3.3访问接口后前台显示:访问路径为 127.0.0.1:8080/sbDemo/hello

    注:路径前面对应你的配置文件application.properties中server的ip,port,context-path. ip默认是localhost,端口默认是8080,context-path默认为/,这三个值都可以不配。我只是做个如何配置的示范。不要就按我图中的ip来配了。都不配时按默认直接访问 localhost:8080 + 接口名 就可以了

    补充:html中需要引用css与js文件时,如果css和js不是直接在static下,而是在static下的文件夹中。在后台需要配置对静态资源的引用,否则访问不到资源文件。添加配置后,html中就可以用@{...}来引用static下的js/css/img等资源。当然,如果你只提供接口提供数据不涉及页面的话就不需要这些了。

前台引用如图:

 

三,springboot访问数据库

3.1 pom.xml中引入相关数据库如mysql驱动依赖和mybatis依赖:

<!-- mysql 数据库相关 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- mybatis 依赖相关 -->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>1.1.1</version>
</dependency>

引入数据库依赖后,在application.properties中配置数据库连接

3.2 编写Dao层从数据库获取数据,并测试

建库后,插入表数据sql:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(40) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
​
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', '东邪', '41');
INSERT INTO `users` VALUES ('2', '吸毒', '42');

建立dao层获取表数据,类中引用Mapper注解即可。(当然,也可以选择用mapper.xml文件的形式,此处不介绍。)

测试类测试能不能获取到数据,用自带的src/test/java即可:

测试成功结果如下。这时候就可以写controller、service、dao层的调用关系了。此时,前台的数据就可以显示为从数据库获取的数据了。

 

        总结:至此,建立springboot工程,连接数据库,编写dao层,service层,controller层,提供接口,提供页面的功能就完成了。都没花什么时间,作为程序猿,除了引入一些必要jar包和简单的配置一下之外,我们什么都不用做了。只需要集中精力于我们的业务逻辑就行。整个过程没花多少时间,感觉还是很不错的。

 

---------------------------------------------------------------分割线---------------------------------------------------------------------------

分割线后面的内容是一些扩展的内容,有需要的话再看一些。不然只看前面三步就行了。

 

四,springboot自定义异常处理

4.1 springboot默认的异常处理是/error接口。可以通过实现ErrorController来重新定义对错误信息的处理。

 

提供两种返回错误。一种是页面返回、当你是页面请求的时候就会返回到我们指定的页面 error.html

另外一种是json请求的时候就会返回json错误

 

五,springboot自定义定时任务

同样可以使用spring框架中的@Scheduled创建定时任务,在定时任务类中加入component注解让spring找到,然后在需要定时执行的方法上加上@Scheduled就行。

但是springboot中需要在主类中加入@EnableScheduling注解,表示启用定时任务的配置

 

六,springboot自定义切面(AOP)

与spring中切面的使用一致。都是先定义自己的注解,然后定义自己的切面。使用时在指定相应的PointCut即可。PointCut可以指定到任意地方。某个类中,某个方法,某个包下都可以。此文不做介绍。仅介绍springboot切面的基本使用,如下

6.1 可以定义自己的注解,使以该注解为切入点

6.2 定义自己的切面

其中获取注解中的信息的方法如下:

 

6.3 前台发送请求,后台接收后执行我们的切面。

 

结果就是在调用接口执行testAsp()方法前,被我们定义的MyAspect先拦截了一波,执行了相应的操作,成功地使用了我们的自定义切面。

常见的切面使用有 log日志记录、事务处理、访问拦截等以实现某些功能。

 

七,springboot导出jar/war包

7.1 导出jar包

由于生成的pom.xml文件中默认指定生成的是jar,

<packing>jar<packing>

所以在pom.xml文件所在的目录下,打开命令窗口,运行如下命令打包即可。(表示不需要测试类的打包)

mvn clean package -Dmaven.test.skip=true

如果不想打那么长的后面那串,可以在pom中properties标签下把skipTests直接设置为true.即可只用 mvn clean package命令打包

如图,打包成功Build SUCCESS后

  jar包会生成到target目录中

移动jar包至任意位置运行,在jar所在位置使用java -jar [jar包名] 即可使用jar包启动项目。效果与在IDE中运行一致。

 

7.2 导出war包

如果是想部署到外置的tomcat容器中,那就需要导出war包。

    7.21 此时在pom.xml中将打包类型改为war.

<packaging>war</packaging>

此时可能pom文件会报web.xml文件缺失的错误。加上以下语句即可。

<failOnMissWebXml>false</failOnMissWebXml>

    7.22 打包类型改成war包后,此时需要移除springboot的内置tomcat依赖。并添加servlet所需的依赖。

 

    7.23 因为打war包后就不会运行main方法了,入口改为了SpringBootServletInitializer。所以我们需要在我们的主类Application.java同级的目录下,新建一个类继承SpringBootServletInitializer,并在类中指向我们需要运行的主类Application对象。这样war包就有入口了。

    7.24 此时,就可以打war包了。还是运行打包的命令:

mvn clean package -Dmaven.test.skip=true

7.25 此时在target目录中得到的就是war包了,将war包移到tomcat的webApp目录下,然后在tomcat的bin目录下运行startup.bat启动tomcat,就能启动项目了。前台访问地址= ip+端口+war包名+接口地址。

PS:自动生成的war包或者jar包名都会带有一串数字和英文,可以通过finalName来控制生成的名字。

八,springboot上传/下载文件

springboot的文件上传就是spring的文件上传。

前台:

<form enctype="multipart/form-data" method="post" action="/sbDemo/uploadImg">
    图片<input type="file" name="file"/> <input type="submit" value="上传"/>
</form>

后台Controller中:

/**
      * 处理文件上传
    */
    @RequestMapping(value="/uploadImg", method = RequestMethod.POST)
    @ResponseBody
    public String uploadImg(@RequestParam("file") MultipartFile file,
            HttpServletRequest request) {
        String msg = "SUCCESS";
        String fileName = file.getOriginalFilename();
        //该路径可以从文件中读取,此处演示略
        String filePath = "C:\\Users\\Administrator\\Desktop\\test\\upload\\img\\";
        System.err.println("上传路径:"+filePath);
        try {
            uploadFile(file.getBytes(), filePath, fileName);
        } catch (Exception e) {
            msg = e.getMessage();
        }
        //返回json
        return msg;
    }
​
    /**
     * @Description : 上传文件
     * @param file : 文件实体
     * @param filePath : 文件目标路径
     * @param fileName : 文件名
     * @throws Exception
     */
    public void uploadFile(byte[] file, String filePath, String fileName) throws Exception { 
        File targetFile = new File(filePath);  
        if(!targetFile.exists()){    
            targetFile.mkdirs();    
        }       
        FileOutputStream out = new FileOutputStream(filePath+fileName);
        out.write(file);
        out.flush();
        out.close();
    }

     #PS:另外可以在配置文件中设置上传文件的相关参数

uploadPath=..\\sbDemo-Resource\\upload\\
spring.http.multipart.maxFileSize=100Mb
spring.http.multipart.maxRequestSize=100Mb

文件下载:

前台:

<a onclick="window.location.href='/background/url'">下载</a>

后台:

@RequestMapping(value = "/downUserExcelTemplate.admin")
public void downUserExcelTemplate(HttpServletResponse response){
    try{
        fileDownload(response, "E:\\test.xls", "test.xls");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}


public void fileDownload(final HttpServletResponse response, String filePath, String fileName) throws Exception{

    byte[] data = FileUtil.toByteArray2(filePath);
    fileName = URLEncoder.encode(fileName, "UTF-8");
    response.reset();
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
    response.addHeader("Content-Length", "" + data.length);
    response.setContentType("application/octet-stream;charset=UTF-8");
    OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
    outputStream.write(data);
    outputStream.flush();
    outputStream.close();
    response.flushBuffer();
}

 

 

九、前台相关功能介绍

 

十、springboot事务

简单一点的用法就是,直接在controller对应的方法中加入@Transactional注解即可,加入后如果程序出现异常,则已执行的sql将会自动回滚。一般是方法中有多条关联sql时用。

需要注意的是,如果mysql 事务未提交导致死锁 Lock wait timeout exceeded; try restarting transaction 解决办法是:

1.先用这条命令查询数据库阻塞的进程

SELECT * FROM information_schema.innodb_trx 

看 trx_tables_in_use, trx_tables_locked, trx_rows_locked 这三个值是否为0,不为0的则证明有事务未提交。

此时找到该行对应 trx_mysql_thread_id, 记录。

2.然后使用kill命令关闭该事务即可

 kill   id  ; (杀死第一步找到的trx_mysql_thread_id的进程)

 

十一、springboot日志

springboot默认使用Logback来记录日志,且默认是只打印到控制台中,不会输出到文件。需要输出到文件中,只需要在配置文件application.properties中加上一句:loggin.file = xxx 即可。可以使用相对路径或者绝对路径。

logging.file=../logs/sbDemo-test.log

默认是输出INFO以上的内容,可以设置等级如:

  • logging.level.com.jvxb=DEBUG          (执行的sql是以debug的形式输出的,想在控制台显示sql,需要设置成debug)

  • ogging.level.root=WARN:root日志以WARN级别输出

有时候我们希望配置复杂一点的日志记录,如每天保存一次,每个日志文件多大,不同level的文件写入不同的日志记录这种,这时候你就要加载自己的日志配置文件了。 方法是在配置文件application.properties中加上一句:loggin.config=classpath:xxx.xml如下:然后把你自己日志配置的xml放到resource文件夹下即可。

logging.config=classpath:mylogbackConfig.xml

注:springboot默认去加载classpath下的logback-spring.xml,如果直接把logback-spring.xml文件放到classpath下,则不需要在application.properties中使用logging.config

PS:日志配置文件 mylogbackConfig.xml实例:

<configuration debug="false" scan="true" scanPeriod="10 seconds">
    <!-- 日志配置:
    	包名:com.jvxb,将该包下的所有日志按debug、info、warn、error等级分别保存到[项目同一级]文件夹sbDemo-Log下的logback_debug、logback_info、logback_warn、logback_error文件中。每日凌晨0点将日志文件归档保存为zip压缩包,每个压缩包最大30M,若压缩包文件大小超过30M,将会被保存为多个压缩包。最多保留最近30天的归档日志文件。
	-->

    <contextName>logback</contextName>
    
    <!--输出sql语句-->
    <logger name="com.jvxb" level="debug"/>
    <property name="path" value="../sbDemoLog"></property>
    <property name="maxHistory" value="30"/>
    <property name="maxFileSize" value="30MB"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="debug_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一归档 -->
            <fileNamePattern>${path}/logback_debug.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="info_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一归档 -->
            <fileNamePattern>${path}/logback_info.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="warn_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一归档 -->
            <fileNamePattern>${path}/logback_warn.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <appender name="error_file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${path}/logback_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天一归档 -->
            <fileNamePattern>${path}/logback_error.log.%d{yyyy-MM-dd}-%i.zip</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${maxFileSize}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <root>
        <level value="info"/>
        <appender-ref ref="console"/>
        <appender-ref ref="debug_file"/>
        <appender-ref ref="info_file"/>
        <appender-ref ref="warn_file"/>
        <appender-ref ref="error_file"/>
    </root>

</configuration>

 

十二、springboot过滤器

springboot使用过滤器有两种方式,

一种是使用 @WebFilter 注解如

@WebFilter(filterName = "LoginFilter", urlPatterns = "/*")
public class LoginFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		
	}
}

然后在Application主类添加上 @ServletComponentScan 注解。加上@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。这个另外说一个注解 @ComponentScan,我们知道,Spring是一个依赖注入框架。所有的内容都是关于bean的定义及其依赖关系。定义Spring Beans的第一步是使用正确的注解:@Component或@Service或@Repository。但是,Spring不知道你定义了某个bean除非它知道从哪里可以找到这个bean。ComponentScan做的事情就是告诉Spring从哪里找到bean。使用@ComponentScan自动扫描组件实例包扫描会扫描只要标注了@Controller,@Service,@Repository,@Component 这四个注解都会被扫描到容器中。

另一中是使用配置类如:(注意此时LoginFilter不需要用@WebFilter注解,主类也不需要加上@ServletComponentScan注解),

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean filterRegist() {
        FilterRegistrationBean frBean = new FilterRegistrationBean();
        frBean.setFilter(new LoginFilter());
        frBean.addUrlPatterns("/*");
        frBean.setOrder(1);    //order值越小越先加载
        return frBean;
    }
}

 多个过滤器时推荐使用第二种,便于修改和管理。

如访问网站时,未登录者需要先进行登录操作的登录过滤器实例:

package com.jvxb.demo.sbDemo.livable.configuration.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.filter.OncePerRequestFilter;

@WebFilter(filterName = "LoginFilter", urlPatterns = "/*")
public class LoginFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// 获取请求的uri
		String uri = request.getRequestURI();
		// 获取容器路径
		String ctxPath = request.getContextPath();

		// 资源文件不过滤: 以.js|css|png|jpg|jpeg|gif|ico|woff结尾的
		if (uri.matches(".+\\.(html|js|css|png|jpg|jpeg|gif|ico|ttf|woff|woff2)$")) {
			filterChain.doFilter(request, response);
			return;
		}

		// 登录请求、验证登录的请求不过滤
		if (uri.startsWith(ctxPath + "/admin/login") || uri.startsWith("/admin/checkLogin")) {
			filterChain.doFilter(request, response);
			return;
		}

		// 否则执行过滤:未登录者须先前往登录界面登录
		Object obj = request.getSession().getAttribute("user");
		if (null == obj) {
			response.sendRedirect(ctxPath + "/admin/login");
		} else {
			filterChain.doFilter(request, response);
		}

	}

}

 

十三、springboot拦截器

springboot的拦截器也是对请求进行的系列验证或处理,关于拦截器和过滤器的区别此文不做介绍,不清楚的可以参考这里,下面说一下springboot拦截器的实现。如下:

12.1 写一个类实现 HandlerInterceptor 接口,然后重写 里面的 preHandle()、postHandle()、afterCompletion()方法

public class MyInterceptor implements HandlerInterceptor {
    // JDK8后,三个方法已经有了默认实现,所以不一定要重写。但我们写拦截器就是为了重写啊,啊哈哈
    // preHandle()、postHandle()、afterCompletion()
}

preHandle

  • 调用时间:Controller方法处理之前
  • 执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行
  • 若返回false,则中断执行,注意:不会进入afterCompletion

postHandle

  • 调用前提:preHandle返回true
  • 调用时间:Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作
  • 执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序倒着执行
  • 备注:postHandle虽然post打头,但post、get方法都能处理

afterCompletion

  • 调用前提:preHandle返回true
  • 调用时间:DispatcherServlet进行视图的渲染之后
  • 多用于清理资源

12.2 在WebMvcConfigurer的实现类中,重写addInterceptors()方法将该拦截器注册即可。

@EnableWebMvc
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
		 //拦截器
		 registry.addInterceptor(new MyInterceptor()).addPathPatterns("/admin/api/**");
	 }

}

下面举一个拦截器的例子:在系统中会提供对外的数据接口,该拦截器的作用就是是拦截某些访问数据的请求,使访问请求不能过快(恶意请求攻击),或者对请求加一定的验证(如提供某些参数来验证该请求是否有效)等。

代码有点多 - -。就不贴了。 也就是,当 /admin/api/** 的接口被访问时,就记录一下访问者的ip,然后判断该访问请求是否有效(1min钟内超过N次,或者没有携带某些参数,或者参数校验不合格之类的就拦截住这样)

注意:拦截器是在过滤器之后才执行的,所以一般拦截器中的拦截地址,过滤器是不用处理的。否则就进不到拦截器中。还有就是可以注册多个拦截器,会按照注册顺序依次拦截。

 

十四、springboot监听器

监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。通俗的讲,监听器就比如你盯着一盘好吃的,有人拿你的吃的的时候,你会立马采取相应的行动。Java对Servlet中的ServletContext(上下文),HttpSession,ServletRequest这三种对象提供了一些监听的接口,我们可以自定义监听器来实现这些接口,对这三种对象的一些事件进行监听。

此处我们以ServletRequestListener来举例,先说明一下,ServletRequestListener是一个接口,这个接口是用来监听请求的,里面有两个方法,分别是请求创建和请求销毁。

package com.jvxb.demo.sbDemo.livable.configuration.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;

@WebListener
public class MyRequestListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("请求创建了:");
        //将请求开始时间添加到该请求,用于计算本次请求的用时
        String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        sre.getServletRequest().setAttribute("startTime", startTime);
        try {
            //项目经理说,系统效率需要先慢一点,客户提出优化时再进行优化
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("请求结束了");
        String uri = ((HttpServletRequest) sre.getServletRequest()).getRequestURL().toString();
        System.out.println("请求路径:" + uri);
        String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        System.out.println("请求结束时间:" + endTime);
        String startTime = sre.getServletRequest().getAttribute("startTime").toString();
        System.out.println("请求耗时:" + startTime + " - " + endTime);
    }

}

(使用@WebListener注解时APP主类需添加@ServletComponentScan注解) 

此时我们前台访问:localhost:8080,查看控制台。可以看到监听器与过滤器的信息如下

监听器与过滤器

其它监听器的使用可以参考另外一篇:《Servlet中几个监听器Listener的使用实例

 

十五、springboot多数据源

1.配置多个数据源(以两个为例)

注意 url 不同,多个数据源时改为 jdbc-url

2.建立多个数据源的配置文件

主数据源配置:

package com.jvxb.test.dbtest.livable.module.test.conf;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.jvxb.test.dbtest.livable.module.*.mapper", sqlSessionFactoryRef = "testSqlSessionFactory")
public class TestDbConf {
    // 将这个对象放入Spring容器中
    @Bean(name = "testDataSource")
    // 表示这个数据源是默认数据源
    @Primary
    // 读取application.properties中的配置参数映射成为一个对象
    // prefix表示参数的前缀
    @ConfigurationProperties(prefix = "spring.datasource.test")
    public DataSource getTestDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "testSqlSessionFactory")
    // 表示这个数据源是默认数据源
    @Primary
    // @Qualifier表示查找Spring容器中名字为test1DataSource的对象
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                // 设置mybatis的xml所在位置
                new PathMatchingResourcePatternResolver().getResources("com.jvxb.test.dbtest.livable.module.*.mapper"));
        return bean.getObject();
    }
    @Bean("testSqlSessionTemplate")
    // 表示这个数据源是默认数据源
    @Primary
    public SqlSessionTemplate testSqlsessiontemplate(
            @Qualifier("testSqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }
}

其它数据源配置:

package com.jvxb.test.dbtest.livable.module.test.conf;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.jvxb.test.dbtest.livable.devmodule.*.devmapper", sqlSessionFactoryRef = "devSqlSessionFactory")
public class DevDbConf {

    @Bean(name = "devDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.dev")
    public DataSource getDevDateSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "devSqlSessionFactory")
    public SqlSessionFactory devSqlSessionFactory(@Qualifier("devDataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("com.jvxb.test.dbtest.livable.devmodule.*.devmapper"));
        return bean.getObject();
    }

    @Bean("devSqlSessionTemplate")
    public SqlSessionTemplate devsqlsessiontemplate(
            @Qualifier("devSqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }

}

注意mapper的扫描位置,和设置PathMatchingResourcePatternResolver的位置,改为自己项目中的。不同数据库的mapper需要放在不同目录下。

3.使用多个数据源

并查看结果:

 

十六、springboot其它

resource下放入: banner.txt 文件, 内容如下,会有好运发生喔

${AnsiColor.BRIGHT_YELLOW}

//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\  =  /O                              //
//                      ____/`---'\____                           //
//                    .'  \\|     |//  `.                         //
//                   /  \\|||  :  |||//  \                        //
//                  /  _||||| -:- |||||-  \                       //
//                  |   | \\\  -  /// |   |                       //
//                  | \_|  ''\---/''  |   |                       //
//                  \  .-\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\  `. . ___                     //
//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            佛祖保佑       永不宕机     永无BUG                 //

${AnsiColor.BRIGHT_BLUE}
::: Project (version:${application.version}) :::             \(^O^)/    Spring-Boot ${spring-boot.version}
${AnsiColor.DEFAULT}

#插入一波: 基于Springboot的简易后台框架

  • 27
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 24
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值