关于注解:注解式项目开发详细解析Java中各个注解的作用和使用方式

@Target作用: 指明了润饰的这个注解的应用范畴, 即被形容的注解能够用在哪里 @Target(ElementType.Type)ElementType取值的类型: TYPE: 类,接口或者枚举FIELD: 域,蕴含枚举常量METHOD: 办法PARAMETER: 参数CONSTRUCTOR: 构造方法LOCAL_VARIABLE: 局部变量ANNOTATION_TYPE: 注解类型PACKAGE: 包 @Retention作用: 指明润饰的注解的生存周期, 即会保留到哪个阶段RetentionPolicy的取值类型有三种: SOURCE: 源码级别保留,编译后即抛弃CLASS: 编译级别保留,编译后的class文件中存在,在jvm运行时抛弃,这是默认值RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,能够被反射调用 @Documented作用: 指明润饰的注解,能够被例如javadoc此类的工具文档化 只负责标记没有成员取值 @Inherited作用: 容许子类继承父类中的注解@Inherited须要和@AliasFor一起应用: 在子注解对应的属性应用@AliasFor 注解是能够继承的,然而注解是不能继承父注解的属性也就是说,在类扫描时的注解的属性值仍然是父注解的属性值,而不是自定义注解的属性值须要在注解的属性上应用@AliasFor @ComponentScan作用: 定义扫描的门路从中找出标识了须要拆卸的类主动拆卸到spring的bean容器中默认会扫描该类所在的包下所有的配置类@ComponentScan中的参数类型: value: 用于对指定包的门路进行扫描basePackages: 用于指定包的门路进行扫描,用法和value一样.倡议应用valuebasePackageClasses: 用于对指定某个类的所在的包的门路进行扫描nameGenerator: 用于为Spring容器中的检测到bean组件命名useDefaultFilters: 是否开启对 @Component,@Repository,@Service,@Controller的类进行检测excludeFilters: 依照过滤条件进行排除 FilterType.ANNOTATION: 依照注解FilterType.ASSIGNABLE_TYPE: 依照给定的类型FilterType.ASPECTJ: 应用ASPECTJ表达式FilterType.REGEX: 应用正则表达式FilterType.CUSTOM: 依照自定义规定includeFilters: 依照过滤条件进行蕴含 FilterType.ANNOTATION: 依照注解FilterType.ASSIGNABLE_TYPE: 依照给定的类型FilterType.ASPECTJ: 应用ASPECTJ表达式FilterType.REGEX: 应用正则表达式FilterType.CUSTOM: 依照自定义规定 @Filter作用: 配置过滤条件的过滤器注解@Filter中的参数类型: typeclass @interface作用: 自定义注解主动继承java.lang.annotation.Annotation接口,由编译程序主动实现其余细节在定义注解时,不能继承其余的注解或接口@interface用来申明一个注解: 其中的每一个办法实际上是申明一个配置参数办法的名称就是参数的名称办法的返回值类型就是参数的类型返回值类型只能是根本类型,Class,String,enum能够通过default来申明参数的默认值定义注解的格局: public @interface 注解名 {定义体}注解参数反对的数据类型: 根本数据类型: int,float,boolean,byte,double,char,long,shortString类型Class类型enum类型Annotation类型以上类型组合的数组Annotation类型中参数设定规定: 只能用public或default默认拜访权润饰:参数成员只能用根本类型byte,short,char,int,long,float,double,boolean八种根本数据类型和String,Enum,Class,annotations等数据类型,以及这一些类型的数组如果只有一个参数成员,最好把参数名称设为value,后加小括号注解元素的默认值: 注解元素必须有确定的值要么在定义注解的默认值中指定,要么在应用注解时指定,非根本类型的注解元素的值不可为null因而应用空字符串或0作为默认值束缚这个束缚使得处理器很难体现一个元素的存在或缺失的状态: 因为每个注解的申明中,所有元素都存在,并且都具备相应的值为了绕开这个束缚,只能定义一些非凡的值(比方空字符串或者正数),示意某个元素不存在 @AliasFor作用: 为注解的属性增加别名在同一个注解内,对两个不同的属性一起应用,互为别名: 无论为哪个属性名设置属性值,另一个属性名也是同样的属性值互为别名的属性值必须雷同,否则会报错属性必须要有默认的属性值public @interface RequestMapping { @AliasFor("path") // 此时path和value值必须是一样的,否则会报错 String[] value() default {}; @AliasFor("value") // 此时path和value值必须是一样的,否则会报错 String[] path() default {}; }显式的笼罩元注解中的属性: ...

June 28, 2021 · 3 min · jiezi

关于注解:深入理解Java注解类型

Java注解是在JDK5时引入的新个性,鉴于目前大部分框架(如Spring)都应用了注解简化代码并进步编码的效率,因而把握并深刻了解注解对于一个Java工程师是来说是很有必要的事。 了解Java注解实际上Java注解与一般修饰符(public、static、void等)的应用形式并没有多大区别,上面的例子是常见的注解: public class AnnotationDemo { @Test public static void A(){ System.out.println("Test....."); } @Deprecated @SuppressWarnings("uncheck") public static void B(){ }}通过在办法上应用@Test注解后,在运行该办法时,测试框架会自动识别该办法并独自调用,@Test实际上是一种标记注解,起标记作用,运行时通知测试框架该办法为测试方法。而对于@Deprecated和@SuppressWarnings(“uncheck”),则是Java自身内置的注解,在代码中,能够常常看见它们,但这并不是一件坏事,毕竟当办法或是类下面有@Deprecated注解时,阐明该办法或是类都曾经过期不倡议再用,@SuppressWarnings 则示意疏忽指定正告,比方@SuppressWarnings(“uncheck”),这就是注解的最简略的应用形式,那么上面咱们就来看看注解定义的根本语法 根本语法申明注解与元注解咱们先来看看后面的Test注解是如何申明的: @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Test {}咱们应用了@interface申明了Test注解,并应用@Target注解传入ElementType.METHOD参数来表明@Test只能用于办法上,@Retention(RetentionPolicy.RUNTIME)则用来示意该注解生存期是运行时,从代码上看注解的定义很像接口的定义,的确如此,毕竟在编译后也会生成Test.class文件。对于@Target和@Retention是由Java提供的元注解,所谓元注解就是标记其余注解的注解,上面别离介绍 @Target 用来束缚注解能够利用的中央(如办法、类或字段),其中ElementType是枚举类型,其定义如下,也代表可能的取值范畴 public enum ElementType { /**表明该注解能够用于类、接口(包含注解类型)或enum申明*/ TYPE,/** 表明该注解能够用于字段(域)申明,包含enum实例 */ FIELD, /** 表明该注解能够用于办法申明 */ METHOD, /** 表明该注解能够用于参数申明 */ PARAMETER, /** 表明注解能够用于构造函数申明 */ CONSTRUCTOR, /** 表明注解能够用于局部变量申明 */ LOCAL_VARIABLE, /** 表明注解能够用于注解申明(利用于另一个注解上)*/ ANNOTATION_TYPE, /** 表明注解能够用于包申明 */ PACKAGE, /** * 表明注解能够用于类型参数申明(1.8新退出) * @since 1.8 */ TYPE_PARAMETER, /** * 类型应用申明(1.8新退出) * @since 1.8 */ TYPE_USE }请留神,当注解未指定Target值时,则此注解能够用于任何元素之上,多个值应用{}蕴含并用逗号隔开,如下: ...

May 4, 2021 · 5 min · jiezi

关于注解:注解入门

java注解注解是什么?注解是元数据的一种模式,,为程序提供了一些数据,然而并不是程序的一部分。注解对代码并不间接地提供操作。 作用:告知编译器一些信息,例如查看重写(@Override)、克制正告(@SuppressWarnings)编译器以及部署期 的解决: 软件工具能够解决注解去生成代码,xml文件运行时解决:一些注解能够在运行时被查看到根底注解的格局@Entity @Author( name = "Benjamin Franklin", date = "3/27/2003" ) 哪里能够被应用:能够被用在 类、办法、字段、或者参数 public enum ElementType { /* Class, interface (including annotation type), or enum declaration / TYPE, /* Field declaration (includes enum constants) / FIELD, /* Method declaration / METHOD, /* Formal parameter declaration / PARAMETER, /* Constructor declaration / CONSTRUCTOR, /* Local variable declaration / LOCAL_VARIABLE, /* Annotation type declaration / ANNOTATION_TYPE, /* Package declaration / PACKAGE, /*Type parameter declaration/ TYPE_PARAMETER, /* Use of a type/ TYPE_USE} ...

March 31, 2021 · 2 min · jiezi

关于注解:SSMspringboot注解总结

注解常识回顾 第一:分类(1)jdk自带的注解(5个):罕用的就一个:@Override(2)元注解(5个):罕用的两个:@Target(指定注解应用的地位) @Retention(形容生命周期)(3)自定义注解:(框架里大部分都是) 第二:元注解 @Target 指定其余注解能够应用的地位(包上、类上、办法上、属性上)示意以后注解对谁无效 对类无效@Retention 指定其余注解的生命周期(源文件中、运行时、class文件中),注解什么时候失效 运行期无效@Documented 示意将该注解形容的注解外部的正文局部,也生成到相应的API中 是否动静的生成文档信息@Inherited 示意该注解能够被继承第三:自定义注解自定义注解须要配合元注解应用(罕用@Target&@Retention )第四:@Target注解(指定其余注解能够应用的地位)(1)其余注解应用在单个地位(如何指定?) @Target(ElementType.Type)(2)其余注解应用在多个地位(如何指定?)底层保护的是一个数组 @Target({ElementType.Type,ElementType.Field}) (3)@Target注解的取值{值被保护在ElementType中} ElementType.Type ElementType.Field ElementType.Method第五:@Retention注解(指定其余注解的生命周期) @Retention注解的取值{值被保护在RetentionPolicy工具类中} RetentionPolicy.SOURCE RetentionPolicy.CLASS RetentionPolicy.RUNTIME第六:自定义注解论述(1)定义: @inteface 注解名{}(2)配合元注解,指定自定义注解应用的地位,以及自定义注解的生命周期第七:给注解增加性能---属性(也有人称为办法)定义(1): @inteface Annotation{ String name(); //没有设置默认值,应用该注解时,该属性必须增加值。//String name() default "lisi";//给name赋默认值lisi,设置了默认值后,应用该注解不须要手动设置name的属性值。}应用(1): @Annotation(name="zhangsan") public void sayhello(){}定义(2): @inteface Test{ int value(); //int value() default 10;//给value赋默认值10}应用(2): @Test(value=100) public void sayhello(){ }因为value的特殊性,在应用注解时,能够省略“value=”,例如@Test(100)如果想间接应用@Test不写值100,能够在定义注解时,设置value的默认值为100@inteface Test{ int value() default 100;//给value赋默认值100}问题剖析: @inteface Test{ String name() default "lisi";//给name赋默认值lisiint value() default 100;//给value赋默认值100}(1)能够间接应用这个注解,间接写@Test(2)保留name的默认值,改value的值("value="可省略) @Test(10)(3)保留value的默认值,改name的值(“value=”可省略) @Test(name="张三")(4)同时改两个值时("value="不能省略) @Test(name="张三",value=10)第八:框架(framework)中罕用的注解(1)@SpringBootApplication形容SpringBoot工程启动类的特定注解(2)@SpringBootTest形容SpringBoot工程测试类的特定注解应用条件:如果须要在测试类中,引入spring容器机制,这是才是用该注解,否则没必要加。引入spring容器机制:比如说,咱们要在测试类中,通过spring容器来注入某个类的实例时,就须要应用该注解。(3)@AutoWired主动拆卸-由Spring框架为咱们指定的属性类型注入值.咱们罕用接口类型的变量来接管spring框架注入的值(多态的思维,为了升高耦合,让程序更容易保护)具体的注入(DI)过程: ...

January 30, 2021 · 2 min · jiezi

关于注解:SpringBoot注解最全详解整合超详细版本

应用注解的劣势: 1.采纳纯java代码,不在须要配置繁冗的xml文件 2.在配置中也可享受面向对象带来的益处 3.类型平安对重构能够提供良好的反对 4.缩小简单配置文件的同时亦能享受到springIoC容器提供的性能 一、注解详解(装备了欠缺的释义)------(可采纳ctrl+F 来进行搜寻哦~~~~) @SpringBootApplication:申明让spring boot主动给程序进行必要的配置,这个配置等同于: @Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三个配置。 @ResponseBody:示意该办法的返回后果间接写入HTTP response body中,个别在异步获取数据时应用,用于构建RESTful的api。在应用@RequestMapping后,返回值通常解析为跳转门路,加上@esponsebody后返回后果不会被解析为跳转门路,而是间接写入HTTP response body中。比方异步获取json数据,加上@Responsebody后,会间接返回json数据。该注解个别会配合@RequestMapping一起应用。 @Controller:用于定义控制器类,在spring我的项目中由控制器负责将用户发来的URL申请转发到对应的服务接口(service层),个别这个注解在类中,通常办法须要配合注解@RequestMapping。 @RestController:用于标注管制层组件(如struts中的action),@ResponseBody和@Controller的合集。 @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。 @EnableAutoConfiguration:SpringBoot主动配置(auto-configuration):尝试依据你增加的jar依赖主动配置你的Spring利用。例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连贯beans,那么咱们将主动配置一个内存型(in-memory)数据库”。你能够将@EnableAutoConfiguration或者@SpringBootApplication注解增加到一个@Configuration类上来抉择主动配置。如果发现利用了你不想要的特定主动配置类,你能够应用@EnableAutoConfiguration注解的排除属性来禁用它们。 @ComponentScan:示意将该类主动发现扫描组件。集体了解相当于,如果扫描到有@Component、@Controller、@Service等这些注解的类,并注册为Bean,能够主动收集所有的Spring组件,包含@Configuration类。咱们常常应用@ComponentScan注解搜寻beans,并联合@Autowired注解导入。能够主动收集所有的Spring组件,包含@Configuration类。咱们常常应用@ComponentScan注解搜寻beans,并联合@Autowired注解导入。如果没有配置的话,Spring Boot会扫描启动类所在包下以及子包下的应用了@Service,@Repository等注解的类。 @Configuration:相当于传统的xml配置文件,如果有些第三方库须要用到xml文件,倡议依然通过@Configuration类作为我的项目的配置主类——能够应用@ImportResource注解加载xml配置文件。 @Import:用来导入其余配置类。 @ImportResource:用来加载xml配置文件。 @Autowired:主动导入依赖的bean @Service:个别用于润饰service层的组件 @Repository:应用@Repository注解能够确保DAO或者repositories提供异样转译,这个注解润饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不须要为它们提供XML配置项。 @Bean:用@Bean标注办法等价于XML中配置的bean。 @Value:注入Spring boot application.properties配置的属性的值。示例代码: @Inject:等价于默认的@Autowired,只是没有required属性; @Component:泛指组件,当组件不好归类的时候,咱们能够应用这个注解进行标注。 @Bean:相当于XML中的,放在办法的下面,而不是类,意思是产生一个bean,并交给spring治理。 @AutoWired:主动导入依赖的bean。byType形式。把配置好的Bean拿来用,实现属性、办法的组装,它能够对类成员变量、办法及构造函数进行标注,实现主动拆卸的工作。当加上(required=false)时,就算找不到bean也不报错。 @Qualifier:当有多个同一类型的Bean时,能够用@Qualifier(“name”)来指定。与@Autowired配合应用。@Qualifier限定描述符除了能依据名字进行注入,但能进行更细粒度的管制如何抉择候选者,具体应用形式如下: @Resource(name=”name”,type=”type”):没有括号内内容的话,默认byName。与@Autowired干相似的事。 二、注解列表如下 @SpringBootApplication:蕴含了@ComponentScan、@Configuration和@EnableAutoConfiguration注解。其中 @ComponentScan:让spring Boot扫描到Configuration类并把它退出到程序上下文。 @Configuration :等同于spring的XML配置文件;应用Java代码能够查看类型平安。 @EnableAutoConfiguration :主动配置。 @ComponentScan :组件扫描,可主动发现和拆卸一些Bean。 @Component可配合CommandLineRunner应用,在程序启动后执行一些根底工作。 @RestController:注解是@Controller和@ResponseBody的合集,示意这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST格调的控制器。 @Autowired:主动导入。 @PathVariable:获取参数。 @JsonBackReference:解决嵌套外链问题。 @RepositoryRestResourcepublic:配合spring-boot-starter-data-rest应用。 三、JPA注解 @Entity:@Table(name=”“):表明这是一个实体类。个别用于jpa这两个注解个别一块应用,然而如果表名和实体类名雷同的话,@Table能够省略 @MappedSuperClass:用在确定是父类的entity上。父类的属性子类能够继承。 @NoRepositoryBean:个别用作父类的repository,有这个注解,spring不会去实例化该repository。 @Column:如果字段名与列名雷同,则能够省略。 @Id:示意该属性为主键。 @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = “repair_seq”):示意主键生成策略是sequence(能够为Auto、IDENTITY、native等,Auto示意可在多个数据库间切换),指定sequence的名字是repair_seq。 @SequenceGeneretor(name = “repair_seq”, sequenceName = “seq_repair”, allocationSize = 1):name为sequence的名称,以便应用,sequenceName为数据库的sequence名称,两个名称能够统一。 ...

January 12, 2021 · 2 min · jiezi

关于注解:java-Annotations

注解定义和用法Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate. 注解是一种元数据,它可能关联代码中不同元素和构造,不会间接影响它注解的元素,然而能够被编译器辨认,能够联合反射等应用。一个简略的注解申明如下,@interface 关键字引入了新的注解类型。 @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface TaskName { String value();}注解能够申明有默认值和没有默认值的属性 @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface TaskName { String value(); int taskId() default 0;}如果注解申明了没有默认值的属性,那么在该注解被利用的所用中央都应该提供注解属性值。 @TaskName (name = "test")如果注解只有一个属性,并且是value那么名字能够被省略,然而应用的时候必须指定value的值,例如:@TaskName ("test") public @interface TaskName { String value();}保留策略每个注解都有一个被称为保留策略(Retention Policy)的特色,它是一组如何保留注解的策略组合的枚举(RetentionPolicy类型)。 策略形容CLASS注解被编译器记录在class文件中,然而在运行时不须要虚拟机保留(即运行时不存在)RUNTIME注解被编译器记录在class文件中并且在运行时被虚拟机保留,因而能够通过反射机制获取SOURCE注解被编译器抛弃(即注解仅在源码中保留,class文件中不存在)上面的代码,RetentionPolicy.RUNTIME将会保障注解在编译过程和运行的应用程序中存在。 @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface TaskName { String value();}元素类型每个注解必须有它可能利用的元素类型,元素类型被定义成一组可能的元素类型的枚举(ElementType)。 ...

December 30, 2020 · 1 min · jiezi

关于注解:JsonFormat与DateTimeFormat注解的使用

背景:从数据库获取工夫传到前端进行展现的时候,咱们有时候可能无奈失去一个称心的工夫格局的工夫日期,在数据库中显示的是正确的工夫格局,获取进去却变成了很丑的工夫戳,@JsonFormat注解很好的解决了这个问题,咱们通过应用@JsonFormat能够很好的解决:后盾到前台工夫格局保持一致的问题,其次,另一个问题是,咱们在应用WEB服务的时,可能会须要用到,传入工夫给后盾,比方注册新用户须要填入出生日期等,这个时候前台传递给后盾的工夫格局同样是不统一的,而咱们的与之对应的便有了另一个注解,@DataTimeFormat便很好的解决了这个问题。 @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") private Date symstarttime; @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") private Date symendtime;我这里就只贴这两个属性了,这里我两个注解都同时应用了,因为我既须要取数据到前台,也须要前台数据传到后盾,都须要进行工夫格局的转换,能够同时应用。 总结: 注解@JsonFormat次要是后盾到前台的工夫格局的转换 注解@DataFormAT次要是前后到后盾的工夫格局的转换

December 19, 2020 · 1 min · jiezi

关于注解:面试官说说你对注解的理解

关注“Java后端技术全栈” 回复“000”获取大量电子书 本文次要内容如下: 背景当初曾经处于注解流行时代,注解@Override ,这个注解是再相熟不过了,还有@Controller、@RequestMapping、@Service..... 注解曾经是作为一个开发中必备的技能了。 如果在面试中被问到注解,说不出个123,就只能回去等告诉了。 什么是注解?注解annotation是JavaSE5.0中新增性能。能够了解为注解是一种标记,这种标记能够在编译、类加载、运行时被读取,并执行相应的解决。 它能够增加到程序的任何元素上:包申明、类型申明、构造方法、一般办法、成员变量、参数。 注解的老大: package java.lang.annotation;    //是个接口    public interface Annotation {            boolean equals(Object obj);        int hashCode();         String toString();         //获取注解类型        Class<? extends Annotation> annotationType();    }JDK自带为咱们提供了元注解,上面就来聊聊JDK的元注解。 元注解有哪些?JDK为咱们提供五个元注解,位于java.lang.annotation 包目录下。别离为: @Retention@Target@Documented@Inherited@RepeatableRetention注解字面翻译: 该注解就是定义该注解是作用于什么阶段。 编译、类加载、运行(应用最多) 注解源码: @Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.ANNOTATION_TYPE)    public @interface Retention {        /**         * Returns the retention policy.         * @return the retention policy         */        RetentionPolicy value();    }保留策略 package java.lang.annotation;    public enum RetentionPolicy {         SOURCE,         CLASS,         RUNTIME    }SOURCE:只在Java源代码中,编译器编译的时候会把它间接抛弃。 CLASS:编译器将注解记录在.class文件中。当运行Java程序时,JVM不能获取注解信息。这是默认值。 RUNTIME:编译器将注解记录在.class文件中。当运行Java程序时,JVM也能获取注解信息。程序能够通过反射获取注解信息。 留神:如果应用该注解的时候必须指定value的值。 Target注解字面意义为指标,上面来看看器源码: @Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.ANNOTATION_TYPE)    public @interface Target {        //元素类型        ElementType[] value();    }    public enum ElementType {         TYPE,         FIELD,         METHOD,         PARAMETER,         CONSTRUCTOR,         LOCAL_VARIABLE,         ANNOTATION_TYPE,         PACKAGE,         /** @since 1.8*/        TYPE_PARAMETER,        /** @since 1.8*/        TYPE_USE    }Target注解是在申明创立一个注解的时候,批示该注解能够作用于程序中的哪些元素。 它里边也蕴含一个名为value的成员变量,value成员变量的值有如下几种 ANNOTATION_TYPE:指定以后注解只能润饰其它注解CONSTRUCTOR:指定以后注解只能润饰构造方法FIELD:指定以后注解只能润饰成员变量LOCAL_VARIABLE:指定以后注解只能润饰局部变量METHOD:指定以后注解只能润饰办法PACKAGE:指定以后注解只能润饰包PARAMETER:指定以后注解只能润饰参数TYPE:指定以后注解能够润饰类,接口,其它注解,枚举等类型比如说:罕用的Spring的注解@Controller,是用来作用于类上的。 Documented 注解字面意义就是文档。@Documented用于形容其它类型的annotation应该被作为被标注的程序成员的公共API,因而能够被例如javadoc此类的工具文    档化。Documented是一个标记注解,没有成员 。 @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Documented {}次要是用来生成文档的,工作中基本上很少应用,作为理解就能够了。 Inherited 注解字面意义: @Inherited注解是在申明创立一个注解的时候,指定该注解将具备继承性。 Inherited 源码: @Documented    @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.ANNOTATION_TYPE)    public @interface Inherited {    }如果某个类应用了该注解@Xx,则其子类也将主动被此注解@Xx所润饰。 应用模板: @Retention(RetentionPolicy.RUNTIME)    @Target(ElementType.TYPE)    @Inherited    public @interface A {            }    @A    public class Base {            }    //Sub也带有@A    public class Sub extends Base {    }Repeatable注解@Repeatable元注解,顾名思义,反复注解,就是在申明创立注解的时候,指定该注解能够被同一个程序元素屡次应用。这是JDK8新增的注解,反复注解只是一种简单化写法,这种简单化写法是一种假象。多个反复注解其实会被作为“容器”注解的value成员变量的数组元素。 比方上面Spring中注解ComponentScan: @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();    }应用案例 import org.springframework.context.annotation.Bean;    import org.springframework.context.annotation.ComponentScan;    import org.springframework.context.annotation.ComponentScans;        @ComponentScans(value = {            @ComponentScan(value = "com.tian.pakage0"),            @ComponentScan(value = "com.tian.pakage1")})    public class MainConfig {        @Bean        public User person() {            return new User();        }    }根本注解注解必须应用工具来解决,工具负责提取注解中蕴含的元数据,工具还会依据这些元数据减少额定的性能。上面咱们先来理解一下5个根本注解的用法。 OverrideSafeVarargsSuppressWarningsFunctionalInterfaceDeprecated上面咱们就来说说这五个注解。 Override用于标识办法,标识该办法属于重写父类的办法 。 //只能用于办法上    @Target(ElementType.METHOD)    //源码中编译时就会应用    @Retention(RetentionPolicy.SOURCE)    public @interface Override {    }此注解外表是重写父类的办法,只能用于办法上,并且用于编译阶段,咱们在开发的时候,如果注解使用不当,在源码编译时立马就会做出提醒。 ...

December 6, 2020 · 1 min · jiezi

关于注解:自定义注解的魅力你到底懂不懂

前言你晓得自定义注解的魅力所在吗?你晓得自定义注解该怎么应用吗? 本文一开始的这两个问题,须要您认真思考下,而后联合这两个问题来浏览上面的内容;如果您在浏览完文章后对这两个问题有了比拟清晰的,请动动您发财的小手,点赞留言呀! 本文主线:注解是什么;实现一个自定义注解;自定义注解的实战利用场景;留神:本文在介绍自定义注解实战利用场景时,须要联合拦截器、AOP进行应用,所以本文也会简略聊下AOP相干知识点,如果对于AOP的相干内容不太分明的能够参考此 细说Spring——AOP详解 文章进行理解。注解注解是什么?①、援用自维基百科的内容:Java注解又称Java标注,是JDK5.0版本开始反对退出源代码的非凡语法 元数据 。Java语言中的类、办法、变量、参数和包等都能够被标注。和Javadoc不同,Java标注能够通过反射获取标注内容。在编译器生成类文件时,标注能够被嵌入到字节码中。Java虚拟机能够保留标注内容,在运行时能够获取到标注内容。 当然它也反对自定义Java标注。 ②、援用自网络的内容:Java 注解是在 JDK5 时引入的新个性,注解(也被称为 元数据 )为咱们在代码中增加信息提供了一种形式化的办法,使咱们能够在稍后某个时刻十分不便地应用这些数据。元注解是什么?元注解 的作用就是负责注解其余注解。Java5.0定义了4个规范的meta-annotation(元注解)类型,它们被用来提供对其它 annotation类型作阐明。规范的元注解:@Target@Retention@Documented@Inherited在具体说这四个元数据的含意之前,先来看一个在工作中会常常应用到的 @Autowired 注解,进入这个注解外面瞧瞧: 此注解中应用到了@Target、@Retention、@Documented 这三个元注解 。@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Autowired { boolean required() default true;}@Target元注解:@Target注解,是专门用来限定某个自定义注解可能被利用在哪些Java元素下面的,表明作用范畴;取值在java.lang.annotation.ElementType 进行定义的。public enum ElementType { /** 类,接口(包含注解类型)或枚举的申明 */ TYPE, /** 属性的申明 */ FIELD, /** 办法的申明 */ METHOD, /** 办法形式参数申明 */ PARAMETER, /** 构造方法的申明 */ CONSTRUCTOR, /** 局部变量申明 */ LOCAL_VARIABLE, /** 注解类型申明 */ ANNOTATION_TYPE, /** 包的申明 */ PACKAGE}依据此处能够晓得 @Autowired 注解的作用范畴: ...

November 15, 2020 · 6 min · jiezi

Spring-讲解四

Spring 中使用注解注入注解:就是一个类,使用 @ 注解名称。实际开发中:使用注解取代 xml 配置文件。 1、常用注解释义 @component 取代 <bean class="">@Component("id") 取代 <bean id="" class="">web开发,提供3个 @Component 注解衍生注解取代<bean class=""> @Repository(“名称”):dao层@Service(“名称”):service层 @Controller(“名称”):web层 web 开发中其他常用注解 @Autowired:自动根据类型注入@Qualifier(“名称”):指定自动注入的id名称 @Resource(“名称”) @ PostConstruct 自定义初始化 @ PreDestroy 自定义销毁 2、案例代码演示 接口和实现类public interface UserService { void add(User user);}=========================================================================================@Componentpublic class UserServiceImpl implements UserService { @Override public void add(User user) { System.out.println("添加用户信息..."+user); }}配置 bean.xml 文件<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解--> <context:annotation-config/> <!-- 注解的位置--> <context:component-scan base-package="com.example.demo"/></beans>测试函数public class ServiceTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = context.getBean(UserServiceImpl.class); User user = new User(); user.setUsername("元始天尊"); user.setAge(999); user.setPassword("555"); userService.add(user); }}控制台日志信息如下:添加用户信息...User{username='元始天尊', password='555', age=999} ...

November 5, 2019 · 1 min · jiezi

Spring-Boot-Condition-注解组合条件你知道吗

上一篇文章 你应该知道的 @ConfigurationProperties 注解的使用姿势,这一篇就够了 介绍了如何通过 @ConfigurationProperties 注解灵活读取配置属性,这篇文章将介绍如何灵活配置 Spring Bean 写在前面当我们构建一个 Spring 应用的时候,有时我们想在满足指定条件的时候才将某个 bean 加载到应用上下文中, 在Spring 4.0 时代,我们可以通过 @Conditional 注解来实现这类操作 我们看到 @Conditional 注解接收的参数是 extends Condition 接口的泛型类,也就是说,我们要使用 @Conditional 注解,只需要实现 Condition 接口并重写其方法即可: 看到接口的 matches 方法返回的是 boolean 类型,是不是和我们自定义 validation annotation 有些类似,都是用来判断是否满足指定条件。另外注意看,以上注解和接口都在 org.springframework.context.annotation package 中 终于到了 Spring Boot 时代,在这个全新的时代,Spring Boot 在 @Conditional 注解的基础上进行了细化,无需出示复杂的介绍信 (实现 Condition 接口),只需要手持预定义好的 @ConditionalOnXxxx 注解印章的门票,如果验证通过,就会走进 Application Context 大厅 注解详解Spring Boot 对 @Conditional 注解为我们做了细化,这些注解都定义在 org.springframework.boot.autoconfigure.condition package 下 逐个打开这 13 个注解,我们发现这些注解上有相同的元注解: ...

September 9, 2019 · 2 min · jiezi

Java注解之如何利用RetentionPolicySOURCE生存周期

上一篇文章简单讲了下Java注解的学习之元注解说明,学习了Java注解是如何定义的,怎么使用的,但是并没有介绍Java的注解是怎么起作用的,像Spring Boot里面的那些注解,到底是怎么让程序这样子运行起来的?特别是讲到RetentionPolicy这一块,到底在SOURCE阶段,在CLASS阶段,在RUNTIME阶段有什么差别,注解是如何在这三个阶段起作用的呢?而且,在SOURCE阶段,在CLASS阶段,程序又不运行,那注解到底会用来做些什么呢? 带着这些疑问,我就去了解下了如何让注解起作用,发现RUNTIME阶段的介绍到处都是,但是SOURCE和CLASS阶段就很少文章讲解到了,真的得搜刮好几十篇文章才能成功的把程序跑起来,几乎每一篇文章都少讲了些东西。 本文优先讲的是SOURCE阶段注解如何发挥作用,发现这一块涉及的知识非常多且难,资料还少,另外还发现,Java服务器端编程的人用这个反而不如Android开发的人用得多。对我学习SOURCE阶段的注解帮助最大的是Pluggable Annotation Processing API,JSR269插件化注解API以及JVM进阶 -- 浅谈注解处理器。搜索这方面的资料用“插件化注解处理API”这个关键词能搜得更全。 这几篇文章基本上把SOURCE阶段的注解实现和使用讲了,但是具体细节,比如用javac直接编译运行代码,javac使用jar包,使用maven等三个方式如何运行注解处理器,倒是基本上蜻蜓点水,摸索了我好久才搞定了。关于注解处理器的知识,可以从如上三篇文章了解,本文主要讲注解的定义和运行。 我用的代码是摘抄至 JVM进阶 -- 浅谈注解处理器 ,它定义了一个CheckGetter的注解,用来检查一个类里面的字段,哪个没有Getter方法,没有的话编译就报错。不过我的和他的稍稍不同,他的代码定义没有放到package里面,我的放到package里面了,这样子的使用和执行又有了点不同。 首先,定义CheckGetter注解: package com.shahuwang.processor;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.TYPE, ElementType.FIELD})@Retention(RetentionPolicy.SOURCE)public @interface CheckGetter {}注意上面的代码,是放到 package com.shahuwang.processor 里面的,因此,先创建如下结构的文件夹 Processor —— com —— shuhuwang —— processor —— CheckGetter.java 注解已经定义好了,现在先来用一下这个注解: package com.shahuwang.processor;@CheckGetterpublic class TestGetter { String name; String first; public String getName(){ return this.name; }}这个类有两个字段,但是有一个字段没有设置getter。 下面才是真正重要的代码,就是让CheckGetter这个注解真正能运行起来,发挥作用: package com.shahuwang.processor;import javax.annotation.processing.AbstractProcessor;import javax.annotation.processing.RoundEnvironment;import javax.annotation.processing.SupportedAnnotationTypes;import javax.annotation.processing.SupportedSourceVersion;import javax.lang.model.SourceVersion;import javax.lang.model.element.ExecutableElement;import javax.lang.model.element.Modifier;import javax.lang.model.element.TypeElement;import javax.lang.model.element.VariableElement;import javax.lang.model.util.ElementFilter;import javax.tools.Diagnostic;import javax.lang.model.element.Element;import java.util.Set;// 这个地方换了包名就需要改过来,否则processor就不会执行了, 这里是最需要注意的地方,千万注意!!!!!@SupportedAnnotationTypes("com.shahuwang.processor.CheckGetter")@SupportedSourceVersion(SourceVersion.RELEASE_11)public class CheckGetterProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement annotatedClass : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(CheckGetter.class))) { for (VariableElement field : ElementFilter.fieldsIn(annotatedClass.getEnclosedElements())) { if(!containsGetter(annotatedClass, field.getSimpleName().toString())){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format("getter not found for '%s.%s'.", annotatedClass.getSimpleName(), field.getSimpleName())); } } } return false; } private static boolean containsGetter(TypeElement typeElement, String name){ String getter = "get" + name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); for(ExecutableElement executableElement: ElementFilter.methodsIn(typeElement.getEnclosedElements())){ if(!executableElement.getModifiers().contains(Modifier.STATIC) && executableElement.getSimpleName().toString().equals(getter) && executableElement.getParameters().isEmpty()){ return true; } } return false; }}上面这段代码要理解的概念有点儿多,实际上我现在也不是很懂,但是本文的目标是先让注解处理器跑起来,所以先不管。这里最重要也是折磨我最惨的地方,就是这一句@SupportedAnnotationTypes("com.shahuwang.processor.CheckGetter"),你定义的注解在哪个package下,这里就要写完整了,如果写错了,注解处理器就不起作用了。 ...

August 20, 2019 · 2 min · jiezi

Spring-注解编程之-AnnotationMetadata

在上篇文章 Spring 注解编程之模式注解 中我们讲到 Spring 模式注解底层原理,依靠 AnnotationMetadata 接口判断是否存在指定元注解。 这篇文章我们主要深入 AnnotationMetadata,了解其底层原理。 Spring 版本为 5.1.8-RELEASEAnnotationMetadata 结构使用 IDEA 生成 AnnotationMetadata 类图,如下: AnnotationMetadata 存在两个实现类分别为 StandardAnnotationMetadata与 AnnotationMetadataReadingVisitor。StandardAnnotationMetadata主要使用 Java 反射原理获取元数据,而 AnnotationMetadataReadingVisitor 使用 ASM 框架获取元数据。 Java 反射原理大家一般比较熟悉,而 ASM 技术可能会比较陌生,下面主要篇幅介绍 AnnotationMetadataReadingVisitor 实现原理。 基于 AnnotationMetadata#getMetaAnnotationTypes方法,查看两者实现区别。AnnotationMetadataReadingVisitorASM 是一个通用的 Java 字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。 ASM 虽然提供与其他 Java 字节码框架如 Javassist,CGLIB 类似的功能,但是其设计与实现小而快,且性能足够高。 Spring 直接将 ASM 框架核心源码内嵌于 Spring-core中,目前 Spring 5.1 使用 ASM 7 版本。 ASM 框架简单应用Java 源代码经过编译器编译之后生成了 .class 文件。 Class文件是有8个字节为基础的字节流构成的,这些字节流之间都严格按照规定的顺序排列,并且字节之间不存在任何空隙,对于超过8个字节的数据,将按 照Big-Endian的顺序存储的,也就是说高位字节存储在低的地址上面,而低位字节存储到高地址上面,其实这也是class文件要跨平台的关键,因为 PowerPC架构的处理采用Big-Endian的存储顺序,而x86系列的处理器则采用Little-Endian的存储顺序,因此为了Class文 件在各中处理器架构下保持统一的存储顺序,虚拟机规范必须对起进行统一。Class 文件中包含类的所有信息,如接口,字段属性,方法,在内部这些信息按照一定规则紧凑排序。ASM 框会以文件流的形式读取 class 文件,然后解析过程中使用观察者模式(Visitor),当解析器碰到相应的信息委托给观察者(Visitor)。 ...

July 13, 2019 · 1 min · jiezi

SpringBoot-动态代理反射注解AOP-优化代码三注解

上一篇SpringBoot 动态代理|反射|注解|AOP 优化代码(二)-反射 我们实现了通过反射完善找到目标类,然后通过动态代理提供默认实现,本篇我们将使用自定义注解来继续优化。 创建注解1.创建枚举 ClientType,用来标明Handler的实现方式 public enum ClientType { FEIGN,URL}2.创建注解ApiClient,用来标明Handler的实现方式 @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ApiClient { ClientType type();}3.创建HandlerRouterAutoImpl注解,来标记该HandlerRouter是否通过代理提供默认实现 @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface HandlerRouterAutoImpl { /** * 在spring容器中对应的名称 * @return */ String name();}4.DeviceHandlerRouter添加注解,以动态代理提供默认实现 @HandlerRouterAutoImpl(name = "deviceHandlerRouter")public interface DeviceHandlerRouter extends HandlerRouter<DeviceHandler> {}5.DeviceHandlerFeignImpl、DeviceHandlerUrlImpl 添加注解标明具体的实现方式 @ApiClient(type = ClientType.FEIGN)@Component@Slf4jpublic class DeviceHandlerFeignImpl implements DeviceHandler { @Autowired private DeviceFeignClient deviceFeignClient; @Override public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) { RestResult restResult = deviceFeignClient.create(remoteAddDeviceParam); ... } @Override public void remoteDeleteBatch(Integer envValue, List<String> snsList) { RestResult restResult = deviceFeignClient.deleteBySnList(snsList); ... } }@ApiClient(type = ClientType.URL)@Component@Slf4jpublic class DeviceHandlerUrlImpl implements DeviceHandler { @Override public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) { String url = getAddUrlByEnvValue(envValue); String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false); RestResult restResult = JSON.parseObject(response, RestResult.class); ... } @Override public void remoteDeleteBatch(Integer envValue, List<String> snsList) { String url = getDelUrlByEnvValue(envValue); String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false); RestResult restResult = JSON.parseObject(response, RestResult.class); ... }}6.通过注解扫描目标类 ...

July 11, 2019 · 2 min · jiezi

SpringBoot-动态代理反射注解AOP-优化代码二反射

SpringBoot 动态代理|反射|注解|AOP 优化代码(一)-动态代理提供接口默认实现 我们抛出问题,并且提出解决问题的第一步的方法。下面我们继续深入,动态代理和反射继续解决我们的问题。 改动代码结构新增一个HandlerRougter接口,其目的就是替代上一篇的DeviceHandlerRouter public interface HandlerRouter<T> { T getHandler(Integer env,Object... args);}其中T是具体的业务接口。下面实现DeviceHandler的HandlerRouter: public interface DeviceHandlerRouter extends HandlerRouter<DeviceHandler> {}那么上层代码的调用方式将会类似下面的代码: DeviceHandlerRouter deviceHandlerRouter = ...deviceHandlerRouter.getHandler(...). remoteAddBatch(...)反射+动态代理前面说过,每增加一种接口调用,就需要重新实现xxxHandlerRouter,那么下面我们通过动态代理和反射提供DeviceHandler的默认实现。 1.通过反射获取HandlerRouter<T>的子接口和泛型对应的类 首先加入下面的依赖 <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency>/** * 通过反射扫描出所有HandlerRouter的子类 * @return */private Set<Class<?>> getHandlerRouterClasses() { Reflections reflections = new Reflections( "package.name.*", new TypeAnnotationsScanner(),//注解扫描,本节用不到 new SubTypesScanner() ); return reflections.getSubTypesOf(HandlerRouter.class);}Set<Class<?>> classes = getHandlerRouterClasses();//获取HandlerRouter的子接口的泛型Class 例如:DeviceHandlerRouter接口的DeviceHandlerfor (Class<?> clazz : classes) { //clazz 对应DeviceHandlerRouter.class Type[] types = clazz.getGenericInterfaces(); ParameterizedType type = (ParameterizedType) types[0]; //typeName对应DeviceHandlerRouter extends HandlerRouter<DeviceHandler> 中的DeviceHandler.class String typeName = type.getActualTypeArguments()[0].getTypeName();}2.SpringBoot ApplicationContext 获取注入的bean ...

July 11, 2019 · 2 min · jiezi

TRR-立志做最简单易上手易扩展易维护的TP反射注释路由架构

简介TRR 开源地址Github: https://github.com/china-wang...Gitee: https://gitee.com/china_wangy...博客:https://china-wangyu.github.io/ TRR 是什么?TRR 是 ThinkPHP51 Reflection Restful API(注:API设计风格) 的字母第一个字符大写后拼接而来,从ThinkPHP51 Reflection Restful API全称可以看出来,这套接口框架设计主要围绕反射来做Restful API接口设计的。 TRR 可以做什么?你可以先了解一下ThinkPHP5.1能做什么。ThinkPHP5.1 能做的都能做,而且在反射路由方面,我们比ThinkPHP5.1更为专注专注做什么: 反射 API 接口路由反射 API 接口文档生成反射 API 参数快速验证让接口开发更简单、直观、迅捷让接口维护更轻松、明了、快速我们专注研究PHP反射相关的知识点,想让PHP web功能开发、接口开发更加简单、迅捷。 想让更多的朋友更加专注于业务开发,不再反复去做路由添加、修改,接口文档编写等一些列的问题 我们只想你的项目更快、更稳定、更以维护的成型。 使用须知在使用TRR时必定会用到的技能,你得做一个评估,查看自己是否可以无障碍使用。 涉及技术或框架 PHP7.1 一种支持热编译的脚本语言 你需要熟练掌握 PHP7.1 相关知识点,如果你精通此技能那就再好不过了,不精通也没关系,请根据我收集的教程和资料进行学习 PHP 官方文档: https://php.net/manual/zh/【极客学院】PHP全套教学视频: https://www.bilibili.com/video/av10274152?from=search&seid=2228250606023131784韩顺平php从入门到精通: https://pan.baidu.com/s/1YDQo... 提取码: 6hyyThinkPHP5.1 :中国比较流行且会一直流行的PHP框架如果你对ThinkPHP5.1不太了解,或者一知半解,请到官方文档进行查阅,补充效果知识点。官方文档 Reflection PHP 反射机制 如果你对 PHP 反射相关知识点不是很了解,推荐先进行了解一下 PHP官方文档: https://php.net/manual/zh/book.reflection.phpThinkPHP5.1 反射相关知识点: https://www.kancloud.cn/manual/thinkphp5_1/469333wangyu/reflex-core composer扩展使用: https://github.com/china-wangyu/php-reflex-coreRestful API 是一种API接口设计风格或者说潮流 如果你对 Restful API 还不了解,我收集了一些比较好的译文。 - RESTful 架构风格概述: https://juejin.im/entry/57c7a323a633bd006cfc1d84 ...

July 10, 2019 · 2 min · jiezi

Java系列之注解

原文发于微信公众号jzman-blog,欢迎关注交流。Java 注解(Annotation)又称之为 Java 标注、元数据,是 Java 1.5 之后加入的一种特殊语法,通过注解可以标注 Java 中的类、方法、属性、参数、包等,可以通过反射原理对这些元数据进行访问,注解的使用不会影响程序的正常运行,只会对编译器警告等辅助工具产生影响。 注解功能编译器可以使用注解来检测错误和取消警告;使用注解可以生成特定代码,如 ButtferKnife 使用注解简化 findViewById等;某些注解可以在运行时进行检查和操作。定义注解注解的定义使用 @interface 作为关键字,实际上表示自动继承了 java.lang.annotation.Annotation 接口,定义格式参考如下: @元注解public @interface AnnotationName{ //配置参数(参数类型 参数名称()) String name() default "hello";}配置参数里面的类型包括基本类型、String、class、枚举以及相关类型的数组,可以使用 default 设置配置参数的默认值,定义一个注解具体如下: @Target(value = {ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface TestDefineAnnotation { String[] name() default "test";}内置注解@Override@Deprecated@SuppressWarnings下面是上面三个内置注解的声明: //表示当前的方法将覆盖超类中的方法,编译时进行格式检查@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}//表示一个类或者是方法不再建议使用,将其标记为过时,但还是可以使用@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings { String[] value();}//表示关闭不当的编译器警告信息@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}根据对上面三个注解的声明来看,@SuppressWarnings 中定义了一个数组,这个数组表示在该注解上具体的目标是那些,如可在 SuppressWarnings 上使用的值,常用的具体如下: deprecation:使用了过时的类或方法时的警告unused:有未使用的变量时的警告unchecked:执行了未检查的转换时的警告fallthrough:当 switch 程序块直接通往下一种情况而没有 break 时的警告path:在类路径、源文件路径等中有不存在的路径时的警告serial:当在可序列化的类上缺少serialVersionUID 定义时的警告finally :任何 finally 子句不能正常完成时的警告all:关于以上所有情况的警告下面看一个案例,具体如下: ...

April 22, 2019 · 2 min · jiezi

TypeScript 注解(下)

引言继上周的文章《TypeScript 注解(上)》,补充在最后未实现的属性注解。本文可能需要扎实的JavaScript功底,我尽量给大家通俗地讲解。实现建立新项目上周的项目建立的太不正规了,直接建个文件夹,然后写的TS代码,太过不规范。这次我们使用npm init初始化一个新的前台项目。创建成功后,项目中就出现了我们熟知的package.json文件。安装依赖本次,我们需要使用反射实现一些高级的功能。反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。根据官方文档的环境要求,使用TypeScript反射,我们需要先安装reflect-metadata包。npm i reflect-metadata –save属性注解属性注解在属性上声明,属性注解的表达式在运行期间像函数一样被调用,并带着以下两个参数:如果是在静态成员上,则是类的构造函数;如是在实例成员上,则是类的原型。成员的名称。所以,一个标准的属性注解如下所示:返回一个带有两个参数的闭包。function annotation(target: string) { return function(target, propertyKey) { }}看下面的实例代码:这里直接返回Reflect.metadata代替原闭包,因为metadata方法的返回值,就符合该规范。import “reflect-metadata”;// 声明唯一的元数据keyconst formatMetaDataKey = Symbol(“format”);// 前缀注解function prefix(target: string) { // 根据key将前缀存至元数据中 return Reflect.metadata(formatMetaDataKey, target);}class Person { constructor(message: string) { this.message = message; } @prefix(“hello, “) message: string; speak(): void { // 获取元数据前缀,key,当前对象,message属性 const prefix = Reflect.getMetadata(formatMetaDataKey, this, “message”); console.log(prefix + this.message); }}const person = new Person(“world”);person.speak();通过在字符串属性上声明的prefix注解,来添加该字符串应该显示的前缀。功能很简单,也就不再赘述了。以后想写类似功能时照葫芦画瓢就是了,这里主要是对一些比较繁琐的代码加以研究。Symbol大家想必注意到了这里的Symbol了,这是个什么东西呢?// 声明唯一的元数据keyconst formatMetaDataKey = Symbol(“format”);Symbol是一种新的基本数据类型。去Google搜索JavaScript数据类型,发现谷歌的第一条已然过时,面试也常考,这里纠正一下。JavaScript共有7种数据类型,重点!!!6种基本数据类型:BooleanNullUndefinedNumberStringSymbol (ECMAScript 6 新定义)和Object。例子说来说去,Symbol到底是个啥玩意?一个例子带你看明白。let obj = { id: 1};obj[‘id’] = 2;// 覆盖了原idconsole.log(obj);const id = Symbol(‘id’);obj[id] = 3;// 添加Symbol key的属性console.log(obj);const id2 = Symbol(‘id’);obj[id2] = 6;// 不覆盖原Symbol keyconsole.log(obj);对象中允许字符串或Symbol作为key,从这个例子我们可以看出,字符串的key不具有唯一性,而Symbol的key能保证唯一,不进行覆盖。Token这个唯一性覆盖问题就像之前在网上送检项目中遇到的问题,因为项目已停工,也就没有和大家提。这里和大家简单说一下。使用的全局配置是使用字符串注入的,因为是一个容器,假设我们的框架中也用到了’CONFIG’的字符串注入,那我们加入的配置就会覆盖原框架配置,给框架注入了错误的对象,然后就出现了错误但是就是找不着原因的一系列问题。providers: [ { provide: ‘CONFIG’, useValue: YUNZHI_GLOBAL_CONFIG }]class TestComponent { constructor(@Inject(‘CONFIG’) private config) { }}其实Angular已经解决,以后不要再通过字符串注入,推荐通过InjectionToken注入。const CONFIG_TOKEN = new InjectionToken<VALUE_TYPE>(‘CONFIG’);总结初级程序员的业余生活,学习,学习,学习。 ...

April 19, 2019 · 1 min · jiezi

乐字节-Java8新特性之Base64和重复注解与类型注解

上一篇小乐给大家说了《乐字节-Java8新特性之Date API》,接下来小乐继续给大家说一说Java8新特性之Base64和重复注解与类型注解。一、Base64在Java 8中,内置了Base64编解码相关的特性。Java 8中使用三种类型的Base64编解码:简易模式:输出是完全按照A-Za-z0-9+/字符集映射的。编码不会自己增加输出行,解码器也不会接受任何超出A-Za-z0-9+/范围的内容。URL模式:输出基于A-Za-z0-9+/的映射,但对于URL和文件名是安全的。MIME模式:输出对于MIME类型的内容是友好的。如果超过76个字符,则会换行输出。,并且换行符n之后会自动添加一个r。如果某行没有r则说明输出的内容已经结束。1、Base64 内部类与方法Base64相关的内部类:Base64.Encoder:这是一个静态类。实现了Base64的编码功能,格式遵循了RFC 4648和RFC 2045标准。Base64.Decoder:也是一个静态类。实现了Base64的解码功能。相关的方法:getEncoder():该方法返回一个使用基本Base64编码格式的Encoder对象。相反的解码方法是getDecoder()。getUrlEncoder():该方法返回一个使用URL类型的Base64编码格式的Encoder对象。相反的解码方法是getUrlDecoder()。getMimeEncoder():该方法返回一个使用MIME类型的Base64编码格式的Encoder对象。相反的解码方法是getMimeDecoder()。2、Base64 使用对于Base64应用场景 无论是传统软件还是互联网项目开发都是比较常见的,比如传统的邮件,Http Url 地址通常都会应用Base64 来对协议内容或Url 地址信息进行编解码操作。public static void main(String[] args) throws Exception { // 使用基本的Base64编码 String base64encodedString = Base64.getEncoder() .encodeToString(“java8 is so Easy!!!".getBytes(“utf-8”)); System.out.println(“Basic base64 encoding:” + base64encodedString); // 解码并输出结果 byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString); System.out.println(“Original content: " + new String(base64decodedBytes, “utf-8”)); // 使用URL类型的Base64编码 base64encodedString = Base64.getUrlEncoder().encodeToString(“https://www.sina.com”.getBytes(“utf-8”)); System.out.println(“URL base64 encoding:” + base64encodedString); // MIME类型的Base64编码 StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 10; ++i) { stringBuilder.append(UUID.randomUUID().toString()); } byte[] mimeBytes = stringBuilder.toString().getBytes(“utf-8”); String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes); System.out.println(“MIME base64 encoding:” + mimeEncodedString); }二、重复注解与类型注解Java5引入了注解特性,使得开发更加的灵活,特别是现在很多的应用都是基于注解零配置开发,都是建立在Annotation基础之上,同时使得开发变得简单,Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。 通常用于框架底层代码开发1、可重复注解定义与使用/** * 定义可重复注解 /@Repeatable(MyParams.class)@Target({ ElementType.FIELD, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MyParam { String value() default “”;}@Target({ ElementType.FIELD, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MyParams { MyParam[] value();}// 使用注解 方法上标注重复注解@MyParam(“hello”)@MyParam(“java8”)public void testAnnotation(){ System.out.println(“可重复注解测试…”);}/* 查找指定方法级别注解 遍历输出注解value 值*/public static void main(String[] args) throws Exception{ Class<TestAnnotation> clazz = TestAnnotation.class; Method method = clazz.getMethod(“testAnnotation”); MyParam[] params = method.getAnnotationsByType(MyParam.class); for (MyParam param : params) { System.out.println(param.value()); }}2、用于类型的注解@Repeatable(MyParams.class)@Target({ ElementType.FIELD, ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface MyParam { String value() default “”;}//使用public class TestAnnotation { private MyParam param;// 定义成员变量param 类型为MyParam 注解类型 public static void main(String[] args) throws Exception{ // 获取成员变量 并输出变量类型 Field field= clazz.getDeclaredField(“param”); System.out.println(field); System.out.println(field.getType()); }} ...

April 15, 2019 · 1 min · jiezi

TypeScript 注解(上)

引言上次学习了一下typecirpt-ioc项目,一个优秀的IOC容器,那个项目中用到了TypeScript注解,反正比我写的容器高级多了。是时候学习一下TypeScript注解了。探究测试环境选择在WebStorm环境下建立一个示例项目,试了一下,报错。Experimental support for decorators is a feature that is subject to change in a future release. Set the ’experimentalDecorators’ option to remove this warning.对装饰器的实验性支持是一个在未来版本中可能会发生变化的特性,请设置xxx选项以移除该警告。注解发展史编译器所说的装饰器,其实就是我们所说的注解。看这个提示,应该是目前还不支持注解,还是在试验阶段。咦?那为什么Angular启用了注解呢?后来查到了这张图:主流浏览器全面支持ES5,所以我们无论用AngularJS的原生JavaScript开发也好,用Angular、React的TypeScript开发也好,最后到浏览器执行的代码都是ES5脚本。我们应该认识下面三个圈,ES5,ES6涵盖了ES5并扩充了类与模块,TypeScript又是ES6的超集。最外围的AtScript由Google AtScript团队提出,对TypeScript又进行了扩充,支持注解与introspection(这个不知道是啥,百度翻译是“自我反省”,水平不够不敢随便翻译)。Google AtScript团队已经将注解作为ES7的提案。但是经过测试,ES7环境下还不支持注解,可能是草案没有通过?后来,Google AtScript与Microsoft合作:We’re excited to announce that we have converged the TypeScript and AtScript languages, and that Angular 2, the next version of the popular JavaScript library for building web sites and web apps, will be developed with TypeScript.我们很高兴:我们已经融合了TypeScript和AtScript,并且Angular 2将采用TypeScript来开发。这就是我们的Angular。同样是TypeScript,我们可以像Spring中一样去加注解,而不需要像React一样去继承。class ShoppingList extends React.Component { render() { return ( <div className=“shopping-list”> <h1>Shopping List for {this.props.name}</h1> <ul> <li>Instagram</li> <li>WhatsApp</li> <li>Oculus</li> </ul> </div> ); }}自定义注解新建tsconfig.json文件,将compilerOptions中的experimentalDecorators设置为true,以忽略警告。注解(装饰器)是一类特殊类型的声明,可以用在类、方法、构造器、属性和参数上。其实本质上,定义一个注解,就是定义一个TypeScript方法,只是该方法必须符合官方的规范。注解工厂定义两个方法hello和world方法,两个方法分别返回符合规范的函数闭包,参数target、propertyKey、descriptor。经测试,这三个参数中target和propertyKey是必须的,没有的话编译过不去,descriptor可以省略。function hello() { console.log(“hello(): 加载.”); return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log(“hello(): 执行.”); }}function world() { console.log(“world(): 加载.”); return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log(“world(): 执行.”); }}class Main { @hello() @world() method() { console.log(‘method(): 执行.’); }}编译、执行:注解方法在编译期间执行。注解方法加载从上到下,闭包功能执行从下到上。打印三个参数的结果,具体含义在下面的方法注解一栏中讲解。类注解类注解应用于类的构造函数,可以使用它去观察、修改或替换类的定义。类注解不能在声明文件中被使用,或其他ambient context中使用。class Person { message: string; constructor(message: string) { this.message = message; } greet() { console.log(Hello ${this.message} !); }}const person = new Person(‘World’);person.greet();使用类注解,我们可以覆盖原来的构造函数,所以依赖注入可能就是用这种方式实现的。function classDecorator<T extends {new(…args:any[]):{}}>(constructor:T) { return class extends constructor { message = ‘Decorator’; }}@classDecoratorclass Person { message: string; constructor(message: string) { this.message = message; } greet() { console.log(Hello ${this.message} !); }}const person = new Person(‘World’);person.greet();覆盖掉了原来的构造函数。方法注解方法注解应用于方法的属性描述器,也可以观察、修改替换方法定义。方法注解不能在声明文件中被使用,或者是方法重载,或其他ambient context中使用。class Person { message: string; constructor(message: string) { this.message = message; } greet() { console.log(Hello ${this.message} !); }}const person = new Person(‘World’);for (const property in person) { console.log(property);}for in person对象,遍历出了message和greet。function enumerable(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { descriptor.enumerable = value; };}class Person { message: string; constructor(message: string) { this.message = message; } @enumerable(false) greet() { console.log(Hello ${this.message} !); }}const person = new Person(‘World’);for (const property in person) { console.log(property);}添加方法注解,@enumerable,将该方法设置为不可枚举。遍历时就没有greet了,恕我直言,我觉得这个属性描述器好像没什么用。致歉身体抱恙,头脑混乱,若有错误之处欢迎指出,还有一个利用反射实现的属性装饰器,以后再与大家详述。总结工程要的是经验,框架要的是功底。你喜欢哪一个? ...

April 14, 2019 · 2 min · jiezi

注解的奇妙之旅一

导读注解释义java开发人员对注解,应该不会很陌生。我们在开发的过程中,经常会用到注解,那么,什么是注解呢?注解,也被称为元数据,为我们在代码中添加信息,提供了一种形式化的方法是,使我们在稍后某个时刻,可以非常方便地使用这些原数据(thinking in java)。这句话是什么意思?举一个hibernate的@Table注解,我们在实体类上定义该注解,它不会立即生效,当我们启动Tomcat时,借助spring工作,便触发了该注解,从而创建了数据表。也就是说,我们先定义注解,等到合适的时间,我们在使用该注解。内置三大注解override注解我们在开发的过程中,经常会用到override注解,如代码所示: @Override public Result<List<Account>> listAccountById(Long memberId);该注解表示当前方法将覆盖超类中的方法。阿里巴巴规范要求,如果当前方法覆盖超类的方法,必须写上override注解。因为我们如果不小心拼写错误,或者方法签名对不上覆盖的方法,编译器就会发出错误地提示。我们忘记写override注解,但这并不影响使用。比如,我们需要重写接口AccountService的auditAcct方法签名,但并没有加上override注解,编译器就会错误的提示,但不是报错。我解释了override注解,其次,还有Deprecated注解和SuppressWarnings注解。Deprecated注解SuppressWarnings注解

April 11, 2019 · 1 min · jiezi

Flutter系列:4.基于注解的代码生成应用

前言api数据序列化为model实例是移动开发中很常见也是很基础的技术点,得益于运行时等动态技术在ios开发中我们可以借助JSONModel或者SwiftyJSON很方便的实现序列化,对于刚刚接触flutter的开发者来说其序列化体验无疑是非常糟糕的。本身Dart语言是支持反射的,但是在Flutter中,Dart几乎放弃了脚本语言动态化的特性,如不支持反射、也不支持动态创建函数等;所以序列化只有依靠拦截注解来动态生成代码的方式实现。注解注解是一种可以为代码提供一些语义信息或元数据的标注,这在其他语言中也很常见,在dart中常见的注解有@deprecated、@override等,注解是以@开头的,他们可以作用于类,函数,属性等。dart中自定义注解很简单,其实现就是一个带有const构造函数的类library todo;class Todo { final String who; final String what; const Todo(this.who, this.what);}然后就可以这样使用Todo这个注解了import ’todo.dart’;@Todo(‘seth’, ‘make this do something’)void doSomething() { print(‘do something’);}source_gen通过注解的方式我们就可以为类或者属性添加一个额外的数据信息,source_gen可以拦截注解获取并解析上下文信息,通过解析注解实现source_gen的相关Generator就可以动态的生成代码了;source_gen是封装自build和 analyzer,并在此基础上提供友好的api封装。build是一个提供构建控制的库,analyzer是提供dart语法静态分析功能的库,source_gen将其整合便可以实现一套基于注解的代码生成工具。代码生成使用Annotation+source_gen的方式可以便捷的生成代码,source_gen通过拦截Annotation,解析其上下文element然后通过builder即可动态生成代码,下面简易的代码生成Demo。创建package终端运行:flutter create –template=package code_gen_demovscode打开刚刚创建的package, pubspec.yaml添加source_gen和build_runner依赖dependencies: flutter: sdk: flutter source_gen: ‘>=0.8.0’lib目录下创建注解mark.dartclass Mark { final String name; const Mark({this.name});}创建代码生成器generator.dart 负责拦截我们的注解Mark, 解析注解的类名称,路径及其参数name并返回import ‘package:analyzer/dart/element/element.dart’;import ‘package:source_gen/source_gen.dart’;import ‘package:build/build.dart’;import ‘mark.dart’;class MarkGenerator extends GeneratorForAnnotation<Mark> { @override generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) { String className = element.displayName; String path = buildStep.inputId.path; String name =annotation.peek(’name’).stringValue; return “//$className\n//$path\n//$name”; }}lib目录创建构建器builder.dart, 添加一个顶级方法markBuilder供build runner解析调用import ‘package:source_gen/source_gen.dart’;import ‘package:build/build.dart’;import ‘mark_generator.dart’;Builder markBuilder(BuilderOptions options) => LibraryBuilder(MarkGenerator(), generatedExtension: ‘.mark.dart’);在package根目录下添加build.yaml文件(buildRunner会解析其配置执行builder指定的方法),配置成刚刚创建的builder内容如下targets: $default: builders: code_gen_demo|mark_builder: enabled: truebuilders: mark_builder: import: ‘package:code_gen_demo/builder.dart’ builder_factories: [‘markBuilder’] build_extensions: { ‘.dart’: [’.mark.dart’] } auto_apply: root_package build_to: sourceimport指定了builder的位置,builder_factories指定了builder的具体调用,build_extensions指定了输入输入文件的格式匹配,此列会生成".mark.dart"结尾的文件。至此代码生成相关的Annotation、 builder和Generator都准备好了,接下来我们创建example工程来做示例创建example工程在package的根目录下创建example工程,example是一个完整的flutter工程,执行命令:flutter create example在example工程中引入我们的package, 在example的pubspec.yaml中添加依赖package,以及添加对builder_runner的依赖来执行编译命令dependencies: flutter: sdk: flutter code_gen_demo: path: ../ # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2dev_dependencies: flutter_test: sdk: flutter build_runner: ‘>=0.9.1’创建一个示例类,mark_demo.dart, 并添加Mark注解import ‘package:code_gen_demo/mark.dart’;@Mark(name: “hello”)class MarkDemo { }好了,接下来在example目录下执行builder runner命令来为Mark注解的mark_demo.dart生成一个相关代码mark_demo.mark.dartflutter packages pub run build_runner build –delete-conflicting-outputs重新执行run builder_runner前最好先clean一下flutter packages pub run build_runner clean命令执行完成后就可以看到在mark_demo.dart文件下生成了一个mark_demo.mark.dart的文件,其内容是mark_generator.dart中为Mark这个注解创建的Generator返回的内容:// GENERATED CODE - DO NOT MODIFY BY HAND// **************************************************************************// MarkGenerator// **************************************************************************//MarkDemo//lib/mark_demo.dart//hello本demo源码位置GitHubeasy_router目前在Flutter中常见的代码生成主要应用在json序列化库json_serializable中,在国内闲鱼技术团队使用这一技术实现了一套router的路由映射解决方案annotation_route,感兴趣的可以看看。作为学习我参考了闲鱼的annotation_route实现了一个简单的Flutter页面路由匹配方案easy_router,不同于闲鱼annotation_route的复杂和全面,简单实现路由url的匹配、参数解析赋值并返回page实例。easy_router源码戳我使用方式使用@EasyRoute来注解需要加入Router的page, url作为page的唯一标识,例如@EasyRoute(url: “easy://flutter/pagea”)class PageA extends StatefulWidget { final EasyRouteOption routeOption; PageA(this.routeOption); @override _PageAState createState() => _PageAState();}easy_router会调用page的构造函数并传入EasyRouteOption参数,所以每个page都应该有一个这样的构造函数,如果url有参数,参数会放到EasyRouteOption对象的params属性中,以便page获取。使用@easyRouter来注解你的router, 这样就会生成router相关的内部逻辑, 例如import ‘package:example/route.router.internal.dart’;import ‘package:easy_router/route.dart’;@easyRouterclass Router { EasyRouterInternal internalImpl = EasyRouterInternalImpl(); dynamic getPage(String url) { EasyRouteResult result = internalImpl.router(url); if(result.state == EasyRouterResultState.NOT_FOUND) { print(“Router error: page not found”); return null; } return result.widget; } }EasyRouterInternalImpl就是最终生成的router实现, 执行命令生成EasyRouterInternalImpl实现flutter packages pub run build_runner build –delete-conflicting-outputs调用router打开url对应的pageMaterialButton( child: Text(‘ToPageA’), onPressed: (){ Navigator.of(context).push( MaterialPageRoute( builder: (context) { return Router().getPage(’easy://flutter/pagea?parama=a’); } ) ); },),感兴趣自己改改,详细使用参看源码example实现方式routeParseBuilder:负责解析@EasyRoute注解的page页面,完成page和url的映射关系routerBuilder:读取routeParseBuilder生成的映射,完成对EasyRouterInternalImpl写入,依赖mustache4dart库完成替换写入 ...

March 14, 2019 · 2 min · jiezi

Java之注解的定义及使用

Java的注解在实际项目中使用得非常的多,特别是在使用了Spring之后。本文会介绍Java注解的语法,以及在Spring中使用注解的例子。注解的语法注解的例子以Junit中的@Test注解为例@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Test { long timeout() default 0L;}可以看到@Test注解上有@Target()和@Retention()两个注解。这种注解了注解的注解,称之为元注解。跟声明了数据的数据,称为元数据是一种意思。之后的注解的格式是修饰符 @interface 注解名 { 注解元素的声明1 注解元素的声明2 }注解的元素声明有两种形式type elementName();type elementName() default value; // 带默认值常见的元注解@Target注解@Target注解用于限制注解能在哪些项上应用,没有加@Target的注解可以应用于任何项上。在java.lang.annotation.ElementType类中可以看到所有@Target接受的项TYPE 在【类、接口、注解】上使用FIELD 在【字段、枚举常量】上使用METHOD 在【方法】上使用PARAMETER 在【参数】上使用CONSTRUCTOR 在【构造器】上使用LOCAL_VARIABLE 在【局部变量】上使用ANNOTATION_TYPE 在【注解】上使用PACKAGE 在【包】上使用TYPE_PARAMETER 在【类型参数】上使用 Java 1.8 引入TYPE_USE 在【任何声明类型的地方】上使用 Java 1.8 引入@Test注解只允许在方法上使用。@Target(ElementType.METHOD)public @interface Test { … }如果要支持多项,则传入多个值。@Target({ElementType.TYPE, ElementType.METHOD})public @interface MyAnnotation { … }此外元注解也是注解,也符合注解的语法,如@Target注解。@Target(ElementType.ANNOTATION_TYPE)表明@Target注解只能使用在注解上。@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Target { ElementType[] value();}@Retention注解@Retention指定注解应该保留多长时间,默认是RetentionPolicy.CLASS。在java.lang.annotation.RetentionPolicy可看到所有的项SOURCE 不包含在类文件中CLASS 包含在类文件中,不载入虚拟机RUNTIME 包含在类文件中,由虚拟机载入,可以用反射API获取@Test注解会载入到虚拟机,可以通过代码获取@Retention(RetentionPolicy.RUNTIME)public @interface Test { … }@Documented注解主要用于归档工具识别。被注解的元素能被Javadoc或类似的工具文档化。@Inherited注解添加了@Inherited注解的注解,所注解的类的子类也将拥有这个注解注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface MyAnnotation { … }父类@MyAnnotation class Parent { … }子类Child会把加在Parent上的@MyAnnotation继承下来class Child extends Parent { … }@Repeatable注解Java 1.8 引入的注解,标识注解是可重复使用的。注解1public @interface MyAnnotations { MyAnnotation[] value(); }注解2@Repeatable(MyAnnotations.class)public @interface MyAnnotation { int value();}有使用@Repeatable()时的使用@MyAnnotation(1)@MyAnnotation(2)@MyAnnotation(3)public class MyTest { … }没使用@Repeatable()时的使用,@MyAnnotation去掉@Repeatable元注解@MyAnnotations({ @MyAnnotation(1), @MyAnnotation(2), @MyAnnotation(3)})public class MyTest { … } 这个注解还是非常有用的,让我们的代码变得简洁不少,Spring的@ComponentScan注解也用到这个元注解。元素的类型支持的元素类型8种基本数据类型(byte,short,char,int,long,float,double,boolean)StringClassenum注解类型数组(所有上边类型的数组)例子枚举类public enum Status { GOOD, BAD}注解1@Target(ElementType.ANNOTATION_TYPE)public @interface MyAnnotation1 { int val();}注解2@Target(ElementType.TYPE)public @interface MyAnnotation2 { boolean boo() default false; Class<?> cla() default Void.class; Status enu() default Status.GOOD; MyAnnotation1 anno() default @MyAnnotation1(val = 1); String[] arr(); }使用时,无默认值的元素必须传值@MyAnnotation2( cla = String.class, enu = Status.BAD, anno = @MyAnnotation1(val = 2), arr = {“a”, “b”})public class MyTest { … }Java内置的注解@Override注解告诉编译器这个是个覆盖父类的方法。如果父类删除了该方法,则子类会报错。@Deprecated注解表示被注解的元素已被弃用。@SuppressWarnings注解告诉编译器忽略警告。@FunctionalInterface注解Java 1.8 引入的注解。该注释会强制编译器javac检查一个接口是否符合函数接口的标准。特别的注解有两种比较特别的注解标记注解 : 注解中没有任何元素,使用时直接是 @XxxAnnotation, 不需要加括号单值注解 : 注解只有一个元素,且名字为value,使用时直接传值,不需要指定元素名@XxxAnnotation(100)利用反射获取注解Java的AnnotatedElement接口中有getAnnotation()等获取注解的方法。而Method,Field,Class,Package等类均实现了这个接口,因此均有获取注解的能力。例子注解@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})public @interface MyAnno { String value(); }被注解的元素@MyAnno(“class”)public class MyClass { @MyAnno(“feild”) private String str; @MyAnno(“method”) public void method() { } }获取注解public class Test { public static void main(String[] args) throws Exception { MyClass obj = new MyClass(); Class<?> clazz = obj.getClass(); // 获取对象上的注解 MyAnno anno = clazz.getAnnotation(MyAnno.class); System.out.println(anno.value()); // 获取属性上的注解 Field field = clazz.getDeclaredField(“str”); anno = field.getAnnotation(MyAnno.class); System.out.println(anno.value()); // 获取方法上的注解 Method method = clazz.getMethod(“method”); anno = method.getAnnotation(MyAnno.class); System.out.println(anno.value()); } }在Spring中使用自定义注解注解本身不会有任何的作用,需要有其他代码或工具的支持才有用。需求设想现有这样的需求,程序需要接收不同的命令CMD,然后根据命令调用不同的处理类Handler。很容易就会想到用Map来存储命令和处理类的映射关系。由于项目可能是多个成员共同开发,不同成员实现各自负责的命令的处理逻辑。因此希望开发成员只关注Handler的实现,不需要主动去Map中注册CMD和Handler的映射。最终效果最终希望看到效果是这样的@CmdMapping(Cmd.LOGIN)public class LoginHandler implements ICmdHandler { @Override public void handle() { System.out.println(“handle login request”); }}@CmdMapping(Cmd.LOGOUT)public class LogoutHandler implements ICmdHandler { @Override public void handle() { System.out.println(“handle logout request”); }}开发人员增加自己的Handler,只需要创建新的类并注上@CmdMapping(Cmd.Xxx)即可。具体做法具体的实现是使用Spring和一个自定义的注解定义@CmdMapping注解@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Componentpublic @interface CmdMapping { int value(); }@CmdMapping中有一个int类型的元素value,用于指定CMD。这里做成一个单值注解。这里还加了Spring的@Component注解,因此注解了@CmdMapping的类也会被Spring创建实例。然后是CMD接口,存储命令。public interface Cmd { int REGISTER = 1; int LOGIN = 2; int LOGOUT = 3;}之后是处理类接口,现实情况接口会复杂得多,这里简化了。public interface ICmdHandler { void handle(); }上边说过,注解本身是不起作用的,需要其他的支持。下边就是让注解生效的部分了。使用时调用handle()方法即可。@Componentpublic class HandlerDispatcherServlet implements InitializingBean, ApplicationContextAware { private ApplicationContext context; private Map<Integer, ICmdHandler> handlers = new HashMap<>(); public void handle(int cmd) { handlers.get(cmd).handle(); } public void afterPropertiesSet() { String[] beanNames = this.context.getBeanNamesForType(Object.class); for (String beanName : beanNames) { if (ScopedProxyUtils.isScopedTarget(beanName)) { continue; } Class<?> beanType = this.context.getType(beanName); if (beanType != null) { CmdMapping annotation = AnnotatedElementUtils.findMergedAnnotation( beanType, CmdMapping.class); if(annotation != null) { handlers.put(annotation.value(), (ICmdHandler) context.getBean(beanType)); } } } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; }}主要工作都是Spring做,这里只是将实例化后的对象put到Map中。测试代码@ComponentScan(“pers.custom.annotation”)public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class); HandlerDispatcherServlet servlet = context.getBean(HandlerDispatcherServlet.class); servlet.handle(Cmd.REGISTER); servlet.handle(Cmd.LOGIN); servlet.handle(Cmd.LOGOUT); context.close(); }}> 完整项目总结可以看到使用注解能够写出很灵活的代码,注解也特别适合做为使用框架的一种方式。所以学会使用注解还是很有用的,毕竟这对于上手框架或实现自己的框架都是非常重要的知识。 ...

February 19, 2019 · 2 min · jiezi

注解认证

问题描述权限认证权限认证一直是比较复杂的问题,如果是实验这种要求不严格的产品,直接逃避掉权限认证。软件设计与编程实践的实验,后台直接用Spring Data REST,好使是好使,但是不能在实际项目中运用,直接把api自动生成了,谁调用都行。在商业项目中,没有权限是不行的。注解关于权限,一直没有找到很好的解决方案。直到网上送检项目,因功能简单,且用户角色单一,潘老师提出了利用注解实现权限认证的方案。两个注解,AdminOnly标注只能给管理员用的方法,Anonymous标注对外的无需认证的接口,其他的未标注的是给普通用户使用的。示例代码示例代码地址:todo开发环境:Java 1.8 + Spring Boot 2.1.2.RELEASE实现拦截器根据三类方法,对用户权限进行拦截,使用拦截器 + AOP的模式实现。拦截器拦截下那些没有AdminOnly与Anonymous注解标注的方法请求,并进行用户认证。拦截器过完之后,去执行请求方法。AOP切AdminOnly注解的前置通知,植入一段管理员认证的切面逻辑。对Anonymous注解不进行任何处理,实现了匿名用户的访问。区别这样一看,拦截器就和AOP很像。那是因为我们这个例子还远没有发挥出AOP的实际价值。AOP比这个例子中看上去,强大得多。最近学习了设计模式中的代理模式,与AOP息息相关,我会在以后的文章中与大家一同学习。拦截器声明拦截器,第三个参数就是当前被拦截的方法。public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler;}基本思路利用反射获取当前方法中是否标注有AdminOnly与Anonymous注解,如果没有,则进行普通用户认证。AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);if (adminOnly != null && anonymous != null) { return true;}boolean result = false;// 进行用户认证return result;性能优化反射每次请求,都要走拦截器,调用getMethodAnnotation方法。我们去看看getMethodAnnotation方法的源码实现:org.springframework.web.method.HandlerMethod中的getMethodAnnotation方法:@Nullablepublic <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) { return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);}该方法又调用了AnnotatedElementUtils.findMergedAnnotation方法,我们再点进去看看:org.springframework.core.annotation.AnnotatedElementUtils中的findMergedAnnotation实现:@Nullablepublic static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { // Shortcut: directly present on the element, with no merging needed? A annotation = element.getDeclaredAnnotation(annotationType); if (annotation != null) { return AnnotationUtils.synthesizeAnnotation(annotation, element); } // Exhaustive retrieval of merged annotation attributes… AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false); return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null);}该方法是调用AnnotatedElement接口中声明的getDeclaredAnnotation方法进行注解获取:AnnotatedElement接口,存在于java反射包中:话不多说,反射,就存在性能问题!个人理解同样是Java,我们看看Google对于Android反射的态度就好了。我记得之前我去过Google Android的官网,官方不推荐在Android中使用框架,这可能带来严重的性能问题,其中就有考虑到传统Java框架中大量使用的反射。这是国外一篇关于反射的文章,反射到底有多慢?:How Slow is Reflection in Android?文中提到了一项规范,即用户期待应用的启动时间的平均值为2s。NYTimes Android App中使用Google的Gson进行数据解析,这个在我们后台使用的还是挺广泛的,和阿里的fastjson齐名,都是非常火的json库。NYTimes的工程师发现Gson中使用反射来获取数据类型,导致应用启动时增加了大约700ms的延迟。ActiveAndroid是一个使用反射实现的库,特意去Github逛了一手,4000多star,这是相当流行的开源项目了!Scribd:1093ms for call com.activeandroid.ActiveAndroid.initialize。Myntra:1421ms for call com.activeandroid.ActiveAndroid.initialize。Data-Binding打脸?Android不是不推荐使用框架吗?那为什么Google又推出了Data-Binding呢?注意,Google考虑的是第三方框架高额的开销而引发性能问题。去看看Data-Binding的优点,最重要的一条就是该框架不使用反射,使用动态代码生成技术,不会因为使用该框架而造成性能问题。直接根据编写的代码生成原生Android的代码,所以不会存在任何性能问题!解决方案为了解决拦截器中使用反射的性能问题,我们学习SpringBoot的设计思路,在启动时直接完成所有反射注解的读取,存入内存。之后每次拦截器直接从内存中读取,提高性能。监听容器启动事件,在容器启动时执行以下代码,扫描所有控制器,及其方法上的注解,如果符合条件,则放到HashMap中。// 初始化组件扫描Scanner,禁用默认的filterClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);// 添加过滤条件,要求组件上有RestController注解scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));// 在当前项目包下扫描所有符合条件的组件for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) { // 获取当前组件的完整类名 String name = beanDefinition.getBeanClassName(); try { // 利用反射获取相关类 Class<?> clazz = Class.forName(name); // 初始化方法名List List<String> methodNameList = new ArrayList<>(); // 获取当前类(不包括父类,所以要求控制器间不能继承)中所有声明方法 for (Method method : clazz.getDeclaredMethods()) { // 获取方法上的注解 AdminOnly adminOnly = method.getAnnotation(AdminOnly.class); Anonymous anonymous = method.getAnnotation(Anonymous.class); // 如果该方法不存在AdminOnly和Anonymous注解 if (adminOnly == null && anonymous == null) { // 添加到List中 methodNameList.add(method.getName()); } } // 添加到Map中 AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList); } catch (ClassNotFoundException e) { logger.error(“扫描注解配置时,发生了ClassNotFoundException异常”); }}拦截器修改原来的拦截器是这样的:AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);if (adminOnly != null && anonymous != null) { return true;}boolean result = false;// 进行用户认证return result;现在是这样的:logger.debug(“获取当前请求方法的组件类型”);Class<?> clazz = handlerMethod.getBeanType();logger.debug(“获取当前处理请求的方法名”);String methodName = handlerMethod.getMethod().getName();logger.debug(“获取当前类中需认证的方法名”);List<String> authMethodNames = AuthAnnotationConfig.getAnnotationsMap().get(clazz);logger.debug(“如果List为空或者不包含在认证方法中,释放拦截”);if (authMethodNames == null || !authMethodNames.contains(methodName)) { return true;}logger.debug(“进行用户认证”);boolean result = false;// 用户认证return result;之前用了两次反射,现在是调用了handlerMethod.getBeanType()和handlerMethod.getMethod().getName()。再去看看这两个的实现:getBeanTypepublic Class<?> getBeanType() { return this.beanType;}getMethodpublic Method getMethod() { return this.method;}都是在org.springframework.web.method.HandlerMethod类中直接返回属性,我们推断:这个HandlerMethod,应该是Spring在容器启动时就已经构造好的方法对象,在拦截器执行期间,没有调用反射。注解的注解现在是注解少,我们写两行,感觉问题不大:// 获取方法上的注解AdminOnly adminOnly = method.getAnnotation(AdminOnly.class);Anonymous anonymous = method.getAnnotation(Anonymous.class);以后如果认证注解多了呢?我们期待这样,有一个通用的注解来判定当前方法是否要被拦截,而AdminOnly和Anonymous应继承该注解的功能,这样以后再想添加不被拦截器拦截的注解,就不需要修改启动时扫描的方法了。// 获取授权注解AdminAuth adminAuth = method.getAnnotation(AdminAuth.class);我们期望像Spring Boot一样,在注解上加注解,实现复合注解。@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@ControllerAdvice@ResponseBodypublic @interface RestControllerAdvice {}构造注解如果对Java自定义注解不了解,可以去慕课网学习相关课程:全面解析Java注解 - 慕课网@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface AdminAuth {}@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),该注解可以标注在方法上,也可以标注在其他注解上。@Retention(RetentionPolicy.RUNTIME),该注解一直保留到程序运行期间。给注解加注解AdminOnly:@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@AdminAuthpublic @interface AdminOnly {}Anonymous:@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@AdminAuthpublic @interface Anonymous {}解析注解加注解很简单,重要的是怎么解析该注解。调用反射包中的Method类提供的getAnnotation方法,只会告诉我们当前标注了什么注解。比如:@AdminOnlypublic void test() {}我们可以通过getAnnotation获取AdminOnly,但是获取不到注解在@AdminOnly上的@AdminAuth注解。怎么获取注解的注解呢?找了一上午,不得不说,我解决这个问题还是靠一定的运气的。在我要放弃的时候,在Google搜出了SpringFramework中的注解工具类AnnotationUtils:随手打开文档:Class AnnotationUtils - Spring Core Docs第四个方法就是我想要的:使用该工具类,能直接获取方法上标注在注解上的注解:@AdminOnlypublic void test() {}AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class);这种方法能获取到标注在test方法上继承而来的@AdminAuth注解。最终代码:@Componentpublic class InitAnnotationsConfig implements ApplicationListener<ContextRefreshedEvent> { // 基础包名 private static final String basePackageName = “com.mengyunzhi.checkApplyOnline”; // 日志 private static final Logger logger = LoggerFactory.getLogger(InitAnnotationsConfig.class); @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 初始化组件扫描Scanner,禁用默认的filter ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); // 添加过滤条件,要求组件上有RestController注解 scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class)); // 在当前项目包下扫描所有符合条件的组件 for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) { // 获取当前组件的完整类名 String name = beanDefinition.getBeanClassName(); try { // 利用反射获取相关类 Class<?> clazz = Class.forName(name); // 初始化方法名List List<String> methodNameList = new ArrayList<>(); // 获取当前类(不包括父类,所以要求控制器间不能继承)中所有声明方法 for (Method method : clazz.getDeclaredMethods()) { // 获取授权注解 AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class); // 如果该方法不被授权,则需要认证 if (adminAuth == null) { // 添加到List中 methodNameList.add(method.getName()); } } // 添加到Map中 AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList); } catch (ClassNotFoundException e) { logger.error(“扫描注解配置时,发生了ClassNotFoundException异常”); } } }}总结学会了一个解决问题的新办法:某个框架应该也遇到过你所遇到的问题,去找找框架中的工具类,这可能会很有帮助。 ...

January 23, 2019 · 2 min · jiezi