SpringBoot自定义注解

news2024/10/5 13:02:50

SpringBoot自定义注解

1. 创建一个注解的基本元素

修饰符:访问修饰符必须为public,不写默认为pubic;
关键字:关键字为@interface;
注解名称:注解名称为自定义注解的名称
注解类型元素:注解类型元素是注解中内容,根据需要标志参数,例如上面的注解的value;
规则总结如下:

  1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
  2. 参数成员只能用public或默认(default)这两个访问权修饰
  3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
  4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
  5. 注解也可以没有定义成员, 不过这样注解就没啥用了

2. 元注解(@Target、@Retention、@Inherited、@Documented)

我们上面的创建的注解XinLinLog上面还有几个注解(@Target、@Retention、@Inherited、@Documented),这四个注解就是元注解,元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的元注解类型,它们被用来提供对其它 注解类型作标志操作(可以理解为最小的注解,基础注解)
@Target:用于描述注解的使用范围,该注解可以使用在什么地方
在这里插入图片描述
@Retention:表明该注解的生命周期
在这里插入图片描述
@Inherited:是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
@Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化

3. 如何自定义注解

注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测这些标记从而执行一些特殊操作,因此自定义注解使用的基本流程为:

  1. 第一步,定义注解 – 相当于定义标记
  2. 第二步,配置注解 – 把标记打在需要用到的程序代码中
  3. 第三步,解析注解 – 在编译期或运行时检测到标记,并进行特殊操作

4. 示例代码

创建一个maven项目,导入下列依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.7.0</version>
    </parent>
    <groupId>com.young</groupId>
    <artifactId>Annotation02</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
    </dependencies>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>

目录结构如下:
在这里插入图片描述
application.yml:

server:
  port: 8089

Annotation02App.class

package com.young;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Annotation02App {
    public static void main(String[] args) {
        SpringApplication.run(Annotation02App.class,args);
    }
}

4.1 创建一个ArgIntercept注解,用于类、接口、枚举的方法

package com.young.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD,ElementType.TYPE})//注解范围为类,接口,枚举的方法上
@Retention(RetentionPolicy.RUNTIME) //被虚拟机保存,可用反射机制读取
public @interface ArgIntercept {
    boolean required()default true;
}

创建Person.class

package com.young.entity;

import com.young.annotation.ArgIntercept;

public class Person {
    private String name;
    private String mobile;
    private Integer age;
    private String sex;
    public Person(){}
    public Person(String name,String mobile,Integer age,String sex){
        this.name=name;
        this.mobile=mobile;
        this.age=age;
        this.sex=sex;
    }
    public void setName(String name){
        this.name=name;
    }
    public void setMobile(String mobile){
        this.mobile=mobile;
    }
    public void setAge(Integer age){
        this.age=age;
    }
    public void setSex(String sex){
        this.sex=sex;
    }
    public String getName(){
        return this.name;
    }
    @ArgIntercept
    public String getMobile(){
        return this.mobile;
    }
    @ArgIntercept(required = false)
    public Integer getAge(){
        return this.age;
    }
    public String getSex(){
        return this.sex;
    }
}

创建DemoController.java,用于测试

package com.young.controller;

import com.young.annotation.ArgIntercept;
import com.young.entity.Person;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;

@RestController
public class DemoController {
    @GetMapping("/testMethod1")
    public String testMethod1()throws Exception{
        Person person=new Person("cxy","134****8118",22,"男");
        Method[] methods = Person.class.getMethods();
        String res="";
        for (Method m : methods) {
            String methodName=m.getName();
            if (!methodName.contains("get")||methodName.equals("getClass")){
                continue;
            }
            ArgIntercept declaredAnnotation = m.getDeclaredAnnotation(ArgIntercept.class);
            //当ArgIntercept注解值为true时,跳过
            if (declaredAnnotation!=null&&declaredAnnotation.required()){
                continue;
            }
            //只有没有ArgIntercept或者ArgIntercept的required为false时,才拼接字符串
            String temp=String.valueOf(m.invoke(person))+" ";
            res=res+temp;
        }
        return res;
    }
}

启动项目,访问http://localhost:8089/testMethod01
在这里插入图片描述

4.2 创建一个ClassIntercept,用于对象属性注解

创建一个User.java

package com.young.entity;

import lombok.Data;

import java.time.LocalDateTime;
import java.util.Date;

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private LocalDateTime loginTime;
}

创建LoginIntercept注解

package com.young.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginIntercept {
    boolean required()default true;
}

ControllerConfiguration

package com.young.config;

import com.young.annotation.LoginIntercept;
import com.young.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class ControllerConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
    }
}

LoginInterceptor.java

package com.young.interceptor;

import com.young.annotation.LoginIntercept;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throws Exception{
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod method=(HandlerMethod) handler;
        //判断是否有添加LoginIntercept注解
        LoginIntercept loginIntercept=method.getMethodAnnotation(LoginIntercept.class);
        if (loginIntercept==null||!loginIntercept.required()){
            //没有注解或注解的required为false,直接放行
           return true;
        }
        //鉴权
        String token = request.getHeader("token");
        if (token==null||!"token".equals(token)){
            //校验失败
            return false;
        }
        return true;
    }
}

修改DemoController.java

package com.young.controller;

import com.young.annotation.ArgIntercept;
import com.young.annotation.LoginIntercept;
import com.young.entity.Person;
import com.young.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

@RestController
public class DemoController {
    @GetMapping("/testMethod1")
    public String testMethod1()throws Exception{
        Person person=new Person("cxy","134****8118",22,"男");
        Method[] methods = Person.class.getMethods();
        String res="";
        for (Method m : methods) {
            String methodName=m.getName();
            if (!methodName.contains("get")||methodName.equals("getClass")){
                continue;
            }
            ArgIntercept declaredAnnotation = m.getDeclaredAnnotation(ArgIntercept.class);
            //当ArgIntercept注解值为true时,跳过
            if (declaredAnnotation!=null&&declaredAnnotation.required()){
                continue;
            }
            //只有没有ArgIntercept或者ArgIntercept的required为false时,才拼接字符串
            String temp=String.valueOf(m.invoke(person))+" ";
            res=res+temp;
        }
        return res;
    }
    @GetMapping("/testMethod2")
    @LoginIntercept(required = false)
    public User testMethod2(){
        User user=new User();
        user.setUsername("not require login");
        user.setId(1);
        user.setPassword("123456");
        user.setLoginTime(LocalDateTime.now());
        return user;
    }
    @GetMapping("/testMethod3")
    @LoginIntercept
    public User testMethod3(){
        User user=new User();
        user.setUsername("require login");
        user.setId(2);
        user.setPassword("1234567");
        user.setLoginTime(LocalDateTime.now());
        return user;
    }
}

运行项目
测试testMethod2接口,放行成功,
不带token测试testMethod3
在这里插入图片描述
携带错误token访问testMethod3
在这里插入图片描述
携带正确token
在这里插入图片描述

4.3 创建一个RoleIntercept,用于权限校验

在数据库中创建一个m_user表,结构如下:
在这里插入图片描述
数据如下:
在这里插入图片描述
修改pom.xml,添加下面两个依赖

 <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

修改User.java

package com.young.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.Date;

@Data
@TableName(value = "m_user")
public class User {
    @TableId
    private Integer id;
    private String username;
    private String password;
    private LocalDateTime loginTime;
    private String role;
}

UserVO.java

package com.young.vo;

import lombok.Data;

import java.io.Serializable;

@Data
public class UserVO implements Serializable {
    private String username;
    private String password;
}

修改application.yml

server:
  port: 8089
spring:
  datasource:
    username: root
    password: 3fa4d180
    driver: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC

UserMapper.java

package com.young.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.young.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

UserService.java

package com.young.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.young.entity.User;
import com.young.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public User login(String username,String password){
        LambdaQueryWrapper<User>queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername,username)
                .eq(User::getPassword,password);
        return userMapper.selectOne(queryWrapper);
    }
}

创建AdminIntercept注解

package com.young.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminIntercept {
    boolean required()default true;
}

创建AdminInterceptor.java拦截器

package com.young.interceptor;

import com.young.annotation.AdminIntercept;
import com.young.entity.User;
import com.young.service.UserService;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AdminInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod method=(HandlerMethod) handler;
        //判断是否有adminIntercept注解
        AdminIntercept adminIntercept = method.getMethodAnnotation(AdminIntercept.class);
        if (adminIntercept==null||!adminIntercept.required()){
            //没有注解或注解的required为false,直接放行
            return true;
        }
        //获取会话中的用户
        User user=(User)request.getSession().getAttribute("user");
        //判断用户权限
        if (user==null){
            System.out.println("用户未登录");
            return false;
        }
        if(user.getRole()==null||!"admin".equals(user.getRole())){
            System.out.println("用户没有admin权限");
            return false;
        }
        return true;
    }
}

修改ControllerConfiguration

package com.young.config;

import com.young.annotation.LoginIntercept;
import com.young.interceptor.AdminInterceptor;
import com.young.interceptor.LoginInterceptor;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class ControllerConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/user/**");
    }
}

创建UserController.java

package com.young.controller;

import com.young.annotation.AdminIntercept;
import com.young.entity.User;
import com.young.service.UserService;
import com.young.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public String login(@RequestBody UserVO userVO, HttpServletRequest request){
        User user = userService.login(userVO.getUsername(), userVO.getPassword());
        if (user!=null){
            HttpSession session = request.getSession();
            session.setAttribute("user",user);
            return "登录成功";
        }
        return "登录失败";
    }
    @GetMapping("/info")
    @AdminIntercept
    public User info(HttpServletRequest request){
        User user = (User)request.getSession().getAttribute("user");
        return user;
    }
}

运行项目,测试
未登录测试/user/info
在这里插入图片描述
在这里插入图片描述
登录不是admin的用户
在这里插入图片描述
访问/user/info
在这里插入图片描述
在这里插入图片描述
登录有admin权限的用户
在这里插入图片描述
访问/user/info
在这里插入图片描述

4.4 使用自定义注解,整合Redis实现限流

pom.xml添加下列依赖

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

修改application.yml

server:
  port: 8089
spring:
  datasource:
    username: root
    password: 3fa4d180
    driver: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
  redis:
    host: 127.0.0.1
    port: 6379
    jedis:
      max-idle: 8
      pool:
        max-active: 8
        min-idle: 0
        max-wait: 3000
    timeout: 5000

LimitIntercept.java

package com.young.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitIntercept {
    boolean required()default true;
    
    //设置默认5秒内最多点击3次 
    
    int maxCount()default  3; //默认最多点击3次
    int waitTime()default 5; //默认时长5秒
}

修改ControllerConfiguration.java

package com.young.config;

import com.young.annotation.LoginIntercept;
import com.young.interceptor.AdminInterceptor;
import com.young.interceptor.LimitInterceptor;
import com.young.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class ControllerConfiguration implements WebMvcConfigurer {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/user/**");
        registry.addInterceptor(new LimitInterceptor(stringRedisTemplate));
    }
}

RedisConstant.java

package com.young.constants;

public class RedisConstant {
    public final static String LIMIT_KEY="limit";
}

添加LimitInterceptor.java

package com.young.interceptor;

import com.young.annotation.LimitIntercept;
import com.young.constants.RedisConstant;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.invoke.MethodHandle;
import java.util.concurrent.TimeUnit;

public class LimitInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;
    public LimitInterceptor(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate=stringRedisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
        if (!(handler instanceof HandlerMethod)){
            return false;
        }
        HandlerMethod method=(HandlerMethod) handler;
        LimitIntercept limitIntercept = ((HandlerMethod) handler).getMethodAnnotation(LimitIntercept.class);
        if (limitIntercept==null||!limitIntercept.required()){
            return true;
        }
        int maxCount=limitIntercept.maxCount();
        int waitTime=limitIntercept.waitTime();
        //当未过期时
        if (stringRedisTemplate.hasKey(RedisConstant.LIMIT_KEY)){
            Integer count = Integer.valueOf(stringRedisTemplate.opsForValue().get(RedisConstant.LIMIT_KEY));
            if (count<=0){
                System.out.println("限流了=============");
                return false;
            }
            //减少次数
            stringRedisTemplate.opsForValue().decrement(RedisConstant.LIMIT_KEY);
            return true;
        }
        //设置到redis中
        stringRedisTemplate.opsForValue().set(RedisConstant.LIMIT_KEY,maxCount+"",waitTime, TimeUnit.SECONDS);
        return true;
    }
}

添加LimitController.java

package com.young.controller;

import com.young.annotation.LimitIntercept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/limit")
public class LimitController {
    @GetMapping("/test1")
    @LimitIntercept
    public String test1(){
        return "test1";
    }
    @GetMapping("/test2")
    @LimitIntercept(maxCount = 1,waitTime = 10)
    public String test2(){
        return "test2";
    }
}

运行,测试
5秒内访问/limit/test1超过3次,开始限流
在这里插入图片描述
在这里插入图片描述
10秒内访问/limit/test2超过1次,开始限流
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述#### 5. 参考文章
SpringBoot自定义注解
springboot项目中自定义注解的使用总结、java自定义注解实战(常用注解DEMO)

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

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

相关文章

Python---闭包,装饰器,设计模式之工厂模式

1. 闭包定义 定义双层嵌套函数&#xff0c; 内层函数可以访问外层函数的变量 将内存函数作为外层函数的返回&#xff0c;此内层函数就是闭包函数 2. 闭包的好处和缺点 优点&#xff1a;不定义全局变量&#xff0c;也可以让函数持续访问和修改一个外部变量 优点&#xff1a…

【数据分析之道-Matplotlib(一)】Matplotlib Pyplot

系列文章目录 作者&#xff1a;i阿极 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#x1f4c1;评论&#x1f4d2;关注…

Jetson nano之ROS入门- -ROS集成开发搭建与ROS通信学习笔记

文章目录 一、ROS集成开发环境搭建二、ROS通信机制和命令1. 话题通信2. 服务通信3. 参数服务4. ROS常用命令 三、Python实现ROS通信- - 控制和读取小乌龟状态1. 配置package.xml文件2. 配置CMakeLists.txt文件3. 编写Python代码4. 配置launch文件 总结 一、ROS集成开发环境搭建…

反射、枚举

反射的定义&#xff1a;Java的反射机制是在运行状态中&#xff0c;都能对任意的类拿到这个类的所有属性&#xff0c;从而对其进行相应的修改&#xff1b;用途&#xff1a;在日常第三方应用开发中&#xff0c;可以通过反射机制来获取某个类的私有成员变量或是方法&#xff1b;主…

研究生,但是一直摆烂——想办法解决

原因剖析 孤独因为没有朋友&#xff0c; 之前自己思维误区&#xff0c;总觉得好好学习好好锻炼变得优秀就会有朋友&#xff0c;其实不是这个样子的&#xff0c;即使自己一直内卷&#xff0c;但还是很孤独。 现在重新反思自己。 没有朋友因为&#xff1a; 1 外貌 2 聊天的情商…

Which programming language do you choose

NO.1&#xff1a;JavaScript JavaScript&#xff0c;简称JS语言&#xff0c;是一种具有函数优先的轻量级&#xff0c;解释型或即时编译型的高级编程语言。虽然它是作为开发Web页面的脚本语言而出名的&#xff0c;但是它也被用到了很多非浏览器环境中&#xff0c;JavaScript 基…

老王的自动驾驶决策和规划第一章

文章目录 自动驾驶决策规划算法序章第一章&#xff08;1&#xff09; 细说五次多项式&#xff08;2&#xff09; 凸优化与非凸优化(3) 直角坐标与自然坐标转换(上, 下) 自动驾驶决策规划算法 序章 课程链接&#xff1a;序章 第一章 &#xff08;1&#xff09; 细说五次多项…

关于SpringBoot整合Websocket实现简易对话聊天窗

前言 官网链接&#xff1a;Websocket Websocket 是什么&#xff1f;它可以将两个独立的浏览器窗口作为通信的两端。 这种形式的通信与传统的 HTTP、TCP 所不同。传统的 HTTP 请求—响应协议是无法实现实时通信的&#xff0c;也就是说&#xff0c;只能由客户端向服务端发送请求…

前端Web开发,HTML,css,JavaScript

web浏览器响应流程&#xff0c;及技术不同的浏览器&#xff0c;内核不同&#xff0c;对于相同的前端代码解析的效果会存在差异web标准&#xff0c;三个组成部分 HTML&#xff1a;负责网页的结构&#xff08;页面元素和内容&#xff09;CSS&#xff1a;负责页面的表现&#xff0…

VC调试方法大全

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

代码随想录算法训练营第四十八天| 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

文章目录 198.打家劫舍213.打家劫舍II337.打家劫舍III 198.打家劫舍 题目链接&#xff1a;代码随想录 解题思路&#xff1a; 1.dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i] 只是考虑&#xff0c;不一定偷 2.递推…

【STM32CubeMX】F103外部中断

前言 本文记录下我学习STM32CubeMX时的流程&#xff0c;方便以后回忆。系统板是基于STM32F103C6T6。本章记录外部中断。 步骤 该实验步骤以&#xff0c;配置PA1为外部中断下降沿触发事件&#xff0c;在触发事件后点亮板载PC13LED灯 时钟配置和生成文件配置之类的&#xff0c;其…

3.6 cache存储器

学习步骤&#xff1a; 我会采取以下几个步骤来学习Cache存储器&#xff1a; 确定学习目标&#xff1a;Cache存储器作为一种高速缓存存储器&#xff0c;通常用于提高计算机系统的运行效率。因此&#xff0c;我需要明确学习Cache存储器的目的&#xff0c;包括了解其原理、结构和…

No.054<软考>《(高项)备考大全》【冲刺8】《软考之 119个工具 (6)》

《软考之 119个工具 &#xff08;6&#xff09;》 99.应急应对策略:100.风险在评估:101.风险审计:102.偏差和趋势分析:103.技术绩效测量:104.自制或外购分析:105.市场调研:106.投标人会议:107.建议书评价技术:108.独立核算:109.广告:110.采购谈判:111.合同变更控制系统:112.采购…

定位图像坐标系和角度误区

坐标系和角度的常见误区 在学习halcon的时候.常常看文档的时候,会有一些地方比较疑感有些常用的地方有细微的差距,一不留意。就会导致计算的数据出错 常见的误区就在坐标系和角度 halcon的帮助情况 存在Px,Py 大家不要被搞混乱 Px -> Row Py-> Column 很多人定位项…

C语言——字符串及字符函数的介绍

C语言——字符串及字符函数的介绍 一、字符函数1.strlen1.1strlen的使用1.2strlen的三种模拟实现1.2.1计数器实现strlen函数1.2.2递归方法实现strlen函数1.2.3指针方法实现strlen函数 1.3 注意事项 2.strcpy2.1strcpy使用2.2strcpy的模拟实现2.3strcpy的注意事项 3.strcat3.1st…

【小样本分割 2022 ECCV】SSP

文章目录 【小样本分割 2022 ECCV】SSP摘要1. 介绍2. 相关工作3. 自支持小样本语义分割3.1 动机3.2 自支持原型-SSM3.3 自适应自支持背景原型-ASBP3.4 自支持匹配-SSL 3. 代码 【小样本分割 2022 ECCV】SSP 论文题目&#xff1a;Self-Support Few-Shot Semantic Segmentation 中…

SpringCloud 微服务系列——【Gateway、Config组件使用】

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

第二十三章 材质

3D模型主要是通过材质&#xff08;Material&#xff09;和贴图&#xff08;Texture&#xff09;来表现其精美的外表&#xff0c;说白了就是一张“画皮”而已。我们之前的DirectX课程中介绍过材质&#xff0c;它实际就是对光的反射率&#xff0c;这样简单的设置并不能展现3D模型…

Linux线程 概念、特点、线程间资源共享情况

1. 线程概念 线程是轻量级的进程&#xff1b;Linux中&#xff0c;线程本质上仍是进程。 进程是OS分配资源的最小单位&#xff0c;线程是OS调度的最小单位。 NPTL 当前Linux线程库为redHat开发的NPTL&#xff0c;查看本地线程库版本&#xff1a; getconf GNU_LIBPTHREAD_VE…