Springboot项目开发总结
一、创建项目、编写启动类、配置yam
1.1 启动类(Application)代码
⭐ 带@SpringBootApplication注解,表明这是一个springboot启动类
⭐ @MapperScan(“mapper路径”) 扫描mapper
@SpringBootApplication
@MapperScan("com.jzq.server.mapper")
public class YebApplication {
public static void main(String[] args) {
SpringApplication.run(YebApplication.class, args);
}
}
1.2 配置文件(application.yml)
⭐ 配置服务启动端口、mysql数据库、redis、Mybatis-Plus、jwt等相关配置
server:
# 端口
port: 7777
spring:
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yeb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 00000
hikari:
# 连接池名
pool-name: DateHikariCP
# 最小空闲连接数
minimum-idle: 5
# 空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
# 最大连接数 (10)
maximum-pool-size: 10
# 从连接池返回的连接自动提交
auto-commit: true
# 连接最大存活时间,0表示永久,默认为1800000(30分钟)
max-lifetime: 1800000
# 连接超时时间, 默认30000 (30秒)
connection-timeout: 30000
# 测试连接是否可用的查询语句
# connection-test-query: SELECT 1
# Redis配置
redis:
# 超时时间
timeout: 10000ms
# 服务器地址
host: localhost
# 端口号
port: 6379
# 数据库(第几个)
database: 0
# 密码
# password:
lettuce:
pool:
# 最大连接数(默认8)
max-active: 1024
# 最大连接阻塞时间,默认-1
max-wait: 10000ms
# 最大空闲连接
max-idle: 200
# 最小空闲连接
min-idle: 5
main:
# 当遇到同名时,是否覆盖注册
allow-bean-definition-overriding: true
# Mybatis-plus配置
mybatis-plus:
# 配置Mapper映射文件
mapper-locations: classpath*:/mapper/*Mapper.xml
# 配置Mybatis数据返回类型别名(默认别名是类名)
type-aliases-package: com.jzq.server.pojo
configuration:
# 自动驼峰命名
map-underscore-to-camel-case: false
## Mybatis SQL 打印(方法接口所在的包,不是Mapper.xml所在的包)
logging:
level:
com.jzq.server.mapper: debug
# JWT
jwt:
# JWT存储的请求头
tokenHeader: Authorization
# JWT 解密加密使用的密钥
secret: yeb-secret
# JWT的超期限时间(30*60*24)
expiration: 604800
# JWT 负载中拿到开头
tokenHead: Bearer
二、代码生成器,生成项目代码(逆向工程)
⭐ 通过设计好的数据库表的结构生成 controller、service、pojo、mapper层代码框架
自己按需修改,代码生成器代码。
package com.jzq.generator;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* 执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
*
* @author zhoubin
* @since 1.0.0
*/
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotEmpty(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/yeb-generator/src/main/java");
//作者
gc.setAuthor("seven");
//打开输出目录
gc.setOpen(false);
//xml开启 BaseResultMap
gc.setBaseResultMap(true);
//xml 开启BaseColumnList
gc.setBaseColumnList(true);
// 实体属性 Swagger2 注解
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/yeb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia" +
"/Shanghai");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("xxxxxx");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.jzq.server")
.setEntity("pojo")
.setMapper("mapper")
.setService("service")
.setServiceImpl("service.impl")
.setController("controller");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/yeb-generator/src/main/resources/mapper/"
+ tableInfo.getEntityName() + "Mapper"
+ StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
//数据库表映射到实体的命名策略
strategy.setNaming(NamingStrategy.underline_to_camel);
//数据库表字段映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.no_change);
//lombok模型
strategy.setEntityLombokModel(true);
//生成 @RestController 控制器
strategy.setRestControllerStyle(true);
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
//表前缀
strategy.setTablePrefix("t_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
三、通过Security安全框架完成jwt验证、url权限拦截
⭐ 1. 重写configure(WebSecurity web)方法通过web.ignoring().antMatchers(放行路径,分割)添加放行路径
⭐2.authenticationManagerBuilder.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); 这个应该是加密验证
userDetailsService重写了loadUserByUsername()返回Admin自定义用户信息, Admin实体类继承UserDetails类。
⭐ 3. 重写configure(HttpSecurity http)完成Jwt验证、url判断权限验证的过滤操作。
- customFilter : 拦截请求url对比(根据角色获取的菜单),将符合url的角色返回。
- customUrlDecisionManger : 拿到符合url的角色,看当前用户是否符合(符合url的角色)
- jwtAuthencationTokenFilter(): 验证jwt是否有效
- restfulAccessDeniedHandel(): 自定义jwt失效返回结果
- restAuthorizationEnrtyPoint(): 自定义无jwt权限返回结果
3.1 SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private IAdminService adminService;
@Autowired
private CustomFilter customFilter;
@Autowired
private CustomUrlDecisionManger customUrlDecisionManger;
@Override
public void configure(WebSecurity web) throws Exception {
// 设置放行访问
web.ignoring().antMatchers(
"/api/login",
"/api/logout",
"/css/**",
"/js/**",
"/index.html",
"/favicon.ico",
"/doc.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/api-docs/**",
"/api/captcha"
);
}
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception{
authenticationManagerBuilder.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 使用JWT,不需要csrf
http.csrf()
// 禁用csrf
.disable()
// 基于token, 不需要session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 允许登录访问
//.antMatchers("/login", "/logout")
//.permitAll()
// 除了上面的请求,其他的都要认证
.anyRequest()
.authenticated()
// 动态权限配置
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(customUrlDecisionManger);
o.setSecurityMetadataSource(customFilter);
return o;
}
})
.and()
// 禁用缓存
.headers()
.cacheControl();
// 添加 JWT 登录授权过滤器
http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
// 添加自定义未授权和未登录结果的返回
http.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandel())
.authenticationEntryPoint(restAuthorizationEnrtyPoint());
}
@Override
@Bean
public UserDetailsService userDetailsService() { // 重写的了返回的参数(Admin)
return username -> {
Admin admin = adminService.getAdminByUserName(username);
if (admin != null) {
// 设置一下权限
admin.setRoles(adminService.getRoles(admin.getId()));
return admin;
}
throw new UsernameNotFoundException("用户名或密码不正确");
};
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthencationTokenFilter jwtAuthencationTokenFilter() {
return new JwtAuthencationTokenFilter();
}
@Bean
public RestAuthorizationEnrtyPoint restAuthorizationEnrtyPoint() {
return new RestAuthorizationEnrtyPoint();
}
@Bean
public RestfulAccessDeniedHandel restfulAccessDeniedHandel() {
return new RestfulAccessDeniedHandel();
}
}
3.2 jwt的util类
**
* JwtTokenUtil工具类
*/
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/**
* 根据用户信息生成token
* @param userDetails
* @return
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/**
* 根据荷载生成JWT Token
* @param claims
* @return
*/
public String generateToken(Map<String, Object> claims) {
System.out.println("secret:"+secret);
return Jwts.builder()
.setClaims(claims) // 设置载荷
.setExpiration(generateExpirationDate()) // 设置失效时间
.signWith(SignatureAlgorithm.HS512, secret) // 设置签名
.compact();
}
/**
* 生成token失效时间
* @return
*/
public Date generateExpirationDate() {
// 失效时间是就当前系统时间加 有效时间
return new Date(System.currentTimeMillis() + expiration*1000);
}
/**
* 从token中获取登录的用户名
* @param token
* @return
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getUserClaimFromToken(token);
username = claims.getSubject();
}catch (Exception e) {
e.printStackTrace();
username = null;
}
return username;
}
/**
* 从token中获取荷载
* @param token
* @return
*/
public Claims getUserClaimFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
e.printStackTrace();
}
return claims;
}
/**
* 验证token是否有效 (token有效 + 用户一致)
* @param token
* @param userDetails
* @return
*/
public Boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token); // 终归是来源于token的
// 然后在token有效的前提下对比用户名是否一致
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 判断token是否以及失效
* @param token
* @return
*/
public boolean isTokenExpired(String token) {
Date expireDate = getExpiredDateFromToken(token); // 拿到载荷里设置的有效时间
return expireDate.before(new Date()); // 验证载荷里的时间是否以及失效(失效返回true)
}
/**
* 获取token过期时间
* @param token
* @return
*/
public Date getExpiredDateFromToken(String token) {
Claims claims = getUserClaimFromToken(token);
return claims.getExpiration();
}
/**
* 判断token是否可以被刷新
* @param token
* @return
*/
public boolean canRefresh(String token) {
return !isTokenExpired(token);
}
/**
* 刷新token
* @param token
* @return
*/
public String refreshToken(String token) {
Claims claims = getUserClaimFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
}
3.3 (拦截url判断所属角色)CustomFilter
/**
* 权限控制
* 根据请求url,分析请求所需角色
*/
@Component
public class CustomFilter implements FilterInvocationSecurityMetadataSource {
@Autowired
private IMenuService menuService;
// 匹配url的实例
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
// 获取请求的Url
String requestUrl = ((FilterInvocation) o).getRequestUrl();
// 获取菜单信息
List<Menu> menuList = menuService.getMenusWithRole();
for (Menu menu : menuList) {
// 逐个与url进行匹配
if (antPathMatcher.match(menu.getUrl(),requestUrl)) { // 注意这里,第一个是匹配规则,位置不要颠倒
// 如果匹配成功(把这个路径对应的角色封装到一个数组)
String[] strArray = menu.getRoles().stream().map(Role::getName).toArray(String[]::new);
return SecurityConfig.createList(strArray);
}
}
// 如果匹配不上,就默认返回登录权限的路径
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return false;
}
}
3.4 根据拦截url获取的所属角色,判断当前用户是否有角色权限(CustomUrlDecisionManger)
/**
* 权限控制
* 判断用户角色
*/
@Component
public class CustomUrlDecisionManger implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute configAttribute : collection) {
// 当前url所需要的角色
String needRole = configAttribute.getAttribute();
// 判断角色是否登录即可访问的角色,此角色在CustomFilter中设置
if("ROLE_LOGIN".equals(needRole)) {
// 判断是否登录了
if(authentication instanceof AnonymousAuthenticationToken) {
throw new AccessDeniedException("尚未登录,请登录");
}else {
return;
}
}
// 判断角色是否为url所需要的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不足,请联系管理!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return false;
}
@Override
public boolean supports(Class<?> aClass) {
return false;
}
}
3.5 Jwt验证过滤器(JwtAuthencationTokenFilter)
/**
* JWT登录授权过滤器
*/
public class JwtAuthencationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(tokenHeader); // 获取token值
// 存在token (获取的token不为空,且token前面携带的表示是我们设置的标识)
if (authHeader!=null && authHeader.startsWith(tokenHead)){
String authToken = authHeader.substring(tokenHead.length()); // 截取下除了头部标识以后的token
String username = jwtTokenUtil.getUserNameFromToken(authToken); // 通过token就可以拿到用户名(解析荷载部分)
// 看看username存在,但是未登录(就是检测是不是设置在security全局中)
if (username != null && SecurityContextHolder.getContext().getAuthentication()==null) {
// 登录
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 验证token是否有效,重新设置用户对象
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
// 最后放行
filterChain.doFilter(request,response);
}
}
3.6 jwt验证无权限时自定义返回结果
/**
* 当访问接口没有权限的时候,自定义返回结果
*/
public class RestfulAccessDeniedHandel implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter printWriter = response.getWriter();
RespBean respBean = RespBean.error("权限不足,请联系管理员");
respBean.setCode(403);
printWriter.write(new ObjectMapper().writeValueAsString(respBean));
printWriter.flush(); // 强推到浏览器
printWriter.close();
}
}
3.7 jwt验证登录失效或未登录自定义返回结果
/**
* 当未登录或token失效的时候访问接口时,返回的自定义结果
*/
@Controller
public class RestAuthorizationEnrtyPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter printWriter = response.getWriter();
RespBean respBean = RespBean.error("登录失效,请重新登录");
respBean.setCode(401);
printWriter.write(new ObjectMapper().writeValueAsString(respBean));
printWriter.flush(); // 强推到浏览器
printWriter.close();
}
}
四、配置swagger配置类
配置文档地址、全局Authorization等
/**
* Swagger2配置
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.jzq.server.controller"))
.paths(PathSelectors.any())
.build()
.securityContexts(securityContexts())
.securitySchemes(securitySchemes());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("云e办接口文档")
.description("描述")
.contact(new Contact("jzq", "http:localhost:7777/doc.html", "[email protected]"))
.build();
}
private List<ApiKey> securitySchemes() {
// 设置请求头
List<ApiKey> result = new ArrayList<>();
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "Header");
result.add(apiKey);
return result;
}
private List<SecurityContext> securityContexts() {
// 设置需要登录认证的路径
List<SecurityContext> result = new ArrayList<>();
result.add(getContextByPath("/test/.*"));
return result;
}
private SecurityContext getContextByPath(String pathRegex) {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(pathRegex))
.build();
}
private List<SecurityReference> defaultAuth() {
List<SecurityReference> result = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
result.add(new SecurityReference("Authorization", authorizationScopes));
return result;
}
}
五、 自定义结果类
package com.jzq.server.config.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {
private long code;
private String message;
private Object result;
/**
* 成功返回 (提示)
* @param message
* @return
*/
public static RespBean success(String message) {
return new RespBean(200, message, null);
}
/**
* 成功返回 (结果)
* @param message
* @param result
* @return
*/
public static RespBean success(String message, Object result) {
return new RespBean(200, message, result);
}
/**
* 失败返回 (提示)
* @param message
* @return
*/
public static RespBean error(String message) {
return new RespBean(500, message, null);
}
public static RespBean warning(String message) {
return new RespBean(500, message, null);
}
}
六、google验证码配置类
/**
* 验证码的配置类
*/
@Configuration
public class CaptchaConfig {
@Bean
public DefaultKaptcha defaultKaptcha() {
//验证码生成器
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
//配置
Properties properties = new Properties();
//是否有边框
properties.setProperty("kaptcha.border", "yes");
//设置边框颜色
properties.setProperty("kaptcha.border.color", "105,179,90");
//边框粗细度,默认为1
// properties.setProperty("kaptcha.border.thickness","1");
//验证码
properties.setProperty("kaptcha.session.key", "code");
//验证码文本字符颜色 默认为黑色
properties.setProperty("kaptcha.textproducer.font.color", "blue");
//设置字体样式
properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅 黑");
//字体大小,默认40
properties.setProperty("kaptcha.textproducer.font.size", "30");
//验证码文本字符内容范围 默认为abced2345678gfynmnpwx
// properties.setProperty("kaptcha.textproducer.char.string", "");
//字符长度,默认为5
properties.setProperty("kaptcha.textproducer.char.length", "4");
//字符间距 默认为2
properties.setProperty("kaptcha.textproducer.char.space", "4");
//验证码图片宽度 默认为200
properties.setProperty("kaptcha.image.width", "100");
//验证码图片高度 默认为40
properties.setProperty("kaptcha.image.height", "40");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
七、redis配置类
7.1 配置类
/**
* redis配置类
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// String类型 key序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
// String类型 value序列其
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// Hash类型 key序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// Hash类型 value序列器
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
1.2 redis的几个使用
- 获取redis
ValueOperations<String, Object> valueOperations =
redisTemplate.opsForValue();- 存
valueOperations.set(“menu_”+admin.getId(), menuList)- 取
valueOperations.get(“menu_”+admin.getId())
后续待补充…