spring自定义注解及使用

news2024/9/22 7:41:18

文章目录

      • 简介
      • 一、定义注解
        • 1.注解示例
        • 2.元注解含义
          • (1)@Target
          • (2)@Retention
          • (3)@Documented
          • (4)@Inherited
          • (5)@Native
          • (6)@Repeatable
      • 二、使用注解
        • 1.aop切点使用注解
        • 2.拦截器获取注解
        • 3.class获取注解
        • 4.spring容器获取注解

简介

      在spring项目中使用注解,简化了代码量,减轻对业务代码的侵入性;对框架统一处理鉴权、日志等起到极大的作用,可以结合着拦截器、aop在请求调用前后添加额外处理。spring有内置的@Controller、@Service等注解,出于业务考虑,我们可以自定义想要的注解。

一、定义注解

      自定义注解类似于定义接口,但是需要指明注解的作用范围、生命周期等属性。

1.注解示例

      下面是一个简单的自定义注解示例,使用@interface修饰,定义了三个属性值,使用注解的时候可以给这些属性赋值。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LogAnnotation {

    String moduleName() default "";
    String operaName() default "";
    String operaType() default "";

}

2.元注解含义

      从jdk1.5开始,在包java.lang.annotation下提供了四种元注解:@Target、@Retention、@Documented、@Inherited,java1.8后,annotation包下新提供了两种元注解:@Native、@Repeatable。自定义注解的时候需要使用元注解修饰,来看下各个元注解的使用说明。

(1)@Target

      标识注解可以使用的范围,例如使用在方法、字段、构造方法上。看下源码:

//Target源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

//Target可配置的类型
public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

从源码中可以看出@Target只有一个属性value,属性类型为ElementType类型的数组,ElementType各个枚举值的作用范围如下:

①ElementType.TYPE:允许被修饰的注解作用在:类、接口、枚举上;

②ElementType.FIELD:允许被修饰的注解作用在:属性字段上;

③ElementType.METHOD:允许被修饰的注解作用在:方法上;

④ElementType.PARAMETER:允许被修饰的注解作用在:方法参数上;

⑤ElementType.CONSTRUCTOR:允许被修饰的注解作用在:构造器上;

⑥ElementType.LOCAL_VARIABLE:允许被修饰的注解作用在:本地局部变量上;

⑦ElementType.ANNOTATION_TYPE:允许被修饰的注解作用在:注解上;

⑧ElementType.PACKAGE:允许被修饰的注解作用在:包名上;

⑨ElementType.TYPE_PARAMETER:允许被修饰的注解作用在:类型参数上,jdk1.8提供;

//ElementType.TYPE_PARAMETER示例
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
}

//泛型声明
public class TypeParameterClass<@TypeParameterAnnotation T> {
    public <@TypeParameterAnnotation P> T too(T t){
        return t;
    }
}

⑩ElementType.TYPE_USE:允许被修饰的注解作用在:任何语句中(声明语句、泛型、强制转化),jdk1.8提供。

(2)@Retention

      标识注解的生命周期,来看下源码:

//Retention源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {

    RetentionPolicy value();
}

//RetentionPolicy源码
public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

从源码可以看出@Retention只有一个属性value,属性类型为RetentionPolicy,看下RetentionPolicy枚举值的生命周期:

①RetentionPolicy.SOURCE:编译阶段丢弃,编译之后注解没有任何作用,不会写入字节码文件中。例如@Override、@SuppressWarnings、@Deprecated都属于这类注解;

②RetentionPolicy.CLASS:类加载阶段丢弃,类加载进jvm后没有任何作用,在字节码文件处理中有用。注解默认使用这种方式;

③RetentionPolicy.RUNTIME:始终不会丢弃,程序运行期也保留此注解,自定义注解通常使用这种方式,因此可以通过反射获取到注解配置的属性值。

(3)@Documented

      标识注解是否在javadoc文档中显示,看下源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

当定义的注解中加入了@Documented元注解,则生成的javadoc文档中包含注解,来看一个例子:

@Documented
public @interface DocumentAnnotation {
    String name() default "张三";
    int age() default 18;
}

public class DocumentTest {

    @DocumentAnnotation(name="lisi",age = 30)
    public void test(){

    }
}

此时生成javadoc文件,生成的方式为:
在这里插入图片描述
在这里插入图片描述
​ 文档中包含注解信息:
在这里插入图片描述

自定义注解DocumentAnnotation去掉@Documented,javadoc文档中不包含注解:
在这里插入图片描述

(4)@Inherited

      标识注解是否能继承到子类,看下源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

使用@Inherited修饰的注解,在class使用它时,class的子类能够继承此注解,类似于InheritableThreadLocal,父子类能够共享资源。

(5)@Native

      标识字段是否可以被本地代码引用,看下源码:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}

此注解作用在字段上,生命周期为编译阶段丢弃。

(6)@Repeatable

      标识可以重复使用注解,看下源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}

作用在注解上,只有一个属性value,属性的类型继承了Annotation,之所以继承Annotation是因为Annotation是所有注解的父接口,看下关系图:
在这里插入图片描述

来看一个demo:

//定义注解
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}

//定义注解,Repeatable声明RepeatableAnnotations
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    String name();
    int age();
}

//测试类
public class RepeatableDemo {
    @RepeatableAnnotation(name="张三",age=18)
    @RepeatableAnnotation(name="李四",age=30)
    private String userMessage;

    public static void main(String[] args) throws NoSuchFieldException {
        Field declaredField = RepeatableDemo.class.getDeclaredField("userMessage");
        Annotation[] annotations = declaredField.getDeclaredAnnotations();
        System.out.println("注解的数量:"+annotations.length);
        System.out.println("注解内容:"+Arrays.toString(annotations));
    }
}

测试类输出结果:

注解的数量:1
注解内容:[@com.RepeatableAnnotations(value=[@com.RepeatableAnnotation(name=张三, age=18), @com.RepeatableAnnotation(name=李四, age=30)])]

定义一个可重复的注解,需要使用@Repeatable来声明,@Repeatable的值为此原注解数组形式的新注解。从测试类可以看出最终注解的数量还是1个,是使用@Repeatable值的数组形式接收,每个值为原注解类型。

      在spring中ComponentScan定义bean的扫描范围,就是这样使用的,看下它的源码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
    ComponentScan[] value();
}

//使用
@ComponentScan(basePackages = {"com.xxx1","com.xxx2"})

使用@Repeatable注意事项:

①原注解的@Target作用范围要比@Repeatable值的范围大或者相同,否则编译错误,例如:

//比RepeatableAnnotation多了ElementType.METHOD
@Target(value={ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}

@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    String name();
    int age();
}

在这里插入图片描述

②原注解的@Retention生命周期要比@Repeatable值的小或者相同,否则编译错误,生命周期大小:SOURCE <
CLASS < RUNTIME。例如:

//定义的CLASS比RUNTIME要小
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}

@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    String name();
    int age();
}

在这里插入图片描述

二、使用注解

      定义注解就是为了方便系统开发,现在来看一些使用场景。

1.aop切点使用注解

      自定义注解结合着aop来使用的场景很多,例如日志的收集就可以使用。

①定义注解:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    //模块名称(枚举类)
    ModuleNameEnum moduleName() default ModuleNameEnum.UNKNOWN;

    //操作对象
    String operaName() default "";

    //操作类型(枚举类)
    OperaTypeEnum operaType() default OperaTypeEnum.UNKNOWN;
}

②定义aop切面类:

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    XxxLogService xxxLogService;

    //切点:使用LogAnnotation注解标识的方法都进行切入,也可以使用通配符配置具体要切入的方法名
    @Pointcut("@annotation(com.xxx.aop.LogAnnotation)")
    public void pointCut(){

    }

    //环绕通知
    @Around("pointCut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object jsonResult = joinPoint.proceed(); //执行方法
        try {
            //获取请求签名
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            //获取切入点所在的方法
            Method method = signature.getMethod();
            //获取注解值
            LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
            //获取属性
            String moduleName = annotation.moduleName().getValue();
            String operaName = annotation.operaName();
            String operaType = annotation.operaType().getValue();

            XxxLog xxxLog = new XxxLog();
            xxxLog.setModuleName(moduleName);
            xxxLog.setOperaName(operaName);
            xxxLog.setOperaType(operaType);
            //添加日志
            xxxLogService.insertOne(xxxLog);
        } catch (Exception e){
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return jsonResult;
    }
}

③方法中添加注解

       当注解属性名为value时,赋值的时候可以省略属性名,其他名称的属性名需要使用xx=yy的方式指定。

    @LogAnnotation(moduleName= ModuleNameEnum.FeedBack,operaName="添加消息",operaType=OperaTypeEnum.Insert)
    public void insertOne(Integer id) {
        
    }

过程为:定义注解,定义属性值;创建切面类,使用@annotation来指定切点为自定义注解,环绕方法获取注解及属性值,把属性值保存到业务数据库中;业务代码中需要保存日志的方法加上注解,并设置属性值。

2.拦截器获取注解

      可以在拦截器中获取注解,在controller层响应前后做一些额外的处理或判断,例如判断权限、判断是否需要分页等。来看一个分页的demo:

①定义注解

@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface EnablePaging {
    int value() default 50;
}

②定义拦截器

public class PagingInterceptor implements HandlerInterceptor {
    //controller响应之前执行
    @Override
    public boolean preHandle(@NotNull HttpServletRequest request,
                             @NotNull HttpServletResponse response,
                             @NotNull Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //获取方法中的注解
        EnablePaging enablePaging = handlerMethod.getMethodAnnotation(EnablePaging.class);
        //不包含注解,直接通过
        if (enablePaging == null) {
            return true;
        }
        //包含注解,则获取注解中的值,值保存到TreadLocal线程变量中(此处使用RequestContextHolder.currentRequestAttributes().setAttribute保存),在执行sql查询时取出使用
        PagingContextData data = PagingContextData.getInstance(RequestAttributes.SCOPE_REQUEST, true);
        data.setEnabled(true);
        //把注解中配置的值设置进去
        if (enablePaging.value() > 0) {
            data.setDefaultPageSize(enablePaging.value());
        }
        return true;
    }
}

③注册拦截器

@Configuration
public class PagingHttpConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PagingInterceptor()).order(Ordered.HIGHEST_PRECEDENCE);
    }
}

④方法中添加注解

    @PostMapping("/datasource/xxxPage")
    @EnablePaging(20)
    public Object xxxPage(@RequestBody String json) {
        return xxxService.xxxPage(json);
    }

过程为:定义注解,定义属性值;创建拦截器,在拦截器的方法中获取注解及属性值,把属性值保存到线程变量ThreadLocal中;把拦截器注册到InterceptorRegistry中;业务代码中需要分页的接口方法加上注解,并设置属性值。

3.class获取注解

      通过class可以获取到注解,提供了从method方法、field字段等获取注解。获取class的方式有:

①对象.getClass()方法

Student stu = new Student();
Class clazz = stu.getClass();

②对象.class

Class clazz = Student.class;

③Class.forName(“xxx”),例如数据库驱动的获取

Class clazz = Class.forName("com.xxx.Student")

从method中获取注解示例:

//获取所有方法
Method[] methods = SampleClass.class.getMethods();
for(int i = 0;i < methods.length;i++) {
    //获取方法中的注解
    CustomAnnotaion annotation = methods[i].getAnnotation(CustomAnnotaion.class);
    if(null != annotation) {
        //输出属性值
        System.out.println(annotation.name());
    }
}

//获取指定方法
Method oneMethod = SampleClass.class.getDeclaredMethod("getSampleField");
//获取方法中的注解值
CustomAnnotaion annotation = oneMethod.getAnnotation(CustomAnnotaion.class);
System.out.println("annotation="+annotation.name());

从字段中获取注解示例:

//获取指定字段
Field declaredField = RepeatableDemo.class.getDeclaredField("userMessage");
//获取字段中的注解
Annotation[] annotations = declaredField.getDeclaredAnnotations();

4.spring容器获取注解

      在bean对象中加入注解,当spring容器加载完bean之后,可以从bean中获取到哪些方法加了指定的注解,从而拿到方法,对这些方法进行特殊处理。在xxl-job开源项目中就有使用,看下使用方式:

private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
        if (applicationContext == null) {
            return;
        }
        // init job handler from method
        //从程序上下文中获取到所有的bean名称集合
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        //遍历bean集合
        for (String beanDefinitionName : beanDefinitionNames) {
            //根据bean名称从程序上下文获取到此bean对象
            Object bean = applicationContext.getBean(beanDefinitionName);

            Map<Method, XxlJob> annotatedMethods = null; 
            try {
                //对Bean对象进行方法过滤,查询到方法被XxlJob注解修饰,是则放到annotatedMethods集合中
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        new MethodIntrospector.MetadataLookup<XxlJob>() {
                            @Override
                            public XxlJob inspect(Method method) {
                                //判断方法被XxlJob注解修饰才返回
                                return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
                            }
                        });
            } catch (Throwable ex) {
                logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
            }
            //当前遍历的bean没有被XxlJob注解修饰,则跳过处理
            if (annotatedMethods==null || annotatedMethods.isEmpty()) {
                continue;
            }

            //循环处理当前Bean下被XxlJob修饰的方法
            for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
                //执行的方法
                Method executeMethod = methodXxlJobEntry.getKey();
                //XxlJob注解类
                XxlJob xxlJob = methodXxlJobEntry.getValue();
                //注册此任务处理器
                registJobHandler(xxlJob, bean, executeMethod);
            }
        }
    }

从spring上下文applicationContext中获取到所有的bean名称集合,遍历bean名称集合,根据bean名称从程序上下文获取到此bean对象,对Bean对象进行方法过滤,查询到被XxlJob注解修饰的方法,放到map集合中,循环处理map中的记录,key为Method方法,value为XxlJob注解,这也是使用注解的场景。

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

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

相关文章

mqtt订阅地址有串了情况是怎么回事「艾思科技」

当mqtt订阅地址出现串连的情况时&#xff0c;可能有以下几种可能的原因&#xff1a; 订阅主题错误&#xff1a;订阅地址中指定的主题可能有误。在mqtt中&#xff0c;订阅地址通常由主题和客户端ID组成&#xff0c;如果主题名称或者客户端ID设置错误&#xff0c;可能会导致订阅地…

高完整性系统工程(四): Checking Formal Specifications

目录 1. Alloy Modelling Overview 2. 有限状态机&#xff08;Finite State Machines&#xff09; 3. 在Alloy中建模有限状态机&#xff08;Modelling FSMs in Alloy&#xff09; 4. 在Alloy中检查规格&#xff08;CHECKING SPECIFICATIONS IN ALLOY&#xff09; 4.1 Allo…

Apache Kafka - 灵活控制Kafka消费_动态开启/关闭监听实现

文章目录 概述思路Code扩展KafkaListenerEndpointRegistry 概述 在实际应用中&#xff0c;往往需要根据业务需求动态开启/关闭Kafka消费者监听。例如&#xff0c;在某些时间段内&#xff0c;可能需要暂停对某个Topic的消费&#xff0c;或者在某些条件下才开启对某个Topic的消费…

毫米波雷达信号处理中的通道间相干与非相干积累问题

说明 相干和非相干积累是雷达信号处理中的常用方法&#xff0c;这两个概念一般是用在多脉冲积累这个问题上&#xff1a;积累可以提高信号的SNR&#xff0c;从而提高检出概率。不过本文内容与脉冲积累无关&#xff0c;本文讨论的话题是将这两个概念(non-coherent combination、c…

HEVC变换编码介绍

介绍 ● 图像变换编码是指将以空间域中像素形式描述的图像转化至变换域&#xff0c;以变换系数的形式加以表示&#xff1b; ● 图像都含有较多平坦区域和内容变化缓慢的区域&#xff0c;适当的变换可使图像能量在空间域的分散分布转为在变换域的相对集中分布&#xff0c;实现…

推出“百亿生态”,拼多多“极限左移”

文 | 螳螂观察 作者 | 陈小江 任何一个成功穿越周期的企业&#xff0c;都有一个共同的特点——在发展顺利的时候&#xff0c;主动跳出“舒适圈”&#xff0c;进而跳出一般企业“起始、成长、成熟、衰退”的发展周期&#xff0c;为企业发展画出漂亮的S型增长曲线。 日前&…

DC1通关

环境自己百度装好。 我的一台kali&#xff0c;一台DC都是52网段 1.nmap 扫一扫52网段 确定是143&#xff0c;然后针对143进行扫描 80开放&#xff0c;进去。 老熟人了 Drupal&#xff0c;直接msf开打 试了几个&#xff0c;use2直接getshell了 看看权限 尝试SUID提权 进入sh…

零基础如何入门网络安全?

要学习网络安全&#xff0c;其实自学是有局限的&#xff0c;没有设备和网络环境&#xff0c;除了web渗透&#xff0c;其他很多方面&#xff0c;只能在培训班实战环境里才合适&#xff0c;比如路由交换技术、安全设备、学会怎么架构和配置一个企业网络安全只架构。 还要学习系统…

chatgpt赋能Python-python中导入numpy

介绍 在Python编程领域中&#xff0c;NumPy是一个非常常用的库&#xff0c;它提供了高性能的多维数组对象和许多用于操作这些数组的函数。在科学计算、数据分析和机器学习等领域中&#xff0c;NumPy是必不可少的工具。 本文将详细介绍如何在Python中导入NumPy库&#xff0c;并…

opencv初步了解

https://www.bilibili.com/video/BV1PV411774y?p2&vd_sourcee7d12c9f66ab8294c87125a95510dac9 这里写目录标题 下载计算机眼中的图像视频gray cv2.cvColor(frame, cv.COLOR_BGR2GRAY) ROI边界填充数值计算图像融合 下载 pip install input cv2 cv2.__version__下载一些…

io之io模型

写在前面 本文一起看下常见的io模型。 1&#xff1a;基础知识 同步异步&#xff0c;阻塞阻塞&#xff0c;区别如下&#xff1a; 同步异步:描述的通信模式&#xff0c;即结果如果是主动的获取则是同步&#xff0c;处理结果是被动的接收则是异步 阻塞非阻塞&#xff1a;描述的…

网页提交文件无法打开问题解决办法(以学习通为例)

时长会碰到这样的情况&#xff0c;日常实训课在机房写实训作业时&#xff0c;将未完成的作业先暂存先在学习通里&#xff0c;但后续在登陆学习通时发现未提交的附件打不开了&#xff0c;经过翻阅之前web的相关资料&#xff0c;总结出了这样的解决办法&#xff0c;供各位参考。 …

时间基础概念及Linux中的时间函数

时间基础概念及Linux中的时间函数 时间相关概念GMT 时间UTC 时间时区 Time Zone夏令时 DST本地时间 localtime Linux 系统中的时间时钟基础概念系统节拍数 jiffiesLinux系统查看时间及配置时区获取时间函数获取 当前时间 time()获取 当前时间&#xff08;微秒&#xff09; gett…

Qt Quick系列(5)—键盘输入

&#x1f680;作者&#xff1a;CAccept &#x1f382;专栏&#xff1a;Qt Quick 文章目录 前言代码示例单一按键组合按键 前言 本篇将介绍如何处理Qt Quick中的键盘输入。键盘输入在现代应用程序中扮演着重要角色&#xff0c;无论是快捷键还是文本输入都离不开它。通过本篇教…

(2021,FastGAN)用于高保真 few-shot 图像合成的更快、更稳定的 GAN 训练

Towards faster and stabilized gan training for high-fidelity few-shot image synthesis 公众号&#xff1a;EDPJ 目录 0. 摘要 1. 简介 2. 相关工作 3. 方法 3.1 跳跃层通道激励 3.2 自监督判别器 4. 实验 4.1 图像合成性能 4.2 更多分析与应用 5. 结论 参…

JL-8D/4X1 定时限电流继电器 用于输变电线路中 JOSEF约瑟

名称:定时限电流继电器型号:JL-8D/4X1触点容量250V5A功率消耗&#xff1c;5W返回系数0.90.97整定范围0.039.9A;0.130A辅助电源24220VDC/AC 系列型号&#xff1a; JL-8D/3X1定时限电流继电器&#xff1b;JL-8D/3X2定时限电流继电器&#xff1b; JL-8D/4X1定时限电流继电器&am…

【发电厂 HDLN-1-2Z 不带辅助电源电流继电器 导轨安装 JOSEF约瑟】

品牌&#xff1a;上海约瑟&#xff0c;名称&#xff1a;不带辅助电源电流继电器&#xff0c;型号&#xff1a;HDLN-1-2Z-2&#xff0c;触点容量&#xff1a;250V5A&#xff0c;整定范围&#xff1a;2-99.9A;特点&#xff1a;精度高&#xff0c;整定范围宽&#xff0c;动作快&am…

看完这篇 教你玩转渗透测试靶机vulnhub—DarkHole2

Vulnhub靶机DarkHole渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;Git信息泄露&#xff1a;③&#xff1a;SQL注入&#xff1a;④&#…

APP出海的现状与挑战​

随着国内移动互联网市场的饱和&#xff0c;越来越多的国内APP开发者和企业将目光投向了海外市场&#xff0c;寻求新的增长机会。根据艾瑞咨询的数据&#xff0c;2020年上半年&#xff0c;全球APP下载量达到715亿次&#xff0c;用户总支出达到501亿美元&#xff0c;其中中国出海…

Pytorch基本概念和使用方法

目录 1 Adam及优化器optimizer&#xff08;Adam、SGD等&#xff09;是如何选用的&#xff1f; 1&#xff09;Momentum 2&#xff09;RMSProp 3&#xff09;Adam 2 Pytorch的使用以及Pytorch在以后学习工作中的应用场景。 1&#xff09;Pytorch的使用 2&#xff09;应用场…