<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
dependency>
public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("E:\\IdeaProjects\\guli_parent\\service\\service_edu" + "/src/main/java");
gc.setAuthor("Tzc");
gc.setOpen(false); // 生成后是否打开资源管理器
gc.setFileOverride(false); // 重新生成时文件是否覆盖
// UserServie
gc.setServiceName("%sService"); // 去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); // 主键策略
gc.setDateType(DateType.ONLY_DATE);// 定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("eduservice"); // 模块名
// 包 com.atguigu.eduservice
pc.setParent("com.atguigu");
// 包 com.atguigu.eduservice.controller
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("edu_course","edu_course_description","edu_chapter","edu_video");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
/**
* @author Tzc
* 对字段生成值(如 gmtCreate创建时间 gmtModified修改时间),实现公共字段自动写入
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 属性名称,不是字段名称
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
// 主键自增策略 19位
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private String id;
// 根据开发规范一个实体类应该包含 创建时间和更新时间
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>${swagger.version}version>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>${swagger.version}version>
dependency>
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* 访问地址 域名+swagger-ui.html
* @EnableSwagger2 //swagger注解
* @Configuration //配置类
* @author Tzc
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.paths(Predicates.not(PathSelectors.regex("/admin/.*")))
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("网站-课程中心API文档")
.description("本文档描述了课程中心微服务接口定义")
.version("1.0")
.contact(new Contact("java", "www.crowd-funding.com", "[email protected]"))
.build();
}
}
测试地址:主机域名:8001/swagger-ui.html
// 用在controller类上,对controller功能进行解释
@Api(description=“课程管理”)// 用在controller里的方法上,解释方法作用
@ApiOperation(value = “Excel批量导入”)// 用在方法参数上,解释参数意思
@ApiParam(name = “id”, value = “课程ID”, required = true)
// 用在entity类上,对entity类做注释
@ApiModel(value=“EduChapter对象”, description=“课程”)
// 用在entity类的属性上,对属性做注释
@ApiModelProperty(value = “章节ID”)
npm install --save [email protected]
/**
* @author Tzc
*/
@Component
public class ScheduledTask {
@Autowired
private StatisticsDailyService staService;
/**
* 在每天凌晨1点,把前一天数据进行数据查询添加
*/
@Scheduled(cron = "0 0 1 * * ?")
public void task2() {
staService.registerCount(DateUtil.formatDate(DateUtil.addDays(new Date(), -1)));
}
/*
* 在线生成cron表达式 http://cron.qqe2.com/
* 0/5 * * * * ?表示每隔5秒执行一次这个方法
@Scheduled(cron = "0/5 * * * * ?")
public void task1() {
System.out.println("**************task1执行了..");
}
*/
}
@Configuration
@EnableScheduling // 2.开启定时任务
public class DynamicScheduleTask implements SchedulingConfigurer {
@Mapper
public interface CronMapper {
@Select("select cron from cron limit 1")
public String getCron();
}
@Autowired //注入mapper
CronMapper cronMapper;
/**
* 执行定时任务.
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
//1.添加任务内容(Runnable)
() -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
//2.设置执行周期(Trigger)
triggerContext -> {
//2.1 从数据库获取执行周期
String cron = cronMapper.getCron();
//2.2 合法性校验.
if (StringUtils.isEmpty(cron)) {
// Omitted Code ..
}
//2.3 返回执行周期(Date)
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
}
}
//@Component注解用于对那些比较中立的类进行注释;
//相对与在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释
@Component
@EnableScheduling // 1.开启定时任务
@EnableAsync // 2.开启多线程
public class MultithreadScheduleTask {
@Async
@Scheduled(fixedDelay = 1000) //间隔1秒
public void first() throws InterruptedException {
System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
Thread.sleep(1000 * 10);
}
@Async
@Scheduled(fixedDelay = 2000)
public void second() {
System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 分页条件查询
* @param current 当前页
* @param limit 每页记录数
* @param teacherQuery 查询条件 为空时查询所有
* @return
*/
@ApiOperation(value = "分页条件查询")
@PostMapping("pageTeacherCondition/{current}/{limit}")
public R pageTeacherCondition(@PathVariable long current,@PathVariable long limit,
@RequestBody(required = false) TeacherQuery teacherQuery) {
// 创建page对象
Page<EduTeacher> pageTeacher = new Page<>(current,limit);
// 构建条件
QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
// 多条件组合查询
// mybatis学过 动态sql
String name = teacherQuery.getName();
Integer level = teacherQuery.getLevel();
String begin = teacherQuery.getBegin();
String end = teacherQuery.getEnd();
// 判断条件值是否为空,如果不为空拼接条件
if(!StringUtils.isEmpty(name)) {
// 构建条件
wrapper.like("name",name);
}
// 教师级别
if(!StringUtils.isEmpty(level)) {
wrapper.eq("level",level);
}
// 添加开始时间(范围)
if(!StringUtils.isEmpty(begin)) {
wrapper.ge("gmt_create",begin);
}
// 添加结束时间(范围)
if(!StringUtils.isEmpty(end)) {
wrapper.le("gmt_create",end);
}
// 排序 让他加的数据在第一页显示
wrapper.orderByDesc("gmt_create");
// 调用方法实现条件查询分页
// pageTeacher 翻页对象 wrapper 实体对象封装操作类
// 调用方法时候,底层封装,把分页所有数据封装到pageTeacher对象(page对象)里面
teacherService.page(pageTeacher,wrapper);
// 总记录数
long total = pageTeacher.getTotal();
// 数据list集合
List<EduTeacher> records = pageTeacher.getRecords();
return R.ok().data("total",total).data("rows",records);
}
export default {
// 1 讲师列表(条件查询分页)
// current当前页 limit每页记录数 teacherQuery条件对象
getTeacherListPage(current, limit, teacherQuery) {
return request({
url: `/eduservice/teacher/pageTeacherCondition/${current}/${limit}`,
method: 'post',
// teacherQuery条件对象,后端使用RequestBody获取数据
// data表示把对象转换json进行传递到接口里面
data: teacherQuery
})
}
<!-- 分页 -->
<el-pagination
:current-page="page"
:page-size="limit"
:total="total"
style="padding: 30px 0; text-align: center"
layout="total, prev, pager, next, jumper"
@current-change="getList"
/>
methods: {
// 创建具体的方法,调用teacher.js定义的方法
// 讲师列表(查询加分页)
getList(page = 1) {
this.page = page;
teacher
.getTeacherListPage(this.page, this.limit, this.teacherQuery)
.then((response) => {
// 请求成功
// response接口返回的数据
// console.log(response)
this.list = response.data.rows;
this.total = response.data.total;
console.log(this.list);
console.log(this.total);
});
}
}
/**
* @author Tzc
* 对字段生成值(如 gmtCreate创建时间 gmtModified修改时间),实现公共字段自动写入
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class);
@Override
public void insertFill(MetaObject metaObject) {
// 属性名称,不是字段名称
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
// 版本号默认值
this.setFieldValByName("version", 1, metaObject);
// 逻辑删除默认值 0代表没逻辑删除 可以显示
this.setFieldValByName("deleted", 0, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
@EnableTransactionManagement // 开启事务支持
@Configuration
@MapperScan("cn.tt.mapper")
public class MybatisPlusConfig {
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/**
* 逻辑删除 被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
* @return
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
/**
* SQL 执行性能分析插件
* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void testSelectList() {
System.out.println(("----- selectAll method test ------"));
//UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
//所以不填写就是无任何条件
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
@Test
public void testInsert(){
User user = new User();
user.setName("Helen");
user.setAge(18);
user.setEmail("[email protected]");
int result = userMapper.insert(user);
System.out.println(result); //影响的行数
System.out.println(user); //id自动回填
}
@Test
public void testUpdateById(){
User user = new User();
user.setId(1L);
user.setAge(28);
int result = userMapper.updateById(user);
System.out.println(result);
}
/**
* 乐观锁插件
*/
@Test
public void testOptimisticLocker() {
//查询
User user = userMapper.selectById(1506203888129167362L);
//修改数据
user.setName("Helen Yao");
user.setEmail("[email protected]");
//执行更新
userMapper.updateById(user);
}
/**
* 批量查询
*/
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
/**
* 简单分页
*/
@Test
public void testSelectPage() {
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getCurrent());
System.out.println(page.getPages());
System.out.println(page.getSize());
System.out.println(page.getTotal());
System.out.println(page.hasNext());
System.out.println(page.hasPrevious());
}
/**
* 测试 逻辑删除
*/
@Test
public void testLogicDelete() {
int result = userMapper.deleteById(2L);
System.out.println(result);
}
/**
* 测试 性能分析插件
*/
@Test
public void testPerformance() {
User user = new User();
user.setName("我是Helen");
user.setEmail("[email protected]");
user.setAge(18);
userMapper.insert(user);
}
/**
* 复杂条件查询
* ge、gt、le、lt、isNull、isNotNull
* SQL:UPDATE user SET deleted=1
* WHERE deleted=0
* AND name IS NULL
* AND age >= ?
* AND email IS NOT NULL
*/
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
/**
* eq、ne
* seletOne返回的是一条实体记录,当出现多条时会报错
* SELECT id,name,age,email,create_time,update_time,deleted,version
* FROM user
* WHERE deleted=0 AND name = ?
*/
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 对比字符串
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
/**
* between、notBetween
* 包含大小边界
* SELECT COUNT(1)
* FROM user
* WHERE deleted=0 AND age BETWEEN ? AND ?
*/
@Test
public void testSelectCount() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 在两数之间
queryWrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(queryWrapper);
System.out.println(count);
}
/**
* allEq
*SELECT id,name,age,email,create_time,update_time,deleted,version
* FROM user
* WHERE deleted=0 AND name = ? AND id = ? AND age = ?
*/
@Test
public void testSelectList01() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("name", "Jack");
map.put("age", 20);
queryWrapper.allEq(map);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
/**
* like、notLike、likeLeft、likeRight
* selectMaps返回Map集合列表
* SELECT id,name,age,email,create_time,update_time,deleted,version
* FROM user
* WHERE deleted=0 AND name NOT LIKE ? AND email LIKE ?
*/
@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.notLike("name", "e")
.likeRight("email", "t");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}
/**
* in、notIn、inSql、notinSql、exists、notExists
* SELECT id,name,age,email,create_time,update_time,deleted,version
* FROM user
* WHERE deleted=0 AND id IN (select id from user where id < 3)
*
* inSql、notinSql:可以实现子查询
* 例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
* 例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)
*/
@Test
public void testSelectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.in("id", 1, 2, 3);
queryWrapper.inSql("id", "select id from user where id < 3");
List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
objects.forEach(System.out::println);
}
/**
* or、and
* 这里使用的是 UpdateWrapper 不调用or则默认为使用 and 连
*UPDATE user SET name=?, age=?, update_time=?
* WHERE deleted=0
* AND name LIKE ? OR age BETWEEN ? AND ?
*/
@Test
public void testUpdate1() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
/**
* 嵌套or、嵌套and
* 这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号
* UPDATE user SET name=?, age=?, update_time=?
* WHERE deleted=0
* AND name LIKE ?
* OR ( name = ? AND age <> ? )
*/
@Test
public void testUpdate2() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or(i -> i.eq("name", "李白").ne("age", 20));
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
/**
* orderBy、orderByDesc、orderByAsc
* SELECT id,name,age,email,create_time,update_time,deleted,version
* FROM user
* WHERE deleted=0 ORDER BY id DESC
*/
@Test
public void testSelectListOrderBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
/**
* last 直接拼接到 sql 的最后
* SELECT id,name,age,email,create_time,update_time,deleted,version
* FROM user
* WHERE deleted=0 limit 1
*/
@Test
public void testSelectListLast() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.last("limit 1");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
/**
* 指定要查询的列
* SELECT id,name,age FROM user WHERE deleted=0
*/
@Test
public void testSelectListColumn() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name", "age");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
/**
* set、setSql
* 最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段
* UPDATE user SET age=?, update_time=?, name=?, email = '[email protected]'
* WHERE deleted=0 AND name LIKE ?
*/
@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(99);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '[email protected]'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}
}
根据方法对其返回结果进行缓存(redis),下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
属性值:
value :缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames: 与 value 差不多,二选一即可
key:可选属性,可以使用 SpEL 标签自定义缓存的key
会清空指定的缓存 一般用在更新或者删除方法上
属性值:
value :缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames: 与 value 差不多,二选一即可
key:可选属性,可以使用 SpEL 标签自定义缓存的key
allEntries: 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
beforeInvocation : 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
属性值:
value :缓存名,必填,它指定了你的缓存存放在哪块命名空间
cacheNames: 与 value 差不多,二选一即可
key:可选属性,可以使用 SpEL 标签自定义缓存的key
/**
* 对应上传的Excel表格格式模板 用于数据保存
* @author Tzc
*/
@Data
public class SubjectData {
// 一级分类名称 index = 0 代表表格第一列
@ExcelProperty(index = 0)
private String oneSubjectName;
// 二级分类名称 index = 1 代表表格第二列
@ExcelProperty(index = 1)
private String twoSubjectName;
}
/**
* 业务 用于读取excel表格内容 并保存到数据库
* @author Tzc
*/
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
// 因为SubjectExcelListener不交给spring进行管理,需要自己new,不能注入其他对象
// 不能实现数据库操作
public EduSubjectService subjectService;
public SubjectExcelListener() {}
// 创建有参数构造,传递subjectService用于操作数据库
public SubjectExcelListener(EduSubjectService subjectService) {
this.subjectService = subjectService;
}
/**
* 读取excel表格内容,一行一行进行读取 并保存到数据库中
* @param subjectData
* @param analysisContext
*/
@Override
public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
if(subjectData == null) {
throw new GuliException(20001,"文件数据为空");
}
// 一行一行读取,每次读取有两个值,第一个值一级分类,第二个值二级分类
// 判断一级分类是否重复(多个一级目录下有多个二级目录,只显示一个一级目录)
EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName());
// 添加一级分类
if(existOneSubject == null) {
// 没有相同一级分类,进行添加
existOneSubject = new EduSubject();
existOneSubject.setParentId("0");
// 一级分类名称
existOneSubject.setTitle(subjectData.getOneSubjectName());
subjectService.save(existOneSubject);
}
// 获取一级分类id值
String pid = existOneSubject.getId();
// 判断二级分类是否重复
EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
// 添加二级分类
if(existTwoSubject == null) {
existTwoSubject = new EduSubject();
existTwoSubject.setParentId(pid);
// 二级分类名称
existTwoSubject.setTitle(subjectData.getTwoSubjectName());
subjectService.save(existTwoSubject);
}
}
/**
* 根据parent_id=0 可以判断是一级课程级分类 如果查询结果唯一返回查询的对象 用于于保存
* @param subjectService
* @param name 调用者传入的Excel表格一级分类名称
* @return
*/
private EduSubject existOneSubject(EduSubjectService subjectService,String name) {
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id","0");
EduSubject oneSubject = subjectService.getOne(wrapper);
return oneSubject;
}
/**
* 根据二级课程分类的parent_id=一级课程分类的id值 可以判断是二级课程分类 如果查询结果唯一返回查询的对象 用于余保存
* @param subjectService
* @param name 调用者传入的Excel表格二级分类名称
* @param pid 一级分类id值
* @return
*/
private EduSubject existTwoSubject(EduSubjectService subjectService, String name, String pid) {
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title", name);
wrapper.eq("parent_id", pid);
EduSubject twoSubject = subjectService.getOne(wrapper);
return twoSubject;
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
@Autowired
private BCryptPasswordEncoder passwordEncoder;
public String encode(CharSequence rawPassword) {
String salt;
if (strength > 0) {
if (random != null) {
salt = BCrypt.gensalt(strength, random);
}
else {
salt = BCrypt.gensalt(strength);
}
}
else {
salt = BCrypt.gensalt();
}
return BCrypt.hashpw(rawPassword.toString(), salt);
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() == 0) {
logger.warn("Empty encoded password");
return false;
}
if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
logger.warn("Encoded password does not look like BCrypt");
return false;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
export default {
layout: 'sign',
data() {
return {
params: { //封装注册输入数据
mobile: '',
code: '', //验证码
nickname: '',
password: ''
},
sending: true, //是否发送验证码
second: 60, //倒计时间
codeTest: '获取验证码'
}
},
methods: {
timeDown() {
let result = setInterval(() => {
// 倒计时每次减一(每执行一次方法)
--this.second;
// 倒计时赋值个静态 “获取验证码” 字符串
this.codeTest = this.second
if (this.second < 1) {
clearInterval(result);
this.sending = true;
//this.disabled = false;
this.second = 60;
this.codeTest = "获取验证码"
}
}, 1000);
},
//通过输入手机号发送验证码
getCodeFun() {
registerApi.sendCode(this.params.mobile)
.then(response => {
this.sending = false
//调用倒计时的方法
this.timeDown()
})
}
}
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
dependency>
dependencies>
@RestController
@RequestMapping("/edumsm/msm")
@CrossOrigin
public class MsmController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
// 发送短信
@GetMapping("send/{phone}")
public R sendMsm(@PathVariable String phone) {
//1 从redis获取验证码,如果获取到直接返回
String code = redisTemplate.opsForValue().get(phone);
if(!StringUtils.isEmpty(code)) {
return R.ok();
}
//2 如果redis获取 不到,进行阿里云发送
//生成随机值,传递阿里云进行发送
code = RandomUtil.getFourBitRandom();
Map<String,Object> param = new HashMap<>();
param.put("code",code);
//调用service发送短信的方法
boolean isSend = msmService.send(param,phone);
if(isSend) {
//发送成功,把发送成功验证码放到redis里面
//设置有效时间
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
return R.ok();
} else {
return R.error().message("短信发送失败");
}
}
}
package com.atguigu.crowd.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Tzc
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Component
@ConfigurationProperties(prefix = "short.message")
public class ShortMessageProperties {
private String host;
private String path;
private String method;
private String appCode;
private String sign;
private String skin;
}
short:
message:
app-code: 130790069fcf4dd4a3dc9749779969b7
host: https://jumsendsms.market.alicloudapi.com
method: GET
path: /sms/send-upgrade
sign: 阿里云短信测试
skin: SMS_154950909
ribbon:
ReadTimeout: 10000
ConnectTimeout: 10000
@Autowired
private ShortMessageProperties shortMessageProperties;
@Service
public class MsmServiceImpl implements MsmService {
@Autowired
private ShortMessageProperties shortMessageProperties;
/* public static void main(String[] args_) throws Exception {
java.util.List args = java.util.Arrays.asList(args_);
com.aliyun.dysmsapi20170525.Client client = Sample.createClient("accessKeyId", "accessKeySecret");
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName("阿里云短信测试")
.setTemplateCode("SMS_154950909")
.setPhoneNumbers("收短信的电话号码")
.setTemplateParam("{\"code\":\"1234\"}");
// 复制代码运行请自行打印 API 的返回值
client.sendSms(sendSmsRequest);
}*/
/**
* 发送短信
* @param param
* @param phone 手机号
* @return
*/
@Override
public boolean send(Map<String, Object> param, String phone) {
if(StringUtils.isEmpty(phone)) {
return false;
}
DefaultProfile profile = DefaultProfile.getProfile("default", "LTAI5t71z3dRfTPu5gcqomJi", "2jtLZrb6GXcu95oopardJoE4d1fzLb");
IAcsClient client = new DefaultAcsClient(profile);
// 设置相关固定的参数
CommonRequest request = new CommonRequest();
// request.setProtocol(ProtocolType.HTTPS);
request.setMethod(MethodType.POST);
request.setDomain("dysmsapi.aliyuncs.com");
request.setVersion("2017-05-25");
request.setAction("SendSms");
// 设置发送相关的参数
// 手机号
request.putQueryParameter("PhoneNumbers",phone);
,
// 读取配置类参数
// 申请阿里云 签名名称
request.putQueryParameter("SignName",shortMessageProperties.getSign());
// 申请阿里云 模板code
request.putQueryParameter("TemplateCode", shortMessageProperties.getSkin());
// 验证码数据,转换json数据传递
request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));
try {
// 最终发送
CommonResponse response = client.getCommonResponse(request);
boolean success = response.getHttpResponse().isSuccess();
return success;
}catch(Exception e) {
e.printStackTrace();
return false;
}
}
}
public class JwtUtils {
// 常量
public static final long EXPIRE = 1000 * 60 * 60 * 24; // token过期时间
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; // 秘钥
// 生成token字符串
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id) // 设置token主体部分 ,存储用户信息
.claim("nickname", nickname) // 用户昵称
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if (StringUtils.isEmpty(jwtToken)) {
return false;
}
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token字符串获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
@ApiOperation(value = "会员登录")
@PostMapping("login")
public R loginUser(@RequestBody UcenterMember member) {
// member对象封装手机号和密码
// 调用service方法实现登录
// 返回token值,使用jwt生成
String token = memberService.login(member);
return R.ok().data("token", token);
}
@Override
public String login(UcenterMember member) {
// 获取用户输入的登录手机号和密码
String mobile = member.getMobile();
String password = member.getPassword();
// 手机号和密码非空判断
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new GuliException(20001, "手机号或密码不能为空,请重新输入!");
}
// 判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
UcenterMember mobileMember = baseMapper.selectOne(wrapper);
if (mobileMember == null) {
throw new GuliException(20001, "输入的手机号有误,请重新输入!");
}
// 判断密码
// 因为存储到数据库密码肯定加密的
// 把输入的密码进行加密,再和数据库密码进行比较
// 加密方式 MD5
if (!MD5.encrypt(password).equals(mobileMember.getPassword())) {
throw new GuliException(20001, "输入的密码有误,请重新输入!");
}
// 判断用户是否禁用
if (mobileMember.getIsDeleted()) {
throw new GuliException(20001, "用户已被禁用!");
}
// 登录成功
// 生成token字符串,使用jwt工具类
String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
return jwtToken;
}
import request from '@/utils/request'
export default {
// 登录的方法
submitLoginUser(userInfo) {
return request({
url: `/educenter/member/login`,
method: 'post',
data: userInfo
})
},
// 根据token获取用户信息
getLoginUserInfo() {
return request({
url: `/educenter/member/getMemberInfo`,
method: 'get'
})
}
}
import request from '@/utils/request'
export default {
// 登录的方法
submitLoginUser(userInfo) {
return request({
url: `/educenter/member/login`,
method: 'post',
data: userInfo
})
},
// 根据token获取用户信息
getLoginUserInfo() {
return request({
url: `/educenter/member/getMemberInfo`,
method: 'get'
})
}
}
methods: {
//登录的方法
submitLogin() {
//第一步 调用接口进行登录,返回token字符串
loginApi.submitLoginUser(this.user)
.then(response => {
// 第二步 获取token字符串放到cookie里面
// 第一个参数cookie名称,第二个参数值,第三个参数作用范围
cookie.set('guli_token',response.data.data.token,{domain: 'localhost'})
//第四步 调用接口 根据token获取用户信息,为了首页面显示
loginApi.getLoginUserInfo()
.then(response => {
this.loginInfo = response.data.data.userInfo
//获取返回用户信息,放到cookie里面
cookie.set('guli_ucenter',JSON.stringify(this.loginInfo),{domain: 'localhost'})
//跳转页面
window.location.href = "/";
})
})
}
}
mport axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import cookie from 'js-cookie'
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:9001', // 网关地址 api的base_url
timeout: 20000 // 请求超时时间
})
// http request 拦截器
service.interceptors.request.use(
config => {
//debugger
if (cookie.get('guli_token')) {
config.headers['token'] = cookie.get('guli_token');
}
return config
},
err => {
return Promise.reject(err);
})
//1 生成微信扫描二维码
@GetMapping("login")
public String getWxCode() {
//固定地址,后面拼接参数
// String url = "https://open.weixin.qq.com/" +
// "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";
// 微信开放平台授权baseUrl %s相当于?代表占位符
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
//对redirect_url进行URLEncoder编码
String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
try {
redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
}catch(Exception e) {
}
//设置%s里面值
String url = String.format(
baseUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
redirectUrl,
"atguigu"
);
//重定向到请求微信地址里面
return "redirect:"+url;
}
@Autowired
private UcenterMemberService memberService;
// 2 获取扫描人信息,添加数据
@GetMapping("callback")
public String callback(String code, String state) {
try {
//1 获取code值,临时票据,类似于验证码
//2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
// 拼接三个参数 :id 秘钥 和 code值
String accessTokenUrl = String.format(
baseAccessTokenUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
ConstantWxUtils.WX_OPEN_APP_SECRET,
code
);
//请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid
//使用httpclient发送请求,得到返回结果
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
//从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
//把accessTokenInfo字符串转换map集合,根据map里面key获取对应值
//使用json转换工具 Gson
Gson gson = new Gson();
HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String)mapAccessToken.get("access_token");
String openid = (String)mapAccessToken.get("openid");
// 把扫描人信息添加数据库里面
// 判断数据表里面是否存在相同微信信息,根据openid判断
UcenterMember member = memberService.getOpenIdMember(openid);
// memeber是空,表没用户表中没有相同微信数据,进行添加
if(member == null) {
// 3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息
//访问微信的资源服务器,获取用户信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
//拼接两个参数
String userInfoUrl = String.format(
baseUserInfoUrl,
access_token,
openid
);
// 4.使用HttpClient发送请求(因为不同客户端的请求方式不一样)
String userInfo = HttpClientUtils.get(userInfoUrl);
// 获取返回userinfo字符串(Json)扫描人信息(使用谷歌的gson转换为map集合)
HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String)userInfoMap.get("nickname");// 昵称
String headimgurl = (String)userInfoMap.get("headimgurl");// 头像
// 将扫码人数据添加到用户数据库实现注册
member = new UcenterMember();
member.setOpenid(openid);
member.setNickname(nickname);
member.setAvatar(headimgurl);
memberService.save(member);
}
// 5.使用jwt根据member对象生成token字符串
String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
// 最后:返回首页面,通过路径传递token字符串
return "redirect:http://localhost:3000?token="+jwtToken;
}catch(Exception e) {
throw new GuliException(20001,"登录失败");
}
}
/**
* 根据token字符串获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
// 获取单个请求头name对应的value值
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) {return "";}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
/**
* 根据token获取用户信息
* @param request
* @return
*/
@ApiOperation(value = "根据token获取登录用户信息")
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request) {
// 调用jwt工具类的方法 根据request对象获取头信息,返回用户id
String memberId = JwtUtils.getMemberIdByJwtToken(request);
// 查询数据库根据用户id获取登录用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("userInfo", member);
}
import cookie from 'js-cookie'
import loginApi from '@/api/login'
export default {
data() {
return {
token:'',
loginInfo: {
id: '',
age: '',
avatar: '',
mobile: '',
nickname: '',
sex: ''
}
}
},
created() {
//获取路径里面token值
this.token = this.$route.query.token
if(this.token) {//判断路径是否有token值
this.wxLogin()
}
this.showInfo()
},
methods:{
//微信登录显示的方法
wxLogin() {
//console.log('************'+this.token)
//把token值放到cookie里面
cookie.set('guli_token',this.token,{domain: 'localhost'})
cookie.set('guli_ucenter','',{domain: 'localhost'})
//console.log('====='+cookie.get('guli_token'))
//调用接口,根据token值获取用户信息
loginApi.getLoginUserInfo()
.then(response => {
// console.log('################'+response.data.data.userInfo)
this.loginInfo = response.data.data.userInfo
cookie.set('guli_ucenter',this.loginInfo,{domain: 'localhost'})
})
},
//创建方法,从cookie获取用户信息
showInfo() {
//从cookie获取用户信息
var userStr = cookie.get('guli_ucenter')
// 把字符串转换json对象(js对象)
if(userStr) {
this.loginInfo = JSON.parse(userStr)
}
},
//退出
logout() {
//清空cookie值
cookie.set('guli_token','',{domain: 'localhost'})
cookie.set('guli_ucenter','',{domain: 'localhost'})
//回到首页面
window.location.href = "/";
}
}
};
/**
* 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
*/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
// 登录地址拦截
public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
this.setPostOnly(false);
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
}
/**
* 进行用户密码验证
* @param req
* @param res
* @return
* @throws AuthenticationException
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException {
try {
// 得到用户输入的用户名和密码
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
// 会找到UserDetailsService
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* @author Tzc
* 用户关联角色 角色关联权限
*/
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AdminService adminService;
@Autowired
private RoleService roleService;
@Autowired
private AuthService authService;
/**
* 根据表单提交的用户名查询admin对象,并添加角色、权限等信息封装到security的User对象中返回
*
* @param username
* @return
* @throws UsernameNotFoundException
* 源码鉴赏
* public User(String username, String password, boolean enabled,
* boolean accountNonExpired, boolean credentialsNonExpired,
* boolean accountNonLocked, Collection extends GrantedAuthority> authorities) {
*
* if (((username == null) || "".equals(username)) || (password == null)) {
* throw new IllegalArgumentException(
* "Cannot pass null or empty values to constructor");
* }
*
* this.username = username;
* this.password = password;
* this.enabled = enabled;
* this.accountNonExpired = accountNonExpired;
* this.credentialsNonExpired = credentialsNonExpired;
* this.accountNonLocked = accountNonLocked;
* this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
* }
*
*/
/**
* // 设置角色前面要加ROLE_ 用于在集合中区分角色和权限
* 源码鉴赏
* public static List createAuthorityList(String... roles) {
* List authorities = new ArrayList(roles.length);
*
* for (String role : roles) {
* authorities.add(new SimpleGrantedAuthority(role));
* }
*
* return authorities;
* }
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1.根据账号名称查询Admin对象
Admin admin = adminService.getAdminByLoginAcct(username);
// 2.获取adminId
Integer adminId = admin.getId();
// 用户关联角色 角色关联权限
// 3.根据adminId查询登录用户拥有的角色
List<Role> assignedRoleList = roleService.getAssignedRole(adminId);
// 4.根据adminId查询登录用户拥有的权限
List<String> authNameList = authService.getAssignedAuthNameByAdminId(adminId);
// 5.创建集合对象用来存储GrantedAuthority
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
// 6.遍历assignedRoleList存入角色信息
for (Role role : assignedRoleList) {
String roleName = "ROLE_" + role.getName();
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(roleName);
authorities.add(simpleGrantedAuthority);
}
// 7.遍历authNameList存入权限信息
for (String authName : authNameList) {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(authName);
authorities.add(simpleGrantedAuthority);
}
// 8.封装到SecurityAdmin对象 包含登录用户的原始Admin对象和拥有的角色和权限
SecurityUser securityUser = new SecurityUser(admin, authorities);
return SecurityUser;
}
}
/**
* 登录成功
*
* @param req
* @param res
* @param chain
* @param auth
* @throws IOException
* @throws ServletException
*/
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
Authentication auth) throws IOException, ServletException {
// 获取到已授权的用户
SecurityUser user = (SecurityUser) auth.getPrincipal();
// 在已授权用户对象中得到未授权的用户(当前登录用户) 生成token字符串
String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
// 将用户名(微信openid)作为key 用户拥有的权限作为value存入redis
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
ResponseUtil.out(res, R.ok().data("token", token));
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
// 从request中获取token
String token = request.getHeader("token");
if (token != null && !"".equals(token.trim())) {
String userName = tokenManager.getUserFromToken(token);
// 根据用户名从redis查询权限
List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
// 组装权限列表
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) {continue;}
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
if (!StringUtils.isEmpty(userName)) {
return new UsernamePasswordAuthenticationToken(userName, token, authorities);
}
return null;
}
return null;
}
package thread.lock.double_check;
/**
* 双检锁单例模式
*/
public class Singleton {
/**
* 该类实例, volatile主要防止第29行指令重排序
*/
private volatile static Singleton instance;
/**
* 获取实例的方法
* @return
*/
public static Singleton getInstance() {
// 第一把锁, 如果实例为null, 则继续执行
if (instance == null) {
// 为该对象加锁
synchronized (Singleton.class) {
// 加完锁后, 再次判断实例是否为null, 主要解决第一次
// 判断完是否为null的之后, 加锁之前是否创建好了对象
if (instance == null) {
// 如果程序能够执行到这里, 按道理来讲, 该不会出现问题了
// 但是为了提高性能,编译器和处理器常常会对既定的代码
// 执行顺序进行指令重排序
// 为 instance 加上 volatile 可以防止指令重排序
instance = new Singleton();
}
}
}
return instance;
}
}