SpringBoot整合篇

news2024/9/28 1:19:32

SpringBoot整合第三方技术

1、整合缓存


何为缓存?

  • 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
  • 使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能
  • 缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间

Spring缓存抽象

Spring 从3.1开始定义了 org.springframework.cache.Cacheorg.springframework.cache.CacheManager 接口来统一不同的缓存技术;并支持使用JCache注解简化开发

注解说明
Cache缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager缓存管理器,管理各种缓存组件Cache
@Cacheable主要针对方法配置,能够根据方法的请求参数对其结果进行缓存(查询)
@CacheEvict清空缓存(删除)
@CachePut保证方法被调用,又希望结果被缓存(修改)
@EnableCaching开启基于注解的缓存
keyGenerator缓存数据时key生成策略
serialize缓存数据时value序列化策略

使用缓存

将方法的返回值进行缓存,以后如果需要相同的数据,直接从缓存中获取,不再调用方法,从而提高系统的性能!

  • 引入 spring-boot-starter-cache 模块
  • @EnableCaching 开启缓存
  • 使用缓存注解(@Cacheable@CachePut@CacheEvict
  • (切换为其他缓存产品)

@Cacheable属性

  • cacheNames/value:指定缓存组件的名字,将方法的返回结果放在哪个缓存中,可以指定多个缓存
  • key:缓存数据使用的key值,可以直接指定,默认是使用方法参数的值,可以编写 SpEL
  • keyGenerator:key的生成器,key和keyGenerator,二选一使用
  • cacheManager:指定缓存管理器,或者cacheResolver指定获取解析器,二选一使用
  • condition:指定符合条件的情况下才缓存
  • unless:当unless指定的条件为true,方法的返回值就不会被缓存
  • sync:是否使用异步模式,默认方法执行完,将结果以同步的方式存入缓存中

@CachePut

修改了数据库的某个数据,同时更新缓存,运行时先调用目标方法,将目标方法的结果(返回值)缓存起来

注:更新函数的缓存 Key 值如果和查询函数的 Key 值不一致,那么更新后查询函数查询的结果依然是旧的数据

@CacheEvict

缓存清除

  • key:指定要清除的数据
  • allEntries:默认false,如果为true,表示清除这个缓存中所有数据
  • beforeInvocation:缓存的清除是否在方法之前,默认false,即缓存清除在方法执行后进行,如果方法出现异常缓存就不会清除。如果设为true,则表示清除缓存在方法运行之前,无论方法是否出现异常,缓存都会被清除

整合 Redis

默认使用的是 ConcurrentMapCacheManagerConcurrentMapCache ,但在实际开发中通常会使用 Redis、memcached、Ehcache

  • 导入 spring-boot-starter-data-redis
  • 配置文件配置 redis 的连接地址等信息

RedisTemplate & StringRedisTemplate

RedisAutoConfiguration 中自动配置了 RedisTemplateStringRedisTemplate,可以很方便的来操作Redis!

  • StringRedisTemplate:存取的数据都是字符串时使用
  • RedisTemplate:存取的数据是对象时使用

Redis常见的五大数据类型和RedisTemplate的相关API

  • redisTemplate.opsForValue() String
  • redisTemplate.opsForList() List
  • redisTemplate.opsForSet() Set
  • redisTemplate.opsForHash() Hash
  • redisTemplate.opsForZSet() ZSet

序列化

RedisTemplate 默认使用的序列化类是 JdkSerializationRedisSerializer,而 StringRedisTemplate 使用的是 StringRedisSerializer

如果需要更改序列化规则,如将数据以 JSON 形式保存,可以自定义 RedisTemplate 设置序列化规则并注入容器中

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> jsonRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 默认的Key序列化器为:JdkSerializationRedisSerializer
        template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
        return template;
    }
}

自定义CacheManager

引入 Redis 的 starter 后,容器中注册的但是 RedisCacheManager,默认的SimpleCacheManager 失效。RedisManager 帮助我们创建 RedisCache 来作为缓存组件,RedisCache 通过操作 redis 来缓存数据

整合 Elasticsearch

数据检索

开源的 ElasticSearch 是目前全文搜索引擎的首选。它可以快速的存储、搜索和分析海量数据。Spring Boot通过整合Spring Data ElasticSearch为我们提供了非常便捷的检索功能支持

Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard(分片)的方式保证数据安全,并且提供自动resharding的功能,Stack Overflow等大型的站点也是采用了ElasticSearch作为其搜索服务

ES 简介

Elasticsearch 是面向文档的,意味着它存储整个对象或文档。ES 不仅存储文档,而且索引每个文档的内容,使之可以被检索。在 ES 中,我们对文档进行索引、检索、排序和过滤,而不是对行列数据

Elasticsearch 使用 JavaScript Object Notation(或者 JSON)作为文档的序列化格式。JSON 序列化为大多数编程语言所支持,并且已经成为 NoSQL 领域的标准格式。 它简单、简洁、易于阅读

存储数据到 Elasticsearch 的行为叫做索引,但在索引一个文档之前,需要确定将文档存储在哪里。一个 Elasticsearch 集群可以包含多个索引 ,相应的每个索引可以包含多个类型。这些不同的类型存储着多个文档,每个文档又有多个属性

  • Index(索引):数据库
  • Document 文档:数据库表中的一条记录
  • Field 字段:数据库中的字段

在这里插入图片描述

注:ES 里的 Index 可以看做一个库,而 Type 相当于表,Documents 则相当于表的行(记录)

这里 Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个 type,Elasticsearch 7.X 中,Type 的概念已经被删除了,type都为_doc

安装 ES

  • 拉取镜像:docker pull elasticsearch:7.8.0

  • 运行容器

    # -d 后台运行
    # -v 绑定一个数据卷
    # -p(小写) 指定要映射的IP和端口	hostPort:containerPort
    # -e "discovery.type=single-node"   单节点集群
    # -e ES_JAVA_OPTS="-Xms512m -Xmx512m"  制定运行参数,不然如果机器内存太小,启动后会非常卡顿
    # --name 起个别名
    docker run -p 9200:9200 -p 9300:9300 --name=es-7.8.0 \
    -e "discovery.type=single-node" \
    -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
    -d elasticsearch:7.8.0
    

ES官方文档

安装Kibana

  • 拉取镜像:docker pull kibana:7.8.0

  • 运行容器

    # kibana版本必须和elasticsearch版本保持一致
    # 启动容器
    # YOUR_ELASTICSEARCH_CONTAINER_NAME_OR_ID 正在运行的ES容器ID或name
    # docker run --link YOUR_ELASTICSEARCH_CONTAINER_NAME_OR_ID:elasticsearch -p 5601:5601 {docker-repo}:{version}
    docker run --link es-7.8.0:elasticsearch -p 5601:5601 -d --name=kibana-7.8 kibana:7.8.0
    

整合 ES

导入整合依赖包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
实体类映射操作
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "product")
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;

    // 必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
    @Id
    private Long id;            // 商品唯一标识
    /**
     * type : 字段数据类型
     * analyzer : 分词器类型
     * index : 是否索引(默认:true)
     * Keyword : 短语,不进行分词
     */
    @Field(type = FieldType.Text)
    private String title;       // 商品名称

    @Field(type = FieldType.Keyword)
    private String category;    // 分类名称

    @Field(type = FieldType.Double)
    private Double price;       // 商品价格

    @Field(type = FieldType.Keyword, index = false)
    private String images;      // 图片地址
}
DAO 数据访问对象
public interface ProductRepository extends ElasticsearchRepository<Product,Long> {
}
CRUD 操作
@SpringBootTest
class ElasticsearchApplicationTests {
    @Autowired
    private ElasticsearchRestTemplate restTemplate;

    @Autowired
    private ProductRepository productRepository;

    // ****** 索引操作 ******
    @Test
    public void testCreateIndex(){
        // 创建索引并增加映射配置
        // 创建索引,系统初始化会自动创建索引
        System.out.println("创建索引...");
    }

    @Test
    public void testDelIndex(){
        // 删除索引
        boolean flag = restTemplate.indexOps(Product.class).delete();
        System.out.println("flag = " + flag);
    }

    // ****** 文档操作 ******
    @Test
    public void testAdd() {
        Product product = new Product(1001L, "华为mate50 Pro", "手机", 2699.00, "https://img11.360buyimg.com/n1/s450x450_jfs/t1/100178/24/22008/134447/63808261E7e8fdf63/bfc4ff46e04450b6.jpg.avif");
        productRepository.save(product);
    }

    @Test
    public void testUpdate() {
        Product product = new Product(1001L, "Redmi K50", "手机", 2599.00, "https://img12.360buyimg.com/n1/s450x450_jfs/t1/211467/21/27724/73497/63842044E32d227a6/232dbbcc378d8742.jpg.avif");
        productRepository.save(product);
    }

    @Test
    public void testGetById() {
        Optional<Product> product = productRepository.findById(1001L);
        System.out.println("product = " + product);
    }

    @Test
    public void testGetAll() {
        Iterable<Product> products = productRepository.findAll();
        products.forEach(System.out::println);
    }

    @Test
    public void testDel() {
        productRepository.deleteById(1001L);
    }

    @Test
    public void testBatchAdd() {
        List<Product> productList = new ArrayList<>();
        Product p1 = new Product(1003L, "OPPO Reno8 系列", "手机", 2099.00, "https://img13.360buyimg.com/n1/s450x450_jfs/t1/166774/34/31501/126697/636cd9dfE2329529e/bce5ef0bc351e516.jpg.avif");
        Product p2 = new Product(1004L, "Apple iPhone 14", "手机", 6899.00, "https://img14.360buyimg.com/n1/s450x450_jfs/t1/79861/16/22966/34933/6381b0d7Ece7f68c2/6dd5f2a3f892add8.jpg.avif");
        Product p3 = new Product(1005L, "奥克斯(AUX)大3匹空调", "大家电", 5999.00, "https://img12.360buyimg.com/n1/jfs/t1/109793/27/35299/101988/637aeaf7Ede5dd640/22e59a42744809c2.jpg.avif");
        productList.add(p1);
        productList.add(p2);
        productList.add(p3);

        productRepository.saveAll(productList);
    }

    @Test
    public void testBatchDel() {
        List<Long> ids = Arrays.asList(1003L, 1004L);
        productRepository.deleteAllById(ids);
    }

    @Test
    public void testFindByPage() {
        Sort sortRule = Sort.by(Sort.Direction.DESC, "price");
        int currentPage = 0;    // 当前页,第一页从 0 开始,1 表示第二页
        int pageSize = 5;       // 每页显示多少条
        PageRequest pageRequest = PageRequest.of(currentPage, pageSize, sortRule);
        Iterable<Product> products = productRepository.findAll(pageRequest);
        products.forEach(System.out::println);
    }
}

2、整合任务与邮件


异步任务

在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题

使用方法是在主启动类上用 @EnableAsync 开启异步支持,将 @Async 标注在指定的异步方法上

// 告诉Spring这是一个异步方法
@Async
public void hello()  {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("处理数据中...");
}

定时任务

项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息

市面上流行的定时任务技术

  • Quartz
  • Spring Task

相关概念

  • 工作(Job):用于定义具体执行的工作
  • 工作明细(JobDetail):用于描述定时工作相关的信息
  • 触发器(Trigger):用于描述触发工作的规则,通常使用cron表达式定义调度规则
  • 调度器(Scheduler):描述了工作明细与触发器的对应关系

Quartz

导入SpringBoot整合quartz的坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

定义具体要执行的任务,继承QuartzJobBean,并定义工作明细与触发器,并绑定对应关系

@Configuration
public class MyQuartz extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("生成年度报表...");
    }

    @Bean
    public JobDetail jobDetail() {
        // 绑定具体的工作
        return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
    }

    @Bean
    public Trigger trigger(){
        // 绑定对应的工作明细
        CronScheduleBuilder csb = CronScheduleBuilder.cronSchedule("0 59 23 L 12 ? 2022-2060");
        return TriggerBuilder.newTrigger().withSchedule(csb).forJob(jobDetail()).build();
    }
}

Spring Task

在主启动类上添加@EnableScheduling注解开启定时任务功能,然后在需要定时的函数上标注 @Schedule 注解,并设置 cron 表达式

@Component
public class MyTask {
    @Scheduled(cron = "0/6 * * * * ?")      // 每隔6秒执行一次
    public void recordLog(){
        System.out.println("记录日志...");
    }
}

在线Cron表达式生成器

邮件任务

  • SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
  • POP3(Post Office Protocol - Version 3):用于接收电子邮件的标准协议
  • IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议

导入SpringBoot整合JavaMail的坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

配置JavaMail

spring:
  mail:
    host: smtp.qq.com
    username: ********@qq.com
    password: ********

发送邮箱必备四要素

@Autowired
private JavaMailSender mailSender;

@Value(value = "${from}")
private String from;
@Value(value = "${to}")
private String to;
@Value(value = "${subject}")
private String subject;
@Value(value = "${content}")
private String content;

@Test
public void testSendSimpleEmail() {
    SimpleMailMessage msg = new SimpleMailMessage();
    // 邮件设置
    // 发件人
    msg.setFrom(from);
    // 收件人
    msg.setTo(to);
    // 标题
    msg.setSubject(subject);
    // 正文
    msg.setText(content);
    // 发送简单邮箱
    mailSender.send(msg);
}

附件与HTML文本支持

@Test
public void testSendHardMail() {
    try {
        // 创建一个复杂的消息邮箱
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        // 邮箱设置
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject("测试发送复杂邮箱");
        helper.setText("<p><a href='https:www.baidu.com'>点开有惊喜</a></p>", true);

        // 添加附件
        File img1 = new File("D:\\DesktopBackground\\mobile\\73.png");
        File img2 = new File("D:\\DesktopBackground\\mobile\\68.png");
        helper.addAttachment(img1.getName(), img1);
        helper.addAttachment(img2.getName(), img2);
        mailSender.send(mimeMessage);
    } catch (MessagingException e) {
        throw new RuntimeException(e);
    }
}

3、整合消息队列


  • 消息发送方

    • 生产者
  • 消息接收方

    • 消费者
  • MQTT

应用场景

  • 异步处理
  • 应用解耦
  • 流量消峰

JMS

JMS(Java Message Service)JAVA 消息服务:一个规范,等同于JDBC规范,提供了与消息服务相关的API接口

JMS消息模型

  • point to point:点对点模型(队列:queue),消息发送到一个队列中,队列保存消息。队列的消息只能被一个消费者消费,或超时
  • publish-subscribe:发布订阅模型(主题:topic),消息可以被多个消费者消费,生产者和消费者完全独立,不需要感知对方的存在

JMS消息种类

  • TextMessage
  • MapMessage
  • BytesMessage
  • StreamMessage
  • ObjectMessage
  • Message (只有消息头和属性)

JMS实现:ActiveMQ、Redis、HornetMQ、RabbitMQ、RocketMQ(没有完全遵守JMS规范)

AMQP

AMQP(advanced message queuing protocol):一种协议(高级消息队列协议,也是消息代理规范),规范了网络交换的数据格式,兼容JMS

优点:具有跨平台性,服务器供应商,生产者,消费者可以使用不同的语言来实现

AMQP消息模型

  • direct exchange
  • fanout exchange
  • topic exchange
  • headers exchange
  • system exchange

AMQP消息种类:byte[]

AMQP实现:RabbitMQ、StormMQ、RocketMQ

MQTT

MQTT(Message Queueing Telemetry Transport)消息队列遥测传输,专为小设备设计,是物联网(IOT)生态系统中主要成分之一

Spring 支持

  • spring-jms提供了对JMS的支持
  • spring-rabbit提供了对AMQP的支持
  • 通过ConnectionFactory的实现来连接消息代理
  • 提供JmsTemplate、RedisTemplate来发送消息
  • @JmsListener(JMS)、@RabbitListener(AMQP)注解在方法上监听消息代理发布的消息
  • @EnableJms、@EnableRabbit开启支持

SpringBoot 自动配置

  • JmsAutoConfiguration
  • RabbitAutoConfiguration

Docker 安装 RabbitMQ

  • 拉取镜像:docker pull rabbitmq:3-management
  • 运行容器:docker run -d -e RABBITMQ_DEFAULT_USER=root -e RABBITMQ_DEFAULT_PASS=root -p 5672:5672 -p 15672:15672 --name=rabbitmq-3 rabbitmq:3-management

SpringBoot 整合 RabbitMQ

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit-test</artifactId>
    <scope>test</scope>
</dependency>
  • ConnectionFactory:连接工厂
  • RabbitProperties:封装了RabbitMQ的配置
  • RabbitTemplate:给RabbitMQ发送和接收消息
  • AmqpAdmin:RabbitMQ系统管理功能组件

序列化机制

RabbitTemplate 默认使用 SimpleConverter (即JDK反序列规则),要将序列化规则设置为 JSON 形式,则可以使用如下方法:

@Configuration
public class RabbitMQConfig {
    public static final String EXCHANGE_NAME = "boot_topic_exchange";
    public static final String QUEUE_NAME = "boot_queue";
    // #:匹配一个或多个词	item.# = item.insert.abc.xxx.../item.insert
    // *:只能匹配一个次	item.* = item.insert
    public static final String ROUTING_KEY = "boot.#";

    // 1.交换机
    @Bean
    public Exchange exchange() {
        return ExchangeBuilder.topicExchange(EXCHANGE_NAME).build();
    }

    // 2.队列
    @Bean
    public Queue queue() {
        return QueueBuilder.durable(QUEUE_NAME).build();
    }

    // 3.队列与交换机的绑定
    /* 1.知道那个队列
       2.知道那个交换机
       3.routing key
    */
    @Bean
    public Binding bindingQueueExchange(Queue queue, Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY).noargs();
    }

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

发送数据

@Test
public void testTopicModel() {
    // 只需要传入要发送的对象,自动序列化发送到 RabbitMQ
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "小白");
    map.put("data", Arrays.asList("苹果", "甜橙", "葡萄"));
    rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.rabbit.hello", map);
}

接收数据

@Test
public void testReceive() {
    Object o = rabbitTemplate.receiveAndConvert(RabbitMQConfig.QUEUE_NAME);
    System.out.println(o.getClass());
    System.out.println(o);
}

监听队列

在消费者应用中,通常需要监听队列,以获取消息。在 Spring Boot 中如何要监听队列,需要在主启动类上开启 @EnableRabbit,然后在指定方法上使用 @RabbitListener 进行监听

@Service
public class UserServiceImpl {
    @RabbitListener(queues = "boot_queue")
    public void receive(User u) {
        System.out.println("收到消息:" + u);
    }

    @RabbitListener(queues = "boot_queue")
    public void receiveMsg(Message message){
        System.out.println(message.getBody());
        System.out.println(message.getMessageProperties());
    }
}

AmqpAdmin

AmqpAdmin 可以帮助我们创建和删除 Queue,Exchange,Binding规则

@Test
public void testAmqpAdmin() {
    // 创建交换机
    amqpAdmin.declareExchange(new DirectExchange("amqpAdmin.exchange"));
    // 创建队列
    amqpAdmin.declareQueue(new Queue("amqpAdmin.queue", true));
    // 绑定关系
    amqpAdmin.declareBinding(new Binding("amqpAdmin.queue",
            Binding.DestinationType.QUEUE,
            "amqpAdmin.exchange",
            "amqp.hello", null));
}

4、SpringBoot与安全


Spring Security

在 SpringBoot 中常用的安全框架有 Spring SecurityShiro

Spring Security 是针对 Spring 项目的安全框架,也是 Spring Boot 底层安全模块默认的技术选型。它可以实现强大的 web 安全控制。对于安全控制,只需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

登录 & 认证 & 授权 & 注销 & 记住我

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("VIP1")
                .antMatchers("/level2/**").hasRole("VIP2")
                .antMatchers("/level3/**").hasRole("VIP3");
        // 开启自动配置的登录功能,/login来到登录页,登录失败重定向到/login?error,支持更多详细定制
        http.formLogin().usernameParameter("uname").passwordParameter("upwd").loginPage("/userLogin");
        // 开启自动配置的注销功能,/logout发起注销请求,并清空session,注销成功默认会返回 /login?logout页面
        http.logout().logoutSuccessUrl("/userLogin");
        // 开启记住我功能(登录成功之后,将cookie发送给浏览器进行保存,下次访问页面的时候带上这个cookie,只要通过检查就会自动登录,点击注销也会删除该cookie)
        http.rememberMe();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 定义认证规则
        // 从spring security 5.X开始,需要使用密码编码器,也就是需要对你的明文密码进行加密
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("VIP1", "VIP2", "VIP3")
                .and()
                .withUser("xiaobai").password(new BCryptPasswordEncoder().encode("666666")).roles("VIP1", "VIP2");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/656866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云最新社招面经出炉(面试原题+答案解析)

前言 大家好&#xff0c;今天给大家分享一篇腾讯云的面经&#xff0c;以下是面试题和答案。加油&#xff0c;一起卷。 聊聊项目&#xff0c;好的设计&#xff0c;好的代码 谈谈什么是零拷贝&#xff1f; 一共有几种 IO 模型&#xff1f;NIO 和多路复用的区别&#xff1f; F…

Get请求参数过多导致请求失败

1. 问题 系统正常使用没有问题&#xff0c;但是有极个别的用户出现系统异常&#xff0c;通过日志发现某个get请求&#xff0c;传入的城市list太多&#xff0c;就会抛出异常 java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map。 2. 排查过程 …

Elasticsearch 安装(Linux)

ElasticSearch 概念安装安装es 后台启动 & 停止启动nohup 记录pid 停止 其他启动错误max number of threads内存不足 Cannot allocate memoryfailed to obtain node locks 概念 ES是一款分布式全文搜索引擎&#xff0c;基于Lucene&#xff0c;进行了二次封装&#xff0c;更…

ElasticSearch笔记02-ElasticSearch入门

ElasticSearch安装 下载软件 ElasticSearch的官网&#xff0c;视频教程里用的Version是7.8.0&#xff0c;所以&#xff0c;我们也是用7.8.0版本的ElasticSearch。 下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases#elasticsearch&#xff0c;然后搜索…

车载诊断协议 —— 诊断服务Service 11

我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 在最艰难的时候,自己就别去幻想太远的将来,只要鼓励自己过好今天就行了! 这世间有太多的猝不及防,有些东西根本不配占有自己的情绪,人生就是一场体验,…

一种栅格数据的空间聚类方法(ACA-Cluster)

本文结合实例详细讲解了如何使用Python对栅格数据进行空间聚类&#xff0c;关注公众号GeodataAnalysis&#xff0c;回复20230616获取示例数据和代码&#xff0c;包含整体的写作思路&#xff0c;上手运行一下代码更容易弄懂。 带有非空间属性的空间数据聚类分析是空间聚类研究的…

English Learning - L3 作业打卡 Lesson6 Day43 2023.6.16 周五

English Learning - L3 作业打卡 Lesson6 Day43 2023.6.16 周五 引言&#x1f349;句1: Thousands of lanterns slowly drift out to sea guiding the dead on their return journey to the other world.成分划分弱读连读爆破语调 &#x1f349;句2: This is a moving spectacl…

炎炎夏日!东南亚LazadaShopee泳衣品类热销榜单来袭

6月商机无限&#xff0c;趁热打铁&#xff01;3大节庆即将来袭。小编特为卖家整理了6月最强爆单选品指南&#xff0c;揭秘东南亚泳衣市场。赶紧一睹为快吧&#xff01; 炎炎夏日&#xff0c;马上即将迎来暑假&#xff0c;海边游玩肯定成了小朋友即家长们的首选之地&#xff0c…

js将json字符串转换为json对象的方法解析

将json字符串转换为json对象的方法。在数据传输过程中&#xff0c;json是以文本&#xff0c;即字符串的形式传递的&#xff0c;而JS操作的是JSON对象&#xff0c;所以&#xff0c;JSON对象和JSON字符串之间的相互转换是关键。 例如&#xff1a; JSON字符串: var str1 ‘{ “n…

MIT 6.S081 Lab Three

MIT 6.S081 Lab Three 引言page tablesPrint a page table (easy)代码解析 A kernel page table per process (hard)代码解析 Simplify copyin/copyinstr&#xff08;hard&#xff09;代码解析 可选的挑战练习 引言 本文为 MIT 6.S081 2020 操作系统 实验三解析。 MIT 6.S081…

shardingsphere第一课-前置课程-Mysql的集群搭建以及多数据源管理

一.Mysql的集群搭建(使用docker搭建省事) 1、关闭防火墙&#xff0c;重启docker** #关闭docker systemctl stop docker #关闭防火墙 systemctl stop firewalld #启动docker systemctl start docker2.1、准备主服务器 解释&#xff1a; 端口号是3306&#xff0c; 指定宿主机配…

FPGA基础知识-时序和延迟

目录 学习目标&#xff1a; 学习内容&#xff1a; 1.延迟模型的类型 2.路径延迟建模 3.时序检查 4.延迟反标注 学习时间&#xff1a; 学习总结 学习目标&#xff1a; 提示&#xff1a;这里可以添加学习目标 鉴别Verilog 仿真中用到的延迟模型的类型&#xff0c;分布延…

YOLOv5改进系列(10)——替换主干网络之GhostNet

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

项目中还不会用SpringSecurity,看这篇文章就够了

安全管理是Java应用开发中无法避免的问题&#xff0c;随着Spring Boot和微服务的流行&#xff0c;Spring Security受到越来越多Java开发者的重视&#xff0c;究其原因,还是沾了微服务的光。作为Spring家族中的一员,其在和Spring家族中的其他产品如SpringBoot、Spring Cloud等进…

client-go的Indexer三部曲之而:性能测试

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《client-go的Indexer》系列的第二篇&#xff0c;在前文咱们通过实例掌握了client-go的Indexer的基本功能&#xff0c;本篇咱们尝试对下面这…

6.pixi.js编写的塔防游戏(类似保卫萝卜)-游戏资源打包逻辑

游戏说明 一个用pixi.js编写的h5塔防游戏&#xff0c;可以用electron打包为exe&#xff0c;支持移动端&#xff0c;也可以用webview控件打包为app在移动端使用 环境说明 cnpm6.2.0 npm6.14.13 node12.22.7 npminstall3.28.0 yarn1.22.10 npm config list electron_mirr…

配置legacyUnhandledExceptionPolicy属性防止处理异常后程序崩溃退出(C#)

这是这篇文章后面遗留的问题&#xff1a; winform中的全局异常信息_winform全局异常捕获_zxy2847225301的博客-CSDN博客 就是线程抛出异常后&#xff0c;被AppDomain.CurrentDomain.UnhandledException注册的事件捕获后&#xff0c;程序依旧崩溃退出。 解决方案&#xff1a;在…

架构-嵌入式模块

章节架构 约三分&#xff0c;主要为选择题 #mermaid-svg-z6RGCDSEQT5AhE1p {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-z6RGCDSEQT5AhE1p .error-icon{fill:#552222;}#mermaid-svg-z6RGCDSEQT5AhE1p .error-text…

【产品经理】从用户体验五要素出发,谈如何设计与体验一款产品

用户体验五要素是产品人必备的知识技能&#xff0c;由于网络碎片化的了解&#xff0c;往往容易造成其高深莫测&#xff0c;晦涩难懂的形象&#xff0c;进而对其束之高阁。但实质不过是一种产品分析与设计的方法论&#xff0c;正确姿势去了解它能帮助我们更好地理解一款产品和从…

移动端小于12px的字体处理方法

今天在按设计稿坐页面时&#xff0c;遇到了下图的情况 ​​​​​ 由于浏览器对字体最小为12px的限制&#xff0c;所以我查阅资料后尝试使用transform:scale来处理 代码如下&#xff1a; <div class"icon"><span class"iconfont icon-a-xuexi3 icon-…