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。。。
}
- 第一个注解AutoConfiguration就是对Configuration注解的封装,表示当前类是java配置类,用于替代xml文件中的配置信息
- 第二个注解ConditionalOnClass表示如果当前项目环境中存在参数中的类时,运行自己的自动装配代码
- 第三个注解ConditionalOnMissingBean表示当前项目spring容器中不存在参数中的实体时才运行自己的自动装配代码
- 第四个注解EnableConfigurationProperties表示引入Properties类,将其作为配置信息,在自动装配过程中会调用其中配置的参数信息
- 第五个注解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)才能正常测试