springBoot(1)--初步理解
在没有用SpringBoot之前,我们用spring和springMVC框架,但是你要做很多比如:
(1)配置web.xml,加载spring和spring mvc
2)配置数据库连接、配置spring事务
3)配置加载配置文件的读取,开启注解
4)配置日志文件
会比较繁琐,但是用springBoot我仅仅只需要非常少的几个配置就可以迅速方便的搭建起来一套web项目或者是构建一个微服务!
小案例
Springboot的Java配置方式是通过 @Configuration 和 @Bean 这两个注解实现的:
1、@Configuration 作用于类上,相当于一个xml配置文件;
2、@Bean 作用于方法上,相当于xml配置中的<bean>;
一.项目project
项目采用的是:maven,eclipse
项目github地址:springbootstudy1
1.pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.binron.springboot</groupId> <artifactId>binron-springboot</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.0.2.RELEASE</version> </dependency> <!-- 连接池 --> <dependency> <groupId>com.jolbox</groupId> <artifactId>bonecp-spring</artifactId> <version>0.8.0.RELEASE</version> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <!-- 资源文件拷贝插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- java编译插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> <pluginManagement> <plugins> <!-- 配置Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
我在配置pom文件的时候,遇到麻烦就是在本地仓库找不到我的相应jar,从远处仓库下载也下载不到,后来才发现是settings.xml的问题
所以我在网上找了个settings.xml文件,直接把我的给替换掉,然后在 clean --install--update就好了。网上settings.xml文件内容。
2.User对象
public class User { private String username; private String password; private Integer age; //提供set和get方法 }
3.编写UserDAO 用于模拟与数据库的交互
我把userDao模拟成连接数据库并取得值
public class UserDAO { public List<User> queryUserList(){ List<User> result = new ArrayList<User>(); // 模拟数据库的查询 for (int i = 0; i < 10; i++) { User user = new User(); user.setUsername("username_" + i); user.setPassword("password_" + i); user.setAge(i + 1); result.add(user); } return result; }
4.编写UserService 用于实现User数据操作业务逻辑
@Service public class UserService { @Autowired // 注入Spring容器中的bean对象 private UserDAO userDAO; public List<User> queryUserList() { // 调用userDAO中的方法进行查询 return this.userDAO.queryUserList(); } }
5. 编写SpringConfig 用于实例化Spring容器
@Configuration //通过该注解来表明该类是一个Spring的配置,相当于一个xml文件 @ComponentScan(basePackages = "cn.itcast.springboot.javaconfig") //配置扫描包 public class SpringConfig { @Bean // 通过该注解来表明是一个Bean对象,相当于xml中的<bean> public UserDAO getUserDAO(){ return new UserDAO(); // 直接new对象做演示 } }
6. 编写测试方法 用于启动Spring容器
public class Main { public static void main(String[] args) { // 通过Java配置来实例化Spring容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // 在Spring容器中获取Bean对象 UserService userService = context.getBean(UserService.class); // 调用对象中的方法 List<User> list = userService.queryUserList(); for (User user : list) { System.out.println(user.getUsername() + ", " + user.getPassword() + ", " + user.getPassword()); } // 销毁该容器 context.destroy(); } }
最后看后台数据有没有成功打印:
一、快速创建项目
springboot官网提供了工具类自动创建web应用:网址:http://start.spring.io/
官网页面
1、快速创建一个
选择web代表这是一个web项目
这样就可以吧项目放到eclipse或者idea中进行运行了。
这里需要注意几点:
1.springboot2X,所需要mevan是3.0+,JDK是1.8+
2.在做项目开发的时候,主入口Application类(带有注解@SpringBootApplication),要放在所有包之上。
二、初解jackson
jackson是springboot自带的json框架
jackson常用标签
(1)指定字段不返回:@JsonIgnore
(2)指定日期格式:@JsonFormat(pattern="yyyy-MM-dd hh:mm:ss",locale="zh",timezone="GMT+8")
(3)空字段不返回:@JsonInclude(Include.NON_NUll)
(4)指定别名:@JsonProperty
举例:
user对象
public class User { @JsonProperty("account") private int age; @JsonIgnore private String pwd; @JsonInclude(Include.NON_NULL) private String phone; @JsonFormat(pattern="yyyy-MM-dd hh:mm:ss",locale="zh",timezone="GMT+8") private Date createTime; //提供set和get方法,tostring方法 }
Controller类中映射方法
@GetMapping("/testjackson") public Object testjson(){ return new User(0, "密码", null, new Date()); }
效果演示 接口测试工具是postman
我们发现:
1:日期时间格式变成了指定格式
2:age属性显示在界面变成了:account
3:pwd并没有显示
4:因为phone属性为null,所以也没有显示。
目录结构,文件上传
一、目录结构
1、目录讲解
src/main/java:存放代码
src/main/resources
static: 存放静态文件,比如 css、js、image, (访问方式 http://localhost:8080/js/main.js)
templates:存放静态页面jsp,html,tpl
config:存放配置文件,application.properties
resources:
2、同个文件的加载顺序,静态资源文件
Spring Boot 默认会挨个从
META/resources > resources > static > public 里面找是否存在相应的资源,如果有则直接返回。
什么意思呢,就是比如你有个index.html文件,springboot默认放在以上文件夹是可以访问到的,而且是按照这个顺序访问。
案例:我在,resources,static ,public ,templates都放一个index.html文件,然后输入:localhost:8080,看访问的是哪个index.html
可以看出:首先访问就是resources里面的index.html
text文件默认是访问不了的,就算你的是localhost:8080/text/index.html也是访问不了的。
不过,你在application.properties配置如下,就可以访问了
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/test/
二、文件上传
一、war包方式进行上传
springboot文件上传的对象是MultipartFile,它是file的子类,源自
1)静态页面直接访问:localhost:8080/index.html
注意点:
如果想要直接访问html页面,则需要把html放在springboot默认加载的文件夹下面
2)MultipartFile 对象的transferTo方法,用于文件保存(效率和操作比原先用FileOutStream方便和高效)
案例:在static文件目录下,
upload.html
<!DOCTYPE html> <html> <head> <title>uploadimg.html</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <!--涉及文件上传,这里就需要multipart/form-data--> <form enctype="multipart/form-data" method="post" action="/upload"> 文件:<input type="file" name="head_img"/> 姓名:<input type="text" name="name"/> <input type="submit" value="上传"/> </form> </body> </html>
FileController类
package com.jincou.ocontroller; import java.io.File; import java.io.IOException; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import com.jincou.model.JsonData; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; @Controller public class FileController { //文件放在项目的images下 private static final String filePath = "C:\\Users\\chenww\\Desktop\\springbootstudy\\springbootstudy\\src\\main\\resources\\static\\images\\"; @RequestMapping(value = "upload") @ResponseBody public JsonData upload(@RequestParam("head_img") MultipartFile file,HttpServletRequest request) { //file.isEmpty(); 判断图片是否为空 //file.getSize(); 图片大小进行判断 String name = request.getParameter("name"); System.out.println("用户名:"+name); // 获取文件名 String fileName = file.getOriginalFilename(); System.out.println("上传的文件名为:" + fileName); // 获取文件的后缀名,比如图片的jpeg,png String suffixName = fileName.substring(fileName.lastIndexOf(".")); System.out.println("上传的后缀名为:" + suffixName); // 文件上传后的路径 fileName = UUID.randomUUID() + suffixName; System.out.println("转换后的名称:"+fileName); File dest = new File(filePath + fileName); try { file.transferTo(dest); //上传成功 return new JsonData(0, fileName); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //上传失败 return new JsonData(-1, "fail to save ", null); } }
JsonData对象,用来返回状态
package com.jincou.model; import java.io.Serializable; public class JsonData implements Serializable { private static final long serialVersionUID = 1L; //状态码,0表示成功,-1表示失败 private int code; //结果 private Object data; //错误描述 private String msg; public int getCode() { return code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public void setCode(int code) { this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public JsonData(int code, Object data) { super(); this.code = code; this.data = data; } public JsonData(int code, String msg,Object data) { super(); this.code = code; this.msg = msg; this.data = data; } }
效果:
这里面如果你想限制上传问价大小,可以在添加如下:
//文件大小配置,启动类里面配置 @Bean public MultipartConfigElement multipartConfigElement() { MultipartConfigFactory factory = new MultipartConfigFactory(); //单个文件最大 factory.setMaxFileSize("10240KB"); //KB,MB /// 设置总上传数据总大小 factory.setMaxRequestSize("1024000KB"); return factory.createMultipartConfig(); }
总结:
1.其实在正式项目里,这里的文件上传地址不会是在本项目里,而是会指定一个文件服务器:
常用文件服务器:fastdfs,阿里云oss,nginx搭建一个简单的文件服务器
2.有关单个文件最大值,路径最好是配置在配置文件里,这样后期好维护。
热部署,配置文件使用
一、热加载
spring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启Spring Boot应用。
devtools的原理
深层原理是使用了两个ClassLoader,一个Classloader加载那些不会改变的类(第三方Jar包),另一个ClassLoader加载会更改的类,称为restart ClassLoader,这样在有代码更改的时候,原来的restart ClassLoader 被丢弃,重新创建一个restart ClassLoader,由于需要加载的类相比较少,所以实现了较快的重启时间。
官方地址:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#using-boot-devtools
实现热部署,首先要引入:spring-boot-devtools.jar包
核心依赖包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
添加依赖后,在ide里面重启应用,后续修改后马上可以生效
默认不被热部署的文件
1、/META-INF/maven, /META-INF/resources, /resources, /static, /public, or /templates
2、指定文件不进行热部署 spring.devtools.restart.exclude=static/**,public/**
在开发中,我们会思考一个问题?
如果你写一个逻辑代码,需要好几个文件,总不能你每保存一次就进行一次热部署,这里有个解决方法。
在application.properties添加手工触发重启
#指定某些文件不进行监听,即不会进行热加载 #spring.devtools.restart.exclude=application.properties #通过触发器,去控制什么时候进行热加载部署新的文件 spring.devtools.restart.trigger-file=trigger.txt
然后在src\main\resources目录下,添加trigger.txt文件
version=1
这样你每次改好代码,不会每次保存就热部署,而是改好代码后,改version=2就会进行热部署。
注意点:生产环境不要开启这个功能,如果用java -jar启动,springBoot是不会进行热部署的
二、SpringBoot注解把配置文件自动映射到属性和实体类实战
方式一、Controller上面配置
简介:讲解使用@value注解配置文件自动映射到属性和实体类
1、配置文件加载
方式一
1、Controller上面配置
@PropertySource({"classpath:resource.properties"})
2、增加属性
@Value("${test.name}")
private String name;
举例
上篇的文件上传的地址我是写死的。
这样显然不科学,这里改成写在1.application.properties配置文件里。
#文件上传路径配置 web.file.path=C:/Users/chenww/Desktop/springbootstudy/springbootstudy/src/main/resources/static/images/
2在FileController 类中
@Controller @PropertySource({"classpath:application.properties"}) public class FileController { //文件放在项目的images下 // private String filePath = "C:\\Users\\chenww\\Desktop\\springbootstudy\\springbootstudy\\src\\main\\resources\\static\\images\\"; @Value("${web.file.path}") private String filePath;
总结:
1:@PropertySource代表读取哪个文件
2:@Value通过key得到value值
方式二:实体类配置文件
步骤:
1、添加 @Component 注解;
2、使用 @PropertySource 注解指定配置文件位置;
3、使用 @ConfigurationProperties 注解,设置相关属性;
4、必须 通过注入IOC对象Resource 进来 , 才能在类中使用获取的配置文件值。
@Autowired
private ServerSettings serverSettings;
案例:
1.在application.properties
#测试配置文件路径 test.domain=www.jincou.com test.name=springboot
2.创建ServerSettings实体
import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; //服务器配置 @Component @PropertySource({"classpath:application.properties"}) @ConfigurationProperties public class ServerSettings { //名称test.domain是key值 @Value("${test.domain}") private String name; //域名地址 @Value("${test.name}") private String domain; //提供set和get方法 }
三创建GetController
import com.jincou.model.ServerSettings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class GetController { //需要注入 @Autowired private ServerSettings serverSettings; @GetMapping("/v1/test_properties") public Object testPeroperties(){ return serverSettings; } }
页面效果
其实上面还可以做个优化:
创建ServerSettings实体
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; //服务器配置 @Component @PropertySource({"classpath:application.properties"}) @ConfigurationProperties(prefix="test") //这里加了个test前缀 public class ServerSettings { //这是不需要写vlaue标签,只要text.name去掉前缀test后的name和这里name相同,就会自动赋值 private String name; //域名地址 private String domain; //提供set和get方法 }
单元测试,全局异常
一、单元测试
1.基础版
1、引入相关依赖
<!--springboot程序测试依赖,如果是自动创建项目默认添加--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
2:关键注解:@RunWith @SpringBootTest
import junit.framework.TestCase; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) //底层用junit SpringJUnit4ClassRunner @SpringBootTest(classes={SpringbootstudyApplication.class})//启动整个springboot工程 public class SpringBootTestDemo { @Test public void testOne(){ System.out.println("test hello 1"); TestCase.assertEquals(1, 1); } @Test public void testTwo(){ System.out.println("test hello 2"); TestCase.assertEquals(1, 1); } @Before public void testBefore(){ System.out.println("before"); } @After public void testAfter(){ System.out.println("after"); } }
输出结果:
2.MockMvc
MockMvc类的使用和模拟Http请求实战
TestController
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @RequestMapping("/vq/test") public String getTest(){ return"我是测试返回值"; } }
MockMvcTestDemo
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * 功能描述:测试mockmvc类 * */ @RunWith(SpringRunner.class) //底层用junit SpringJUnit4ClassRunner @SpringBootTest(classes={SpringbootstudyApplication.class}) //启动整个springboot工程 @AutoConfigureMockMvc public class MockMvcTestDemo { @Autowired private MockMvc mockMvc; @Test public void apiTest() throws Exception { MvcResult mvcResult = mockMvc.perform( MockMvcRequestBuilders.get("/vq/test") ). andExpect( MockMvcResultMatchers.status().isOk() ).andReturn(); int status = mvcResult.getResponse().getStatus(); System.out.println(status); } }
结果:返回200状态码
总结,关键注解:
@RunWith(SpringRunner.class) //底层用junit SpringJUnit4ClassRunner @SpringBootTest(classes={SpringbootstudyApplication.class}) //启动整个springboot工程 @AutoConfigureMockMvc
二、配置全局异常
首先springboot自带异常是不友好的。
举例
@RequestMapping(value = "/api/v1/test_ext") public Object index() { int i= 1/0; return new User(11, "sfsfds", "1000000", new Date()); }
页面
1、配置全局异常
import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; //添加全局异常注解 @RestControllerAdvice public class CustomExtHandler { private static final Logger LOG = LoggerFactory.getLogger(CustomExtHandler.class); //捕获全局异常,处理所有不可知的异常 @ExceptionHandler(value=Exception.class) //@ResponseBody Object handleException(Exception e,HttpServletRequest request){ LOG.error("url {}, msg {}",request.getRequestURL(), e.getMessage()); Map<String, Object> map = new HashMap<>(); map.put("code", 100); map.put("msg", e.getMessage()); map.put("url", request.getRequestURL()); return map; } }
在看页面:
关键注解:
@RestControllerAdvice //全局注解
@ExceptionHandler(value=Exception.class) //捕获不同异常,这里捕获是Exception异常,你也可以指定其它异常
2.自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
1.自定义MyException异常
/** * 功能描述:自定义异常类 * */ public class MyException extends RuntimeException { private static final long serialVersionUID = 1L; public MyException(String code, String msg) { this.code = code; this.msg = msg; } private String code; private String msg; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
controller类
/** * 功能描述:模拟自定义异常 */ @RequestMapping("/api/v1/myext") public Object myexc(){ //直接抛出异常 throw new MyException("499", "my ext异常"); }
页面
过滤器,监听器,拦截器
一、理解它们
看里十几篇博客,总算有点小明白,总的来讲,两张图可以让我看明白点。
通过两幅图我们可以理解拦截器和过滤器的特点
1、过滤器
过滤器是在请求进入tomcat容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。
理解上面这句话我们就可以知道,进入servlet之前,主要是两个参数:ServletRequest,ServletResponse 那我们得到这两个测试可以干哪些事呢?
我们可以通过ServletRequest得到HttpServletRequest,此时你就可以对请求或响应(Request、Response)那就可以对对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、字符集统一等一些高级功能。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。(每次热部署后,都会销毁)。
2、拦截器
从上图我们可以看出过滤器只在servlet前后起作用,所以它既不能捕获异常,获得bean对象等,这些是只能是进入servlet里面的拦截器能过做到。拦截器中用于在某个方法或字段被访问之前,进行拦截然后,在之前或之后加入某些操作。比如日志,安全等。一般拦截器方法都是通过动态代理的方式实现。可以通过它来进行权限验证,或者判断用户是否登陆,或者是像12306 判断当前时间是否是购票时间。
对比一下其实我们可以发现,过滤器能做的事拦截器都能做,二拦截器做的事过滤器不一定做的了。
3、监听器
listener是servlet规范中定义的一种特殊类。用于监听servletContext、HttpSession和servletRequest等域对象的创建和销毁事件。监听域对象的属性发生修改的事件。用于在事件发生前、发生后做一些必要的处理。其主要可用于以下方面:1、统计在线人数和在线用户2、系统启动时加载初始化信息3、统计网站访问量4、记录用户访问路径。
常用的监听器 servletContextListener、httpSessionListener、servletRequestListener)
二、如何创建他们
1、过滤器
自定义Filter 使用Servlet3.0的注解进行配置第三步的@WebFilter就是3.0的注解
1)启动类里面增加 @ServletComponentScan,进行扫描
2)新建一个Filter类,implements Filter,并实现对应的接口
3) @WebFilter 标记一个类为filter,被spring进行扫描
urlPatterns:拦截规则,支持正则
4)控制chain.doFilter的方法的调用,来实现是否通过放行不放行,web应用resp.sendRedirect("/index.html");场景:权限控制、用户登录(非前端后端分离场景)等
application类
@SpringBootApplication @ServletComponentScan public class SpringbootstudyApplication { public static void main(String[] args) { SpringApplication.run(SpringbootstudyApplication.class, args); } }
LoginFilter过滤器
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; //过滤器拦截路径 @WebFilter(urlPatterns = "/api/*", filterName = "loginFilter") public class LoginFilter implements Filter{ /** * 容器加载的时候调用 */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("拦截器进入========拦截器进入========"); } /** * 请求被拦截的时候进行调用 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("拦截中========拦截中========"); HttpServletRequest hrequest = (HttpServletRequest)servletRequest; HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) servletResponse); if(hrequest.getRequestURI().indexOf("/index") != -1 || hrequest.getRequestURI().indexOf("/asd") != -1 || hrequest.getRequestURI().indexOf("/online") != -1 || hrequest.getRequestURI().indexOf("/login") != -1 ) { filterChain.doFilter(servletRequest, servletResponse); }else { wrapper.sendRedirect("/login"); } } /** * 容器被销毁的时候被调用 */ @Override public void destroy() { System.out.println("拦截器销毁========拦截器销毁========"); } }
1、官网地址:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners
二、监听器
import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener; @WebListener public class RequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { // TODO Auto-generated method stub System.out.println("======销毁监听器========"); } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("======进入监听器========"); }
三、拦截器
CustomWebMvcConfigurer主拦截器需要:
1:添加@Configuration注解
2:实现WebMvcConfigurer接口
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; //主拦截器,根据拦截不同路径跳转不同自定义拦截器 (实现WebMvcConfigurer方法) @Configuration public class CustomWebMvcConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginIntercepter()).addPathPatterns("/api1/*/**"); registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/api2/*/**"); //.excludePathPatterns("/api2/xxx/**"); //拦截全部 /*/*/** WebMvcConfigurer.super.addInterceptors(registry); } }
LoginIntercepter子拦截器
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginIntercepter implements HandlerInterceptor{ /** * 进入controller方法之前 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginIntercepter------->preHandle"); // String token = request.getParameter("access_token"); // // response.getWriter().print("fail"); return HandlerInterceptor.super.preHandle(request, response, handler); } /** * 调用完controller之后,视图渲染之前 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("LoginIntercepter------->postHandle"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } /** * 整个完成之后,通常用于资源清理 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("LoginIntercepter------->afterCompletion"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
TwoIntercepter同上
最后总解一下他们:
过滤器:用于属性甄别,对象收集(不可改变过滤对象的属性和行为)
监听器:用于对象监听,行为记录(不可改变监听对象的属性和行为)
拦截器:用于对象拦截,行为干预(可以改变拦截对象的属性和行为)
整合Mybaties增删改查
#数据驱动可配也可以不配,因为系统会自动识别 #spring.datasource.driver-class-name =com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/movie?useUnicode=true&characterEncoding=utf-8 spring.datasource.username =root spring.datasource.password =root #让控制台打印SQL语句,一般用于本地开发环境 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl #springboot有自带数据源这个也可不配置 spring.datasource.type =com.alibaba.druid.pool.DruidDataSource
4.Springboot主类
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //@MapperScan会自动扫描里面的包,而且应该是可以自动给每个类装配一个Bean对象 @SpringBootApplication @MapperScan("com.jincou.mapper") public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
5、UserMapper
import java.util.List; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import com.jincou.model.User; /** * 功能描述:访问数据库的接口 */ public interface UserMapper { //推荐使用#{}取值,不要用${},因为存在注入的风险 @Insert("INSERT INTO user(name,phone,create_time,age) VALUES(#{name}, #{phone}, #{createTime},#{age})") @Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id") //keyProperty java对象的属性;keyColumn表示数据库的字段 int insert(User user); //column指数据库中的列,property是指实体的属性名,如果一致就不需要写 @Select("SELECT * FROM user") @Results({ @Result(column = "create_time",property = "createTime") }) List<User> getAll(); @Select("SELECT * FROM user WHERE id = #{id}") @Results({ @Result(column = "create_time",property = "createTime") }) User findById(Long id); @Update("UPDATE user SET name=#{name} WHERE id =#{id}") void update(User user); @Delete("DELETE FROM user WHERE id =#{userId}") void delete(Long userId); }
7、UserServiseImpl
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.jincou.mapper.UserMapper; import com.jincou.model.User; import com.jincou.service.UserService; @Service public class UserServiceImpl implements UserService{ //因为主类的@MapperScan方法,所以自动为UserMapper装配了一个userMapper对象 @Autowired private UserMapper userMapper; //这里你传过去的时候user的id为null,而insert之后传回回来的user会把数据库中的id值带回来,真强大 @Override public int add(User user) { userMapper.insert(user); int id = user.getId(); return id; } }
8.Controller类
import java.util.Date; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.jincou.mapper.UserMapper; import com.jincou.model.JsonData; import com.jincou.model.User; import com.jincou.service.UserService; @RestController @RequestMapping("/api/v1/user") public class UserController { //在UserServiceImpl定义了@Service实现类所以可以得到默认首字母小写的对象 @Autowired private UserService userService; @Autowired private UserMapper userMapper; /** * 功能描述: user 保存接口 */ @GetMapping("add") public Object add(){ User user = new User(); user.setAge(11); user.setCreateTime(new Date()); user.setName("张三"); user.setPhone("1880177"); int id = userService.add(user); return id; } /** * 功能描述:查找全部用户 * 这里和下面是直接调用跳过Servise层,直接到DAO层 */ @GetMapping("findAll") public Object findAll(){ return userMapper.getAll(); } /** * 查找单个用户 */ @GetMapping("find_by_id") public Object findById(long id){ return userMapper.findById(id); } /** * 删除单个用户 */ @GetMapping("del_by_id") public Object delById(long id){ userMapper.delete(id); return ""; } /** *更新用户 */ @GetMapping("update") public Object update(String name,int id){ User user = new User(); user.setName(name); user.setId(id); userMapper.update(user); return ""; } }
测试
1.准备好数据库数据:
#Sql脚本 CREATE TABLE `user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(128) DEFAULT NULL COMMENT '名称', `phone` varchar(16) DEFAULT NULL COMMENT '用户手机号', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `age` int(4) DEFAULT NULL COMMENT '年龄', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
2测试结果
插入用户,看后台的sql语句
Springboot整合redis
步骤讲解
1、第一步jar导入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
如果你本地没有相应jar包,你可以在mevan存放jar包的库中,找到Setting文件,添加阿里云镜像,在跟新就可以下载相应jar包
<mirror> <id>alimaven-central</id> <mirrorOf>central</mirrorOf> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/repositories/central/</url> </mirror>
第二步:application.properties配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
第三步创建实体
User类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
JsonData工具类实体
/** * 这是后端向前端响应的一个包装类 * 一般后端向前端传值会有三个属性 * 1:响应状态 * 2:如果响应成功,把数据放入 * 3: 描述,响应成功描述,或者失败的描述 */ public class JsonData implements Serializable { private static final long serialVersionUID = 1L; private Integer code; // 状态码 0 表示成功,1表示处理中,-1表示失败 private Object data; // 数据 private String msg;// 描述 public JsonData() { } public JsonData(Integer code, Object data, String msg) { this.code = code; this.data = data; this.msg = msg; } // 成功,只返回成功状态码 public static JsonData buildSuccess() { return new JsonData(0, null, null); } // 成功,传入状态码和数据 public static JsonData buildSuccess(Object data) { return new JsonData(0, data, null); } // 失败,传入描述信息 public static JsonData buildError(String msg) { return new JsonData(-1, null, msg); } // 失败,传入描述信息,状态码 public static JsonData buildError(String msg, Integer code) { return new JsonData(code, null, msg); } // 成功,传入数据,及描述信息 public static JsonData buildSuccess(Object data, String msg) { return new JsonData(0, data, msg); } // 成功,传入数据,及状态码 public static JsonData buildSuccess(Object data, int code) { return new JsonData(code, data, null); } //提供get和set方法,和toString方法 }
第四步创建工具类
1、字符串转对象,对象转字符串工具类
import java.io.IOException; import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.ObjectMapper; /** * 字符串转对象,对象转字符串的工具类 * 因为StringRedisTemplate的opsForValue()方法需要key,value都需要String类型,所以当value值存入对象的时候 * 先转成字符串后存入。 */ public class JsonUtils { private static ObjectMapper objectMapper = new ObjectMapper(); //对象转字符串 public static <T> String obj2String(T obj){ if (obj == null){ return null; } try { return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); } catch (Exception e) { e.printStackTrace(); return null; } } //字符串转对象 public static <T> T string2Obj(String str,Class<T> clazz){ if (StringUtils.isEmpty(str) || clazz == null){ return null; } try { return clazz.equals(String.class)? (T) str :objectMapper.readValue(str,clazz); } catch (IOException e) { e.printStackTrace(); return null; } } }
2、封装Redis类
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; /** * 功能描述:redis工具类 * 对于redisTpl.opsForValue().set(key, value)进行了一次封装,不然每次都要这样保存值 * 而封装后只需:new RedisClient().set(key,value); */ @Component public class RedisClient { @Autowired private StringRedisTemplate redisTpl; //jdbcTemplate // 功能描述:设置key-value到redis中 public boolean set(String key ,String value){ try{ redisTpl.opsForValue().set(key, value); return true; }catch(Exception e){ e.printStackTrace(); return false; } } // 功能描述:通过key获取缓存里面的值 public String get(String key){ return redisTpl.opsForValue().get(key); } }
第五步创建Controller类
RdisTestController
import java.util.Date; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.jincou.model.JsonData; import com.jincou.model.User; import com.jincou.until.JsonUtils; import com.jincou.until.RedisClient; @RestController @RequestMapping("/api/v1/redis") public class RdisTestController { //得到redis封装类 @Autowired private RedisClient redis; //添加字符串 @GetMapping(value="add") public Object add(){ redis.set("username", "xddddddd"); return JsonData.buildSuccess(); } //通过key值得到value字符串 @GetMapping(value="get") public Object get(){ String value = redis.get("username"); return JsonData.buildSuccess(value); } //将对象通过工具类转成String类型,存入redis中 @GetMapping(value="save_user") public Object saveUser(){ User user = new User(1, "abc", "11", new Date()); String userStr = JsonUtils.obj2String(user); boolean flag = redis.set("base:user:11", userStr); return JsonData.buildSuccess(flag); } //通过key值得到value值,让后将value转为对象 @GetMapping(value="find_user") public Object findUser(){ String userStr = redis.get("base:user:11"); User user = JsonUtils.string2Obj(userStr, User.class); return JsonData.buildSuccess(user); } }
测试效果
命名规范:如果我们key按"base:user:11"这来命名,那么那么会自动创建文件夹格式,方便查看。