springboot使用合集
一、 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
* 注意:路径中没有项目名
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"/> |
---|
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的错误页面 |
---|
400: bad request , 常见于请求参数格式错误,导致请求无法解析时。
403: 访问被禁止。跨域时,A设置好自己允许的域,其他所有域的访问都会被禁止
404: 资源未找到时。
405: method not allowed,常见于Restful场景中,请求方式使用错误时。
500: 服务器程序运行异常时。
2.8.2 动态错误页面
使用thymeleaf引擎,定制动态如上html页面,作为错误页面。
错误页面存在模板根目录下 |
---|
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,进而实现过滤功能。
六、热部署
如下内容实现的热部署效果不好,可以用 JRebel插件实现热部署,JRebel是热部署利器
开启配置 |
---|
开启配置,双击shift,搜索“Registry” 即可打开如下窗口 |
---|
<!--
热部署依赖
-->
<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’目录中即可
上一篇: DataBinding快速入门(还在用findViewById?)
下一篇: 表单form