有部分小伙伴反馈说前面基于注解的Spring中大量使用注解,由于对Java的注解不熟悉,有点难受。建议总结一篇的Java注解的基础知识,那么,它来了!

本文内容

  1. 什么是注解?
  2. 如何定义注解
  3. 如何使用注解
  4. 如何获取注解信息
  5. Spring 中对注解做了什么增强?

什么是注解?

什么是代码中写的注释?那是给开发者看的,但是编译之后的字节码文件中是没有注释信息的,也就是说注释对于java编译器和JVM来说是没有意义的,看不到!

类比注释是给人看的,注解则是给java编译器和JVM看的一些标识,编译器和虚拟机在运行的过程中可以获取注解信息来做一些处理。

如何定义注解

注解定义的语法如下:

  1. public @interface 注解类名{
  2. 参数类型 参数名称1() [default 参数默认值];
  3. 参数类型 参数名称2() [default 参数默认值];
  4. }

参数名称可以没有,也可以定义多个,定义细节如下:

  • 参数类型只能是基本类型、String、Class、枚举类型、注解类型以及对应的一维数组
  • 如果注解参数只有1个,建议定义名称为value,方便使用时缺省参数名
  • default 可以指定默认值,如果没有默认值使用注解时必须给定参数值

定义注解时候需要考虑2个主要问题:

  • 注解可以使用在哪里也就是使用范围?
  • 注解保留到什么阶段,源码阶段,还是运行阶段?

java提供了一部分的元注解来解决上面的问题。

@Target指定注解的使用范围

来看下源码,主要是指定了可以应用注释类型的元素种类的数组参数。

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Target {
  5. // 返回可以应用注释类型的元素种类的数组
  6. ElementType[] value();
  7. }
  1. /*注解的使用范围*/
  2. public enum ElementType {
  3. /*类、接口、枚举、注解上面*/
  4. TYPE,
  5. /*字段上*/
  6. FIELD,
  7. /*方法上*/
  8. METHOD,
  9. /*方法的参数上*/
  10. PARAMETER,
  11. /*构造函数上*/
  12. CONSTRUCTOR,
  13. /*本地变量上*/
  14. LOCAL_VARIABLE,
  15. /*注解上*/
  16. ANNOTATION_TYPE,
  17. /*包上*/
  18. PACKAGE,
  19. /*类型参数上 1.8之后*/
  20. TYPE_PARAMETER,
  21. /*类型名称上 1.8之后*/
  22. TYPE_USE
  23. }

@Retention指定注解的保留策略

指示要保留带注释类型的注释多长时间。如果注释类型声明中不存在保留注释,则保留策略默认为 RetentionPolicy.CLASS

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Retention {
  5. RetentionPolicy value();
  6. }
  1. public enum RetentionPolicy {
  2. // 源码阶段,注解被编译器丢弃。
  3. SOURCE,
  4. // 注释将由编译器记录在类文件中,但不需要在运行时由 VM 保留。这是默认行为。
  5. CLASS,
  6. // 注释将由编译器记录在类文件中,并在运行时由 VM 保留,因此可以反射性地读取它们。
  7. RUNTIME
  8. }

综合上面2个注解,自定义一个保留到运行期的仅用在方法上的注解如下。

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. public @interface DemoAnnotation {
  4. String name() default "";
  5. Class targetClazz();
  6. }

如何使用注解

使用语法

  1. @注解类(参数1=值1,参数2=值2,参数3=值3,参数n=值n)
  2. 目标对象

使用前一节的注解来个简单的案例

  1. public class MyBean {
  2. @DemoAnnotation(name = "xxx", targetClazz = MyBean.class)
  3. public void m() {
  4. }
  5. }

来一个综合案例,注解位置包括类上、方法上、构造函数上、方法参数上、字段上、本地变量上、泛型类型参数和类型名称上。

  1. /**
  2. * 综合案例
  3. * @author zfd
  4. * @version v1.0
  5. * @date 2022/1/24 13:31
  6. * @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
  7. */
  8. @StrongAnnotation(value = "用在类上", elementType = ElementType.TYPE)
  9. public class UseStrongAnnotation<@StrongAnnotation(value = "用在类型参数上T0", elementType = ElementType.TYPE_PARAMETER) T0,
  10. @StrongAnnotation(value = "用在类型名称上T1", elementType = ElementType.TYPE_USE) T1> {
  11. @StrongAnnotation(value = "用在字段上", elementType = ElementType.FIELD)
  12. private String field;
  13. @StrongAnnotation(value = "构造方法上", elementType = ElementType.CONSTRUCTOR)
  14. public UseStrongAnnotation(@StrongAnnotation(value = "用在方法参数上", elementType = ElementType.PARAMETER) String field) {
  15. this.field = field;
  16. }
  17. @StrongAnnotation(value = "用在普通方法上", elementType = ElementType.METHOD)
  18. public void m(@StrongAnnotation(value = "方法参数上", elementType = ElementType.PARAMETER) String name) {
  19. @StrongAnnotation(value = "用在本地变量上", elementType = ElementType.LOCAL_VARIABLE)
  20. String prefix = "hello ";
  21. System.out.println(prefix + name);
  22. }
  23. public <@StrongAnnotation(value = "方法的类型参数T2上", elementType = ElementType.TYPE_PARAMETER) T2> void m1() {
  24. }
  25. public <@StrongAnnotation(value = "方法的类型名称T3上", elementType = ElementType.TYPE_USE) T3> void m2() {
  26. }
  27. private Map<@StrongAnnotation(value = "Map后面的尖括号也是类型名称", elementType = ElementType.TYPE_USE) String ,
  28. @StrongAnnotation(value = "Map后面的尖括号也是类型名称", elementType = ElementType.TYPE_PARAMETER)Object> map;
  29. }

如何获取注解信息

java.lang.reflect.AnnotatedElement接口表示当前在此 VM 中运行的程序的注解元素。 该接口允许以反射方式读取注解。 此接口中方法返回的所有注解都是不可变的和可序列化的。 该接口的方法返回的数组可以被调用者修改,而不影响返回给其他调用者的数组。其获取注解的主要方法如下,见名知意。

主要的实现类或接口图如下

对应的实现的含义也很明确:

  • Package:用来表示包的信息
  • Class:用来表示类的信息
  • Constructor:用来表示构造方法信息
  • Field:用来表示类中属性信息
  • Method:用来表示方法信息
  • Parameter:用来表示方法参数信息
  • TypeVariable:用来表示类型变量信息,如:类上定义的泛型类型变量,方法上面定义的泛型类型变量

综合案例

来一个综合案例来解析上一节的注解使用UseStrongAnnotation。测试用例和结果如下,建议多实战敲敲代码。

  1. package com.crab.spring.ioc.demo17;
  2. import com.sun.xml.internal.bind.v2.model.core.ID;
  3. import org.junit.Test;
  4. import java.lang.annotation.Annotation;
  5. import java.lang.reflect.*;
  6. import java.util.Arrays;
  7. import static org.junit.Assert.*;
  8. /**
  9. * @author zfd
  10. * @version v1.0
  11. * @date 2022/1/24 13:52
  12. * @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
  13. */
  14. public class UseStrongAnnotationTest {
  15. @Test
  16. public void test_annotated_class() {
  17. System.out.println("解析类上注解:");
  18. Arrays.stream(UseStrongAnnotation.class.getAnnotations())
  19. .forEach(System.out::println);
  20. }
  21. // 解析类上注解:
  22. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在类上, elementType=TYPE)
  23. @Test
  24. public void test_annotated_class_type_parameter() {
  25. TypeVariable<Class<UseStrongAnnotation>>[] typeParameters = UseStrongAnnotation.class.getTypeParameters();
  26. for (TypeVariable<Class<UseStrongAnnotation>> typeParameter : typeParameters) {
  27. System.out.println(typeParameter.getName() + " 1.8变量参数或变量名称注解信息:");
  28. Annotation[] annotations = typeParameter.getAnnotations();
  29. Arrays.stream(annotations).forEach(System.out::println);
  30. }
  31. }
  32. // T0 1.8变量参数或变量名称注解信息:
  33. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在类型参数上T0, elementType=TYPE_PARAMETER)
  34. // T1 1.8变量参数或变量名称注解信息:
  35. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在类型名称上T1, elementType=TYPE_USE)
  36. @Test
  37. public void test_annotated_field() throws NoSuchFieldException {
  38. Field field = UseStrongAnnotation.class.getDeclaredField("field");
  39. Arrays.stream(field.getAnnotations()).forEach(System.out::println);
  40. }
  41. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在字段上, elementType=FIELD)
  42. @Test
  43. public void test_annotated_constructor() {
  44. Constructor<?> constructor = UseStrongAnnotation.class.getDeclaredConstructors()[0];
  45. for (Annotation annotation : constructor.getAnnotations()) {
  46. System.out.println(annotation);
  47. }
  48. }
  49. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=构造方法上, elementType=CONSTRUCTO
  50. @Test
  51. public void test_annotated_normal_method() throws NoSuchMethodException {
  52. Method method = UseStrongAnnotation.class.getDeclaredMethod("m", String.class);
  53. System.out.println("方法注解:");
  54. for (Annotation annotation : method.getAnnotations()) {
  55. System.out.println(annotation);
  56. }
  57. System.out.println("方法参数注解:");
  58. Parameter[] parameters = method.getParameters();
  59. for (Parameter parameter : parameters) {
  60. System.out.println(parameter.getName() + " 参数注解:");
  61. for (Annotation annotation : parameter.getAnnotations()) {
  62. System.out.println(annotation);
  63. }
  64. }
  65. }
  66. // 方法注解:
  67. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在普通方法上, elementType=METHOD)
  68. // 方法参数注解:
  69. // name 参数注解:
  70. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=方法参数上, elementType=PARAMETER)
  71. @Test
  72. public void test_annotated_map_type() throws NoSuchFieldException {
  73. Field field = UseStrongAnnotation.class.getDeclaredField("map");
  74. // 返回一个 Type 对象,该对象表示此 Field 对象表示的字段的声明类型。
  75. // 如果 Type 是参数化类型,则返回的 Type 对象必须准确反映源代码中使用的实际类型参数。
  76. Type genericType = field.getGenericType();
  77. // 获取返回表示此类型的实际类型参数的 Type 对象数组
  78. Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
  79. // 返回一个 AnnotatedType 对象,该对象表示使用一个类型来指定此 Field 表示的字段的声明类型。
  80. AnnotatedType annotatedType = field.getAnnotatedType();
  81. // 获取此参数化类型的可能带注释的实际类型参数数组
  82. AnnotatedType[] annotatedActualTypeArguments =
  83. ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
  84. int index = 0;
  85. for (AnnotatedType annotatedActualTypeArgument : annotatedActualTypeArguments) {
  86. Type actualTypeArgument = actualTypeArguments[index++];
  87. System.out.println(annotatedActualTypeArgument.getType());
  88. System.out.println(actualTypeArgument.getTypeName() + " 类型上的注解:");
  89. for (Annotation annotation : annotatedActualTypeArgument.getAnnotations()) {
  90. System.out.println(annotation);
  91. }
  92. }
  93. }
  94. // T0 1.8变量参数或变量名称注解信息:
  95. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在类型参数上T0, elementType=TYPE_PARAMETER)
  96. // T1 1.8变量参数或变量名称注解信息:
  97. // @com.crab.spring.ioc.demo17.StrongAnnotation(value=用在类型名称上T1, elementType=TYPE_USE)
  98. }

@Inherited实现子类继承父类的注解

@Inherited指示注解类型是自动继承的。注意针对的父类的注解,接口是无效的

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Inherited {
  5. }

来看一个案例,父类和接口上都有可继承的注解,观察下子类的上的注解情况。

  1. /**
  2. * 测试父类注解的继承
  3. * 注意:是类,不是接口,接口无效
  4. * @author zfd
  5. * @version v1.0
  6. * @date 2022/1/24 17:15
  7. * @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
  8. */
  9. public class TestInherited {
  10. @Target(ElementType.TYPE)
  11. @Retention(RetentionPolicy.RUNTIME)
  12. @Inherited
  13. @interface Annotation1{}
  14. @Target(ElementType.TYPE)
  15. @Retention(RetentionPolicy.RUNTIME)
  16. @Inherited
  17. @interface Annotation2{}
  18. @Annotation1
  19. interface Interface1{}
  20. @Annotation2
  21. static class SupperClass{}
  22. // 继承 SupperClass 实现 Interface1,观察其注解继承情况
  23. static class SubClass extends SupperClass implements Interface1{}
  24. public static void main(String[] args) {
  25. for (Annotation annotation : SubClass.class.getAnnotations()) {
  26. System.out.println(annotation);
  27. }
  28. }
  29. // 输出
  30. // @com.crab.spring.ioc.demo17.TestInherited$Annotation2()
  31. // 只继承了父类注解 无法继承接口上的注解
  32. }

@Repeatable重复注解

常规情况下同一个目标上是无法使用同一个注解多个重复标记的。如果自定义注解需要实现可重复注解,则在定义的时候可以使用 @Repeatable来声明的注解类型是可重复的。

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Repeatable {
  5. // 指定容器注解类型
  6. Class<? extends Annotation> value();
  7. }

模拟 @ComponentScan @ComponentScans来提供一个案例。

  1. /**
  2. * 测试 @Repeatable 注解重复使用
  3. * @author zfd
  4. * @version v1.0
  5. * @date 2022/1/24 17:30
  6. * @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
  7. */
  8. public class TestRepeatable {
  9. @Target(ElementType.TYPE)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Repeatable(ComponentScans.class)
  12. @interface ComponentScan{}
  13. @Target(ElementType.TYPE)
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @interface ComponentScans{
  16. // 注意: 必须定义value参数,其类型是子重复注解的数组类型
  17. ComponentScan[] value();
  18. }
  19. // 重复注解方式1
  20. @ComponentScan
  21. @ComponentScan
  22. static class MyComponent{}
  23. // 重复注解方式2
  24. @ComponentScans({@ComponentScan, @ComponentScan})
  25. static class MyComponentB{}
  26. public static void main(String[] args) {
  27. for (Annotation annotation : MyComponent.class.getAnnotations()) {
  28. System.out.println(annotation);
  29. }
  30. for (Annotation annotation : MyComponentB.class.getAnnotations()) {
  31. System.out.println(annotation);
  32. }
  33. }
  34. // 输出
  35. // @com.crab.spring.ioc.demo17.TestRepeatable$ComponentScans(value=[@com.crab.spring.ioc.demo17
  36. // .TestRepeatable$ComponentScan(), @com.crab.spring.ioc.demo17.TestRepeatable$ComponentScan()])
  37. // @com.crab.spring.ioc.demo17.TestRepeatable$ComponentScans(value=[@com.crab.spring.ioc.demo17
  38. // .TestRepeatable$ComponentScan(), @com.crab.spring.ioc.demo17.TestRepeatable$ComponentScan()])
  39. }

Spring 中@AliasFor对注解的增强

注解的定义参数是不能继承,如注解A上面有注解B,但是实际在使用B注解在目标类C的过程中想要设置A的参数是做不到的。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface AnnotationA {
  4. String name() default "";
  5. int value() default -1;
  6. }
  7. @Target(ElementType.TYPE)
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @AnnotationA
  10. public @interface AnnotationB {
  11. String name() default "";
  12. int value() default 1;
  13. String aliasForName() default "";
  14. }
  15. @AnnotationB(name = "xxx", value = 1) // 无法设置AnnotiaonA的参数值
  16. public class ClassC {
  17. }

Spring 中 提供了@AliasFor 元注解,用于声明注解属性的别名,主要的使用场景:

  • 注解中的显式别名:在单个注解中, @AliasFor可以在一对属性上声明,以表明它们是彼此可互换的别名
  • 元注解中属性的显式别名:如果@AliasFor的annotation属性设置为与声明它的注解不同的注解,则该attribute被解释为元注解中属性的别名(即显式元注解属性覆盖)。 这可以精确控制注解层次结构中覆盖的属性。
  • 注解中的隐式别名:如果注解中的一个或多个属性被声明为相同元注解属性的属性覆盖(直接或传递),则这些属性将被视为彼此的一组隐式别名,从而导致类似于注解中显式别名的行为。

源码简单过一下

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. @Documented
  4. public @interface AliasFor {
  5. @AliasFor("attribute")
  6. String value() default "";
  7. @AliasFor("value")
  8. String attribute() default "";
  9. // 声明别名属性的注解类型。默认为 Annotation,这意味着别名属性在与此属性相同的注解中声明。
  10. Class<? extends Annotation> annotation() default Annotation.class;
  11. }

综合案例

来使用@AliasFor 改造下 AnnotationB。

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @AnnotationA
  4. public @interface AnnotationB {
  5. // 注解AnnotationB内部显式别名
  6. @AliasFor(value = "aliasForName")
  7. String name() default "";
  8. int value() default 1;
  9. // 注解AnnotationB内部显式别名
  10. @AliasFor(annotation = AnnotationB.class, attribute = "name")
  11. String aliasForName() default "";
  12. // 元注解AnnotationA属性name显式别名
  13. @AliasFor(annotation = AnnotationA.class, value = "name")
  14. String aliasForAnnotationAName() default "";
  15. // 元注解AnnotationA属性name显式别名2
  16. @AliasFor(annotation = AnnotationA.class, value = "name")
  17. String aliasForAnnotationAName2() default "";
  18. // 元注解AnnotationA属性value显式别名
  19. @AliasFor(annotation = AnnotationA.class, value = "value")
  20. int aliasForAnnotationAValue() default -1;
  21. }

使用AnnotationB 注解,注意:互为别名的属性设置时只能设置其中一个,否则设置多个会报错。

  1. @AnnotationB(value = 100,
  2. name = "xx",
  3. aliasForAnnotationAName = "a1",
  4. aliasForAnnotationAValue = -100
  5. )
  6. public class ClassC2 {
  7. public static void main(String[] args) {
  8. //spring提供一个查找注解的工具类AnnotatedElementUtils
  9. System.out.println(AnnotatedElementUtils.getMergedAnnotation(ClassC2.class, AnnotationB.class));
  10. System.out.println(AnnotatedElementUtils.getMergedAnnotation(ClassC2.class, AnnotationA.class));
  11. }
  12. }

输出结果显示AnnotationB 通过别名设置AnnotationA中属性成功。

  1. @com.crab.spring.ioc.demo17.AnnotationB(aliasForAnnotationAName=a1, aliasForAnnotationAName2=a1, aliasForAnnotationAValue=-100, aliasForName=xx, name=xx, value=100)
  2. @com.crab.spring.ioc.demo17.AnnotationA(name=a1, value=-100)

总结

本文详解了注解的概念,如何定义注解、使用注解、获取注解;并介绍了元注解@Target、@Retention、@Inherited、@Repeatable 的使用;重点讲解了Spring 中 @AliasFor 注解来为元注解属性设置别名的增强处理。

本篇源码地址: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-ioc/src/main/java/com/crab/spring/ioc/demo17

知识分享,转载请注明出处。学无先后,达者为先!

Spring系列20:注解详解和Spring注解增强(基础内功)的更多相关文章

  1. java中的注解详解和自定义注解

    一.java中的注解详解 1.什么是注解 用一个词就可以描述注解,那就是元数据,即一种描述数据的数据.所以,可以说注解就是源代码的元数据.比如,下面这段代码: @Override public Str ...

  2. [Spring学习笔记 3 ] spring 注解详解,完全注解,常用注解

    .xml使用注解 xml 用来定义bean的信息,注解用来配置依赖信息 ) 在配置文件中配置bean )在javaBean中用注解来指定依赖注入 )在配置文件中开启注解扫描 @Resource标签 j ...

  3. SpringMVC 常用注解 详解

    SpringMVC 常用注解 详解 SpringMVC 常用注解 1.@RequestMapping                                      路径映射 2.@Requ ...

  4. @Autowired 注解详解

    前言 我们平时使用 Spring 时,想要 依赖注入 时使用最多的是 @Autowired 注解了,本文主要讲解 Spring 是如何处理该注解并实现 依赖注入 的功能的. 正文 首先我们看一个测试用 ...

  5. Spring中的注入方式 和使用的注解 详解

    注解:http://www.cnblogs.com/liangxiaofeng/p/6390868.html 注入方式:http://www.cnblogs.com/java-class/p/4727 ...

  6. Spring IoC @Autowired 注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 我们平时使用 Spring 时,想要 依赖 ...

  7. Spring IoC 公共注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 什么是公共注解?公共注解就是常见的Java ...

  8. Spring框架系列(6) - Spring IOC实现原理详解之IOC体系结构设计

    在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解.本文将帮助你站在设计者的角度去看IOC最顶层的结构设计.@pdai Spring框架系列(6) - Spring IOC实现原理详解 ...

  9. Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程

    上文,我们看了IOC设计要点和设计结构:紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的. ...

随机推荐

  1. 阿里云服务器sshd-D cpu占用过高

    发现阿里云服务器cpu占用到达了100%,原因是被植入了挖矿程序,解决方法如下 1.使用top命令查看进程id 直接kill杀死该进程过一会就会重新启动. 2.查看该进程所在的文件目录 这个文件基本上 ...

  2. Ajax使用post方式发送数据注意事项

    Ajax使用post方式给服务器传递数据时,需要将传递的字符串转化为模拟from表单发送数据的XML格式 在open之后奢姿头协议信息,模拟from表单传递数据 xhr.setRequestHeade ...

  3. 解决Wordpress提示FTP登录问题

    向wordpress目录的wordpress-config.php中添加 define("FS_METHOD", "direct"); define(" ...

  4. 获取联系人列表的时候contact_id出现null的值

    因为删除联系人只是把它的contact_id设置为null,所以只要手机上删除过联系人id就会有null,用之前先判断是不是null就好了

  5. jquery.form 兼容IE89文件上传

    导入部分 <script type="text/javascript" src="js/jquery-1.8.3.min.js" charset=&quo ...

  6. netty系列之:让TCP连接快一点,再快一点

    简介 经典的TCP三次握手大家应该很熟悉了,三次握手按道理说应该是最优的方案了,当然这是对于通用的情况来说的.那么在某些特殊的情况下是不是可以提升TCP建立连接的速度呢? 答案是肯定的,这就是今天我们 ...

  7. 加密模块hashlib+日志模块logging

    目录 1.hashlib 加密模块 1.hashlib模块基本使用 1.2 详细操作 ①md5加密模式 ②sha256复杂加密模式 ③加盐操作(普通加盐) ④加盐操作(动态加盐) 2.logging ...

  8. Bootstrap提供的CDN服务标签与下载文档

    目录 1.引入Bootstrap提供的CDN服务 1.选择下载Bootstrap CDN 二:下载Bootstrap官方文档 1.进入Bootstrap官网,选择3版本中文档. 1.引入Bootstr ...

  9. 在Linux中设置php变量的方法

    默认情况下已经安装好了PHP环境,并且知道安装好后的PHP文件路径,然后可以通过以下的方式设置PHP变量,快速执行PHP命令运行PHP文件. 环境:centos 第一步:vi ~/.bash_prof ...

  10. 4、Linux基础--系统目录

    笔记 1.晨考 1.移动文件的命令 mv 2.删除文件的命令及其参数 rm 参数: -r : 递归删除 -f : 不提示删除 -i : 提示删除 3.复制文件的命令及其参数 cp 参数: -r : 递 ...