精华内容
下载资源
问答
  • java 自定义注解以及获得注解的
    2021-12-06 18:45:43

    1.自定义注解

    复制代码
    import java.lang.annotation.*;

    @Documented
    @Target(ElementType.FIELD)
    @Inherited
    @Retention(RetentionPolicy.RUNTIME )
    public @interface MyAnno {
    /**
    * 是否能为null
    * @return
    */
    boolean isCanNull() default true;

    /**
     * 是否能为空字符串
     * @return
     */
    boolean isCanEmpty() default true;
    
    /**
     * 是否能为0
     * @return
     */
    boolean isCanZero() default true;
    

    }
    复制代码
    2.使用注解:

    复制代码
    public class Mouse {

    @MyAnno(isCanNull=true)
    private  String name;
    @MyAnno(isCanNull = false,isCanZero = false)
    private  int age;
    @MyAnno(isCanNull = false)
    private String address;
    @MyAnno(isCanZero = false)
    private double money;
    
    public String getAddress() {
        return address;
    }
    
    public void setAddress(String address) {
        this.address = address;
    }
    
    public double getMoney() {
        return money;
    }
    
    public void setMoney(double money) {
        this.money = money;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    

    }
    复制代码
    3.获得注解的值并对其判断

    复制代码
    package com.vweb.util;

    import com.vweb.webapp.Mouse;
    import com.vweb.webapp.MyAnno;
    import com.vweb.webapp.TestUtil;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.util.Arrays;
    import java.util.List;

    public class IntactCheckUtil {

    public static boolean check(Object obj){
           // list = Arrays.asList(AnnotationParsing.class.getClassLoader().loadClass(((Class)obj).getClass().getName()).getDeclaredFields());
        List<Field>  list = Arrays.asList(obj.getClass().getDeclaredFields());
        for(int i=0;i<list.size();i++){
            Field field = list.get(i);
            if(field.isAnnotationPresent(MyAnno.class)){//是否使用MyAnno注解
                for (Annotation anno : field.getDeclaredAnnotations()) {//获得所有的注解
                    if(anno.annotationType().equals(MyAnno.class) ){//找到自己的注解
                        if(!((MyAnno)anno).isCanNull()){//注解的值
                            if(TestUtil.getFieldValueByName(field.getName(),obj)==null){
                                throw new RuntimeException("类:"+Mouse.class+"的属性:"+field.getName()+"不能为空,实际的值:"+TestUtil.getFieldValueByName(field.getName(),obj)+",注解:field.getDeclaredAnnotations()");
                            }
                        }
                        if(!((MyAnno)anno).isCanEmpty()){
                            if(TestUtil.getFieldValueByName(field.getName(),obj).equals("")){
                                throw new RuntimeException("类:"+Mouse.class+"的属性:"+field.getName()+"不能为空字符串,实际的值:"+TestUtil.getFieldValueByName(field.getName(),obj)+",注解:field.getDeclaredAnnotations()");
                            }
                        }
                        if(!((MyAnno)anno).isCanZero()){
                            if(TestUtil.getFieldValueByName(field.getName(),obj).toString().equals("0") || TestUtil.getFieldValueByName(field.getName(),obj).toString().equals("0.0")){
                                throw new RuntimeException("类:"+Mouse.class+"的属性:"+field.getName()+"不能为空字符0,实际的值:"+TestUtil.getFieldValueByName(field.getName(),obj)+",注解:field.getDeclaredAnnotations()");
                            }
                        }
                    }
                }
            }
    
        }
        return  true;
    }
    

    }
    复制代码
    备注:注解各参数的使用(以下内容来自互联网http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html)

    @Target:

    @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

    取值(ElementType)有:
    

    1.CONSTRUCTOR:用于描述构造器
        2.FIELD:用于描述域
        3.LOCAL_VARIABLE:用于描述局部变量
        4.METHOD:用于描述方法
        5.PACKAGE:用于描述包
        6.PARAMETER:用于描述参数
        7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

    @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

    取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
        2.CLASS:在class文件中有效(即class保留)
        3.RUNTIME:在运行时有效(即运行时保留)

    @Documented:

    该属性用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

    @Inherited:

    @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

    注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

    当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

    自定义注解:

    使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

    定义注解格式:
      public @interface 注解名 {定义体}

    注解参数的可支持数据类型:

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
        2.String类型
        3.Class类型
        4.enum类型
        5.Annotation类型
        6.以上所有类型的数组

    Annotation类型里面的参数该怎么设定:
      第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
      第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
      第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

    更多相关内容
  • 主要介绍了java注解之运行时修改字段注解值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 自定义一个注解 JsonName.class import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; ...

    自定义一个注解 JsonName.class

    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target({FIELD, METHOD})
    public @interface JsonName {
    
    	/**
    	 * @return 自定义字段名,如果value的值无可见字符,则该注解不起作用
    	 */
    	String value() default "";
    	
    	/**
    	 * @return 是否覆盖 @JacksonXmlProperty @JsonProperty @JsonGetter @JsonSetter 注解的定义字段名功能
    	 */
    	boolean override() default true;
    }
    

    创建一个实体类 Person.class 并加注解

    import com.fasterxml.jackson.annotation.JsonGetter;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonSetter;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
    
    public class Person {
    
    	@JsonName("nm")
    	@JsonProperty(value = "xingming")
    	private String name;
    	
    	@JsonName(value = "nianling", override = false)
    	@JacksonXmlProperty(isAttribute = true)
    	private String age;
    	
    	private String sex;
    	
    	private String testName;
    	
    	@JsonName(value = "money", override = false)
    	@JsonProperty("gongzi")
    	private String salary;
    	
    	@JsonProperty("gongzuo")
    	private String work;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public String getAge() {
    		return age;
    	}
    
    	public void setAge(String age) {
    		this.age = age;
    	}
    
    	public String getSex() {
    		return sex;
    	}
    
    	public void setSex(String sex) {
    		this.sex = sex;
    	}
    
    	public String getSalary() {
    		return salary;
    	}
    
    	public void setSalary(String salary) {
    		this.salary = salary;
    	}
    
    	@JsonName(value = "tName", override = false)
    	@JsonGetter("test")
    	public String getTestName() {
    		return testName;
    	}
    
    	@JsonName(value = "tName", override = false)
    	@JsonSetter("test")
    	public void setTestName(String testName) {
    		this.testName = testName;
    	}
    
    	public String getWork() {
    		return work;
    	}
    
    	public void setWork(String work) {
    		this.work = work;
    	}
    
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", testName=" + testName + ", salary="
    				+ salary + ", work=" + work + "]";
    	}
    	
    }
    

    创建序列化修改器 JsonNameBeanSerializerModifier.class

    import java.util.ArrayList;
    import java.util.List;
    
    import com.fasterxml.jackson.annotation.JsonGetter;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.databind.BeanDescription;
    import com.fasterxml.jackson.databind.SerializationConfig;
    import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
    import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
    import com.fasterxml.jackson.databind.util.NameTransformer;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
    
    public class JsonNameBeanSerializerModifier extends BeanSerializerModifier {
    
    	@Override
    	public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
    			List<BeanPropertyWriter> beanProperties) {
    		List<BeanPropertyWriter> vbeanProperties = new ArrayList<BeanPropertyWriter>(beanProperties);
    		beanProperties.clear();
    		for (BeanPropertyWriter beanPropertyWriter : vbeanProperties) {
    			JsonName jsonName = beanPropertyWriter.getAnnotation(JsonName.class);
    			if (jsonName == null) {
    				beanProperties.add(beanPropertyWriter);
    			} else {
    				String value = jsonName.value();
    				value = value == null ? "" : value.trim();
    				if (value.equals("")) {
    					beanProperties.add(beanPropertyWriter);
    				} else {
    					if (jsonName.override()) {
    						beanProperties.add(beanPropertyWriter.rename(new PropertyNameTransformer(value)));
    					} else {
    						JsonProperty jsonProperty = beanPropertyWriter.getAnnotation(JsonProperty.class);
    						JsonGetter jsonGetter = beanPropertyWriter.getAnnotation(JsonGetter.class);
    						JacksonXmlProperty jacksonXmlProperty = beanPropertyWriter.getAnnotation(JacksonXmlProperty.class);
    						if (jsonProperty != null && !isBlank(jsonProperty.value())
    								|| jsonGetter != null && !isBlank(jsonGetter.value())
    								|| jacksonXmlProperty != null && !isBlank(jacksonXmlProperty.localName())) {
    							beanProperties.add(beanPropertyWriter);
    						} else {
    							beanProperties.add(beanPropertyWriter.rename(new PropertyNameTransformer(value)));
    						}
    					}
    				}
    			}
    			// 修改属性名称
    		}
    		return beanProperties;
    	}
    	
    	private static boolean isBlank(String str) {
    		return str == null || str.trim().equals("");
    	}
    
    	private static class PropertyNameTransformer extends NameTransformer {
    		
    		private String value;
    		
    		public PropertyNameTransformer(String value) {
    			super();
    			this.value = value;
    		}
    
    		@Override
    		public String transform(String name) { // 返回新属性名称
    			return value;
    		}
     
    		@Override
    		public String reverse(String transformed) {
    			return transformed;
    		}
    	}
    
    }
    

    创建反序列化修改器 JsonNameBeanDeserializerModifier.class

    import com.fasterxml.jackson.databind.BeanDescription;
    import com.fasterxml.jackson.databind.DeserializationConfig;
    import com.fasterxml.jackson.databind.PropertyName;
    import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
    import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
    import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class JsonNameBeanDeserializerModifier extends BeanDeserializerModifier {
    
        @Override
        public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config, BeanDescription beanDesc, List<BeanPropertyDefinition> propDefs) {
        	List<BeanPropertyDefinition> vbeanProperties = new ArrayList<BeanPropertyDefinition>(propDefs);
        	propDefs.clear();
        	for (BeanPropertyDefinition beanPropertyDefinition : vbeanProperties) {
    			AnnotatedMethod setter = beanPropertyDefinition.getSetter();
    			JsonName jsonName = null;
    			if (setter != null) {
    				jsonName = setter.getAnnotation(JsonName.class);
    			}
    			if (jsonName == null) {
    				jsonName = beanPropertyDefinition.getField().getAnnotation(JsonName.class);
    			}
    			if (jsonName == null) {
    				propDefs.add(beanPropertyDefinition);
    			} else {
    				String value = jsonName.value();
    				value = value == null ? "" : value.trim();
    				if (value.equals("")) {
    					propDefs.add(beanPropertyDefinition);
    				} else {
    					if (!jsonName.override()) {
    						propDefs.add(beanPropertyDefinition);
    					}
    					propDefs.add(beanPropertyDefinition.withName(PropertyName.construct(value)));
    				}
    			}
    		}
        	
            return propDefs;
        }
    }
    

    测试

    import java.io.IOException;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.PropertyNamingStrategies;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    
    public class Test {
    
    	public static void main(String[] args) throws IOException {
    		Person person = new Person();
    		person.setName("一个人");
    		person.setAge("18");
    		person.setSex("1");
    		person.setSalary("20000.00");
    		person.setTestName("测试人");
    		person.setWork("工作");
    		
    		ObjectMapper mapper = new XmlMapper();
    		mapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE);
    		SimpleModule seModule = new SimpleModule();
    		seModule.setSerializerModifier(new JsonNameBeanSerializerModifier());
    		SimpleModule deModule = new SimpleModule();
    		deModule.setDeserializerModifier(new JsonNameBeanDeserializerModifier());
    		mapper.registerModules(seModule, deModule);
    		String value = mapper.writeValueAsString(person);
    		System.out.println(value);
    		
    		Person readValue = mapper.readValue(value, Person.class);
    		System.out.println(readValue);
    	}
    }
    

    打印结果

    <Person nianling="18"><Sex>1</Sex><nm>一个人</nm><test>测试人</test><gongzi>20000.00</gongzi><gongzuo>工作</gongzuo></Person>
    Person [name=一个人, age=18, sex=1, testName=测试人, salary=20000.00, work=工作]
    
    展开全文
  • java 自定义注解验证可自己添加所需要的注解,下面这篇文章主要给大家介绍了关于JAVA中通过自定义注解进行数据验证的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习...
  • 自定义注解,修改注解参数

    千次阅读 2020-11-17 10:31:58
    java中元注解有四个: @Retention @Target @Document @Inherited; @Retention:注解的保留位置  @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含 @Retention...

    java中元注解有四个: @Retention @Target @Document @Inherited
    @Retention:注解的保留位置         
    @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
    @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
    @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
    @Target:注解的作用目标
    @Target(ElementType.TYPE) //接口、类、枚举
    @Target(ElementType.FIELD) //字段、枚举的常量
    @Target(ElementType.METHOD) //方法
    @Target(ElementType.PARAMETER) //方法参数
    @Target(ElementType.CONSTRUCTOR) //构造函数
    @Target(ElementType.LOCAL_VARIABLE) //局部变量
    @Target(ElementType.ANNOTATION_TYPE) //注解
    @Target(ElementType.PACKAGE) //包
    @Document:说明该注解将被包含在javadoc中
    @Inherited:说明子类可以继承父类中的该注解

    1注解的概念

    1.1注解的官方定义

    首先看看官方对注解的描述:
    An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

    翻译:
    注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。

    通过官方描述得出以下结论:

    注解是一种元数据形式。即注解是属于java的一种数据类型,和类、接口、数组、枚举类似。
    注解用来修饰,类、方法、变量、参数、包。
    注解不会对所修饰的代码产生直接的影响。

    1.2 注解的使用范围

    继续看看官方对它的使用范围的描述:
    Annotations have a number of uses, among them:Information for the complier - Annotations can be used by the compiler to detect errors or suppress warnings.Compiler-time and deployment-time processing - Software tools can process annotation information to generate code, XML files, and so forth.Runtime processing - Some annotations are available to be examined at runtime.

    翻译:

    注解又许多用法,其中有:为编译器提供信息 - 注解能被编译器检测到错误或抑制警告。编译时和部署时的处理 - 软件工具能处理注解信息从而生成代码,XML文件等等。运行时的处理 - 有些注解在运行时能被检测到。

    2 如何自定义注解

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

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

    2.1 基本语法

    注解类型的声明部分:

    注解在Java中,与类、接口、枚举类似,因此其声明语法基本一致,只是所使用的关键字有所不同@interface。在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。

    public @interface CherryAnnotation {
    }

    注解类型的实现部分:

    根据我们在自定义类的经验,在类的实现部分无非就是书写构造、属性或方法。但是,在自定义注解中,其实现部分只能定义一个东西:注解类型元素(annotation type element)。咱们来看看其语法:

    public @interface CherryAnnotation {
    	public String name();
    	int age();
    	int[] array();
    }
    

    也许你会认为这不就是接口中定义抽象方法的语法嘛?别着急,咱们看看下面这个:

    public @interface CherryAnnotation {
    	public String name();
    	int age() default 18;
    	int[] array();
    }
    

    看到关键字default了吗?还觉得是抽象方法吗?

    注解里面定义的是:注解类型元素!

    定义注解类型元素时需要注意如下几点:

    访问修饰符必须为public,不写默认为public;
    该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;
    该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
    ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
    default代表默认值,值必须和第2点定义的类型一致;
    如果没有默认值,代表后续使用注解时必须给该类型元素赋值。
    可以看出,注解类型元素的语法非常奇怪,即又有属性的特征(可以赋值),又有方法的特征(打上了一对括号)。但是这么设计是有道理的,我们在后面的章节中可以看到:注解在定义好了以后,使用的时候操作元素类型像在操作属性,解析的时候操作元素类型像在操作方法。

    2.2 常用的元注解

    一个最最基本的注解定义就只包括了上面的两部分内容:

    1. 注解的名字
    2. 注解包含的类型元素。但是,我们在使用JDK自带注解的时候发现,有些注解只能写在方法上面(比如@Override);有些却可以写在类的上面(比如@Deprecated)。当然除此以外还有很多细节性的定义,那么这些定义该如何做呢?接下来就该元注解出场了!
      元注解:专门修饰注解的注解。它们都是为了更好的设计自定义注解的细节而专门设计的。我们为大家一个个来做介绍。

    2.2.1 @Target

    @Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义如下:

    public enum ElementType {
        /** 类,接口(包括注解类型)或枚举的声明 */
        TYPE,
    
        /** 属性的声明 */
        FIELD,
    
        /** 方法的声明 */
        METHOD,
    
        /** 方法形式参数声明 */
        PARAMETER,
    
        /** 构造方法的声明 */
        CONSTRUCTOR,
    
        /** 局部变量声明 */
        LOCAL_VARIABLE,
    
        /** 注解类型声明 */
        ANNOTATION_TYPE,
    
        /** 包的声明 */
        PACKAGE
    }
    
    //@CherryAnnotation被限定只能使用在类、接口或方法上面
    @Target(value = {ElementType.TYPE,ElementType.METHOD})
    public @interface CherryAnnotation {
        String name();
        int age() default 18;
        int[] array();
    }
    

    2.2.2 @Retention

    @Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。
    注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。同样使用了RetentionPolicy枚举类型定义了三个阶段:

    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         * (注解将被编译器忽略掉)
         */
        SOURCE,
    
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         * (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)
         */
        CLASS,
    
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         * (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }
    

    我们再详解一下:

    如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到;
    如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到;
    如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME;
    在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS。

    2.2.3 @Documented

    @Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

    2.2.4 @Inherited

    @Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

    3 自定义注解的配置使用

    回顾一下注解的使用流程:

    第一步,定义注解——相当于定义标记;
    第二步,配置注解——把标记打在需要用到的程序代码中;
    第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。
    到目前为止我们只是完成了第一步,接下来我们就来学习第二步,配置注解,如何在另一个类当中配置它。

    3.1 在具体的Java类上使用注解

    首先,定义一个注解、和一个供注解修饰的简单Java类

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.METHOD})
    @Documented
    public @interface CherryAnnotation {
        String name();
        int age() default 18;
        int[] score();
    }
    
    public class Student{
        public void study(int times){
            for(int i = 0; i < times; i++){
                System.out.println("Good Good Study, Day Day Up!");
            }
        }
    }
    

    简单分析下:

    CherryAnnotation的@Target定义为ElementType.METHOD,那么它书写的位置应该在方法定义的上方,即:public void study(int times)之上;
    由于我们在CherryAnnotation中定义的有注解类型元素,而且有些元素是没有默认值的,这要求我们在使用的时候必须在标记名后面打上(),并且在()内以“元素名=元素值“的形式挨个填上所有没有默认值的注解类型元素(有默认值的也可以填上重新赋值),中间用“,”号分割;
    所以最终书写形式如下:

    public class Student {
        @CherryAnnotation(name = "cherry-peng",age = 23,score = {99,66,77})
        public void study(int times){
            for(int i = 0; i < times; i++){
                System.out.println("Good Good Study, Day Day Up!");
            }
        }
    }
    

    3.2 特殊语法

    特殊语法一:

    如果注解本身没有注解类型元素,那么在使用注解的时候可以省略(),直接写为:@注解名,它和标准语法@注解名()等效!

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.TYPE})
    @Documented
    public @interface FirstAnnotation {
    }
    //等效于@FirstAnnotation()
    @FirstAnnotation
    public class JavaBean{
    	//省略实现部分
    }
    

    特殊语法二:
    如果注解本本身只有一个注解类型元素,而且命名为value,那么在使用注解的时候可以直接使用:@注解名(注解值),其等效于:@注解名(value = 注解值)

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.TYPE})
    @Documented
    public @interface SecondAnnotation {
    	String value();
    }
    
    //等效于@ SecondAnnotation(value = "this is second annotation")
    @SecondAnnotation("this is annotation")
    public class JavaBean{
    	//省略实现部分
    }
    

    特殊用法三:
    如果注解中的某个注解类型元素是一个数组类型,在使用时又出现只需要填入一个值的情况,那么在使用注解时可以直接写为:@注解名(类型名 = 类型值),它和标准写法:@注解名(类型名 = {类型值})等效!

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.TYPE})
    @Documented
    public @interface ThirdAnnotation {
    	String[] name();
    }
    
    //等效于@ ThirdAnnotation(name = {"this is third annotation"})
    @ ThirdAnnotation(name = "this is third annotation")
    public class JavaBean{
    	//省略实现部分
    }
    

    特殊用法四:

    如果一个注解的@Target是定义为Element.PACKAGE,那么这个注解是配置在package-info.java中的,而不能直接在某个类的package代码上面配置。

    4 自定义注解的运行时解析

    这一章是使用注解的核心,读完此章即可明白,如何在程序运行时检测到注解,并进行一系列特殊操作!

    4.1 回顾注解的保持力

    首先回顾一下,之前自定义的注解@CherryAnnotation,并把它配置在了类Student上,代码如下:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.METHOD})
    @Documented
    public @interface CherryAnnotation {
        String name();
        int age() default 18;
        int[] score();
    }
    
    package pojos;
    public class Student {
        @CherryAnnotation(name = "cherry-peng",age = 23,score = {99,66,77})
        public void study(int times){
            for(int i = 0; i < times; i++){
                System.out.println("Good Good Study, Day Day Up!");
            }
        }
    }
    

    注解保持力的三个阶段:
    1.Java源文件阶段;
    2.编译到class文件阶段;
    3.运行期阶段。
    只有当注解的保持力处于运行阶段,即使用@Retention(RetentionPolicy.RUNTIME)修饰注解时,才能在JVM运行时,检测到注解,并进行一系列特殊操作。

    4.2 反射操作获取注解

    因此,明确我们的目标:在运行期探究和使用编译期的内容(编译期配置的注解),要用到Java中的灵魂技术——反射!

    public class TestAnnotation {
        public static void main(String[] args){
            try {
                //获取Student的Class对象
                Class stuClass = Class.forName("pojos.Student");
                //说明一下,这里形参不能写成Integer.class,应写为int.class
                Method stuMethod = stuClass.getMethod("study",int.class);
                if(stuMethod.isAnnotationPresent(CherryAnnotation.class)){
                    System.out.println("Student类上配置了CherryAnnotation注解!");
                    //获取该元素上指定类型的注解
                    CherryAnnotation cherryAnnotation = stuMethod.getAnnotation(CherryAnnotation.class);
                    System.out.println("name: " + cherryAnnotation.name() + ", age: " + cherryAnnotation.age()
                        + ", score: " + cherryAnnotation.score()[0]);
                }else{
                    System.out.println("Student类上没有配置CherryAnnotation注解!");
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }
    

    解释一下:

    如果我们要获得的注解是配置在方法上的,那么我们要从Method对象上获取;如果是配置在属性上,就需要从该属性对应的Field对象上去获取,如果是配置在类型上,需要从Class对象上去获取。总之在谁身上,就从谁身上去获取!
    isAnnotationPresent(Class<? extends Annotation> annotationClass)方法是专门判断该元素上是否配置有某个指定的注解;
    getAnnotation(Class annotationClass)方法是获取该元素上指定的注解。之后再调用该注解的注解类型元素方法就可以获得配置时的值数据;
    反射对象上还有一个方法getAnnotations(),该方法可以获得该对象身上配置的所有的注解。它会返回给我们一个注解数组,需要注意的是该数组的类型是Annotation类型,这个Annotation是一个来自于java.lang.annotation包的接口。

    	//修改自定义注解参数值
        public void excelTemplate() throws Exception{
            //获取BuildingDisc实例
            BuildingDisc bar = new BuildingDisc();
            //获取BuildingDisc的val字段
            Field field = BuildingDisc.class.getDeclaredField("deptName");
            //获取val字段上的Excel注解实例
            Excel excel = field.getAnnotation(Excel.class);
            //获取 Excel 这个代理实例所持有的 InvocationHandler
            InvocationHandler h = Proxy.getInvocationHandler(excel);
            // 获取 AnnotationInvocationHandler 的 memberValues 字段
            Field hField = h.getClass().getDeclaredField("memberValues");
            // 因为这个字段事 private final 修饰,所以要打开权限
            hField.setAccessible(true);
            // 获取 memberValues
            Map memberValues = (Map) hField.get(h);
    
            SysDept sysDept = new SysDept();
            sysDept.setParentId(200L);
            List<SysDept> sysDepts = sysDeptService.selectDeptList(sysDept);
            ArrayList<String> list = new ArrayList<>();
            for (SysDept sysDept1 : sysDepts) {
                list.add(sysDept1.getDeptName());
            }
    
            String[] strings = list.toArray(new String[list.size()]);
            // 修改 value 属性值
            memberValues.put("combo", strings);
            // 获取 foo 的 value 属性值
            Object combo = memberValues.get("combo");
            String s = String.valueOf(combo);
            System.out.println("s = " + s);
        }
    
    展开全文
  • 通过反射动态修改自定义注解属性 java/lang/reflect这个包下面都是Java的反射类和工具。 Annotation注解,也是位于这个包里的。 注解自从Java 5.0版本引入后,就成为了Java平台中非常重要的一部分,常见的有@...

    通过反射动态修改自定义注解属性值

     

    java/lang/reflect 这个包下面都是Java的反射类和工具。

    Annotation 注解,也是位于这个包里的。

    注解自从Java 5.0版本引入后,就成为了Java平台中非常重要的一部分,常见的有 @Override、 @Deprecated

    关于注解更详细的信息和使用方法,网上已经有很多资料,自行查看。

    一个注解通过 @Retention 指定其生命周期,本文所讨论的动态修改注解属性值,建立在 @Retention(RetentionPolicy.RUNTIM) 这种情况。

    这种注解才能在运行时(runtime)通过反射机制进行修改属性的操作。

    我们先定义一个自定义注解 @TestAnno  它有一个类型为 String 的 name属性,该注解应用再Method上:

    @Target({ ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Component
    public @interface TestAnno {
    
    
       String name() default "";
    
    
    }

    我用自定义注解首先得了解清楚注解的值存储在什么地方,我们可以写个main方法测试一下:

    通过反射获取注解@TestAnno的值

    我们定义了一个RetryTestService 在它的方法 retryTest() 上添加@TestAnno 注解,然后在main方法里面反射获取注解的name值

    @Service
    public class RetryTestService {
    
    
        @TimeLog
        @TestAnno(name = "${nba.kobe}")
        public String retryTest(){
            System.out.println("---进行了接口请求....");
            return "success";
        }
    
    
        public static void main(String[] args) throws NoSuchMethodException {
            RetryTestService service = new RetryTestService();
            Method method = service.getClass().getDeclaredMethod("retryTest",null);
            TestAnno testAnno = method.getDeclaredAnnotation(TestAnno.class);
            System.out.println(testAnno.name());
        }
    }

    当前栈中有这么几个变量,不过其中有一点很特别:@TestAnno,其实是个Proxy实例

    Proxy也是 java/lang/reflect下的东西,它的作用是为一个Java类生成一个代理,就像这样:

    public interface A {
        String func1();
    }
    public class B implements A {
        
        @Override
        public String func1() { //do something ... }
        
        public String func2() { //do something ... };
    }
    public static void main(String ...args) {
        B bInstance = new B();
        
        B bProxy = Proxy.newProxyInstance(
            B.class.getClassLoader(),    // B 类的类加载器
            B.class.getInterfaces(), // B 类所实现的接口,如果你想拦截B类的某个方法,必须让这个方法在某个接口中声明并让B类实现该接口
            new InvocationHandler() { // 调用处理器,任何对 B类所实现的接口方法的调用都会触发此处理器
                @Override
                public Object invoke (Object proxy, // 这个是代理的实例,method.invoke时不能使用这个,否则会死循环
                                      Method method, // 触发的接口方法
                                      Object[] args // 此次调用该方法的参数
                                      ) throws Throwable {
                    System.out.println(String.format("调用 %s 之前", method.getName()));
                    /**
                     * 这里必须使用B类的某个具体实现类的实例,因为触发时这里的method只是一个接口方法的引用,
                     * 也就是说它是空的,你需要为它指定具有逻辑的上下文(bInstance)。
                     */
                    Object obj = method.invoke(bInstance, args);
                    System.out.println(String.format("调用 %s 之后", method.getName()));
                    return obj; //返回调用结果
                }
            }
        );
    }

    注意了:

    ClassLoader 这是个class就会有,注解也不例外。那么注解和interfaces有什么关系?

    注解本质上就是一个接口,它的实质定义为: interface SomeAnnotation extends Annotation。

    这个 Annotation 接口位于 java/lang/annotation 包,它的注释中第一句话就是 The common interface extended by all annotation types.

    如此说来,@TestAnno 注解本身只是个接口,这就意味着它没有任何代码逻辑,那么它的 value 属性究竟是存在哪里的呢?

    展开 @TestAnno 可以发现:

    这个 Proxy 实例持有一个 AnnotationInvocationHandler,还记得之前提到过如何创建一个 Proxy 实例么? 第三个参数就是一个 InvocationHandler。

    看名字这个handler即是Annotation所特有的,我们看一下它的代码:

    class AnnotationInvocationHandler implements InvocationHandler, Serializable {
        private final Class<? extends Annotation> type;
        private final Map<String, Object> memberValues;
        private transient volatile Method[] memberMethods = null;
        
        /* 后续无关代码就省略了,想看的话可以查看 sun/reflect/annotation/AnnotationInvocationHandler */
       
    }

    我们一眼就可以看到一个有意思的名字: memberValues,这是一个Map,而断点中可以看到这是一个 LinknedHashMap,key为注解的属性名称,value即为注解的属性值。

    现在我们找到了注解的属性值存在哪里了,那么接下来的事就好办了:

    我这里写两个aop。第一个aop拦截带@TestAnno注解的方法,然后改变注解的name值,第二个aop我们再把注解的name值打印出来,看看是不是真被改了

    第一个aop:

    @Aspect
    @Component
    @Order(1) //aop执行顺序1表示先执行此aop
    public class AuthDemoAspect implements EnvironmentAware {
    
        Environment environment;
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
    
        @Pointcut("@annotation(com.ali.hangz.tooltest.config.TestAnno)")
        public void myPointCut() {
        }
    
    
        @Before(value = "myPointCut()")
        public void check(){
        }
    
    
        @After(value = "myPointCut()")
        public void bye(){
        }
    
    
        /**
         *配置文件配置
         * @return
         */
        @Around("myPointCut() && @annotation(testAnno)")
        public Object around(ProceedingJoinPoint joinPoint, TestAnno testAnno){
            try {
                System.out.println("---修改前注解@TestAnno的name指为:" + testAnno.name());
                String s = environment.resolvePlaceholders(testAnno.name());
                //获取 foo 这个代理实例所持有的 InvocationHandler
                InvocationHandler h = Proxy.getInvocationHandler(testAnno);
                // 获取 AnnotationInvocationHandler 的 memberValues 字段
                Field hField = h.getClass().getDeclaredField("memberValues");
                // 因为这个字段事 private final 修饰,所以要打开权限
                hField.setAccessible(true);
                // 获取 memberValues
                Map memberValues = (Map) hField.get(h);
                // 修改 value 属性值
                memberValues.put("name",s);
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return null;
        }
    }

    第一个aop里面我改变注解的name值,由上面service方法上注解的${nba.kobe} 改成读取配置文件 nba.kobe的配置值

    项目配置文件:application.properties增加一个值

    nba.kobe=科比

    String s = environment.resolvePlaceholders(testAnno.name());

    这行代码其实就是通过原本注解值${nba.kobe}去配置文件取nba.kobe 对应的值。如果你只是修改原来注解的name值而不是去取配置文件大可以不用此行代码,直接给memberValues 里面的name  put新的值就行。

     

    注意:@Order(1) 可以控制aop的执行顺序

    然后我再写第二个aop,打印出注解@TestAnno 的name值看看是不是第一个aop已经成功把值改掉了

    第二个aop:

    @Aspect
    @Component
    @Order(2)
    public class AuthDemoAspectTwo implements EnvironmentAware {
    
    
        Environment environment;
    
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
    
        @Pointcut("@annotation(com.ali.hangz.tooltest.config.TestAnno)")
        public void myPointCut() {
        }
    
    
        @Before(value = "myPointCut()")
        public void check(){
        }
    
    
        @After(value = "myPointCut()")
        public void bye(){
        }
    
    
        /**
         *配置文件配置
         * @return
         */
        @Around("myPointCut() && @annotation(testAnno)")
        public Object around(ProceedingJoinPoint joinPoint, TestAnno testAnno){
            try {
                System.out.println("---修改后的注解名称:" + testAnno.name());
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return null;
        }

    然后我们只需要启动项目调用一下RetryTestService的 retryTest()方法 就可以进入aop 看看打印出来的结果了

    通过结果我们可以发现第一个aop的确把retryTest()方法上面注解@TestAnno的name值由原先的 @TestAnno(name = "${nba.kobe}")     ${nba.kobe}值动态修改成了配置文件里面配置的“科比”了。

     

     

     

     

     

     

     

     

    展开全文
  • 场景:有个API接口,里面存在着枚举,要求我们在入参时进行校验。比如XXEnum[a,b,c],
  • 可使用范围:类、方法、字段、参数。 类型(大致三类): 1 编译器使用的注解 这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。比如:@Override、@SuppressWarnings。 2 由工具处理.class...
  • springboot自定义注解及获取注解

    千次阅读 2020-05-13 11:10:39
    自定义注解 @Target(ElementType.METHOD) //定义是用在方法上 @Retention(RetentionPolicy.RUNTIME) // 定义是在运行时生效 public @interface ZlTest { String param() default ""; //定义参数,默认为空 } ...
  • 自定义注解忽略Json字段(FastJson)

    千次阅读 2021-02-06 18:47:46
    1. JSONFieldFilter import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;... 注意 使用了自定义注解就不能用@ResponseBody
  • 目录1.5、测试2.5、测试三、总结一、利用注解实现AOP的基本流程如果特别熟悉自定义注解实现AOP,可以直接转到第二部分:跳转。Spring中,可以通过自定义注解的方式来实现AOP,比较简单,流程如下:1.1、创建一个注解...
  • //可以给个默认值,然后根据不同的采用不同加密方式 //String type() default "SM4"; } 2、在属性上加入注解 需要加密的字段 //三个实例类注意 //请求报文 @Data public class FallbackRequestModel implements ...
  • SpringBoot中通过自定义字段注解以及反射,实现从数据转换并初始化对象。应用场景主要是通过外部接口,数据库,文本或者Excel读取数据,然后通过反射以及字段注解自动转换为对象,灵活的处理外部数据到对象的转换。
  • 简单来说Spring中的自定义注解是基于AOP(面向切面编程)来实现的,即在一个方法的执行前后对其做修改,以新增一些功能。所以在看本文之前需要先了解AOP的相关知识。 1. 自定义注解类 新建Maven工程,创建注解类...
  • 注解(Annotation)是放在Java源码的类、方法、字段、参数前的一种特殊“注释”,这类注释会被编译器直接忽略,可以被编译器打包进入class文件。因此,注解是一种用作标注的“元数据”,相关信息被存储在 Annotation ...
  • Java自定义注解

    2021-03-04 07:00:45
    以下通过自定义注解来深入了解java注解。一、创建自定义注解package com.sam.annotation;import java.lang.annotation.*;/*** @author sam* @since 2017/7/13*/@Target({ElementType.METHOD, Element...
  • 初学spring的时候使用注解总觉得使用注解很神奇,加一个注解就能实现想要的功能,很好奇,也想自己根据需要写一些自己实现的自定义注解。问题来了,自定义注解到底是什么?肯定会有人和我一样有这个疑惑,我根据自己...
  • 自定义注解

    千次阅读 2021-01-22 11:51:34
    1、什么是注解? Annontation是Java5开始引入的新特征,中文名称叫注解。 它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素...
  • 在 SpringBoot 中,利用 AOP 实现拦截控制的方法有很多,个人觉得相对比较简洁、比较简单的方式是通过自定义注解实现拦截控制。这种实现方式只需要预先定义一个新的注解,并实现拦截控制的具体业务逻辑,当我们想要...
  • 由于需要读取原始表中多个浮点型字段值到服务层,并将其格式化为千位符或者百分比后保留x为小数,其实可以通过读取数据库表数据再遍历的方法去进行逐个格式化,但多个报表的情况下,这样就显得代码很冗余,并且效率...
  • (ps:需求是对部分功能放开强制登录。无需登录即可请求) 首先是在项目的拦截器中放开Token非空条件,实现方式如下图: ​​​​​​​​​​​​​​​​​...接下来解决的就是自定义注解标注的接口上需携带id的情.
  • springBoot之自定义注解

    2022-01-19 18:15:08
    2.怎样自定义注解 定义注解 使用注解(将注解打在需要的代码上) 解析注解(检测到标记并进行特殊操作) 2.1 注解基本语法 注解类型声明 注解在Java中,与类、接口、枚举类似,因此其声明语法基本一致,只是所使用...
  • SpringBoot之自定义注解

    千次阅读 2022-01-19 10:50:52
    下面就来说一下在SpringBoot里开发自定义注解。 1.自定义注解的规则: 1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口. 2. 参数成员...
  • /** * @ClassName ${NAME}.java * @author 神秘的凯 * @version 1.0.0 * @Description JSON 过滤自定义注解 * @createTime 2022/4/3 11:06 上午 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy....
  • 该如何去做到不用get方法去比较两个实体类的属性呢?
  • 前言 最近遇到了这样一个工作场景,需要写一批dubbo接口,再将dubbo接口注册到网关中,但是当dubbo接口异常的时候会给前端返回...通过拦截器+AOP实现自定义注解的实现 1.引入相关依赖 spring-boot-starter:s...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,145
精华内容 18,858
关键字:

自定义注解修改字段值

友情链接: PWM.zip