Java从入门到精通 十六章 反射 Annotation

反射因为是框架实现的核心东西,所以要重点学习下,上一篇文章提到反射,无非就是可以对加载到jvm中的对象实例,通过反射访问器类的成员变量,方法,构造方法,局部变量参数等信息进行访问控制。这一篇主要是利用反射特性讲一下Annotation,定义的方式为@interface xxx。对程序运行没有影响,主要是能够通过Annotation对构造方法,成员变量,成员方法,局部变量进行一个注解提示。如果再结合反射那么就能够对数据进行一个校验控制。具体例子在下面.

定义Annotation类型

public @interface NoMemberAnnotation {
}
public @interface OneMemberAnnotation {
  String  value();
  Class type();
}

public @interface DefaultAnnotation {
   String describe() default  "<默认值>";
   Class type() default Void.class;
}

上面举了2个关于Annotation类型的例子,实际上编译器里面也支持直接创建Annotation类型.其中成员String,Class被称为成员类型,而value,type被称为成员名称.同时可以对成员类型的值进行初始化值操作.default就是进行这个操作的,具体例子请看第三个 default “<默认值>”
在这里插入图片描述
同时在定义Annotation类型的时候,还可以通过@Target来设置适用类型,也就是针对的构造方法,成员变量,参数类型,还是方法等元素。具体使用ElementType.XXX枚举类里面,常用的是Method,Field,Constructor,ParmeterType.
而@Rentention则用于声明Annotation的使用范围.具体参考@RententionPolicy枚举类.

  1. SOURCE,不编译Annotation到Class文件,作用范围最小。
  2. CLASS,编译到Annotation到Class文件,运行时不加载到Annotation到JVM中.
  3. RUNTIME,编译的同时在运行时放入jvm中,通常就是用这个。因为反射的前提的程序运行在jvm中.
    在这里插入图片描述
    在这里插入图片描述
    16.4例子
    定义构造方法,成员方法,成员变量,方法参数的Annotation类型

构造方法Annotation

//用于构造方法中
@Target(ElementType.CONSTRUCTOR)
//Anntation编译到class文件中,并且加载都jvm中
@Retention(RetentionPolicy.RUNTIME)
public @interface Constructor_Anntation {
    String value() default "默认构造方法";//定义一个具有默认值的string型成员
    
}

成员方法/变量/参数 Annotation

@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
// 定义一个字段 方法 参数的Anntation注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Filed_Method_Annotation {
	String descibe();
	Class type() default Void.class;
}

使用类

public class Record {
	// 注释字段 使用无默认值的
	@Filed_Method_Annotation(descibe = "编号", type = int.class)
	int id;
	@Filed_Method_Annotation(descibe = "姓名", type = String.class)
	String name;

	// 采用默认值注释 构造方法
	@Constructor_Anntation()
	public Record() {
		super();
	}

	@Constructor_Anntation(value = "立即初始化构造方法")
	public Record(@Filed_Method_Annotation(descibe = "编号", type = int.class) int id,
			@Filed_Method_Annotation(descibe = "姓名", type = String.class) String name) {
		super();
		this.id = id;
		this.name = name;
	}

	@Filed_Method_Annotation(descibe = "获得编号", type = int.class)
	public int getId() {
		return id;
	}

	@Filed_Method_Annotation(descibe = "设置编号")
	public void setId(@Filed_Method_Annotation(descibe = "编号", type = int.class) int id) {
		this.id = id;
	}

	@Filed_Method_Annotation(descibe = "获得姓名", type = String.class)
	public String getName() {
		return name;
	}

	@Filed_Method_Annotation(descibe = "设置姓名", type = String.class)
	public void setName(@Filed_Method_Annotation(descibe = "姓名", type = String.class) String name) {
		this.name = name;
	}

	public static void main(String[] args) {
		// 利用反射 是访问正在运行在jvm中的程序
		Record record = new Record();
		Class<? extends Record> class1 = record.getClass();
		// seeConstructor(class1);
		// seeField(class1);
		seeMethod(class1);

	}

	/**
	 * 访问成员方法中的注解
	 * 
	 * @param class1
	 */
	private static void seeMethod(Class<? extends Record> class1) {
		Method[] methods = class1.getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if (method.isAnnotationPresent(Filed_Method_Annotation.class)) {
				Annotation[] annotations = method.getAnnotations();
				for (int j = 0; j < annotations.length; j++) {
					Filed_Method_Annotation annotation = (Filed_Method_Annotation) annotations[j];
					System.out.println("成员方法注释:"+annotation.type() + "," + annotation.descibe());
				}
			}
			Annotation[][] annotations = method.getParameterAnnotations();// 返回的参数类型是个二维数组
																			// 防止出现基本数据类型容器的类型
																			// 作为参数
			for (int j = 0; j < annotations.length; j++) {
				int length = annotations[j].length;
				if (length == 0)
					System.out.println("没有参数");
				else
					for (int k = 0; k < length; k++) {
						Filed_Method_Annotation filed_Method_Annotation = (Filed_Method_Annotation) annotations[j][k];
						System.out.println(filed_Method_Annotation.type() + "," + filed_Method_Annotation.descibe());

					}

			}
		}
	}

	/**
	 * 利用反射 访问对象中的成员变量字段
	 * 
	 * @param class1
	 */
	private static void seeField(Class<? extends Record> class1) {
		Field[] fields = class1.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			Annotation[] annotations = field.getAnnotations();
			for (int j = 0; j < annotations.length; j++) {
				Filed_Method_Annotation filed_Method_Annotation = (Filed_Method_Annotation) annotations[j];
				System.out.println(filed_Method_Annotation.type() + "," + filed_Method_Annotation.descibe());
			}
		}
	}

	/**
	 * 查看构造方法及参数注释
	 * 
	 * @param class1
	 */
	private static void seeConstructor(Class<? extends Record> class1) {
		Constructor<?>[] constructors = class1.getConstructors();
		for (int i = 0; i < constructors.length; i++) {
			Constructor constructor = constructors[i];
			// 判断构造方法中是否存在指定的注解,存在的话查询
			if (constructor.isAnnotationPresent(Constructor_Anntation.class)) {
				Constructor_Anntation caAnntation = (Constructor_Anntation) constructor
						.getAnnotation(Constructor_Anntation.class);
				System.out.println(caAnntation.value());
			}
			// 解析构造构造方法中的参数
			Annotation[][] anns = constructor.getParameterAnnotations();
			for (int x = 0; x < anns.length; x++) {
				int length = anns[x].length;
				if (length == 0)
					System.out.println("未添加Annotation的参数");
				else
					for (int y = 0; y < length; y++) {
						Filed_Method_Annotation annotation = (Filed_Method_Annotation) anns[x][y];
						System.out.println(annotation.type() + "," + annotation.descibe());
					}
			}
		}
	}
}

运行结果

默认构造方法
立即初始化构造方法
int,编号
class java.lang.String,姓名
int,编号
class java.lang.String,姓名
没有参数
成员方法注释:class java.lang.String,获得姓名
成员方法注释:int,获得编号
成员方法注释:class java.lang.String,设置姓名
class java.lang.String,姓名
成员方法注释:class java.lang.Void,设置编号
int,编号
没有参数
没有参数
没有参数
没有参数

上面例子中不仅包含了Annotation类型的使用,而且将通过反射访问Annotation的方法也写进去了.通过反射我么可以获得正在运行对象的类信息,结合Annotation我们就可以校验参数,通过Annotation+反射就可以解析对象字段的数据有效性了。目前项目中的例子就有一个这样的例子,给大家分享下。

NotBlank Annotation类型

package com.bxd.core.annotion;

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

import javax.validation.constraints.NotNull;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@NotNull
@Documented
public @interface NotBlank {
	 //是否可以为空
    boolean nullable() default false;
  //最大长度
    int maxLength() default 0;
     
    //最小长度
    int minLength() default 0;
     
    //自定义正则验证
    String regexExpression() default "";
    String message() default "";
}

BeanValid 对象字段校验工具类

package com.bxd.core.util;

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bxd.core.annotion.NotBlank;
public class BeanValid {
	
	 private static Logger log = LoggerFactory.getLogger(BeanValid.class);
	 private static NotBlank blank;
	
	/**
	 * 验证简单model对象中相关字符串字段的有效性
	 * @param object
	 * @return
	 * @throws Exception 
	 */
	public static boolean valid(Object object){
		
		//获取object的类型
        Class<? extends Object> clazz=object.getClass();
        //获取该类型声明的成员
        Field[] fields=clazz.getDeclaredFields();
        boolean flag = true;
        //遍历属性
        for(Field field:fields){
            //对于private私有化的成员变量,通过setAccessible来修改器访问权限
            field.setAccessible(true);
            try {
				validate(field,object);
			} catch (Exception ex) {
				log.error(ex.getMessage());
				flag = false;
			}
            //重新设置会私有权限
            field.setAccessible(false);
        }			
		return flag;
	}
	
	
	
	public static void validate(Field field,Object object) throws Exception{
		 
        String message;
        Object value;
 
        //获取对象的成员的注解信息
        blank=field.getAnnotation(NotBlank.class);
        value=field.get(object);
         
        if(blank==null) return ;
         
        message=blank.message().equals("")?field.getName():blank.message();
         
        /*************注解解析工作开始******************/
        if(!blank.nullable()){
            if(value==null||StringUtil.isEmpty(value.toString())){
                throw new Exception(message+"不能为空");
            }
        }
         
        if(value.toString().length()>blank.maxLength()&&blank.maxLength()!=0){
            throw new Exception(message+"长度不能超过"+blank.maxLength());
        }
         
        if(value.toString().length()<blank.minLength()&&blank.minLength()!=0){
            throw new Exception(message+"长度不能小于"+blank.minLength());
        }
         
         
        if(!blank.regexExpression().equals("")){
            if(!value.toString().matches(blank.regexExpression())){
                throw new Exception(message+"格式不正确");
            }
        }
        /*************注解解析工作结束******************/
    }

	
}

具体使用例子

package com.bxd.app.util;

import com.bxd.core.annotion.NotBlank;
import com.bxd.core.util.BeanValid;


public class Test {
	// 姓名字段 最小长度为2 最大长度为3
	@NotBlank(minLength = 2, maxLength = 4)
	String name;
	@NotBlank(regexExpression="^[1-9]{1,2}|100$")
	int age;
	@NotBlank(regexExpression="^\\w+@\\w+(\\.\\w{2,3})*\\.\\w{2,3}$")
	String email;
	@NotBlank(regexExpression="^(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])\\d{8}")
	String tel;
	public static void main(String[] args) {
		//校验正则表达式
		String regex="^\\w+@\\w+(\\.\\w{2,3})*\\.\\w{2,3}$";
		String age="1@1.xx11.com";
		boolean flag = age.matches(regex);
		System.out.println(flag);
		
		Test test=new Test();
		test.setName("张三");
		test.setAge(11);
		test.setTel("17342064227");
		test.setEmail("xuebaluxian@163.com");
		BeanValid.valid(test);
		
	}
	
	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;
	}


	public String getEmail() {
		return email;
	}


	public void setEmail(String email) {
		this.email = email;
	}


	public String getTel() {
		return tel;
	}


	public void setTel(String tel) {
		this.tel = tel;
	}


	
}

通过运行BeanValid.valid()方法即可添加了Annotation类型注解的对象字段 数据进行校验,可以大大的提高代码的优雅型.以前我都是很lowB的一个一个字段校验的,那太垃圾了。

最后说下这一章节比较重要的练习题,利用反射扩充数组长度.ArrayList可变长度数组 集合的原理

package core.annotation;

import java.lang.reflect.Array;
import java.util.Arrays;

public class ExpendArray {

	private int[] array = { 1, 2, 3 };
	private String[] strings={"x,","y"};

	public static void main(String[] args) {
		ExpendArray expendArray = new ExpendArray();
		System.out.println(Arrays.toString(expendArray.array));
		System.out.println(Arrays.toString(expendArray.strings));
		int newlength = 10;
		expendArray.array = (int[]) expandArray(expendArray.array, newlength);
		expendArray.strings = (String[]) expandArray(expendArray.strings, newlength);
		System.out.println(Arrays.toString(expendArray.array));
		System.out.println(Arrays.toString(expendArray.strings));

	}

	/**
	 * 扩展数组长度
	 * 
	 * @param array2
	 * @param newlength
	 * @return
	 */
	private static Object expandArray(Object obj, int newlength) {
		Object newObject = null;
		Class<?> class1 = obj.getClass().getComponentType();// 取得一个数组的class对象
		newObject =  Array.newInstance(class1, newlength);
		System.arraycopy(obj, 0, newObject, 0, Array.getLength(obj));
		return newObject;
	}
}

运行结果

[1, 2, 3]
[x,, y]
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
[x,, y, null, null, null, null, null, null, null, null]

这里面以前没有碰到的几个api方法主要是getCompentType通过Class类的这个方法可以返回数组类,同时还有个Array.newInstance(xx,int newlength)方法可以创建数组对象,还有一个就是System.copyArray()方法,数组对象复制的api方法

  1. getComponentType()如果Class类存在数组 返回,不存在返回null
  2. newInstance(Class<?> componentType, int length)通过componenttype+length创建一个数组
  3. arraycopy(Object src, int srcPos,
    Object dest, int destPos,
    int length)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值