# springboot高级 **Repository Path**: java_learning2/springboot_advanced ## Basic Information - **Project Name**: springboot高级 - **Description**: springboot高级用法 - **Primary Language**: Java - **License**: MulanPSL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2020-03-26 - **Last Updated**: 2024-10-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # springboot进阶使用 ## 一 缓存 ### 重要概念&缓存注解 | Cache | 缓存接口,定义缓存操作。实现有:redisCache,EhCacheCache,ConcurrentMapCache等 | | -------------- | ------------------------------------------------------------ | | CacheManager | 缓存管理器,管理各种缓存(Cache)组件 | | @Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 | | @CacheEvict | 清空缓存 | | @CachePut | 保证方法被调用,又希望结果被缓存 | | @EnableCaching | 开启基于注解的缓存 | | KeyGenerator | 缓存数据时key生成策略 | | serialize | 缓存数据时value序列化策略 | ```java /** * CacheManager管理多个Cache组件的,对缓存的真正的CRUD操作在Cache组件中,每一个缓存都有自己的名字 * 几个属性: * cacheNames/vale:指定缓存组件的名字; * key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值 * 编写SpEl: #id;参数的id的值 #a0 #p0 #root.args[0] * keyGenerator: key的生成器;可以自己指定key的生成器的组件id * key/keyGenerator: 二选一使用 * cacheManager:指定缓存缓存器;或者cacheResolver指定获取解析器 * condition:指定符合条件的情况下才缓存 * unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断 * unless = "#result==null" 当为空的时候就不缓存 * sync: 异步缓存 */ ``` ### spel可以获得的参数 ![1585202503158](https://s1.ax1x.com/2020/03/26/GSeiZt.png) ### 配置文件 ```properties #mybatis配置数据源 spring.datasource.url=jdbc:mysql://101.200.91.110:3306/mydb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver server.port=8081 #开启驼峰命名法 #mybatis.configuration.map-underscore-to-camel-case=true #开启缓存日志 logging.level.com.example.cache.mapper=debug #redis的相关配置 spring.redis.host=101.200.91.110 spring.cache.type=redis spring.redis.database=2 spring.redis.port=6379 spring.redis.password= 123456 ``` ### 缓存案例 ```java package com.example.cache.controller; import com.example.cache.Bean.sp_type; import com.example.cache.mapper.sp_tpeMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.*; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; //@CacheConfig(cacheNames = "emp")//注解之后下面的所有注解都不用写 catchName @RestController public class TestController { @Autowired sp_tpeMapper sp_tpeMapper; /** * * @param id * @return * CacheManager管理多个Cache组件的,对缓存的真正的CRUD操作在Cache组件中,每一个缓存都有自己的名字 * 几个属性: * cacheNames/vale:指定缓存组件的名字; * key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值 * 编写SpEl: #id;参数的id的值 #a0 #p0 #root.args[0] * keyGenerator: key的生成器;可以自己指定key的生成器的组件id * key/keyGenerator: 二选一使用 * cacheManager:指定缓存缓存器;或者cacheResolver指定获取解析器 * condition:指定符合条件的情况下才缓存 * unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断 * unless = "#result==null" 当为空的时候就不缓存 * sync: 异步缓存 */ //@Cacheable(cacheNames = {"emp"},key = "#root.method+'['+#id+']'") //@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator" ,condition = "#id>1") @Cacheable(cacheNames = {"emp"}) @GetMapping("/emp/{id}") public sp_type getType(@PathVariable("id") Integer id){ System.out.println("查询员工"); System.out.println(sp_tpeMapper.getTypeByid(id)); return sp_tpeMapper.getTypeByid(id); } /** * * @param sp_type * @return * CachePut同步更新缓存 * 调用之后删除之前的缓存,更新现在的缓存 */ @GetMapping("/emp/update") @CachePut(value = "emp",key = "#result.type_id") public sp_type updateType(sp_type sp_type){ sp_tpeMapper.updatetype(sp_type); return sp_type; } /** * CacheEvict删除缓存 * 参数: * allEntries = true 删除所有数据 * beforeInvocation = true 方法是否在缓存清除之前执行 */ @GetMapping("/delemp/{id}") @CacheEvict(value = "emp",key = "#id",allEntries = true,beforeInvocation = true) public void deleteEmp(@PathVariable("id") Integer id){ System.out.println("deleteEmp"+id); } /** *Caching 指定多个进行缓存 */ @Caching( cacheable = { @Cacheable(value = "emp",key = "#id") }, put = { @CachePut(value = "emp",key = "#result.delete_time"), @CachePut(value = "emp",key = "#result.type_name") } ) @GetMapping("/emp2/{id}") public sp_type getType2(@PathVariable("id") Integer id){ System.out.println("查询员工"); System.out.println(sp_tpeMapper.getTypeByid(id)); return sp_tpeMapper.getTypeByid(id); } } ``` ### 自定义keyGenerator ```java package com.example.cache.config; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; /** * 自定义缓存值的写法 */ @Configuration public class MykeyGenerator { @Bean("myKeyGenerator") public KeyGenerator keyGenerator(){ return new KeyGenerator(){ @Override public Object generate(Object o, Method method, Object... objects) { return method.getName()+"["+ Arrays.asList(objects).toString()+"]"; } }; } } ``` ### redis字符编码设置 ```java package com.example.cache.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import java.net.UnknownHostException; /** * 设置redis存储值的时候的字符编码 */ @Configuration public class MyRedisConfig { @Bean public RedisCacheManager JsonCacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() // 使用GenericJackson2JsonRedisSerializer序列化得到Value .serializeValuesWith(RedisSerializationContext.SerializationPair. fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); } } ``` ## 二消息队列 ### pom 文件 ```xml org.springframework.amqp spring-rabbit-test test ``` ### 设置取数据和存数据的编码格式 ```java package com.amqp.config; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2XmlMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyAMQConfig { @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } } ``` ### 普通模式 ```java package com.amqp; import org.junit.jupiter.api.Test; import org.springframework.amqp.core.AmqpAdmin; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @SpringBootTest class AmqpApplicationTests { @Autowired RabbitTemplate rabbitTemplate; @Autowired AmqpAdmin amqpAdmin; //创建交换机 @Test void AmqpAdmin(){ //床架交换机 // amqpAdmin.declareExchange(new DirectExchange("amqpAdmin.exchange")); // System.out.println("创建成功"); //创建队列 //amqpAdmin.declareQueue(new Queue("amqpadmin.queue",true)); //创建绑定规则 amqpAdmin.declareBinding(new Binding("amqpadmin.queue", Binding.DestinationType.QUEUE,"amqpAdmin.exchange","ampq.hahah",null)); } @Test void contextLoads() { //Message需要自己构建一个;定义消息体内容和消息头 //rannitTemplate.send(exchange,routekey.message); //object默认当做消息体,只需要传入要发送的对象,自动序列化发送给rabbitmq //rabbitTemplate.converAndSend(exchange,routeKey,object) Map map = new HashMap<>(); map.put("msg","这是第一个消息"); map.put("data", Arrays.asList("Helloword",123,true)); //对象默认虚拟化 rabbitTemplate.convertAndSend("exchange.direct","atguigu.news",map); } /** * 取数据 */ @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("atguigu.news"); System.out.println(o.getClass()); System.out.println(o); } /** * 广播模式 */ @Test public void sendMsg(){ Map map = new HashMap<>(); map.put("msg","这是第一个消息"); map.put("data", Arrays.asList("Helloword",123,true)); rabbitTemplate.convertAndSend("exchange.fanout","",map); } } ``` ### 基于注解的监听 ```java package com.amqp; import org.springframework.amqp.rabbit.annotation.EnableRabbit; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 自动配置 * 1,RabbitAutoConfiguration * 2,有自动配置了连接工厂ConnectionFactory * 3,RabbitProperties 封装了RabbitMq配置 * 4,RabbitTemplate:给RabbitMQ发送和接受消息 * 5,AmqpAdmin:RabbitMQ系统管理功能组件 */ @EnableRabbit//开启基于注解的RabbitMQ @SpringBootApplication public class AmqpApplication { public static void main(String[] args) { SpringApplication.run(AmqpApplication.class, args); } } ``` ```java package com.amqp.Service; import com.amqp.bean.Book; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Service; @Service public class BookService { @RabbitListener(queues = "atguigu.news") public void receive(Book book){ System.out.println("收到消息"+book); } } ``` ## 三 全文检索 elasticsearch ### 安装 docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 elasticsearch ## 四 异步任务 ### 开启异步任务注解@EnableAsync ```java package com.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @EnableAsync//开启异步注解 @SpringBootApplication public class TaskApplication { public static void main(String[] args) { SpringApplication.run(TaskApplication.class, args); } } ``` ### 标注异步方法 @Async ```java package com.task.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncService { @Async //标注为异步方法 public void hello(){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("数据处理中。。。。。"); } } ``` ## 五 定时任务 ### cron表达式 ![img](https://s1.ax1x.com/2020/03/27/GCMiSx.png) ### 注解使用 ```java package com.task.service; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service public class ScheduledService { /** * second(秒) ,minute(分),hour(时),day of month,month(月),day of week(周几) * 0*****MON-FRI 周一到周五每秒启动一次 * 【0 0/5 14,18 ** ?】 每天14点整,和18点整,每个5分钟执行一次 * 【0 15 10 ? * 1-6】 每个月的周一到周六10:15执行一次 * 【0 0 2 ? * 6L】 每个月的最后一个工作日凌晨2点执行一次 * 【0 0 2-4 ? * 1#1】 每个月的第一个周一凌晨2点到四点期间,每个整点都执行一次 * */ @Scheduled(cron = "0 * * * * MON-FRI") public void hello(){ System.out.println("hello ..."); } } ``` ### 主方法使用 ```java package com.task; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @EnableAsync//开启异步注解 @EnableScheduling//开启基于注解的定时任务 @SpringBootApplication public class TaskApplication { public static void main(String[] args) { SpringApplication.run(TaskApplication.class, args); } } ``` ## 六 邮件发送 ### 邮件地址的配置 ```properties #邮件地址等信息 spring.mail.username=3095329264@qq.com spring.mail.password=tnipdtmjucuideag spring.mail.host=smtp.qq.com ``` ### 操作邮箱 ```java package com.task; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import javax.mail.internet.MimeMessage; import java.io.File; @SpringBootTest class TaskApplicationTests { @Autowired JavaMailSenderImpl mailSender; @Test void contextLoads() { SimpleMailMessage message = new SimpleMailMessage(); //邮件测试 message.setSubject("通知-今晚开会"); message.setText("今晚7:30开会"); message.setTo("3095329264@qq.com"); message.setFrom("3095329264@qq.com"); mailSender.send(message); } @Test void test() throws Exception{ MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true); //邮件测试 helper.setSubject("通知-今晚开会"); helper.setText("今晚7:30开会",true); helper.setTo("3095329264@qq.com"); helper.setFrom("3095329264@qq.com"); //上传文件 helper.addAttachment("1.jpg",new File("C:\\Users\\方玉龙\\Pictures\\Saved Pictures\\heise_zhuti-005.jpg")); mailSender.send(mimeMessage); } } ``` ## 七 安全 ```xml org.springframework.boot spring-boot-starter-security ``` ```java package com.config; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @EnableWebSecurity public class MySecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("zhangshan").password("123456").roles("VIP1","VIP2") .and() .withUser("zhangshan2").password("123456").roles("VIP1","VIP2"); } @Override public void configure(HttpSecurity http) throws Exception { //定制请求的授权规则 http.authorizeRequests().antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("VIP1") .antMatchers("/level2/**").hasRole("VIP2") .antMatchers("/level3/**").hasRole("VIP3"); //开启自动配置的登录功能 http.formLogin().loginPage("/"); //1 /login来到登录页面 没有权限跳转登录页面 //2 重定向到/login?error表示登录失败 //3 更多详细规定 //开启自动配置的注销功能 http.logout().logoutSuccessUrl("/"); // 访问/logout表示用户注销,清空session //记住登录状态 http.rememberMe(); } } ``` ## 八 分布式 ### eureka #### 配置 ```properties server: port: 8761 eureka: instance: hostname: eureka-server #eureka实例的主机名 client: register-with-eureka: false #不把自己注册到eureka上 fetch-registry: false #不从eureka上来获取服务的注册信息 service-url: defaultZone: http://localhost:8761/eureka/ ``` #### 开启 ```java @EnableEurekaServer ``` ### 服务提供者 #### 配置 ```properties server: port: 8001 spring: application: name: provider-ticket eureka: instance: prefer-ip-address: true #注册服务的时候用到ip地址 client: service-url: defaultZone: http://localhost:8761/eureka/ ``` #### Service ```java package com.service; import org.springframework.stereotype.Service; @Service public class testService { public String hello(){ return "厉害了我的哥"; } } ``` #### Controller ```java package com.controller; import com.service.testService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @Autowired testService testService; @Value("${server.port}") String port; @GetMapping(value = "/hello") public String controller(){ System.out.println(port); return testService.hello()+port; } } ``` ### 服务消费者 #### 配置 ```yml spring: application: name: consumer-user server: port: 8200 eureka: instance: prefer-ip-address: true #注册服务的时候用到ip地址 client: service-url: defaultZone: http://localhost:8761/eureka/ ``` #### controller ```java package com.user.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class UserController { @Autowired RestTemplate restTemplate; @GetMapping("/buy") public String butTicket(String name){ String s = restTemplate.getForObject("http://PROVIDER-TICKET/hello",String.class); return name+"购买了"+""+s; } } ``` ## 九 热部署插件 ```xml org.springframework.boot spring-boot-devtools true ``` ctrl + F9之后自动修改