欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springboot使用合集

程序员文章站 2022-05-14 23:46:03
...

一、 SpringBoot

1.1 什么是 springboot

Spring Boot是由Pivotal团队提供的全新工具集,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

spring大家都知道,boot是启动的意思。所以,spring boot其实就是一个启动spring项目的一个工具而已。从最根本上来讲,Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用。
以前在写spring项目的时候,要配置各种xml文件,还记得曾经被ssh框架支配的恐惧。随着spring3,spring4的相继推出,约定大于配置逐渐成为了开发者的共识,大家也渐渐的从写xml转为写各种注解,在spring4的项目里,你甚至可以一行xml都不写。

虽然spring4已经可以做到无xml,但写一个大项目需要茫茫多的包,maven配置要写几百行,也是一件很可怕的事。

现在,快速开发一个网站的平台层出不穷,nodejs,php等虎视眈眈,并且脚本语言渐渐流行了起来(Node JS,Ruby,Groovy,Scala等),spring的开发模式越来越显得笨重。

在这种环境下,spring boot伴随着spring4一起出现了。
springboot 的使用很简单,我们只需要将原先我们的 xml 配置中的内容通过 java 方式配置过去即可,大部分配置已经被 springboot 自己装配,我们只需要将需要我们自己写的配置单独写出来即可

1.2 hello world

基于 springBoot 快速搭建web项目

maven3.2+;java1.8+;spring-boot-2.x;

1.2.1 安装springboot

安装很简单:一个父pom,一个依赖,一个插件

<!-- === 1) 指定父pom === 
	 继承spring-boot-starter-parent 
     springBoot运行中需要很多依赖和插件,springBoot给出了父pom,用于帮助用户使用这些依赖和插件。
     其中做了依赖管理,插件管理:在导入依赖和插件时可以不用定义version和参数配置,都由父pom统一管理
	 只需要定义groupId和artifactId即可。
-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
</parent>
<!-- === 2) 导入 springboot 的 web 依赖 === 
	 此处不需要添加版本,由 parent 统一管理 
     为项目添加web的依赖:主要是引入springMVC的依赖,和引入内置的tomcat
-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 至此,就已经用了springBoot的两个start啦:【spring-boot-starter-parent】
                                      	  【spring-boot-start-web】
     sprngBoot中提供了很多start,用于支持不同功能的自动配置。即在使用springBoot时,可根据不同的需要导
     入不同的start,springBoot根据项目中引入的不同的start,完成对应的自动配置。 
-->
<!-- === 3) 添加 springboot plugin ===
	 提供springboot的maven支持,导入此插件后,就可以通过maven打包springboot项目
     生成 “executable jars”
	 还提供了spring-boot:run功能,可以启动springboot项目(和main函数功能相似)
-->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

1.2.2 启动类

1) 自定义启动类:BootApplication

2) 定义一个Controller类:UserController

注意启动类建议在Controller类所在包的父包中

package com.rj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

//@EnableAutoConfiguration //自动装载配置 根据项目中导入的 start 自动装载配置
//@ComponentScan //扫描当前包和所有子包的所有类
@SpringBootApplication
public class BootApplication {
    public static void main(String[] args) {
        //项目启动,启动工厂,启动tomcat,部署项目
        SpringApplication.run(SpringApplication.class,args);
    }
}
package com.rj.controller;
...

@Controller
public class UserController {
    @RequestMapping("helloworld")
    @ResponseBody
    public String helloWorld() {
        return "hello moto";
    }
}

1.2.3 启动项目

* 启动方式1:
* 直接运行 入口类的 main 方法即可
* 启动方式2:
* mvn spring-boot:run

1.2.4 访问项目

* 正常访问即可 http://localhost:8080/helloworld
* 注意:路径中没有项目名

springboot使用合集

1.2.5 springboot-配置

在resources下建立springboot的配置文件:application.yml 或 application.properties

重启项目,配置即可生效。

# application.yml ( 通过tab缩进区分级别,:后要有一个空格然后再定义值 )
server:
  # tomcat端口 , 默认 “8080”
  port: 8989
  # 项目部署名(上下文名称),默认 “/”
  servlet:
    context-path: /fruit9
  tomcat:
  	# 防止get请求参数中文乱码,默认 “utf-8”
    uri-encoding: utf-8
# application.properties
server.port=8989
server.tomcat.uri-encoding=utf-8
server.servlet.context-path=/boot

二、SpringMVC细节

2.1 拦截器

2.1.1 定义拦截器

与原先的使用方式一致

2.1.2 配置拦截器

项目中不再出现mvc的配置文件,更建议的配置方式基于类的。

@Configuration
public class SpringMVCConfig implements WebMvcConfigurer{//继承 WebMvcConfigurer
    @Override
    public void addInterceptors(InterceptorRegistry registry) { //添加自定义拦截器
        //注册 拦截器
        registry.addInterceptor(new MyInterceptor()) //注册拦截器
                .addPathPatterns("/user/*","/user/test3/a"); //定义拦截路径
    }
}

2.2 Json处理

默认采用的依然是Jackson。

相关注解:@RestController @ResponseBoby @RequestBody 使用照旧。

如果要使用FastJson,则需要配置如下:

@Configuration
public class SpringMVCConfig implements WebMvcConfigurer{
	.....
    
    // 注册FastJsonHttpMessageConverter
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 1. 定义一个converters转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 2. 获取配置对象
        FastJsonConfig fastJsonConfig = fastConverter.getFastJsonConfig();
        // 2.1 全局设置日期格式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH");
        // 2.2 null值,依然写出。默认忽略null值
        fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
        // 3. 设置支持的数据格式
        fastConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
        converters.add(0,fastConverter);//将FastJson的HttpMeesageConverter置在Jackson之前,使其生效
    }
}
@Configuration
public class FastJsonConf {
    /**
    	HttpMessageConvertersAutoConfiguration是负责做Converters自动注册的组件,其中:
    	@Bean
        @ConditionalOnMissingBean //此注解含义:如果工厂中没有定义名为"httpMessageConverters"
                                  //          或类型为"HttpMessageConverters"的bean,则执行如下自动装配
        public HttpMessageConverters messageConverters() {
            return new HttpMessageConverters(
                    this.converters != null ? this.converters : Collections.emptyList());
        }
        所以有如下类型为 HttpMessageConverters的bean,会使得此项自动装配失效
    */
    @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {//更方便的注册HttMessageConverter的方式
        // 1. 定义一个converters转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 2. 获取配置对象
        FastJsonConfig fastJsonConfig = fastConverter.getFastJsonConfig();
        // 设置日期格式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH");
        // null值,依然写出。默认忽略null值
        fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
        // 设置支持的数据格式
        fastConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
        // 3. 返回HttpMessageConverters对象
        return new HttpMessageConverters(fastConverter);
    }
}

2.3 Rest支持

已注册好:OrderedHiddenHttpMethodFilter,用户供浏览器发请求时,模拟put和delete请求。

已注册好:OrderedHttpPutFormContentFilter,用在在put请求中发送 application/x-www-form-urlencoded编码的参数

2.4 跨域

此注解只有在Controller类上才有效

@CrossOrigin(value = "http://localhost:8080",allowCredentials = "true")

或者也可以定义配置

@Configuration
public class MVCConfig implements WebMvcConfigurer{
    ....
    //设置跨域
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:8080").allowCredentials(true);
    }
}

2.5 静态资源

自动注册了静态资源处理的handler,ResourceHttpRequestHandler 其requestMapping为 【/**

用于处理静态资源,所以静态资源已经自动解决。

webapp目录下的静态资源可以正常访问。

新增静态资源目录:‘resources’下新建 static目录,static中存放静态资源 ( ResourceProperties 管理此目录)

访问:http://ip:port/contextPath/js/aa.js http://ip:port/contextPath/png/boot.jpg 访问:<script src="/contextPath/js/aa.js"> </script> <img src="/contextPath/png/boot.jpg"/>
springboot使用合集
webapp下的静态资源访问路径 照旧!但此时可以不定义webapp目录!

2.6 异常解析器

如果出现服务器错误,即,服务器端出现异常,此时有两种选择:

1> 各个方法中添加 try... catch...

2> 定义异常解析器,做全局的异常处理

@ControllerAdvice
public class MyExceptionResolver {

    /**
     * 可选参数: request response session model
     * 可选返回值: String ResponseBody ModelAndView void
     */
    @ExceptionHandler(value={MyException1.class,MyException2.class}) //定义要捕获的异常,一个或多个
    public String handleEx1(HttpServletRequest request, HttpServletResponse res, HttpSession session, 
                            Exception e){
        System.out.println(e.getClass()+e.getMessage());
        return "forward:/error.html";
    }
    @ExceptionHandler(value={UnknownAccountException.class, IncorrectCredentialsException.class})
    @ResponseBody
    public R handleExLoginError(HttpServletRequest request, HttpServletResponse res, HttpSession session,
                      Exception e){
        e.printStackTrace();
        return R.error("登录失败");// R是自定义的工具类
    }

    /* 了解:另一种形式
    @ExceptionHandler//如果注解中没指定注解,则参数表中最后一个参数被认为是 需要捕获的异常(只能定义一个异常)
    public String handleEx1(HttpServletRequest request, HttpSession session,MyException1 e1){
        System.out.println(e1);
        return "forward:/error.html";
    }*/
}

2.7 上传解析器

不用再手动注册上传解析器,自动注册了,StandardServletMultipartResolver, 且其并不依赖 commons-fileupload。如果需要限定上传文件大小,可做如下配置:

spring:
  servlet:
    multipart:
      max-request-size: 10000MB #请求最大容许体量
      max-file-size: 10000MB #文件容许体量
      #如上两个都设置大值,然后在通过拦截器过滤大小并抛异常,在配合异常解析器即可。
//提交表单 照旧
//接收请求的handler 照旧
//异常解析器,添加一个ExceptionHandler,用于处理文件超大问题
@ControllerAdvice
public class MyExceptionResolver {

    @ExceptionHandler(value={MaxUploadSizeExceededException.class}) //定义要捕获的异常,一个或多个
    public String handleUpload(HttpServletRequest request, HttpServletResponse res, HttpSession session, 
                               Exception e){
        System.out.println(e.getClass()+e.getMessage());
        return "redirect:/error.html";
    }
}

2.8 异常处理

2.8.1 静态错误页面

SpringBoot处理http请求时,出现不同的错误status时(400,403,404,405,500等),会自动由ErrorViewResolver处理错误。

ErrorViewResolver会针对不同的status,到 classpath:/static/error下查找 :
400.html 404.html 500.html等,作为错误页面响应。

如下图,还可以继续创建对应其他 status的错误页面
springboot使用合集

400: bad request , 常见于请求参数格式错误,导致请求无法解析时。

403: 访问被禁止。跨域时,A设置好自己允许的域,其他所有域的访问都会被禁止

404: 资源未找到时。

405: method not allowed,常见于Restful场景中,请求方式使用错误时。

500: 服务器程序运行异常时。

2.8.2 动态错误页面

使用thymeleaf引擎,定制动态如上html页面,作为错误页面。

错误页面存在模板根目录下
springboot使用合集

2.9 Jsp

2.9.1 导入依赖

<!-- webapp目录下的jsp文件可以被访问,但默认情况下不能被编译,执行,所以即使被访问也并不能正常显示在浏览器 -->
<!-- servlet 和 jstl 依赖 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>
<!-- jsp 编译依赖 -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

2.9.2 配置

spring:
  mvc:
    view:
      prefix: /
      suffix: .jsp
#默认前后缀都是 “”。修改后handler中即可通过 return "hello" 跳转 /hello.jsp. 
#否则必须使用 return "forward:/hello.jsp" 跳转 

JSP即可正常使用啦。

三、整合 mybatis

3.1 pom 依赖文件

<!-- 持久层依赖 mysql+mybatis+druid(连接池)+分页 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.13</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.5</version>
</dependency>

3.2 application.yml

mybatis:
  #扫描
  mapper-locations: classpath:com/rj/dao/mapper/*.xml
  type-aliases-package: com.rj.domain
spring:
  #连接池
  datasource:
      druid:
        max-active: 50
        initial-size: 5
        min-idle: 5
        max-wait: 60000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        validation-query: select 1
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
        driver-class-name: com.mysql.jdbc.Driver
        # 务必要指定utf8 否则mysql通信中文乱码 (库和表在建立时也要指定utf8)
        url: jdbc:mysql://localhost:3306/db9?useUnicode=true&characterEncoding=utf8&useSSL=false
        username: root
        password: 111111
pagehelper:
  # 自动调整过界页号 小于1 大于最大页号
  reasonable: true

3.3 pojo

public class User implements Serializable{
    private Integer id;
    private String nick;
    private String tel;
	// set/get...
}

3.4 mapper

//dao接口,mapper文件照旧,但需要在启动类上添加 @MapperScan("com.rj.dao") 作用于MapperScannerConfigurer
@SpringBootApplication
@MapperScan("com.rj.dao")// 作用于MapperScannerConfigurer
public class AppBoot {
    public static void main(String[] args) {
        SpringApplication.run(AppBoot.class,args);
    }
}

3.5 service

@Service //声明
public class UserService implements IUserService{
    @Autowired //注入
    private UserDAO userDAO;

    public User queryUser(Integer id) {
        return userDAO.queryUser(id);
    }

    @Transactional //事务
    public void updateUser(User user) {
        userDAO.updateUser2(user);
    }

    @Transactional //事务
    public void insertUser(User user) {
        userDAO.insertUser(user);
    }

    @Transactional //事务
    public void deleteUser(Integer id) {
        userDAO.deleteUser(id);
    }
}

3.6 cache

照旧,可以自定义Cache组件,在mapper文件中 <cache type="cache组件"/>

3.7 测试

<!-- 用于集成junit测试 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AppBoot.class})//AppBoot是启动类
public class Test9 {

    @Autowired
    private UserDAO userDAO;//注入要测试的bean
    @Autowired
    private UserService userService;//注入要测试的bean
    @Test
    public void test1(){ //测试方法
        PageHelper.startPage(-1,10);//分页
        List<User> users = userDAO.queryUsers("%rj%");
        for (User user : users) {
            System.out.println(user);
        }
        PageInfo<User> pageInfo = new PageInfo<>(users);
        System.out.println(pageInfo.getPages());
    }
    @Test
    public void test2(){ //测试方法
        //userDAO.insertUser(new User(null,"rjrj",false,new Date()));
        userService.insertUser(new User(null,"rjrj2",false,new Date()));
    }
}

dao,service都测试通过后,controller注入service后测试C即可

四、整合AOP

<!-- AOP依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
spring:
  aop:
    proxy-target-class: false #默认为true,即使用cglib代理
//定义Aspect,方式照旧
@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(* com.rj.service.UserServiceImpl.*(..))")
    public void tx(){}
    
    //String pc = "execution(* com.rj.service.UserServiceImpl.*(..))";
    
    @Before("tx()") // AspectJ的注解
    public void beforerj(JoinPoint a) {
        System.out.println("target:"+a.getTarget());
        System.out.println("args:"+a.getArgs());
        System.out.println("method's name:"+a.getSignature().getName());
        System.out.println("before~~~~");
    }

    //returning="ret"==> 目标的返回值传递给当前方法的ret形参
    @AfterReturning(value="tx()",returning="ret9") // AspectJ的注解
    public void myAfterReturning(JoinPoint a,Object ret9){
        System.out.println("after~~~~:"+ret9);
    }
    @Around("tx()") // AspectJ的注解
    public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
        System.out.println("interceptor1~~~~");
        Object ret = p.proceed();
        System.out.println("interceptor2~~~~");
        return ret;
    }
    @AfterThrowing(value="tx()",throwing="ex") // AspectJ的注解
    public void myThrows(JoinPoint jp,Exception ex){
        System.out.println("throws");
        System.out.println("===="+ex.getMessage());
    }
}

五、整合shiro

5.1 导入依赖

<!-- shiro本身的依赖,会传递导入shiro-core shiro-web -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<!-- spring-data-redis,此处是要和redis集成,做权限和session缓存 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>
<!-- 此处依然需要mybatis依赖,照旧。realm中查询用户身份和权限时需要通信数据库 -->

5.2 shiro编码

如下列举的是根据依赖关系的又下到上的顺序。

编码的顺序是:Service、Realm、SecurityManager、ShiroFilter、记住我、注解(权限控制)、缓存、session

5.2.1 记住我

CookieRememberMeManager 注册到 SecurityManager

@Configuration
public class ShiroConfig {
    @Bean
    public SimpleCookie simpleCookie(){
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(120);
        return cookie;
    }

    @Bean
    public CookieRememberMeManager cookieRememberMeManager() throws NoSuchAlgorithmException {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(simpleCookie());
        return cookieRememberMeManager;
    }
}

5.2.2 session管理

@Configuration
public class ShiroConfig {
    @Bean
    public DefaultWebSessionManager defaultWebSessionManager(SessionDAO sessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(180000);
        sessionManager.setSessionDAO(sessionDAO);
        return sessionManager;
    }
}

5.2.3 密码比对器

@Configuration
public class ShiroConfig {
    @Bean //密码比对器
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("SHA-256");
        matcher.setHashIterations(1000);
        matcher.setStoredCredentialsHexEncoded(false);
        return matcher;
    }
}

5.2.4 Realm

注入密码匹配器,查询用户信息的Service,查询权限信息的Service

@Component
public class MyRealm extends AuthorizingRealm {
    @Autowired //注入密码匹配器
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        super.setCredentialsMatcher(credentialsMatcher);
    }
    @Autowired //注入service
    private UserService userService;
    @Autowired
    private RoleService roleService;
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {...}
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {...}
}

5.2.5 SecurityManager

将其他组件都注入给SecurityManager

@Configuration
public class ShiroConfig {
    @Bean // SecurityManager
    public SecurityManager securityManager(CookieRememberMeManager rememberMeManager,
                                               MyRealm myRealm,  
                                               MyShiroCacheManager myShiroCacheManager,
                                               SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入realm
        securityManager.setRealm(myRealm);
        // 注入sessionManager
        securityManager.setSessionManager(sessionManager);
        // 注入cacheManager
        securityManager.setCacheManager(xxx);
        // 设置记住我
        securityManager.setRememberMeManager(rememberMeManager);
        return securityManager;
    }
}

5.2.5 通知器

对接shiro的注解开发,需要注入SecurityManager

注意:需要导入 aop的starter

@Configuration
public class ShiroConfig {
    @Bean //对应shiro注解,定义代理的advisor
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

5.2.6 异常解析

@ControllerAdvice
public class MyExResolver {
    @ExceptionHandler(value={UnauthenticatedException.class, UnknownAccountException.class, IncorrectCredentialsException.class})
    public String handleEx(HttpServletRequest request, HttpServletResponse res, HttpSession session,
                            Exception e){
        e.printStackTrace();
        return "redirect:/login.jsp";
    }
    @ExceptionHandler(value={UnauthorizedException.class})
    public String handleEx2(HttpServletRequest request, HttpServletResponse res, HttpSession session,
                           Exception e){
        e.printStackTrace();
        return "redirect:/error.jsp";
    }
    @ExceptionHandler(value={UnknownAccountException.class, IncorrectCredentialsException.class})
    @ResponseBody
    public R handleExLoginError(HttpServletRequest request, HttpServletResponse res, HttpSession session,
                      Exception e){
        e.printStackTrace();
        return R.error("登录失败");
    }
}

5.2.7 ShiroFilter

@Configuration
public class ShiroConfig {
    
    @Bean // ShiroFilter
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        return shiroFilter;
    }
}

5.3 shiro_redis

此步的最后,务必需要将cacheManager注入给SecurityManager.

务必将SessionDAO注入给SessionManager,并将SessionManager注入给SecurityManager

注意linux的防火墙,redis的密码设置,bind设置 都可能会导致无法访问redis

5.3.1 配置

spring:
  redis:
      lettuce:
        pool:
          # 最小空闲连接,至少保障连接池中有一个连接实例空闲
          min-idle: 0
          # 连接池,连接实例数上线
          max-active: 5
          # 最大空闲连接,空闲连接不能超过2个
          max-idle: 2
          # 索要连接时的 最大等待时间
          max-wait: 60000ms
      host: 192.168.110.135
      port: 8000
      database: 0
      timeout: 4000ms
      password: rj

5.3.2 两个RedisTemplate

@Configuration
public class RedisConfig{
	// springboot 默认已经注册了一个RedisTemplate beanName="redisTemplate"
    // 此处又再次声明一个BeanName="redisTemplate" 的Bean,会覆盖springboot默认注册的。即以@Bean注册的为最终版本
    // 默认注册的那个被覆盖掉啦。ops:如果@Bean注册的beanName不是"redisTemplate"则不会覆盖
    @Bean //供Realm的Cache使用
    public RedisTemplate redisTemplate(LettuceConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(factory);
        // value的序列化方案:默认为jdk序列化
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //null值不进入json
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        jacksonSerializer.setObjectMapper(objectMapper);
        // key的序列化方案,默认为jdk序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 设置各种序列化方案
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jacksonSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jacksonSerializer);

        //调用此方法,主要是因为template中的属性 initialized需要设置为true,代表已经初始化
        //template的其他方法中会判断这个属性是否为true。为false会抛异常。
        template.afterPropertiesSet();
        return template;
    }
    @Bean //供保存session到redis时使用
    public RedisTemplate redisTemplate2(LettuceConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(factory);
        // key的序列化方案,默认为jdk序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 设置各种序列化方案
        template.setKeySerializer(stringRedisSerializer);
        //调用此方法,主要是因为template中的属性 initialized需要设置为true,代表已经初始化
        //template的其他方法中会判断这个属性是否为true。为false会抛异常。
        template.afterPropertiesSet();
        return template;
    }
}

5.3.3 缓存管理器

@Component
public class MyShiroCacheManager extends AbstractCacheManager {

    @Resource(name="redisTemplate")//注入一个beanName="redisTemplate"的bean
    private RedisTemplate redisTemplate;
    // 权限缓存prefix : com.rj.realm.MyRealm.authorizationCache
    protected Cache createCache(String prefix) throws CacheException {
        MyShiroCache cache = new MyShiroCache(prefix);//MyShiroCache定义照旧,且不需要作为bean
        cache.setTemplate(redisTemplate);
        return cache;
    }
}
public class MyShiroCache implements Cache {
    //和之前的cache无任何差异
}

5.3.4 SessionDAO

// 定义SessionDAO,负责存储Session对象
@Component
public class MySessionDAO extends AbstractSessionDAO {
    @Resource(name="redisTemplate2")
    private RedisTemplate template;//需要注入RedisTemplate,以支持后续的Redis通信

    //将Session对象存入redis;(由SimpleSessionFactory创建的Session对象)
    protected Serializable doCreate(Session session) {
        // 生成sessionID
        Serializable sessionId = generateSessionId(session);
        //存入redis
        System.out.println("save session to redis");
        assignSessionId(session,sessionId);// 将sessionID赋值给Session对象
        // key = 前缀+sessionID  ;  value = session对象
        // 将Session对象存入Redis,存入时定义了"前缀"
        template.opsForValue().set("session"+sessionId,session,30, TimeUnit.MINUTES);
        return sessionId;
    }

    //从redis获取session对象,供项目使用
    @Override
    protected Session doReadSession(Serializable sessionId) {
        // 从redis获取session,获取时,也要定义 “前缀”; [前缀+sessionID]
        Session session = (Session)template.opsForValue().get("session"+sessionId);
        template.expire("session"+sessionId,30,TimeUnit.MINUTES);
        return session;
    }

    //session中数据变动时,要将session对象同步到redis
    @Override
    public void update(Session session) throws UnknownSessionException {
        System.out.println("update session");
        Serializable sessionId = session.getId();
        // 将session,覆盖存储到Redis
        template.opsForValue().set("session"+sessionId,session);
        template.expire("session"+sessionId,30,TimeUnit.MINUTES);
    }

    //session过期时,从redis中删除session
    @Override
    public void delete(Session session) {
        Serializable sessionId = session.getId();//获取sessionID
        // 从redis删除session
        template.delete("session"+sessionId);
    }

    //session检测时,需要获取所有session
    @Override
    public Collection<Session> getActiveSessions() {
        //从redis获取全部或部分session
        Set keys = template.keys("session*");//通过前缀获取Session
        List<Session> list = template.opsForValue().multiGet(keys);
        return list;
    }
}

综上,总体使用,没有改变。

不过有一点需要0注意,DelegatingFilterProxy不再需要,因为springboot会自动识别创建的Filter,进而实现过滤功能。

springboot使用合集

六、热部署

如下内容实现的热部署效果不好,可以用 JRebel插件实现热部署,JRebel是热部署利器

开启配置
springboot使用合集
开启配置,双击shift,搜索“Registry” 即可打开如下窗口
springboot使用合集
<!-- 
热部署依赖
-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

务必需要重启IDEA。此后,程序改动会自动重启。页面改动会热部署。

七、切换配置文件

spring-boot的配置文件,可以分为多个,分别对应生产环境和开发环境

  • application.yml

  • application-dev.yml

  • application-pro.yml

# application.yml
server:
  tomcat:
    uri-encoding: UTF-8
spring:
  aop:
    proxy-target-class: false
  mvc:
    view:
      prefix: /
      suffix: .jsp
  profiles:
    active: dev #使用 application-dev.yml
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.qianfeng.pojo
# application-dev.yml
server:
  port: 8080
  servlet:
    context-path: /boot1905
spring:
  redis:
    lettuce:
      pool:
        # 最小空闲连接,至少保障连接池中有一个连接实例空闲
        min-idle: 0
        # 连接池,连接实例数上线
        max-active: 5
        # 最大空闲连接,空闲连接不能超过2个
        max-idle: 2
        # 索要连接时的 最大等待时间
        max-wait: 60000ms
    host: 192.168.110.135
    port: 8000
    database: 0
    timeout: 4000ms
    password: rj
  datasource:
    druid:
      max-active: 3
      initial-size: 1
      min-idle: 1
      max-wait: 3000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      driver-class-name: com.mysql.jdbc.Driver
      # 务必要指定utf8 否则mysql通信中文乱码 (库和表在建立时也要指定utf8)
      url: jdbc:mysql://localhost:3306/db1905?useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: 111111
# application-pro
....同上

八、Class-Base-Config

@Configuration @Bean @Scope

@ImportResource

@PropertySource @ConfigurationProperties

8.1 基本使用

@Configuration @Bean AnnotationConfigApplicationContext

@Bean的方式声明组件,都是非自定义的类,此种类之前是要定义在 applicationContext.xml中的

如果类是自定义的类,直接使用 @Component即可

@Configuration // 【重点:声明此类为配置类】
public class RedisConfig{
    // 【重点:@Bean,等价于一个<bean></bean>,用于声明一个组件】
    @Bean // 【重点:将方法的返回值声明为bean,beanName == redisTemplate(方法名==beanName)】
    @Scope("prototype") // 声明为原型模式,默认为单例
    public RedisTemplate redisTemplate(LettuceConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(factory);
        // key的序列化方案,默认为jdk序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 设置各种序列化方案
        template.setKeySerializer(stringRedisSerializer);
        // 调用此方法,主要是因为template中的属性 initialized需要设置为true,代表已经初始化
        // template的其他方法中会判断这个属性是否为true。为false会抛异常。
        template.afterPropertiesSet();
        return template;
    }
    @Bean
    public xxx xxx(){...}
    ...
}

8.2 注入

常规注入

@Component //beanName= myRealm
public class MyRealm extends AuthorizingRealm {
	@Autowired //常规注入
    private XX xx;
}

参数注入

@Configuration
public class ShiroConfig {
    // 【重点:方法的参数位置会自动注入,先基于类型注入,如果有多个同类型的,则根据名字注入】
    @Bean 
    public SecurityManager securityManagerBean(MyRealm myRealm,SessionManager sessionManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 将注入来的realm赋值给需要它的组件
        securityManager.setRealm(myRealm);
        ...
    }
    @Bean  
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(180000);
        return sessionManager;
    }
    ...

8.3 导入配置参数

@PropertySource @ConfigurationProperties

8.3.1 定义配置参数

# 随意创建一个文件:redis_pool.properties
redis.maxIdle=1
redis.maxTotal=5
redis.minIdle=1
redis.blockWhenExhausted=true
redis.maxWaitMillis=30000
redis.testOnBorrow=false
redis.testWhileIdle=false
redis.minEvictableIdleTimeMillis=60000
redis.timeBetweenEvictionRunsMillis=30000
redis.numTestsPerEvictionRun=3
redis.lifo=true

# 默认配置文件中:application.yml
...
# 如果此处和如上properties文件中有同名,此处更优先
redis2:
  minIdle: 2
  maxTotal: 6
  ...

8.3.2 导入配置文件_1

@Configuration
// 导入外部参数文件, application.yml不用导入,并且如果同名则application.yml中优先取值
@PropertySource("classpath:redis_pool.properties")
public class RedisConfig {
    @Value("${redis.minIdle}") // 获取redis.minIdle值,来自redis_pool.properties
    private Integer minIdle;
    @Value("${redis2.maxTotal}") // 获取redis2.maxTotal的值, 来自application.yml
    private Integer maxTotal;
    // 后续结合@Bean,将属性值传入组件中

8.3.3 导入配置文件_2

@Configuration
// 导入外部参数文件, application.yml不用导入,并且如果同名则application.yml中优先取值
@PropertySource("classpath:redis_pool.properties")
//自动获取前缀为redis的属性值,自动复制给属性 (需要set/get),比定义@Value要更简便
@ConfigurationProperties(prefix = "redis") 
@Data//添加set/get
public class RedisConfig {
    private Integer minIdle;
    private Integer maxTotal;
    //后续结合@Bean,将属性值传入组件中

8.4 追加配置文件

了解

springboot中没有了spring的配置文件,如果需要在项目中导入配置文件,可以使用:@ImportResource

SpringBootApplication
@MapperScan("com.rj.dao")
@ImportResource(locations = {"classpath:com/rj/config/spring.xml"})  //导入外部的spring配置文件
public class AppBoot {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AppBoot.class, args);
        context.getBean("xxx");//可以测试获取spring.xml中声明的bean,当然也可以将其注入给其他bean
    }
}

九 打包项目

9.1 jar

<build>
    <finalName>boot1905</finalName>
    <!-- 包含webapp下的内容:jsp -->
    <resources>
        <resource>
            <directory>src/main/webapp</directory>
            <targetPath>META-INF/resources</targetPath>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <!-- 修改版本:必须是此版本 -->
            <version>1.4.2.RELEASE</version>
        </plugin>
    </plugins>
</build>

直接打包即可。

java -jar xxxx.jar 即可运行。

9.2 war

<groupId>com.rj.boot</groupId>
...
<!-- 改为war -->
<packaging>war</packaging>
<!-- 在web-start中移除内置tomcat -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- 移除嵌入式tomcat -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <!-- 改为provided -->
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <!-- 改为provided -->
    <scope>provided</scope>
</dependency>
<build>
    <!-- 此名称为最终的war包名称,部署后是最终的项目名,springboot中设置的项目名对war无效 -->
	<finalName>boot1905</finalName>
    <!--<resources>
      <resource>
        <directory>src/main/webapp</directory>
        <targetPath>META-INF/resources</targetPath>
      </resource>
    </resources>-->
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!-- 修改版本:必须是此版本 -->
        <version>1.4.2.RELEASE</version>
        <!-- 此插件会在build后,再次打包,将所有provide的依赖也打包(executeable) ,
             生成executeable jar时需要,此时不再需要,所以禁用掉
        -->
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
            <configuration>
              <skip>true</skip>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
//启动类 修改
public class Spring9Application extends SpringBootServletInitializer{
    public static void main(String[] args) {
        SpringApplication.run(Spring9Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        builder.sources(Spring9Application.class);
        return super.configure(builder);
    }
}
  • 打war包:
    • mvn package
      为最终的war包名称,部署后是最终的项目名,springboot中设置的项目名对war无效 -->
	<finalName>boot1905</finalName>
    <!--<resources>
      <resource>
        <directory>src/main/webapp</directory>
        <targetPath>META-INF/resources</targetPath>
      </resource>
    </resources>-->
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!-- 修改版本:必须是此版本 -->
        <version>1.4.2.RELEASE</version>
        <!-- 此插件会在build后,再次打包,将所有provide的依赖也打包(executeable) ,
             生成executeable jar时需要,此时不再需要,所以禁用掉
        -->
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
            <configuration>
              <skip>true</skip>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
//启动类 修改
public class Spring9Application extends SpringBootServletInitializer{
    public static void main(String[] args) {
        SpringApplication.run(Spring9Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        builder.sources(Spring9Application.class);
        return super.configure(builder);
    }
}
  • 打war包:
    • mvn package
    • 然后将war包放到外置tomcat的 ‘web-apps’目录中即可