SpringBoot

104 阅读5分钟

前言

  1. 为什么要使用SpringBoot? 因为Spring,SpringMVC需要使用的大量的配置文件(Xml文件) 还需要配置各种对象,把使用的对象放入到spring容器中才能使用对象 需要了解其他框架配置规则。
  2. SpringBoot就相当于是不需要配置文件的Spring+SpringMVC。常用的框架和第三方库都已经配置好了,拿来就可以使用了。其实底层还是他俩,在他俩基础上改出来的
  3. SpringBoot开发效率高,使用方便多了

JavaConfig配置类

JavaConfig:使用Java类作为xml配置文件的替代,是配置springs容器的纯Java的方式。在这个Java类这可以创建java对象,把对象放入spring容器中(注入到容器)

回顾xml的形式

在看JavaConfig的方式前,先回顾下之前用xml的方式如何创建bean(maven要引入spring-context上下文依赖)

一个学生的pojo类

package com.pan.vo;

public class Student {
    private String name;
    private Integer age;
    private String sex;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Student() {
    }

    public Student(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}

beans.xml用来管理bean(在resources目录下)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="stu" class="com.pan.vo.Student">
        <property name="name" value="tom"/>
        <property name="age" value="18"/>
        <property name="sex" value="man"/>
    </bean>

</beans>

测试类

package com.pan.vo;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestStudent {
    @Test
    public void test1() {
        String config = "beans.xml";
        ApplicationContext ioc = new ClassPathXmlApplicationContext(config);
        Student stu = (Student) ioc.getBean("stu");// 使用配置文件中bean标签的id获取学生对象
        System.out.println("学生类型:" + stu);// 学生类型:Student{name='tom', age=18, sex='man'}
    }
}

配置类的方式代替xml

使用两个注解:

  1. @Configuration:放在一个类的上面,表示这个类是作为配置文件使用的。
  2. @Bean:声明对象,把对象注入到容器中。

创建SpringConfig类

package com.pan.config;

import com.pan.vo.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// @Configuration表示当前类是作为配置文件使用的,就是用来配置容器的,在类的上面使用
// SpringConfig这个类就相当于beans.xml

@Configuration
public class SpringConfig {

    // 创建一个方法,在上面加@bean注解,方法的返回值是一个对象,返回值的对象就注入到容器中了
    // @Bean:把对象注入到spring容器中,作用相当于配置文件中的<bean></bean>标签
    // @Bean不指定对象的名称,默认方法名就是id
    @Bean
    public Student createStudent() {
        Student s1 = new Student();
        s1.setName("Jack");
        s1.setAge(28);
        s1.setSex("男");
        return s1;
    }
    
    // @Bean当然也可以用name属性指定对象的名称(相当于xml方式里<bean>标签的id)
    @Bean(name = "marry")
    public Student createStudent2() {
        Student s2 = new Student();
        s2.setName("Marry");
        s2.setAge(34);
        s2.setSex("女");
        return s2;
    }

}

测试

package com.pan.vo;

import com.pan.config.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestStudent {
    @Test
    public void test2() {
        ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);// 可以通过配置类的类对象.class获取ioc
        Student student = (Student) ioc.getBean("createStudent");// 通过SpringConfig类中的方法名获取学生对象(默认)
        System.out.println("使用JavaConfig类创建的对象" + student);// 使用JavaConfig类创建的对象Student{name='Jack', age=28, sex='男'}
    }

    @Test
    public void test3() {
        ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
        Student student = (Student) ioc.getBean("marry");// 通过SpringConfig类中的对象名获取学生对象(指定)
        System.out.println("使用JavaConfig类创建的对象(指定名称)" + student);// 使用JavaConfig类创建的对象(指定名称)Student{name='Marry', age=34, sex='女'}
    }
}

配置类结合xml的形式

可以看到完全能用配置类代替xml,那么假如有一部分的bean配置在了xml文件中,还想在配置类中引入xml里的配置,该咋办?

可以在配置类的类上写个 @ImportResource标签,value值引入xml配置文件(可以使个花括号数组的形式引入多个xml)

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="stu" class="com.pan.vo.Student">
        <property name="name" value="张三"/>
        <property name="age" value="60"/>
        <property name="sex" value="男"/>
    </bean>

</beans>

SpringConfig配置类引入beans.xml

package com.pan.config;

import com.pan.vo.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource(value = "classpath:beans.xml")// 相当于之前xml里的<import resource="">标签
public class SpringConfig {

}

测试

@Test
public void test4() {
    ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);// 通过配置类的类对象获取ioc容器
    Student student = (Student) ioc.getBean("stu");// 传入的是xml文件里bean的id
    System.out.println(student);// Student{name='张三', age=60, sex='男'}
}

导入properties配置文件

使用 @PropertiesResource注解读取properties属性配置文件,如jdbc.properties等

  1. 在resources目录下创建xxx.properties文件(采用key=value形式)
  2. 在@PropertiesResource注解中指定properties文件的位置
  3. 使用@Value(value="${key}")的方式使用配置文件中的值

在resources目录下创建一个cat.properties文件

name=tom
age=4

创建一个Cat猫类

package com.pan.vo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("cat1")
public class Cat {

    @Value("${cat.name}")
    private String name;
    @Value("${cat.age}")
    private int age;

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

配置类SpringConfig

package com.pan.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration// 作用是让该类有和xml有一样的作用
@PropertySource(value = "classpath:cat.properties")// 相当于之前xml里的<context:property-placeholder>标签
@ComponentScan(basePackages = "com.pan.vo")// 因为Cat类使用注解@Component,所以要开启扫描,相当于之前xml里的<context:component-scan>标签
public class SpringConfig {

}

测试一下

package com.pan.vo;

import com.pan.config.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestStudent {
    @Test
    public void test5() {
        ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
        Cat cat = (Cat) ioc.getBean("cat1");
        System.out.println(cat);// Cat{name='tom', age=4}
    }
}

SpringBoot

SpringBoot是Spring中的一个成员,可以简化Spring,SpringMVC的使用。他的核心还是IOC容器。

特点:

  • 创建spring应用
  • 有内嵌的tomcat,jetty,Undertow
  • 提供了starter起步依赖,简化应用的配置。比如使用MyBatis框架,需要在Spring项目中,配置MyBatis的对象SqlSessionFactory,Dao的代理对象,在SpringBoot项目中,在pom.xml里面,加入一个mybatis-spring-boot-starter依赖
  • 尽可能去配置spring和第三方库。自动配置(就是把spring中的第三方库中的对象都创建好,放到容器中,开发人员可以直接使用
  • 提供了健康检查,统计,外部化配置
  • 不用生成代码,不用使用xml配置

创建一个SpringBoot项目

方式一:使用Spring提供的初始化器,用向导创建

在IDEA中,点击 设置==>项目结构==>模块==>添加模块==>Spring Initializr==>选择需要的依赖项(spring web)==>创建

(要是pom.xml文件里plugin标签爆红,给其加个version标签即可)

方式二:换个国内地址

方式一用的地址是start.spring.io(可能会很慢),可以将其换成https://start.spri… Initializr里可以换地址

方式三:Maven创建

新建一个普通的maven模块,将pom.xml改成这样

<?xml version="1.0" encoding="UTF-8"?>
<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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/>
    </parent>

    <groupId>org.pan</groupId>
    <artifactId>sb</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

接着在main/resources下创建templates目录、static目录和application.properties文件

如何启动SpringBoot项目?

先写一个控制类

package com.pan.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {
    @RequestMapping("hello")
    @ResponseBody
    public String hello(){
        return "欢迎使用sb框架";
    }
}

若是用Spring提供的初始化器创建的项目,会自己生成一个XxxApplication类

package com.pan;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoSbApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoSbApplication.class, args);
    }
}

点击小三角运行main方法就可以将springboot项目跑起来,然后浏览器访问http://localhost:8080/hello

@SpringBootApplication注解

可以看到,要想将项目跑起来,主方法上有个@SpringBootApplication注解,实际上这是一个复合注解,由下面三个注解组成

@SpringBootConfiguration//使用了@SpringBootConfiguration注解标注的类,可以作为配置文件使用的,可以使用Bean声明对象,注入到容器
@EnableAutoConfiguration//启用自动配置,把java对象配置好,注入到spring容器中。例如可以把mybatis的对象创建好,放入到容器中
@ComponentScan//组件扫描器,找到注解,根据注解的功能创建对象,给属性赋值等等。默认扫描的包是该注解所在的的包和子包

也就是一旦使用了SpringBootApplication注解,可以在该类中@Bean来配置对象注入到容器,当成xml来用,也可以自动配置第三方框架,还可以自动扫描注解

SpringBoot配置文件

名称:application

拓展名:

  • properties(key=value格式)
  • yml(key: value格式)

application.properties和application.yml两种都可以,但现在推荐用yml格式的

application.properties

#设置端口号
server.port=8087
#设置访问应用上下文路径
server.servlet.context-path=/sb

application.yml

server:
  port: 8088
  servlet:
    context-path: /sb2

二选一

多环境配置文件

在实际开发的过程中,我们的项目会经历很多的阶段(开发->测试->上线),每个阶段的配置也会不同,例如:端口、上下文根、数据库等,那么这个时候为了方便在不同的环境之间切换,SpringBoot提供了多环境配置,具体步骤如下:

在resources目录下创建(properties也行):

application.yml(指定当前使用哪个阶段的配置文件)

#设置激活使用哪个配置文件
spring:
  profiles:
    active: dev

application-dev.yml(开发环境核心配置文件)

server:
  port: 8081
  servlet:
    context-path: /sb/dev

application-product.yml(生产环境核心配置文件)

server:
  port: 8082
  servlet:
    context-path: /sb/pro

application-test.yml(测试环境核心配置文件)

server:
  port: 8083
  servlet:
    context-path: /sb/test

自定义配置

上面可以可以看到,能配置多个核心配置文件,供项目在不同开发阶段使用,我们可以使用 @Value 注解,从主配置文件application.yml读取数据

核心配置文件application.yml

server:
  port: 8080
  servlet:
    context-path: /sb

#自定义的学校信息
school:
  name: 清华大学
  addr: 北京

控制类里可以读取到自己配置的值

package com.pan.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {

    @Value("${school.name}")
    private String schoolName;
    @Value("${school.addr}")
    private String stringAddr;

    @RequestMapping("/demo")
    @ResponseBody
    public String test() {
        return schoolName + stringAddr;
    }
}

此时访问http://localhost:8080/sb/demo 会出现“清华大学北京”,成功了!

但是,想一下,学校的信息可以封装成一个学校信息对象,可以这样干:

核心配置文件application.yml不变

server:
  port: 8080
  servlet:
    context-path: /sb

#自定义的学校信息
school:
  name: 清华大学
  addr: 北京

创建一个学校信息类,里面的属性名称和核心配置里的对应,给类上加注解@Component和@ConfigurationProperties

package com.pan.vo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component// 创建一个对象
@ConfigurationProperties(prefix = "school")// 将配置文件中以school开头的属性和该类中的属性对应
public class SchoolInfo {
    private String name;
    private String addr;

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public SchoolInfo() {
    }

    public SchoolInfo(String name, String addr) {
        this.name = name;
        this.addr = addr;
    }
}

在控制类里自动装配一下

package com.pan.controller;

import com.pan.vo.SchoolInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {

    @Autowired// 自动加载
    private SchoolInfo schoolInfo;

    @RequestMapping("/demo")
    @ResponseBody
    public String test() {
        return schoolInfo.toString();
    }
}

此时在浏览器输入http://localhost:8080/sb/demo 会出现“School{name='清华大学', addr='北京'}”,也行!

在SpringBoot项目中使用JSP(了解)

SpringBoot默认已经不支持jsp了,现在用模板技术(如Thymeleaf视图)代替了它,向在SpringBoot项目中使用,需要一系列配置,大致是:

  1. 添加处理jsp的依赖,负责解析jsp文件
  2. 如果需要servlet,jsp,jstl的功能,需要加入他俩的依赖
  3. 创建一个存放jsp的目录,一般叫webapp
  4. 需要在pom.xml指定jsp文件编译后的存放目录META-INF/resources
  5. 创建Controller访问jsp
  6. 在application.properties文件中配置视图解析器

详见www.bilibili.com/video/BV1XQ…

如何使用ApplicationContext容器

怎样手动获取IOC容器?

可以在主方法里接收返回值,run方法返回的就是ioc容器,有ioc容器就可以手动获取对象了

package com.pan;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class DemoSbApplication {
    public static void main(String[] args) {
        ApplicationContext ioc = SpringApplication.run(DemoSbApplication.class, args);// 获取容器
        ioc.getBean("");// 获取对象
    }
}

CommandLineRunner接口,ApplicationRunner接口

现在知道了,xxxApplication类里的主方法里的那一行代码是获取并返回容器的,我么有些操作如读取配置文件、数据库连接等可能就要在容器获取到之后马上执行,那么我们就可以让xxxApplication类实现CommandLineRunner或ApplicationRunner接口,这样就有了个run方法,这个run方法就会在主方法里那行获取ioc代码执行后马上执行

package com.pan;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoSbApplication implements CommandLineRunner {
    public static void main(String[] args) {
        System.out.println("容器创建之前");
        SpringApplication.run(DemoSbApplication.class, args);// 创建容器
        System.out.println("容器创建之后,且下面的run方法执行完才会到这");
    }

    @Override
    public void run(String... args) throws Exception {
        // 这个方法里可以写如读取文件,数据库等操作
        // 在这个方法里就已经可以使用容器中的对象了
        System.out.println("容器创建好之后立马执行这个方法");
    }
}

CommandLineRunner和ApplicationRunner接口的区别是:前者的run方法的参数是个数组,后者的run方法的参数是ApplicationArguments

web组件

组件一共说三个内容,拦截器、servlet、filter

拦截器

拦截器是SpringMVC中一种对象,能拦截对Controller的请求。拦截器框架中有系统的拦截器,还可以自定义拦截器。实现对请求预先处理。

先回顾下如何在springMVC中实现一个自定义拦截器

  1. 创建类实现SpringMVC框架的Handlerlnterceptor接口(具体去看SpringMVC笔记)

    package com.pan.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class TestInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            
        }
    }
    
  2. 需在SpringMVC的配置文件中,声明拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/*"/>  <!-- /*表示拦截一层,/**表示拦截所有路径,要注意 -->
            <mvc:exclude-mapping path="/abc"/>
            <ref bean="testInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>	
    

那么在springboot中该怎样注册拦截器呢?

首先写一个拦截器类,和上面的一样(这里就不实现3个方法了)

package com.pan.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器执行了preHandle");
        return true;
    }
}

然后写一个配置类,注册拦截器

package com.pan.config;

import com.pan.interceptor.TestInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyAppConfig implements WebMvcConfigurer {
    // 添加拦截器对象,注入到容器中
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 创建拦截器对象
        HandlerInterceptor interceptor = new TestInterceptor();
        // 指定拦截的uri地址
        String[] path = {"/user/**"};
        // 指定不拦截的uri地址
        String[] excludePath = {"/user/login"};
        registry.addInterceptor(interceptor).addPathPatterns(path).excludePathPatterns(excludePath);
    }
}

Controller

package com.pan.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
    @RequestMapping("/user/login")
    @ResponseBody
    public String testInterceptor1() {
        return "成功进入/user/login地址";
    }

    @RequestMapping("/user/test")
    @ResponseBody
    public String testInterceptor2() {
        return "成功进入/user/test地址";
    }
}

这时访问/user/login和/user/test发现都会成功显示一句话,但是只要是除了/user/login以外的/user/*访问都会触发拦截器,在控制台输出一句话

Servlet

在springboot里怎么使用servlet对象?

  1. 创建servlet类,继承HttpServlet

    package com.pan.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    // 创建servlet类
    public class TestServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html;charset=utf-8");
            PrintWriter out = resp.getWriter();
            out.print("servlet执行了");
            out.flush();
            out.close();
        }
    }
    
  2. 配置类里注册servlet,让框架能找到servlet

    package com.pan.config;
    
    import com.pan.servlet.TestServlet;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    
    @Configuration
    public class MyAppConfig implements WebMvcConfigurer {
        // 定义方法,注册Servlet对象
        @Bean
        public ServletRegistrationBean servletRegistrationBean() {
            // 第一个参数是Servlet对象,第二个是Url地址
            ServletRegistrationBean bean = new ServletRegistrationBean(new TestServlet(), "/myservlet");//这里用的是有参构造器,也可以使用无参构造器创建,然后调用方法设置servlet对象和地址
            return bean;
        }
    }
    
  3. 访问/myservlet会发现页面一行字:”servlet执行了“

过滤器

Filter是Servlet规范中的过滤器,可以处理请求,对请求的参数,属性进行调整。常常在过滤器中处理字符编码

在框架中使用过滤器:

  1. 创建自定义过滤器类

    package com.pan.filter;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    // 自定义过滤器
    public class TestFilter implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("执行了TestFilter");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    
  2. 注册类里注册过滤器对象

    package com.pan.config;
    
    import com.pan.filter.TestFilter;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MyAppConfig {
        //注册过滤器
        @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setFilter(new TestFilter());
            bean.addUrlPatterns("/user/*");
            return bean;
        }
    }
    
  3. 这时在controller类里访问/user/*下的路径会执行过滤器里的输出语句

    package com.pan.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class TestController {
        @RequestMapping("/user/login")
        @ResponseBody
        public String testInterceptor1() {
            return "成功进入/user/login地址";
        }
    
        @RequestMapping("/stu/test")
        @ResponseBody
        public String testInterceptor2() {
            return "成功进入/stu/test地址";
        }
    }
    

如何在springboot中解决乱码问题

servlet,假如servlet里没有指定charset=utf-8就会在浏览器输出乱码

package com.pan.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 创建servlet类
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");// 这里不设置编码方式charset=utf-8就会在浏览器输出乱码
        PrintWriter out = resp.getWriter();
        out.print("servlet执行了");
        out.flush();
        out.close();
    }
}

在配置类中注册servlet和框架带的字符过滤器

package com.pan.config;

import com.pan.servlet.TestServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;

@Configuration
public class MyAppConfig {
    // 注册servlet
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        TestServlet myServlet = new TestServlet();
        ServletRegistrationBean bean = new ServletRegistrationBean(myServlet, "/myservlet");
        return bean;
    }
    // 注册过滤器
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean reg = new FilterRegistrationBean();
        // 使用框架中的解决乱码的过滤器
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        // 指定使用的编码方式
        filter.setEncoding("utf-8");
        // 指定request,response都使用encoding的值
        filter.setForceEncoding(true);
        reg.setFilter(filter);
        // 指定过滤的url地址
        reg.addUrlPatterns("/*");
        return reg;
    }
}

还需要在核心配置文件中设置一句话

#SpringBoot中默认已经配置了CharacterEncodingFilter,编码默认ISO-8859-1
#设置enabled=false作用是关闭系统中默认配置好的过滤器,使用自定义的CharacterEncodingFilter
server:
  servlet:
    encoding:
      enabled: false

这样再访问servlet发现浏览器不会乱码了

更简单的方式解决乱码问题

上面的有点复杂,其实不需要配置字符集过滤器,直接在核心配置文件中加一句话即可!!

server:
  servlet:
    encoding:
      enabled: true   #让系统的CharacterEncodingFilter生效
      charset: utf-8  #指定使用的编码方式
      force: true     #指定request,response都使用encoding的值

ORM操作MySQL

使用MyBatis框架操作数据库,在SpringBoot框架集成MyBatis

使用步骤:

  1. mybatis起步依赖:完成mybatis对象自动配置,对象放在容器中
  2. pom.xml指定把src/main/java目录中的xml文件包含到classpath中
  3. 创建实体类Student
  4. 创建Dao接口StudentDao,创建一个查询学生的方法
  5. 创建Dao接口对应的Mapper文件,xml文件,写sql语句
  6. 创建Service层对象,创建StudentService接口和他的实现类,去dao对象的方法。完成数据库的操作
  7. 创建Controller对象,访问Service。
  8. 写application.properties文件,配置数据库的连接信息。

用初始化器创建一个springboot项目,添加Spring Web、MySQL Driver、MyBatis Framework依赖

第一种方式:@Mapper

先看一下将来的目录结构:

pom.xml,记得配置build标签下的resources标签,为了将xml文件构建生成出来

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.pan</groupId>
    <artifactId>orm</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>orm</name>
    <description>orm</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <!--resources插件-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

学生实体类

package com.pan.orm.model;

public class Student {
    private Integer id;
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Student() {
    }

    public Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

Dao层(java文件和xml文件放都放在了dao目录下)

package com.pan.orm.dao;

import com.pan.orm.model.Student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

// 告诉MyBatis这是dao接口,创建此接口的代理对象,加在类的上面
@Mapper
public interface StudentDao {
    Student selectById(@Param("stuId") Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.pan.orm.dao.StudentDao">
    <!--sql语句-->
    <select id="selectById" resultType="com.pan.orm.model.Student">
        select * from student where id = #{stuId}
    </select>
</mapper>

service层

package com.pan.orm.service;

import com.pan.orm.model.Student;

public interface StudentService {
    Student queryStudent(Integer id);
}
package com.pan.orm.service.impl;

import com.pan.orm.dao.StudentDao;
import com.pan.orm.model.Student;
import com.pan.orm.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentDao studentDao;

    @Override
    public Student queryStudent(Integer id) {
        return studentDao.selectById(id);
    }
}

Controller层

package com.pan.orm.controller;

import com.pan.orm.model.Student;
import com.pan.orm.service.StudentService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

@Controller
public class StudentController {
    @Resource
    private StudentService studentService;

    @RequestMapping("/student/query/{id}")
    @ResponseBody
    public String queryStudent(@PathVariable("id") Integer id) {
        Student student = studentService.queryStudent(id);
        return student.toString();
    }
}

核心配置文件配置下数据库连接

server.port=8080
server.servlet.context-path=/orm

#连接数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springdb?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

运行主方法,访问http://localhost:8080/orm/student/query/3,发现浏览器显示出了学生信息Student{id=3, name='王五', age=100}

package com.pan.orm;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrmApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrmApplication.class, args);
    }
}

MapperScan

假如上面的写法中,xxxDao.java有好多个,那岂不是要写很多遍@Mapper,可以优化一下,和上面例子唯二的不同就是主类加了个MapperScan注解,dao层的StudentDao的类上面就不用写Mapper注解了

dao层

package com.pan.orm.dao;

import com.pan.orm.model.Student;
import org.apache.ibatis.annotations.Param;

public interface StudentDao {
    Student selectById(@Param("stuId") Integer id);
}

主类

package com.pan.orm;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.pan.orm.dao")// 也可以传一个数组
public class OrmApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrmApplication.class, args);
    }
}

运行主方法发现也ok

优化一下上面例子的目录结构

可以看到上面例子的dao层的目录下xxxDao.xml和xxxDao.java是混着放在一起的

可以优化一下目录结构,将xml映射文件放到resources目录下的mapper目录里,像这样:

放了以后还得在springboot核心配置文件中配置mybatis.mapper-locations

server.port=8080
server.servlet.context-path=/orm

#连接数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springdb?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

#将sql语句的xml文件放在resources目录下,还需要配置一下,让spring能找到xml
mybatis.mapper-locations=classpath:mapper/*.xml

最后在pom.xml文件里将resources插件的扫描路径改成这样,将resources目录下所有文件构建生成到target目录里

<!--resources插件-->
<resources>
    <resource>
        <directory>src/main/resources</directory>
        <includes>
            <include>**/*.*</include>
        </includes>
    </resource>
</resources>

开启MyBatis的日志功能

mybatis.configuration.log-impl可以开启日志

server.port=8080
server.servlet.context-path=/orm

#连接数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springdb?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

#将sql语句的xml文件放在resources目录下需要配置一下
mybatis.mapper-locations=classpath:mapper/*.xml

#开启mybatis日志功能
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

事务

先回顾下在Spring框架中的事务:

  1. 管理事务的对象:事务管理器(接口,接口有很多的实现类) 例如:使用jdbc或mybatis访问数据库,使用的事务管理器:DataSourceTransactionManager
  2. 声明式事务:在xml配置文件或者使用注解说明事务控制的内容 控制事务:隔离级别,传播行为,超时时间
  3. 事务处理方式:
    • Spring框架中的@Transactional
    • aspectj框架可以在xml配置文件中,声明事务控制的内容

SpringBoot中如何使用事务?

和上面的一样,可以用@Transactional注解或者aspectj

@Transactional

  1. 在业务方法上加上@Transactional注解,就有事务功能了
  2. 在主启动类上加上@EnableTransactionManagement(加上最好,不加也行)

写一个添加语句,测试下事务功能

pojo类和上面例子一样,这里不写了

dao层

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.pan.orm.dao.StudentDao">
    <!--通过id查找学生-->
    <select id="selectById" resultType="com.pan.orm.model.Student">
        select * from student where id = #{stuId}
    </select>
	<!--添加学生-->
    <insert id="insert">
        insert into student values (#{id},#{name},#{age})
    </insert>
</mapper>
package com.pan.orm.dao;

import com.pan.orm.model.Student;
import org.apache.ibatis.annotations.Param;

public interface StudentDao {
    Student selectById(@Param("stuId") Integer id);// 通过id查找学生

    int insert(Student student);//添加学生
}

service层,方法上加**@Transactional**开启事务支持

package com.pan.orm.service;

import com.pan.orm.model.Student;

public interface StudentService {
    Student queryStudent(Integer id);

    int addStudent(Student student);
}
package com.pan.orm.service.impl;

import com.pan.orm.dao.StudentDao;
import com.pan.orm.model.Student;
import com.pan.orm.service.StudentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service
public class StudentServiceImpl implements StudentService {
    @Resource
    private StudentDao studentDao;

    @Override
    public Student queryStudent(Integer id) {
        return studentDao.selectById(id);
    }

    @Transactional // 标识开启了事务支持
    @Override
    public int addStudent(Student student) {
        int rows = studentDao.insert(student);
        // 抛出异常,目的是回滚事务,假如下面异常解开会发现添加不了,直接报错,会回滚事务
        // int a = 1 / 0;
        return rows;
    }
}

controller层

package com.pan.orm.controller;

import com.pan.orm.model.Student;
import com.pan.orm.service.StudentService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

@Controller
public class StudentController {
    @Resource
    private StudentService studentService;

    @RequestMapping("/student/query/{id}")
    @ResponseBody
    public String queryStudent(@PathVariable("id") Integer id) {
        Student student = studentService.queryStudent(id);
        return student.toString();
    }

    @RequestMapping("/student/add")
    @ResponseBody
    public String addStudent() {
        Student student = new Student(null, "tom", 18);
        int i = studentService.addStudent(student);
        return "影响了" + i + "行";
    }
}

主方法类上加@EnableTransactionManagement注解(不加也行,最好加上),发现运行没问题

package com.pan.orm;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement
@MapperScan(basePackages = "com.pan.orm.dao")// 也可以传一个数组
public class OrmApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrmApplication.class, args);
    }
}

接口架构风格——RESTful

rest风格具体说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。 它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。 REST风格提倡URL地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发关给服务器的数据作为URL地址的一部分,以保证整体风格的一致性。

操作传统方式REST风格
查询操作getUserByld?id=1user/1(get请求方式)
保存操作saveUseruser(post请求方式)
删除操作deleteUser?id=1user/1(delete请求方式)
更新操作updateUseruser(put请求方式)

但是,一些需要分页,排序的参数仍然需要跟在url后面

http://localhost:8080/user/list/page=1&pageSize=20

注解

SpringBoot开发restful风格的接口主要通过几个注解实现

  1. @PathVariable 从url路径中获取数据
  2. @PostMapping 支持的get请求方式,等同于@RequestMapping(method = RequestMethod.GET)
  3. @DeleteMapping 支持的post请求方式,等同于@RequestMapping(method = RequestMethod.POST)
  4. @PutMapping 支持的put请求方式,等同于@RequestMapping(method = RequestMethod.PUT)
  5. @DeleteMapping 支持的delete请求方式,等同于@RequestMapping(method = RequestMethod.DELETE)
  6. @RestController 复合注解,相当于@Controller和@ResponseBody的组合

接收get

package com.pan.orm.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController//@Controller和@ResponseBody的复合,类上Controller和,里面方法ResponseBody
public class MyRestController {
    @GetMapping("/teacher/{id}")
    public String queryStudent(@PathVariable("id") Integer teacherId) {// 当GetMapping里的参数和形参一样,PathVariable的值可以不写
        return "传过来的老师id是:" + teacherId;
    }
}

http://localhost:8080/orm/teacher/999 这时前端带上参数,后端就能接收了

接收Post

package com.pan.orm.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController//@Controller和@ResponseBody的复合,类上Controller和,里面方法ResponseBody
public class MyRestController {
    @PostMapping("/teacher/add")
    public String addTeacher(String name, Integer age) {// 和post表单里的name属性一一对应
        return "老师姓名:" + name + ",老师年龄:" + age;
    }
}

前端PUT和DELETE不能直接发送,具体怎么发PUT和DELETE看下面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
</head>
<body>
index<br/>
<!--查询所有(get)-->
<a th:href="@{/user}">查询所有用户</a><br/>
<!--查询指定(get)-->
<a th:href="@{/user/1}">查询id为1的用户</a><br/>
<!--添加(post)-->
<form th:action="@{/user}" method="post">
    name:<input type="text" name="username"/><br/>
    password:<input type="password" name="password"/><br/>
    <input type="submit" value="submit"/><br/>
</form>
<!--修改(put)-->
<!--注意:浏览器目前只能发送get和post清求-->
<!--若要发送put和delete清求,需要在web.xml中配置一个过滤器HiddenHttpMethodFilter-->
<!--而且表单得写一个隐藏类型的输入框,里面的name="_method" value="put"得写上-->
<!--注意,发送put和delete外层的表单类型还是post,只是里面说明了而已-->
<form th:action="@{/user}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="submit" value="modify">
</form>
<!--删除(delete)-->
<form th:action="@{/user/1222}" method="post">
    <input type="hidden" name="_method" value="delete">
    <input type="submit" value="del">
</form>
</body>
</html>

注意,想接收PUT和DELETE得先配置springboot核心配置文件

server.port=8080
server.servlet.context-path=/orm

#启用支持put、delete请求
spring.mvc.hiddenmethod.filter.enabled=true

SpringMVC里也有这块的笔记,可以对比回顾

SpringBoot集成Redis

SpringBoot中使用RedisTemplate模版类和StringRedisTemplate模板类操作Redis数据。简化了中间的过程

可以在初始化springboot项目时用初始化器选择添加NoSQL--->Spring Data Redis(Access+Driver),或者自己在pom.xml里添加redis依赖

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

添加依赖后,在核心配置文件application.properties配置redis

#redis
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=12345

启动redis服务,在安装目录双击redis-server

使用RedisTemplate模板类

package com.pan.orm.controller;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class RedisController {
    // 注入RedisTemplate
    // 可以使用泛型<String,String>或者<Object,Object>
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    // 添加数据到redis
    @GetMapping("/redis/add")
    public String addToRedis(String name, String value) {
        // 想操作,先获取valueOperations对象
        ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue();
        // 添加数据到redis中
        valueOperations.set("myname", "tom");
        return "向redis里添加了数据";
    }

    // 从redis获取数据
    @GetMapping("/redis/get")
    public String getValueFromRedis() {
        ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue();
        Object name = valueOperations.get("myname");
        return "myname的值是" + name;
    }
}

访问地址后可以成功将数据保存到redis,但是去redis客户端一看key长这副德行\xac\xed\x00\x05t\x00\x06myname(序列化后的结果)

但是这个模板类可以用来存如一个java对象,其实各有用途

使用StringRedisTemplate模板类

package com.pan.orm.controller;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class RedisControllerTwo {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 添加数据到redis
    @GetMapping("/redis/add")
    public String addToRedis(String name, String value) {
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        // 添加数据到redis中
        valueOperations.set("myaddr", "beijing");
        return "向redis里添加了数据";
    }

    // 从redis获取数据
    @GetMapping("/redis/get")
    public String getValueFromRedis() {
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        Object addr = valueOperations.get("myaddr");
        return "myaddr是" + addr;
    }
}

同样也可以添加到redis,而且在redis客户端输入keys *出来的key不是乱码

二者区别

StringRedisTemplate:把key-value都当做String处理,使用的是String的序列化,可读性好

RedisTemplate:吧key-value都经过序列化处理存到了redis,不能直接识别

回顾一下序列化:

序列化:把对象转化为可传输的字节序列过程称为序列化 反序列化:把字节序列还原为对象的过程称为反序列化

为什么需要序列化:

序列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组。我们必须在把对象转成字节数组的时候就制定一种规则(序列化),那么我们从IO流里面读出数据的时候再以这种规则把对象还原回来(反序列化)。

什么情况下需要序列化:

通过上面我想你已经知道了凡是需要进行“跨平台存储”和“网络传输”的数据,都需要进行序列化。 本质上存储和网络传输都需要经过把一个对象状态保存成一种跨平台识别的字节格式,然后其他的平台才可以通过字节信息解析还原对象信息。

序列化的方式: 序列化只是一种拆装组装对象的规则,那么这种规则肯定也可能有多种多样,比如现在常见的序列化方式有:JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protostuff

java的序列化:把java对象转为byte[],二进制数据

JSON的序列化:转换成JSON字符串

第一个模板的序列化很恶心,可以自定义成String的序列化,改造一下

package com.pan.orm.controller;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class RedisController {
    // 注入RedisTemplate
    // 可以使用泛型<String,String>或者<Object,Object>
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    // 添加数据到redis
    @GetMapping("/redis/add2")
    public String addToRedis(String name, String value) {

        // 可以自定义序列化为String的序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        // 想操作,先获取valueOperations对象
        ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue();
        // 添加数据到redis中
        valueOperations.set("hisname", "zhangsanfeng");
        return "向redis里添加了数据";
    }

    // 从redis获取数据
    @GetMapping("/redis/get2")
    public String getValueFromRedis() {
        ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue();
        Object name = valueOperations.get("hisname");
        return "hisname的值是" + name;
    }
}

这时,再去redis客户端输入keys *发现可以看到"hisname"这个key了

如何对对象进行JSON序列化,然后存到redis里呢?

先写一个类,实现Serializable接口

package com.pan.orm;

import java.io.Serializable;

public class Student implements Serializable {
    private String name;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }
}
package com.pan.orm.controller;

import com.pan.orm.model.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class TestJSONRedisController {
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    @GetMapping("/redis/json/add")
    public String addUserObjToRedis() {
        User user = new User("老六");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(User.class));
        redisTemplate.opsForValue().set("myUser", user);
        return "给redis里添加了一个User对象";
    }

    @GetMapping("/redis/json/get")
    public String getUserObjFromRedis() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(User.class));
        Object myUser = redisTemplate.opsForValue().get("myUser");
        return "从redis里得到了User对象" + myUser;// 从redis里得到了User对象Student{name='老六'}
    }
}

SpringBoot集成Dubbo

创建三个模块,分别是消费者,提供者,和公共接口

公共接口:

这个是普通maven项目

pojo

package com.pan.model;

import java.io.Serializable;

public class Student implements Serializable {
    private Integer id;
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Student() {
    }

    public Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

学生service接口

package com.pan.service;

import com.pan.model.Student;

public interface StudentService {
    Student queryStudent(Integer id);
}

服务提供者

这是个springboot模块,用初始化器创建,需要加入公共接口,dubbo和注册中心

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.pan</groupId>
    <artifactId>service-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service-provider</name>
    <description>service-provider</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <!--把公共接口引入进来-->
        <dependency>
            <groupId>com.pan</groupId>
            <artifactId>interface-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--添加dubbo依赖-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>

        <!--添加注册中心zookeeper-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>2.7.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

学生service接口的实现类

package com.pan.service.impl;

import com.pan.model.Student;
import com.pan.service.StudentService;
import org.apache.dubbo.config.annotation.DubboService;

// 使用Dubbo中的注解暴露服务
@DubboService(interfaceClass = StudentService.class, version = "1.0", timeout = 5000)
public class StudentServiceImpl implements StudentService {
    @Override
    public Student queryStudent(Integer id) {
        Student student = new Student();
        student.setId(1001);
        student.setName("tom");
        student.setAge(18);
        return null;
    }
}

springboot核心配置文件中配置dubbo

#配置dubbo

#服务名称(类似<dubbo:application name="名称">)
spring.application.name=studentservice-provider

#配置扫描的包
dubbo.scan.base-packages=com.pan.service

#配置dubbo协议
#dubbo.protocol.name=dubbo
#dubbo.protocol.port=20888

#指定注册中心
dubbo.registry.address=zookeeper://192.168.0.199:2181

在启动类上加一个注解@EnableDubbo

package com.pan.service;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo// 启用dubbo
public class ServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApplication.class, args);
    }
}

这时启动会报错,log4j被重复添加了,得排除一下,将上面的pom文件里的zookeeper依赖替换成下面这样,排除一下

<!--添加注册中心zookeeper-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-dependencies-zookeeper</artifactId>
    <version>2.7.8</version>
    <type>pom</type>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

这时就能启动成功

服务消费者

创建消费者,和提供者差不多,都是springboot项目,这个要添加web依赖

#配置dubbo

#服务名称(类似<dubbo:application name="名称">)
spring.application.name=studentservice-consumer

#指定注册中心
dubbo.registry.address=zookeeper://192.168.0.199:2181
package com.pan.controller;

import com.pan.model.Student;
import com.pan.service.StudentService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DubboController {

    // @DubboReference引用远程服务,把创建好的代理对象,注入给studentService
    @DubboReference(interfaceClass = StudentService.class, version = "1.0")
    private StudentService studentService;

    @GetMapping("/query")
    public String queryStudent() {
        Student student = studentService.queryStudent(1001);
        return "调用远程接口获取对象" + student;
    }
}
package com.pan;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo
public class ServiceConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApplication.class, args);
    }
}

大概流程是这样的,不过没跑起来 > _ < ||

打包

war

打包成war文件后可以放到其他的服务器上运行

  1. 在pom.xml里指定打包后的名称,还得指定打包类型

    <build>
        <!--打包后的文件名称-->
        <finalName>sbpro</finalName>
        
        <!--resources插件-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
    <packaging>war</packaging>
    
  2. 打war包,主启动类还需要继承SpringBootServletInitializer类,该类的作用是代替原有的web.xml

  3. 还得重写方法,返回主类

    package com.pan.orm;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @SpringBootApplication
    @EnableTransactionManagement
    @MapperScan(basePackages = "com.pan.orm.dao")// 也可以传一个数组
    public class OrmApplication extends SpringBootServletInitializer {// 继承这个类,才能使用独立tomcat服务器
        public static void main(String[] args) {
            SpringApplication.run(OrmApplication.class, args);
        }
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(OrmApplication.class);
        }
    }
    
  4. 点击maven生命周期中的package,生成的war在target目录下,将war文件放到外部的tomcat服务器下的webapp目录下,然后去bin目录下运行startup.bat

  5. 地址栏访问即可,注意访问的路径是http://localhost:8080/war包名/xxx

jar

直接修改打包方式为jar,指定生成的jar包名,点击maven生命周期中的package,生成的jar在target目录下,在cmd中java -jar xxx.jar即可运行,(jar包的话主类不用继承那个类)

模板

jsp显示网页很落后了,那想用后台直接渲染页面该咋办?可以使用模板技术,如Thymeleaf、Freemaker...

www.bilibili.com/video/BV1XQ… p93

总结

spring + springmvc + springboot + mybatis注解总结

===创建对象的:

@Controller
放在类的上面,创建控制器对象,注入到容器中

@RestController
放在类的上面,创建控制器对象,注入到容器中。作用:复合注解是@Controller,@ResponseBody,使用这个注解类的,里面的控制器方法的返回值都是给浏览器的数据

@Service
放在业务层的实现类上面,创建service对象,注入到容器

@Repository
放在dao层的实现类上面,创建dao对象然后放入到容器。没有使用这个注解,是因为现在使用MyBatis框架,dao对象是MyBatis通过代理生成的。不需要使用@Repository,所以没有使用

@Component:
放在类的上面,创建此类的对象,放入到容器中。

===赋值的:

@Value
简单类型的赋值,例如在属性的上面使用Value("李四") private String name,还可以使用@Value,获取配置文件者的数据(properties或yml)。
@QValue("${server.port}") private Integer port

@Autowired
引用类型赋值自动注入的,支持byName,byType,默认是byType。放在属性的上面,也可以放在构造方法的上面。推荐是放在构造方法的上面

@Qualifer
给引用类型赋值,使用byName方式。@Autowird,@Qualifer都是Spring框架提供的。

@Resource
来自jdk中的定义,javax.annotation。实现引用类型的自动注入,支持byName,byType,默认是byName,如果byName失败,再使用byType注入。在属性上面使用

===其他的:

@Configuration
放在类的上面,表示这是个配置类,相当于xml配置文件

@Bean
放在方法的上面,把方法的返回值对象注入到spring容器中。

@ImportResource
加载其他的xml配置文件,把文件中的对象注入到spring容器中

@PropertySource
读取其他的properties属性配置文件

@ComponentScan
扫描器,指定包名,扫描注解的

@ResponseBody
放在方法的上面,表示方法的返回值是数据,不是视图

@RequestBody
把请求体中的数据,读取出来,转为java对象使用。

@ControllerAdvice
控制器增强,放在类的上面,表示此类提供了方法,可以对Controller增强功能。

@ExceptionHandler
处理异常的,放在方法的上面

@Transcational
处理事务的,放在service实现类的public方法上面,表示此方法有事务

===springboot

@SpringBootApplication
放在启动类上面,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan

===mybatis

@Mapper
放在类的上面,让MyBatis找到接口,创建他的代理对象

@MapperScan
放在主类的上面,指定扫描的包,把这个包中的所有接口都创建代理对象。对象注入到容器中

@Param
放在dao接口的方法的形参前面,作为命名参数使用的。

===Dubbo注解

@DubboService
在提供者端使用的,暴露服务的,放在接口的实现类上面

@DubboReference
在消费者端使用的,引用远程服务,放在属性上面使用。

@EnableDubbo
放在主类上面,表示当前应用启用Dubbo功能。