…
starter :
1、这个场景需要使用到的依赖是什么?
2、如何编写自动配置
@Configuration //指定这个类是一个配置类
@ConditionalOnMissingBean、@ConditionalOnClass
@ConditionalOnXXX //在指定条件成立的情况下 自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容 器中添加组件
@ConfigurationPropertie 结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容 器中
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META- INF /spring. factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
3、模式:
启动器(starter)启动器只用来做依赖导入,专门来写一个自动配置模块,启动器依赖自动配置,别人只需要引入启动器 例如:spring-boot-starter-web 作用只是导入依赖,依赖里面有具体实现
官方命名规范:前缀 “spring-boot-starter-[模块名]” 例如:“spring-boot-starter-web”
自定义命名规范:后缀 “-spring-boot-starter” 例如:“mybatis-spring-boot-starter”
可以参阅 博客
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
一、搭建基本环境
1. 导入数据库文件创建出department和employee表
创建javaBean封装数据
整合MyBatis操作数据库
配置数据源信息
使用注解版的MyBatis;
1) 、@MapperScan指定需 要扫描的mapper接口所在的包
二、快速体验缓存
步骤:
开启基于注解的缓存
标注缓存注解
将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法;
CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字;
里面的几个属性:
cacheNames/value: 指定缓存组件的名字;
key: 缓存数据使用的key;可以用它来指定。默认是使用方法参数的值1 -方法的返回值
编写SpEL; #id;参数id的值 #a0 #pθ #root . args[0]
keyGenerator: key的生成器;可以自己指定key的生成器的组件id
key/keyGenerator: 二选一使用
cacheManager: 指定缓存管理器;或者cacheResol ver指定获取解析器
condition: 指定符合条件的情况下才缓存; condition=“#id>0”
unless: 否定缓存,当unles s指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
unless = "#result == null "
sync:是否使用异步模式
原理:
1、自动配置类; CacheAutoConfiguration
2、缓存的配置类 org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration…3、哪个配置类默认生效: SimpleCacheConf iguration;
4、给容器中注册了- -个CacheManager: ConcurrentMapCacheManager
5、可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中;
@Cacheable:
1、方法运行之前,先去查询Cache (缓存组件),按照cacheNames指定的名字获取;
( CacheManager先获取相应的缓存),第一次获取緩存如果没有Cache组件会自动创建。
2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
key是按照某种策略生成的;默认是使用keyGenerator生成的, 默认使用SimpleKeyGenerator生 成key;
SimpleKeyGenerator生成key的默认策略;
如果没有参数; key=new SimpleKey();
如果有一个参数: key=参数的值
如果有多个参数: key=new SimpleKey(params);
3、没有查到缓存就调用目标方法:
4、将目标方法返回的结果,放进缓存中
@Cacheabl e标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;
1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator
存储缓存: 在目标方法执行之前判断有没有对应缓存中的key,
如果有,就取缓存中的值不在执行方法。没有等方法执行完后存到缓存中。
存在缓存中的 key 默认是方法参数名 value是方法返回的结果 ,可以用cacheable中的 key 属性指定key值
@Service
@EnableCaching
public class EmpServiceimpl implements EmpService {
@Autowired
EmployeeMapper employeeMapper;
@Cacheable(cacheNames = "emp")
public Employee getEmp(Integer id) {
System.out.println("查询第"+id+"号员工");
return employeeMapper.getById(id);
}
}
@CachePut: 既调用方法,又更新缓存数据;修改了数据库的某个数据,同时更新缓存;
运行时机:
1、先调用目标方法
2、将目标方法的结果缓存起来
测试步骤:
1、查询1号员工;查到的结果会放在缓存中;
2、更新1号员工; [lastName : zhangsan; gender:1]
将方法的返回值也放进缓存了;
4、再查询1号员工?
应该是更新后的员工;
为什么是没更新前的员工 [1号员工没有在缓存中更新]
第一次传入缓存的是key : 1 value: Las tName:张三
第二次传入缓存的是key: 传入的employee对象值 返回的employee对象;
@EnableCaching
public class EmpServiceimpl implements EmpService {
@Autowired
EmployeeMapper employeeMapper;
@Cacheable(cacheNames = "emp")
public Employee getEmp(Integer id) {
System.out.println("查询第"+id+"号员工");
return employeeMapper.getById(id);
}
@CachePut(value = "emp",key = "#result.id") //key = "#employee.id"
public Employee updateEmp(Employee employee) {
System.out.println("----------------> update:"+employee);
employeeMapper.updataEmp(employee);
return employee;
}
}
@CacheEvict:缓存清除
key:指定要清除的数据
allEntries = true: 指定清除这个缓存中所有的数据 再次查询需要从数据库再从新获取值
beforeInvocation = false: 缓存的清除是否在方法之前执行
默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
beforeInvocation = true:
代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除
@Override
@CacheEvict(value = "emp",allEntries = true,beforeInvocation = true)
public void delEmp(Integer id) {
System.out.println("delete :"+id);
//employeeMapper.delEmp(id);
//int i=10/0;
}
coustmize 自定义配置
@Caching(
cacheable = {
@Cacheable(value = "emp",key = "#lastName") //按方法参数lastName存储
},
put={ //put注解是方法执行后调用,所有一定会执行该方法
@CachePut(value = "emp",key = "#result.id"), //按方法返回值id存储
@CachePut(value = "emp",key = "#result.email") //按方法返回值email存储
}
)
public Employee getByLastName(String lastName) {
return employeeMapper.getByLastName(lastName);
}
指定公共属性 抽取缓存的公共配置
@Service
@CacheConfig(cacheNames="emp")
@EnableCaching
public class EmpServiceimpl implements EmpService {
@Autowired
EmployeeMapper employeeMapper;
@Cacheable(/*cacheNames = "emp"*/)
public Employee getEmp(Integer id) {
System.out.println("查询第"+id+"号员工");
return employeeMapper.getById(id);
}
}
1、安装redis:使用docker;
2、引入redis的starter
3、配置redis
4、测试缓存
原理: CacheManager===Cache 缓存组件来实际给缓存中存取数据
1)、引入redis的starter, 容器中保存的是RedisCacheManager;
2)、RedisCacheManager 帮我们创建RedisCache 来作为缓存组件;
默认使用的是RedisTemplate 也就是Lettuce 更详细的请看redis Study document
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
dependencies>
spring.datasource.url=jdbc:mysql://localhost:3306/spring_cache?characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1008611
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.configuration.map-underscore-to-camel-case=true
logging.level.com.whgc.mapper=debug
debug=true #自动配置报告
spring.redis.host=192.168.154.130
@SpringBootTest
class Springboot01CacheApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Test
void t2(){ //用set会有序列化影响, append 不会
redisTemplate.opsForValue().append("testSet01","9874564");
}
RedisConfig
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
return template;
}
}
@Test
void t3(){
Employee emp = empService.getEmp(2); //即使存储对象也不会序列化
redisTemplate.opsForValue().set("employee:"+emp.getId(), emp);
}
如果加了redis ,Cache 会自动把缓存存储到redis中
配置RedisCacheManager Serializer
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为json序列化
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
.fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(pair);
//设置默认超过期时间是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
//初始化RedisCacheManager
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
@Service
public class DeptServiceimpl implements DeptService {
@Autowired
DepartmentMapper departmentMapper;
@Override
@Cacheable(cacheNames = "dept")
public Department getDept(Integer id) {
return departmentMapper.getDept(id);
}
}
大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力
消息服务中两个重要概念:
消息代理(message broker)和目的地(destination)
当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目
的地。消息队列主要有两种形式的目的地
队列(queue) :点对点消息通信(point-to-point)
主题(topic) :发布(publish) /订阅(subscribe) 消息通信
点对点式:
消息发送者发送消息, 消息代理将其放入-一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列
消息只有唯一的发送者和接受者,但并不是说只能有一个接收者发布订阅式:
发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息Spring支持
spring-jms提供了对 **JMS(java内部的)**的支持spring-rabbit提供了对 **AMQP (跨平台跨语言)**的支持
需要ConnectionFactory的实现来连接消息代理
提供Jms Template、RabbitTemplate来发送消息
@JmsListener (JMS)、@RabbitListener (AMQP)注解在方法上监听消息代理发布的消息
@EnableJms、@EnableRabbit开启支持
RabbitMQ简介:
RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。
核心概念
Message
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由-系列的可选属性组成,这些属性包括routing-key (路由键)、priority (相对于其他消息的优先权)、delivery-mode (指出该消息可能需要持久性存储)等。
Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。
Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange有4种类型: direct(默认), fanout, topic,和headers,不同类型的Exchange转发消息的策略有
所区别
在Linux 上安装好后 Rabbitmq 图形化管理系统 账号 : guest 密码 : guest
引入spring-boot-starter-amqp
application.ym|配置
测试RabbitMQ
AmqpAdmin:管理组件
RabbitTemplate: 消息发送处理组件
自动配置
1、Rabbi tAutoConfiguration
2、有自动配置了连接工厂ConnectionFactory;
3、RabbitProperties 封装了RabbitMQ的配置
4、RabbitTemplate :给RabbitMQ发送和接受消息;
5、AmqpAdmin : RabbitMQ系统管理功能组件
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.amqpgroupId>
<artifactId>spring-rabbit-testartifactId>
<scope>testscope>
dependency>
dependencies>
spring.rabbitmq.host=192.168.154.130
@SpringBootTest
class SpringbootRabbitmqApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
//1. 单播,点对点
@Test
void contextLoads() {
Map<String,Object> map = new HashMap<>();
map.put("msg","this is a info");
map.put("data", Arrays.asList("Hello World",123,true));
rabbitTemplate.convertAndSend("exchange.direct","whgc.news",new Book("西游记","吴承恩"));
}
//接收数据
@Test
void t1(){
Object o = rabbitTemplate.receiveAndConvert("whgc");
System.out.println(o.getClass());
System.out.println(o);
}
//广播
@Test
void t2(){
rabbitTemplate.convertAndSend("exchange.fanout","",new Book("三国演义","罗贯中"));
}
@Autowired
AmqpAdmin amqpAdmin; //amqpAdmin创建管理工具
@Test
void createExchange(){
// amqpAdmin.declareExchange(new DirectExchange("amqpadmin.exchange"));
// System.out.println("Create sueecss finish!");
// //Create bind 规则 创建一个队列
// amqpAdmin.declareQueue(new Queue("amqpadmin.queue",true));
//将 amqpadmin.queue 队列绑定到 amqpadmin.exchange 交换器
amqpAdmin.declareBinding(new Binding("amqpadmin.queue",Binding.DestinationType.QUEUE,"amqpadmin.exchange","amqp.haha",null));
}
}
@Service
public class RabbitServiceimpl implements RabbitService {
@Override //test的时候,在发布在消息队列里的内容会被拦截,而且打印不出内容
//只有在原本就有内容的时候,一次性全部清空获得数据
@RabbitListener(queues = "whgc.news")
public void booklistener(Book book) {
System.out.println("收到book 信息"+book);
}
@Override
@RabbitListener(queues = "whgc.#")
public void booklistener01(Message message) {
System.out.println(message.getBody());
System.out.println(message.getMessageProperties());
}
}
@EnableRabbit
@SpringBootApplication
public class SpringbootRabbitmqApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootRabbitmqApplication.class, args);
}
}
简介
我们的应用经常需要添加检索功能,开源的ElasticSearch是目前全文搜索引擎的首选。他可以快速的存储、搜索和分析海量数据。Spring Boot通过整合SpringData ElasticSearch为我们提供了非常便捷的检索功能支持;
Elasticsearch是一个分布式搜索服务, 提供Restful API,底层基于Lucene,采用多shard (分片)的方式保证数据安全,并且提供自动resharding的功能,github等大型的站点也是采用了ElasticSearch作为其搜索服务,
docker install ElasticSearch 访问 有数据表示成功安装
1. 启动
docker run -di --name=自定义名字 -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" -p 9200:9200 -p 9300:9300 elasticsearch:版本号
2. 查看状态,
docker ps
3. 查看容器日志错误信息
docker logs -f [容器id]/[name]
#max virtual memory areas vm.max_map_count [65530]...
4. 查看max_map_count
cat /proc/sys/vm/max_map_count
5. 设置max_map_count
sysctl -w vm.max_map_count=262144
- 引入sp ring-boot-starter-data-elasticsearch
- 安装Spring Data对应版本的ElasticSearch
- application.yml配置
- Spring Boot自动配置的
- ElasticsearchRepository、ElasticsearchTemplate、 Client
- 测试ElasticSearch
SpringBoot默认支持两种技术来和ES交互;
1、Jest. (默认不生效) //过时了
需要导入jest的工具包(io. searchbox. client. JestClient)
2、SpringData ElasticSearch
ElasConfig , 参照官网,不管是什么技术,官网教程都很完善。只是有一些写的不细致,让人拐进去,特别注意例子中的变量就ok了。
@Configuration
public class ElasConfig extends AbstractElasticsearchConfiguration {
/* @Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("192.168.154.130",9200,"http")));
return client;
}*/
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("192.168.154.130:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
使用 ElasticsearchRepository的子接口来操作ES;
@Repository
public interface BookRepository extends ElasticsearchRepository<Book, String> {
//其他方法写法参照官方文档
List<Book> findByBooknameLike(String bookName);
Book findByBookname(String bookName);
List<Book> findByBooknameOrAuthor(String bookName,String author);
//指定索引查询
List<Book> findByIdBetween(String index1,String index2);
}
@Document(indexName = "whgc", type = "book", shards = 1, replicas = 0,refreshInterval = "-1")
public class Book {
@Id //建议id不要写Integer类型
private String id;
@Field
private String bookname;
@Field
private String author;
} //bean class
@SpringBootTest
class SpringbootElasticsearchApplicationTests {
@Autowired
BookRepository bookRepository;
@Test
void contextLoads() {
System.out.println("Test Context Run ---");
//bookRepository.deleteAll();
for(Book books:bookRepository.findByBooknameLike("红")){
System.out.println(books);
}
Book book = bookRepository.findByBookname("三国");
if(book!=null){
System.out.println(book);
}else {
System.out.println("query a book you need not exists! ");
}
}
//delete info delete failed not rollback
@Test
void tOr(){ //Or只出现前面的。不晓得为啥
//List books = bookRepository.findByBooknameOrAuthor("三","红");
//List books = bookRepository.findByIdBetween("1","3");
//bookRepository.deleteById("3");
//List books = bookRepository.findByIdBetween("1","3");
final Iterable<Book> books = bookRepository.findAll();
if(books == null){
System.out.println("query a book you need not exists! ");
return;
}
for(Book book : books){
System.out.println(book);
}
}
}
在开发中,我们可能经常会遇到一些需要执行时间很长的任务,如果放在前端,会让用户一直卡在那儿等待或者一直转圈圈,体验非常不好。为了改善这种体验,我赶紧上网搜索,果然,前人早已有解决办法了。那就是异步。在Django中,我们可以使用celery异步框架,我们可以把耗时的任务扔到后台,而前端给用户立即返回,待用户需要查看结果时,点击查看即可,并且可以随时看到任务执行的状态。
实现:
@Service
public class AsyncServiceimpl implements AsyncService {
@Override
@Async
public void asynHello() {
try{
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
// 即时要睡5秒 前端的页面不会等待,不受到影响
System.out.println("task on doing...");
}
}
@RestController
public class HelloController {
@Autowired
AsyncService asyncService;
@GetMapping({"/","/hello"})
public String hello(){
asyncService.asynHello();
return "hello";
}
}
@EnableAsync //start scan 注解 @Async
@SpringBootApplication
public class SpringbootTaskApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTaskApplication.class, args);
}
}
项目开发中经常需要执行一 些定时任务,比如需要在每天凌晨时候,分析一 ~次前一天的日志信息。 Spring为我们提供 了异步执行任务调度的方式,提供TaskExecutor、TaskScheduler接口。
@EnableScheduling @Scheduled
cron表达式:
字段 | 允许值 | 允许的特殊字符 |
---|---|---|
秒 | 0-59 | , - * / |
分 | 0-59 | , - * / |
时 | 0-23 | , - * / |
日期 | 1-31 | , - * / ? L W C |
月份 | 1-12 | , - * / |
星期 | 0-7 或 SUN-SAT 0,7是SUN | , - * / ? L C # |
特殊字符 | 表示含义 | 特殊字符 | 表示含义 |
---|---|---|---|
, | 枚举 | - | 区间 |
* | 任意 | / | 步长(间隔多长时间一次) |
? | 日/ 星期冲突匹配(选择星期一 就不能用*) | L | 最后(最后一个周六,最后一个周日) |
W | 工作日 | C | 和calendar联系后计算过的值 |
# | 星期 ,4#2,第二个星期四 |
示例:
@Service
public class scheduledServiceimpl implements scheduledService {
/**
* second(秒),minute (分),hour (时),,day of month(日) month (月),day ofweek (周几)
* 0 * * * * MON-FRI 周一到周五,每天每时每分 0秒启动一次
* 0,1,2,3,4 * * * * MON-FRI 周一到周五,每天每时每分 0,1,2,3,4秒启动一次
* 0-4 * * * * MON-FRI 周一到周五,每天每时每分 0-4 秒 都启动一次
* 0/4 * * * * MON-FRI 周一到周五,每天每时每分 每隔4秒 都启动一次
* [0 0/5 14,18 * * ?]每天14点整,和18点整,每隔5分钟执行- -次
* [0 15 10 ? * 1-6] 每个月的周一至周六10:15分执行- -次
* [0 0 2 ? * 6L]每个月的最后一个周六凌晨2点执行一次
* [0 0 2 LW * ?]每个月的最后一个工作日凌晨2点执行一-次
* [0 2-4 ? * 1#1]每个月的第一一个周一凌晨2点到4点期间,每个整点都执行一次;
*/
//@Scheduled(cron = "0/4 * * ? * FRI")
@Scheduled(cron = "0/4 * * ? * FRI")
public void testScheduled() {
System.out.println("hello "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
@EnableAsync //开启注解扫描的异步任务
@EnableScheduling //开启注解扫描的定时任务
@SpringBootApplication
public class SpringbootTaskApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTaskApplication.class, args);
}
}
- 邮件发送需要引入spring-boot-starter-mail
- Spring Boot自动配置MailSenderAutoConfiguration
- 定义MailProperties内容,配置在application.ymI中
- 自动装配JavaMailSender
- 测试邮件发送
[email protected]
spring.mail.password=ungigghkclzedcgi
spring.mail.host=smtp.qq.com
需要开启 IMAP 服务,
IMAP是什么?
IMAP,即Internet Message Access Protocol(互联网邮件访问协议),您可以通过这种协议从邮件服务器上获取邮件的信息、下载邮件等。IMAP与POP类似,都是一种邮件获取协议。
# 登录第三方客户端时,密码框请输入“授权码”进行验证。生成授权码
@SpringBootTest
class SpringbootTaskApplicationTests {
@Autowired
JavaMailSenderImpl javaMailSender;
@Test
void contextLoads() { //发送一个普通邮件
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("通知-今晚开会");
message.setText("今晚 7:30 开会");
message.setTo("[email protected]");
message.setFrom("[email protected]");
javaMailSender.send(message);
}
@Test
void testSendFile() throws Exception{
//1. 创建一个复杂的消息邮件
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,true);
helper.setSubject("通知-今晚开会");
helper.setText("源文本"," 今晚 7:30 开会 ");
helper.setTo("[email protected]");
helper.setFrom("[email protected]");
helper.addAttachment("星河1.jpg",new File("C:\\Users\\thisissirz\\Desktop\\software\\wallpaper\\星河\\星河1.jpg"));
helper.addAttachment("星河2.jpg",new File("C:\\Users\\thisissirz\\Desktop\\software\\wallpaper\\星河\\星河2.jpg"));
javaMailSender.send(message);
}
}
- 应用程序的两个主要区域是“认证”和"授权”(或者访问控制) 。这两个主要区域是Spring Security的两个目标。
- “认证”(Authentication) ,是建立一个他声明的主体的过程(一
个“主体”-般是指用户,设备或一-些可以在你的应用程序中执行动
作的其他系统)。- "授权”(Authorization) 指确定一个主体是否允许在你的应用程序执行一个动作的过程。为了抵达需要授权的店,主体的身份已经有认证过程建立。
- 这个概念是通用的而不只在Spring Security中。
更详细请参考官方文档,官方文档是最好的老师。
pom.xml file
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
<version>3.0.4.RELEASEversion>
dependency>
config.java
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
User.UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("zhangsan").password("123456").roles("VIP1","VIP2").build());
manager.createUser(users.username("lisi").password("123456").roles("VIP2","VIP3").build());
manager.createUser(users.username("wangwu").password("123456").roles("VIP1","VIP3").build());
//manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
return manager;
}
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(authorize -> authorize
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("VIP1")
.antMatchers("/level2/**").hasRole("VIP2")
.antMatchers("/level3/**").hasRole("VIP3")
)
.httpBasic(withDefaults());
//开启自动配置的登陆功能,效果,如果没有登陆,没有权限就会来到登陆页面
http.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()).formLogin(withDefaults());
//开启自动配置的注销功能。
http.logout().logoutSuccessUrl("/"); //注销成功会返回到哪个页面
//1. 访问 /logout 表示用户注销,清空session
//记住我
//http.rememberMe(); on the now is error
}
}