springboot

20. SpringBoot

20.1 建立springboot项目流程

20.1.1 建立maven项目

导入pom依赖

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

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

20.1.2 编写入口类

//springboot入口类,每一个springboot项目都会存在入口类,是项目唯一入口
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class,args);
    }
}

后面所有包都要放在入口类所在包当中

20.1.3 编写service

@Service
public class HelloService {
    public void hello(){
        System.out.println("hello springboot");
    }
}

20.1.4 编写controller

@Controller
@RestController
@RequestMapping("/hello")
public class HelloController {
    Logger log = LoggerFactory.getLogger(HelloController.class);

    @Autowired
    HelloService helloService;

    @RequestMapping(value = "/list",method = RequestMethod.GET)
    public Map hello(Integer id){
        log.info("参数{}",id);
        helloService.hello();
        Map map = new HashMap();
        map.put("name","ikun");
        return map;
    }
}

项目结构:
在这里插入图片描述

20.2 springboot中的java配置

使用java配置可以代替之前用xml配置的ioc和di,自定义springboot中配置时,就要使用java配置

20.2.1 使用java配置手动配置数据库连接池,代替之前用xml的配置方式

编写properties文件,其中定义数据库连接信息

jdbc.url=jdbc:mysql://localhost:3306/springmvc?useSSL=false
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=1234

在创建配置类,并在其中配置数据库连接池

@Configuration      //标记当前类是java配置类,相当于xml配置文件
@PropertySource("classpath:jdbc.properties")	//引入properties文件
public class SpringConfig {

//下面是使用xml配置数据库连接池时的代码
//    <!--对数据库连接池进行ioc和di-->
//    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
//        <property name="url" value="${jdbc.url}"/>
//        <property name="driverClassName" value="${jdbc.driverClassName}"/>
//        <property name="username" value="${jdbc.username}"/>
//        <property name="password" value="${jdbc.password}"/>
//    </bean>

    //开启spring表达式支持,static使该bean创建优先级最高
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }

    //通过spring表达式从properties中取出配置信息
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.driverClassName}")
    String driverClassName;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

    @Bean(name = "dataSource")
    public DataSource dataSource(){
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setUrl(url);
        basicDataSource.setDriverClassName(driverClassName);
        basicDataSource.setUsername(username);
        basicDataSource.setPassword(password);
        return basicDataSource;
    }
}

测试配置是否成功

public class TestSpring {
    public static void main(String[] args) {
    	//传入配置类的字节码文件,就能得到spring容器
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        //从容器中取出连接池对象
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

运行结果
在这里插入图片描述

20.3 @SpringBootApplication启动类注解

SpringBootApplication是组合注解,其中包含的关键注解有:

@SpringBootConfiguration //相当于xml,用于标记java配置
@EnableAutoConfiguration  //开启自动装配
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	//code。。。
}

20.3.1 @SpringBootConfiguration

就等同于@Configuration注解,用于标记当前类属于配置类,可以在其中写java配置代码,
所以我们也可以在springboot启动类中编写部分java配置代码

20.3.2 @EnableAutoConfiguration

进行自动装配,只要我们在pom中引入了starter,就会自动配置相关环境

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

starter中就包含了spring、springmvc和tomcat,配置了EnableAutoConfiguration后就会自动配置这些环境

20.3.3 @ComponentScan

进行扫包,

20.4 springboot配置下springmvc新注解

20.4.1 @RestController

@RestController = @Controller+@ResponseBody

20.4.2 @GetMapping

@GetMapping = @RequestMapping(method = RequestMethod.GET)

20.4.3 @PostMapping

@PostMapping = @RequestMapping(method = RequestMethod.POST)

20.4.4 @PutMapping

@PutMapping = @RequestMapping(method = RequestMethod.PUT)

20.4.5 @DeleteMapping

@DeleteMapping = @RequestMapping(method = RequestMethod.DELETE)

20.5 springboot启动方式

20.5.1 在开发环境中通过运行springboot启动类直接运行

20.5.2 通过maven命令运行

在pom中引入maven启动springboot项目的依赖

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

启动时进入springboot项目所在目录,使用mvn spring-boot:run 启动

20.5.3 将springboot项目打成jar包发布到linux上

进入项目目录,使用mvn package打成jar包,并发布到linux上,直接在linux的项目目录下运行java -jar jar即可,因为生成的jar包中自带有tomcat,所以无需再进行手动配置tomcat

20.6 springboot自动装配

springboot官方为我们提供了各种starter,我们只需要在pom.xml中导入对应的starter,springboot会自动导入jar包,然后进行自动的配置。

20.6.1 使用第三方starter

当使用非spring官方的starter时,需要指定其version版本号,当我们引用一些第三方starter时,还需要我们对其中的部分信息进行配置,需要在resources目录下application.yml文件中进行配置,否则自动装配会失败,如当我们引入数据库连接池jdbc的starter时,由于springboot不知道我们数据库连接的地址信息,直接启动就会报错,下面举例引入jdbc连接池starter并进行配置

引入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入jdbc连接池starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
		<!--引入数据库连接池驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>
    </dependencies>

在resources目录下创建application.yml文件,其中进行配置

server:
  port: 8081
  servlet:
    context-path: /ssm
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springmvc?useSSL=false
    username: root
    password: 1234

20.7 Springboot中的properties类

properties类用于替代properties文件,主要用于第三方技术和spring整合时使用,由spring或第三方定义,我们一般会编写yml文件和这种类配合,用于配置第三方技术中的部分参数,下面以数据库连接池自动装配类DataSourceAutoConfiguration 举例说明properties类的加载过程

首先看DataSourceAutoConfiguration类上方定义的注解

@AutoConfiguration(before = {SqlInitializationAutoConfiguration.class})
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)//读取proterties类的内容
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
	//code。。。
}
  1. 第一个注解AutoConfiguration就是对Configuration注解的封装,表示当前类是java配置类,用于替代xml文件中的配置信息
  2. 第二个注解ConditionalOnClass表示如果当前项目环境中存在参数中的类时,运行自己的自动装配代码
  3. 第三个注解ConditionalOnMissingBean表示当前项目spring容器中不存在参数中的实体时才运行自己的自动装配代码
  4. 第四个注解EnableConfigurationProperties表示引入Properties类,将其作为配置信息,在自动装配过程中会调用其中配置的参数信息
  5. 第五个注解Import表示引入其他的自动装配类

进入EnableConfigurationProperties注解参数DataSourceProperties源码中:

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
	//code。。。

ConfigurationProperties注解用于标记当前类是Properties类,其中的prefix参数表示的是为其中定义的参数加上前缀,比如下面定义的url,外界在取得时应使用spring.datasource.url,username外界在取得时要使用spring.datasource.username

针对数据库连接池,我们需要配置数据库的连接地址、访问用户名、密码等信息,所以我们编写的application.yml文件如下

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springmvc?useSSL=false
    username: root
    password: 1234

所以当我们配置完application.yml,springboot在启动时就会自动根据Properties中定义的变量的全名(如spring.datasource.url)来到yml文件中寻找我们配置的对应的信息,在寻找完成后,整个propertie类中对应的变量也就被赋值为我们在yml中配置的值。之后在DataSourceAutoConfiguration自动装配类中就能直接从EnableConfigurationProperties注解参数中的properties类中取得对应的自定义参数,完成自动装配过程

20.8 springboot搭建ssm项目

新建项目
在这里插入图片描述
导入pom

<?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.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.furong</groupId>
    <artifactId>ssm</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-ssm</name>
    <description>springboot-ssm</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.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

springboot入口类上注解扫描mapper接口

@SpringBootApplication
@MapperScan("com.furong.mapper")//扫描mapper接口
public class SsmApplication {
    public static void main(String[] args) {
        SpringApplication.run(SsmApplication.class, args);
    }
}

mapper接口

@Mapper//为当前mapper接口创建代理对象,如果在入口类配置了扫描,这里不需要配置
public interface UserMapper {
    User findUserById(Long id);
}

mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 命名空间,用于隔离sql,后面还有一个很重要的作用 -->
<mapper namespace="com.furong.ssm.mapper.UserMapper">

	<!--配置type时,若在sqlSessionFactory中配置了typeAliasesPackage的话,这里就只用写typeAliasesPackage之后的路径-->
    <resultMap id="userMap" type="User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="salt" property="salt"/>
        <result column="phone" property="phone"/>
        <result column="created" property="created"/>
        <result column="last_login_time" property="lastLoginTime"/>
        <result column="status" property="status"/>
    </resultMap>

    <select id="findUserById" parameterType="long" resultMap="userMap">
        select * from gxa_user where id=#{arg0}
    </select>
</mapper>

配置全局的application.yml文件,其中配置springboot中需要自定义的配置信息

server:
  port: 8080
  servlet:
    context-path: /ssm
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
mybatis:
  #配置sqlSessionFactory的typeAliasesPackage属性,配置后在对应的mapper.xml文件中,配置resultType时,只用写com.furong.pojo之后的路径
  type-aliases-package: com.furong.pojo
  #配置mybatis扫描mapper包的位置
  mapper-locations: classpath:mapper/*.xml

测试

@SpringBootTest
class SpringbootSsmApplicationTests {
    @Autowired
    UserMapper userMapper;
    @Test
    void contextLoads() {
        User userById = userMapper.findUserById(1L);
        System.out.println(userById);
    }
}

在这里插入图片描述

20.9 springboot整合mybatisPlus及分页助手

20.9.1 引入依赖

若原本有mybatis的依赖需要去掉,mybatisPlus中自带有mybatis

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

配置application.yml

mybatis-plus:
  #配置mapper的xml文件扫描包位置,路径是resources下的相对路径
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.furong.ssm.pojo

编写mapper接口

编写mapper的xml文件,注意xml文件的namespace、sql语句的id、resultType、parameterType和接口、Java bean的对应关系

20.9.2 利用java配置类手动装配分页助手

导入jar包

        <!--分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.3</version>
        </dependency>

编写分页助手java配置类,配置完成后spring容器中就已经存在PageInterceptor 对象,可以直接使用PageHelper的方法开启分页查询

@Configuration
public class PageHelperConfiguration {
//    <!--配置pagehelper的分页插件-->
//    <bean class="com.github.pagehelper.PageInterceptor">
//        <property name="properties">
//            <value>
//                param1=value1;
//            </value>
//        </property>
//    </bean>
    @Bean
    public PageInterceptor pageHelper(){
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.setProperty("param1","value1");
        pageInterceptor.setProperties(properties);
        return pageInterceptor;
    }
}

20.10 springboot整合swagger

导入依赖

        <!--swagger的依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--swagger-ui的jar包(里面包含了swagger的界面静态文件) -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

拷贝swagger的java配置类文件

package com.furong.ssm.swagger;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

@EnableSwagger2
@Configuration
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .pathMapping("/")
                .select()
                //设置接口的扫描包位置
                .apis(RequestHandlerSelectors.basePackage("com.furong.ssm.controller"))
                .paths(PathSelectors.any())
                .build().apiInfo(apiInfo())
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
    }

    private List<ApiKey> securitySchemes() {
        List<ApiKey> apiKeyList = new ArrayList();
        apiKeyList.add(new ApiKey("token", "token", "header"));
        return apiKeyList;
    }

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("^(?!auth).*$"))//过滤要验证的路径
                .build());
        return securityContexts;
    }

    //增加全局认证
    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("token", authorizationScopes));//验证增加(有许多教程说明中这个地方是Authorization,导致不能带入全局token,因为securitySchemes()方法中header写入token,所以这个地方我改为token就可以了)
        return securityReferences;
    }
    /**
     * 接口文档信息
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("芙蓉医疗")
                .description("芙蓉医疗接口文档")
                .version("1.0.0")
                .termsOfServiceUrl("")
                .license("")
                .licenseUrl("")
                .build();
    }

}

在全局yml文件中更改配置

spring:
	mvc:
	    pathmatch:
	      matching-strategy: ANT_PATH_MATCHER

20.11 迁移代码测试环境

20.12 配置拦截器

线程工具类,用于将当前数据绑定到当前线程中

public class AdminThreadLocal {
    static ThreadLocal<Admin> threadLocal = new ThreadLocal<>();

    /**
     * 绑定管理员到当前线程
     */
    public static void setAdmin(Admin admin){
        threadLocal.set(admin);
    }

    /**
     * 获取当前线程绑定的管理员
     * @return
     */
    public static Admin get(){
        return threadLocal.get();
    }

    /**
     * 移出当前线程绑定的管理员
     */
    public static void remove(){
        threadLocal.remove();
    }
}

编写拦截器类实现HandlerInterceptor接口,这里的拦截器是登录认证拦截器

@Slf4j
//实现登录认证的拦截器
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    //处理器之前调用
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("认证拦截器被调用了");
        //获取jwt令牌
        String token = request.getHeader("token");
        if(token == null || token.equals("null")){
            token = request.getParameter("token");
            if(token==null || token.equals("null")){
                log.debug("没有携带token:{}",request.getRemoteAddr());
                String json = JsonUtils.objectToJson(ResultUtils.buildFailed(20003, "没有携带token"));
                ResponseUtils.responseToJson(json,response);
                return false;
            }
        }

        //验签
        if(!TokenUtils.checkToken(token)){
            log.debug("验签失败{}",token);
            String json = JsonUtils.objectToJson(ResultUtils.buildFailed(20003,"无效token"));
            ResponseUtils.responseToJson(json,response);
            return false;
        }

        //获取载荷数据
        JWT jwt = JWT.of(token);
        Admin admin = new Admin();
        admin.setId((Integer) jwt.getPayload("id"));
        admin.setUsername((String) jwt.getPayload("username"));
        admin.setName((String) jwt.getPayload("name"));
        admin.setPhone((String) jwt.getPayload("phone"));
        admin.setAvatar((String) jwt.getPayload("avatar"));
        //获取载荷中权限uri内容
        //admin.setPermissionUris((String) jwt.getPayload("permissionUris"));
        admin.setTags((String) jwt.getPayload("tags"));
        //绑定载荷数据到当前线程
        AdminThreadLocal.setAdmin(admin);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AdminThreadLocal.remove();
    }
}

编写拦截器的java配置类,用于代替之前用xml配置拦截器的xml文件,需要实现WebMvcConfiguration接口

@Configuration      //springboot在对springmvc自动装配时会检测是否有自定义类实现了WebMvcConfigurer接口,通过实现这个接口我们可以对springboot中的一些默认配置进行自定义更改
                    //接口中有很多可以进行自定义配置的方法,如果实现了接口中对应的方法,就会进行对应配置的注册,这里是addInterceptors,就会注册拦截器
public class MyMvcInterceptorConfiguration implements WebMvcConfigurer {

    //将xml配置拦截器方式改成java配置类方式
//        <!--token拦截器,验证是否登录-->
//		<mvc:interceptor>
//			<!--配置拦截路径-->
//			<mvc:mapping path="/**"/>
//			<!--配置不拦截的资源-->
//			<mvc:exclude-mapping path="/admin/login"/>
//			<!--配置拦截器不拦截swagger资源-->
//			<mvc:exclude-mapping path="/swagger-resources/**"/>
//			<mvc:exclude-mapping path="/webjars/**"/>
//			<mvc:exclude-mapping path="/v2/**"/>
//			<mvc:exclude-mapping path="/swagger-ui.html/**"/>
//			<bean class="com.furong.admin.interceptor.AuthInterceptor"/>
//		</mvc:interceptor>

    //注册登录认证拦截器
    @Bean       //相当于注入自定义的拦截器类AuthInterceptor
    AuthInterceptor authInterceptor(){
        return new AuthInterceptor();
    }
    @Override   //WebMvcConfigurer接口中的方法,用于配置拦截器的拦截或不拦截路径
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(authInterceptor())
                .excludePathPatterns("/swagger-resources/**")
                .excludePathPatterns("/webjars/**")
                .excludePathPatterns("/v2/**")
                .excludePathPatterns("/swagger-ui.html/**")
                .excludePathPatterns("/admin/login");
    }
}

20.13 配置过滤器

20.13.1 配置自定义过滤器

@Slf4j
@WebFilter("/*")    //servlet中注解,用于标记过滤哪些请求
public class MyFilter implements Filter {       //实现Filter接口就能被识别为过滤器
    //自定义过滤器
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter invoked");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

在springboot入口类上标记ServletComponentScan注解,表示让springboot识别原生servlet注解,即识别上面编写自定义过滤器的WebFilter注解

@SpringBootApplication
@MapperScan("com.furong.mapper")//扫描mapper接口
@ServletComponentScan //配置扫描servlet原生注解
public class SsmApplication {
    public static void main(String[] args) {
        SpringApplication.run(SsmApplication.class, args);
    }
}

20.13.2 配置第三方过滤器

这里以中文乱码过滤器为例,直接编写对应的Configuration配置类

@Configuration
public class CharSetEncodingConfiguration {
    /**
     * 配置第三方中文乱码过滤器,把第三方过滤器包装成spring定义的bean,使用java配置
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new CharacterEncodingFilter());
        //设置过滤器名
        filterRegistrationBean.setName("characterEncodingFilter");
        //配置初始化参数
        Map map = new HashMap();
        map.put("encoding","UTF-8");
        filterRegistrationBean.setInitParameters(map);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }
}

20.14 配置异常处理器

编写异常处理器类即可

@ControllerAdvice       //针对controller产生代理对象,底层使用aop的机制实现异常处理
@Slf4j
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(Exception.class)      //当抛出Exception异常时调用
    public Result error(Exception e){
        log.error("未知异常{}",e.getMessage());
        return ResultUtils.buildFailed(50000,"系统异常");
    }
    @ResponseBody
    @ExceptionHandler(CustomException.class)      //当抛出自定义异常类CustomException异常时调用
    public Result custom(CustomException e){
        log.error("未知异常{}",e.getMessage());
        return ResultUtils.buildFailed(500,e.getMsg());
    }
}

20.15 自定义自动装配starter

下面以自己编写的对redis操作的工具类以及对该类工具的自动装配举例

20.15.1 以maven创建自动装配项目

在这里插入图片描述

20.15.2 在pom中导入依赖

<?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>

    <groupId>ikun.redis</groupId>
    <artifactId>redis-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--这里要以springboot项目作为父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/>
    </parent>
    
    <dependencies>
        <!--导入实现自动装配的基础依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <scope>compile</scope>
        </dependency>
        <!--导入对properties类的支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <!--导入自动装配流程支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure-processor</artifactId>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <!--导入redis的jar包-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>
    <!--编写自动装配项目时,不能配置plugin插件-->
</project>

20.15.3 编写redis工具类,通过这个类可以更方便地操作redis

RedisTemplate.java

public class RedisTemplate {
    //工具类中需要用到redis的连接池对象
    private JedisPool jedisPool;

    /**
     * 设置String类型的值
     * @param key
     * @param value
     * @return
     */
    public String set(String key,String value){
        Jedis jedis = jedisPool.getResource();
        String result = jedis.set(key, value);
        jedis.close();
        return result;
    }

    /**
     * 获取string类型的值
     * @param key
     * @return
     */
    public String get(String key){
        Jedis jedis = jedisPool.getResource();
        String result = jedis.get(key);
        jedis.close();
        return result;
    }


    /**
     * 设置hash的值
     * @param key
     * @param filed
     * @param value
     * @return
     */
    public Long hset(String key,String filed,String value){
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hset(key, filed, value);
        jedis.close();
        return result;
    }


    /**
     * 获取hash的值
     * @param key
     * @param filed
     * @return
     */
    public String hget(String key,String filed){
        Jedis jedis = jedisPool.getResource();
        String result = jedis.hget(key,filed);
        jedis.close();
        return result;
    }

    /**
     * 获取整合hash的map
     * @param key
     * @return
     */
    public Map<String,String> hgetAll(String key){
        Jedis jedis = jedisPool.getResource();
        Map<String, String> result = jedis.hgetAll(key);
        jedis.close();
        return result;
    }


    /**
     * 设置失效时间
     * @param key
     * @param seconds
     * @return
     */
    public Long expire(String key,Integer seconds){
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.expire(key, seconds);
        jedis.close();
        return result;
    }


    /**
     * 删除整个key
     * @param key
     * @return
     */
    public Long del(String key){
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }


    /**
     * 删除hash某个filed
     * @param key
     * @param filed
     * @return
     */
    public Long hdel(String key,String filed){
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hdel(key,filed);
        jedis.close();
        return result;
    }


    /**
     * 递增
     * @param key
     * @return
     */
    public Long incr(String key){
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.incr(key);
        jedis.close();
        return result;
    }


    /**
     * 递减
     * @param key
     * @return
     */
    public Long decr(String key){
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.decr(key);
        jedis.close();
        return result;
    }


    public JedisPool getJedisPool() {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
}

20.15.4 编写Properties类,用于配置redis工具类的基本信息

@ConfigurationProperties(prefix = "ikun-redis") //声明当前类是一个properties类,在yml中配置其中参数时要同一加上ikun-redis前缀
public class RedisProperties {
    //访问redis的ip地址,这里redis部署在本地所以直接使用localhost
    private String host = "localhost";
    //访问redis的端口号,可以在redis的conf文件中修改这个端口号
    private Integer post = 6379;
    //redis访问密码,windows下默认redis没有密码,所以这里可以设为空
    private String password = "";
    private Integer maxTotal = 30;
    private Integer minIdle = 9;
    private Integer maxWaitMillis = 3000;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPost() {
        return post;
    }

    public void setPost(Integer post) {
        this.post = post;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getMaxTotal() {
        return maxTotal;
    }

    public void setMaxTotal(Integer maxTotal) {
        this.maxTotal = maxTotal;
    }

    public Integer getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(Integer minIdle) {
        this.minIdle = minIdle;
    }

    public Integer getMaxWaitMillis() {
        return maxWaitMillis;
    }

    public void setMaxWaitMillis(Integer maxWaitMillis) {
        this.maxWaitMillis = maxWaitMillis;
    }
}

这里可以对比一下使用xml方式配置时,上面这些属性对应的配置方法
在这里插入图片描述

从上面的配置中可以看出来,要想装配redis工具类,需要先装配数据库连接池的配置类,再将数据库连接池配置类装配到连接池,最后将连接池装配到redis工具类中,所以下面按照这个装配顺序编写自动装配类

20.15.5 编写连接池配置对象的自动装配

RedisPoolConfigAutoConfiguration.java

@Configuration		//标记当前类属于java配置类,就等同于xml配置文件
@ConditionalOnClass({JedisPoolConfig.class})	//springboot启动时会检测当前环境中是否加载了JedisPoolConfig类,只有当存在时才进行下面的自动装配
@EnableConfigurationProperties(RedisProperties.class)   //加载propertie配置类
public class RedisPoolConfigAutoConfiguration {
    @Autowired	//通过autowired可以获取上方注解中的properties类,下面就可以使用其中的自定义参数
    RedisProperties redisProperties;
    
    @Bean(name = "jedisPoolConfig")		//Bean注解就相当于xml配置中的bean标签,执行完这个方法就相当于在xml配置文件中完成了ioc和di,被标记的方法返回的对象会放入spring容器中,参数中传递的是这个对象放入spring容器后的名字
    @ConditionalOnMissingBean(name = "jedisPoolConfig")		//ConditionalOnMissingBean注解是用于在ioc和di前,先检查spring容器中是否已存在名字为jedisPoolConfig的对象,若存在则说明之前已经ioc过了,不需要再执行下面的代码进行ioc装配了
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        //从Properties类中取出参数,并设置到JedisPoolConfig对象属性中,这里就相当于在进行di
        jedisPoolConfig.setMaxTotal(redisProperties.getMaxTotal());
        jedisPoolConfig.setMinIdle(redisProperties.getMinIdle());
        jedisPoolConfig.setMaxWaitMillis(redisProperties.getMaxWaitMillis());
        //返回jedisPoolConfig对象给spring容器
        return jedisPoolConfig;
    }
}

对比使用xml进行ioc和di时的代码:
在这里插入图片描述

20.15.6 编写连接池对象的自动装配

RedisPoolAutoConfiguration.java

@Configuration
@ConditionalOnClass({Jedis.class, JedisPool.class})
@EnableConfigurationProperties(RedisProperties.class)   //加载properties配置类
@Import(RedisPoolConfigAutoConfiguration.class)     //导入连接池配置类的自动装配文件,因为装配连接池时要使用到连接池配置类
public class RedisPoolAutoConfiguration {           //对redis连接池的自动装配类
    @Autowired
    RedisProperties redisProperties;
    
    @Bean(name = "jedisPool")
    @ConditionalOnMissingBean(name = "jedisPool")
    @ConditionalOnClass(JedisPoolConfig.class)
    public JedisPool jedisPool(JedisPoolConfig jedisPoolConfig){
        JedisPool jedisPool;
        //当有配置类中密码值为空时不用为连接池装配密码
        if(StringUtils.isEmpty(redisProperties.getPassword())){
            //因为在类上面使用了Import注解引入了连接池配置类的自动配置文件,所以这里可以直接获取其中装配的连接池配置类对象,再传入连接池的ip地址和端口号
            jedisPool = new JedisPool(jedisPoolConfig,redisProperties.getHost(),redisProperties.getPost());
        }else {     //当有配置类中密码值不为空时再为连接池装配密码
            jedisPool=new JedisPool(jedisPoolConfig,redisProperties.getHost(),redisProperties.getPost(),redisProperties.getMaxWaitMillis(),redisProperties.getPassword());
        }
        return jedisPool;
    }
}

对比使用xml进行ioc和di时的代码:
在这里插入图片描述

20.15.7 编写redis工具类的自动装配

RedisAutoConfiguration.java

@Configuration
@ConditionalOnClass({Jedis.class, JedisPool.class,RedisTemplate.class})
@Import(RedisPoolAutoConfiguration.class)       //引入连接池自动装配类
public class RedisAutoConfiguration {
    @Bean(name = "redisTemplate")
    @ConditionalOnBean(name = "jedisPool")
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate redisTemplate(JedisPool jedisPool){
        RedisTemplate redisTemplate = new RedisTemplate();
        //将连接池装配类中所装配的连接池对象装配到redis工具类中
        redisTemplate.setJedisPool(jedisPool);
        return redisTemplate;
    }
}

对比使用xml进行ioc和di时的代码:
在这里插入图片描述

20.15.8 编写自动装配类的spring.factories文件

在resources/META-INF目录下编写spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.ikun.redis.RedisAutoConfiguration

这个文件是所有springboot的自动装配类都必须的,springboot项目在启动时会首先扫描项目中所有jar包,并看每个jar包中是否包含resources/META-INF/spring.factories文件,若存在则说明当前类属于自动装配类,其中需要指明自动装配项目中的所有自动装配类,springboot会一一执行其中的所有装配方法完成装配,这里由于我在装配redis工具类时引入的连接池装配类,装配连接池时又引入了连接池配置装配类,所以只需要在这里引入reids的装配类就能自动完成所有装配,如果没有在之前依次引入前置装配类的话,这里就要将所有自动装配类都配置上去

到此整个redis工具自动装配类的编码已经完毕,整个工程的结构如下
在这里插入图片描述

20.15.9 使用mvn命令将redis工具类自动装配项目打包成jar包,并将其存入本地的maven仓库中

在这里插入图片描述

20.15.10 在其他springboot项目的pom中引入redis工具自动装配类的依赖

这个maven坐标的参数在redis工具自动装配项目的pom文件中可以自定义

<dependency>
    <groupId>ikun.redis</groupId>
    <artifactId>redis-starter</artifactId>
    <version>1.1</version>
</dependency>

20.15.11 在全局的yml文件中配置redis工具类的连接信息

在这里插入图片描述
我在redis工具自动装配类中所配置的默认参数都是和我本地redis一致的,所以这里不需要再进行单独配置,这个yml文件会再springboot项目启动的时候被扫描,并会与所有自动装配类中的Properties文件进行配合,将文件中的参数注入到Properties文件的属性中

20.15.12 使用springboot单元测试,测试自定义的redis工具自动装配类是否生效

引入springboot单元测试依赖

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

编写测试代码

@SpringBootTest
class SpringbootSsmApplicationTests {
    //直接注入redis工具类对象
    @Autowired
    RedisTemplate redisTemplate;
    @Test
    public void test(){
        //使用工具类对象向redis中写入并读取数据
        redisTemplate.set("ikun1","ikunkun1");
        System.out.println(redisTemplate.get("ikun1"));
    }
}

测试成功
在这里插入图片描述

20.16 springboot的单元测试

引入依赖

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

测试包名一定要在springboot启动类之下,如
在这里插入图片描述

高版本的springboot中的测试,直接在测试类中引入org.junit.jupiter.api.Test,然后在测试类上使用@SpringBootTest就能直接运行

低版本的springboot的测试类中引入的是org.junit.Test,测试类上需要同时使用@SpringBootTest和@RunWith(SpringRunner.class)才能正常测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值