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配置

 配置

 

第三步创建实体

    User类

 User类

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"这来命名,那么那么会自动创建文件夹格式,方便查看。

 

posted @ 2022-02-22 21:31  hanease  阅读(94)  评论(0编辑  收藏  举报