反射因为是框架实现的核心东西,所以要重点学习下,上一篇文章提到反射,无非就是可以对加载到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枚举类.
- SOURCE,不编译Annotation到Class文件,作用范围最小。
- CLASS,编译到Annotation到Class文件,运行时不加载到Annotation到JVM中.
- 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方法
- getComponentType()如果Class类存在数组 返回,不存在返回null
- newInstance(Class<?> componentType, int length)通过componenttype+length创建一个数组
- arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length)