⼀、反射
运⾏时的类信息(将类的各个组成部分封装成为类信息对象)
类是描述⼀组对象的抽象描述,⽽反射则是描述⼀组类
当ClassLoader加载类时,会⾃动获悉类完整构造⾃动封装成⼀个类信息(包括类、属性、⽅法等)
Java的反射机制在⽤在程序运⾏时,可以动态的获取类的信息,调⽤对象的⽅法等
Class的重要组成
阐述常⽤的组成部分使⽤及常⽤API
-
Class 类
获取类的三种⽅式:
1. Class.forName("全类名") //常⽤于配置使⽤ 2. 类型.class 3. 对象.getClass() //延伸 //获取全类名 1. class对象.getName(); //获取简单类名 2. class对象.getSimpleName(); //获取⽗类 3. class对象.getSuperclass();
-
Modifier 修饰
class对象.getModifiers(); //⽤于获取类的修饰,返回int类型 //⽐如如果是默认修饰,则返回0 ,public返回1 对应的返回值对应修饰请参考jdk⽂档
-
Field 属性
下⾯操作返回的是⼀个Field属性对象
//获取对象的public修饰的属性 1. class对象.getField("属性名") //获取public修饰的所有的属性 2. class对象.getFields() //根据属性名获取对象属性(包含私有) 3. class对象.getField("属性名") //获取对象所属性 4class对象.getDeclaredFields();
操作属性对象
//获取对象的属性值,此处的obj你需要取值的类属性值的对象 1. filed对象.get(obj) //对象属性赋值,此处的obj你需要取值的类属性值的对象,value是你需要设置的值 2. filed对象.set(obj,value)
-
Method ⽅法
- 下⾯操作返回的是是⼀个Method对象
//获取对象的public修饰的⽅法 1. class对象.getMethod("⽅法名",Object类型的可变参...) //获取public修饰的所有的⽅法 2. class对象.getMethods() //根据属性名获取对象⽅法(包含私有) 3. class对象.getMethod("⽅法名",Object类型的可变参...) //获取对象所⽅法 4. class对象.getDeclaredMethods();
--操作⽅法对象
//执⾏⽅法,此处的obj你需要执⾏的⽅法的对象,可变参是你调⽤这个⽅法需要传的参数 1.method对象.invoke(obj,可变参...)
-
Construtor 构造
//获取构造 1. class对象.getConstructor(Class类型的可变参...) //获取所有构造 2. class对象.getConstructors() //同上两个⼀样,有Declared相应的⽅法
由上可知,我们⼀般在反射操作上,⼀般都会使⽤带有Declared的⽅法,因为可获取全部的包括私有的⼀些属性、⽅法,使⽤不带Declared的⽅法则可获取包含⽗类的⼀些公有属性、⽅法,Declared只能获取⾃⼰的,若此属性、⽅法是有私有权限控制的,此时我们直接操作属性、⽅法会抛出⼀个java.lang.IllegalAccessException异常,告诉你没有访问权限。获取Declared的⽅法、属性后,若此⽅法、属性是私有的需要设置访问权限setAccessible(true),⼜称暴⼒破解,这样我们就能顺利操作⽅法、属性等数据了。
反射延伸——内省
内省(Introspector) 是Java 语⾔对 JavaBean 类属性、事件的⼀种缺省处理⽅法
⾥边运⽤了反射的⼀些基本原理将JavaBean封装成⼀个BeanInfo对象,⽅便我们进⾏操作.
下⾯的⼀个例⼦是对象转map
/** * 对象转Map * * @param s 对象 * @param <S> 类型 */ public static <S> Map<String, Object> objectToMap(S s) throws IntrospectionException, InvocationTargetException, IllegalAccessException { Map<String, Object> map = new HashMap<>(); //使⽤内省获取对象的基本信息 BeanInfo beanInfo = Introspector.getBeanInfo(BeanUtils.getRealClass(s.getClass())); //获取对象的属性描述进⾏遍历处理 PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { //获取属性名 if ("class".equals(pd.getName())) { continue; } //获取set⽅法并执⾏ Object invoke = pd.getReadMethod().invoke(s); map.put(pd.getDisplayName(), invoke == null ? "" :((Date.class.equals(invoke.getClass()) ||java.sql.Date.class.equals(invoke.getClass())) ? DateUtils.toString((Date)invoke) : invoke)); } return map; }
⼆、注解
注解(也被称为是元数据),为我们在代码中添加信息提供的⼀个形式化的⽅法,让我们可以在稍后的某些时候⽅便的使⽤这些数据。也就是打个标签啦~
Java 内置了许多的注解,在Java Annotation可以找到内置的所有注解,我们最常⻅的注解应该 是@Override,表示这个⽅法是⼀个重写的⽅法。
1、如何创建注解
所有的注解本身都继承于java.lang.annotation.Annotation, 每⼀个注解本身就是⼀个interface,但是注解这种interface有其特殊性,所以,所有的注解都是这样定义的:publice @interface xxxx {}
@Override的定义如下:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
可以看到上⾯⽤到@Targe、@Retention的注解,此类注解为元注解。
元注解(使⽤在注解上的注解)
-
@Documented
标记这个注解@Deprecated将会出现在Java Doc之中
-
@Retention
标明这个注解的⽣命周期,RententionPolicy⾥⾯定义了三种⽣命周期,分别是SOURCE,CLASS,RUNTIME三种,SOURCE表示在编译阶段抛弃,CLASS表示会被记录到class⽂件⾥⾯,但不会出现在vm⾥⾯运⾏,RUNTIME表示在运⾏期⾥⾯存活
-
@Target
表示这个注解可以⽤在何处,表明它可以⽤在构造函数,字段,本地变量,⽅法,包,参数,类
-
@Inherited
表示使⽤了这个注解的注解,再⽤到类上时,可被⼦类继承
-
@Repeatable
表示注解可以重复使⽤,是Java 8引进的特性。之前注解只能在同⼀个地⽅⽤⼀次,⽤了@Repeatable,注解就可以在同⼀个地⽅使⽤多次了
2、注解的属性
注解的属性也叫成员变量。注解只有属性没有⽅法,注解的成员变量在注解的定义中以“⽆形参的⽅法”形式来声明,其⽅法名定义了该成员变量的名字,其返回值定义了该成员变量的类型,且可⽤default进⾏默认赋值,如果没有指定默认值,则此注解属性是必须赋值的!!
需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接⼝、注解及它们的数组。
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) public @interface NotNull { /** * 提示信息 */ String message() default ""; /** * 是否迭代校验 */ boolean isIteration() default false; }
上⾯的代码定义了NotNull这个注解,它拥有message和isIteration两个属性,在使⽤的时候我们可以 给他们赋值:
@NotNull(isIteration = true) private AuditInfo audit_info;
另外,还有⼀种情况。如果⼀个注解内仅仅只有⼀个名字为 value 的属性时,应⽤这个注解时可以直接接属性值填写到括号内,如下:
//定义 @Retention(RetentionPolicy.RUNTIME) public @interface Order { int value() default 0; } //使⽤ @Order(Integer.MIN_VALUE) public class SituationSynProcessor extends BaseExtendSynProcessor<List<Situation>>
3、注解的提取、注解与反射
⼀开始我们已经说了,注解只不过是⼀个标签⽽已,如果没有⼀个提取它解释它使⽤它的解释器,它将不会有什么作⽤。
通过反射获取注解:
//Class 对象的 isAnnotationPresent() ⽅法判断它是否应⽤了某个注解 public boolean isAnnotationPresent(Class<? extends Annotation>annotationClass) {} //getAnnotation() ⽅法来获取 Annotation 对象 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {} //获取所有注解 public Annotation[] getAnnotations() {}
下⾯示例将会暂时简单的注解@Order的提取与使⽤
//1.定义 @Retention(RetentionPolicy.RUNTIME) public @interface Order { int value() default 0; } //2.使⽤ @Order(Integer.MIN_VALUE) public class SituationSynProcessor extends BaseExtendSynProcessor<List<Situation>>
package com.gzzm.govps.itemsyn; import com.gzzm.platform.commons.Tools; import net.cyan.commons.util.BeanUtils; import net.cyan.commons.util.ClassResolver; import net.cyan.commons.util.CollectionUtils; import java.util.*; /** * 事项同步扩展处理器扫描器 * * @author yiuman * @date 2019-12-04 */ public final class ExtendProcessorResolver implements ClassResolver { //未排序Class Set private final static Set<Class<? extends BaseExtendSynProcessor<?>>> notOrderExtendSynProcessors = new HashSet<>(); //记录BaseExtendSynProcessor处理器的类型及其Order顺序 private final static Map<Class<? extends BaseExtendSynProcessor<?>>,Integer> orderProcessorRecord = new HashMap<>(); //已排序的Class Set private final static Set<Class<? extends BaseExtendSynProcessor<?>>> extendSynProcessors = new LinkedHashSet<>(); public ExtendProcessorResolver() { } /** * @return 返回⼀个已经排序过的处理器Set */ static Set<Class<? extends BaseExtendSynProcessor<?>>> getExtendProcessors() { if (CollectionUtils.isEmpty(extendSynProcessors)) { notOrderExtendSynProcessors.stream().sorted(Comparator.comparingInt(orderProcessorRecord::get)) .forEach(extendSynProcessors::add); } return extendSynProcessors; } @SuppressWarnings("unchecked") @Override public void resolve(Class<?> aClass) { try { if (BeanUtils.getRealClass(aClass).getSuperclass() != null && BaseExtendSynProcessor.class.equals(BeanUtils.getRealClass(aClass).getSuperclass())) { //此处通过反射获取注解 Order order = aClass.getAnnotation(Order.class); //将获取到的注解值与使⽤了该注解的类型添加进 Order记录Map⾥ orderProcessorRecord.put((Class<? extends BaseExtendSynProcessor<?>>) aClass, order == null ? 0 : order.value()); //将类型添加进未进⾏排序的Set中 notOrderExtendSynProcessors.add((Class<? extends BaseExtendSynProcessor<?>>) aClass); } } catch (Throwable a) { Tools.log(a); } } }
说明:上⾯的真正⽤到的是,那些Set集合中处理器,因为可能要指定某些处理器的执⾏顺序,⽐如A处 理器⼀定要B处理器之前运⾏,我们平时也可以写死让他们指定顺序执⾏,这在处理器较少的时候是可 以这么做,但是如果处理器有10呢?下次⼜加多10个呢?⽽且还需要执⾏他们某些的执⾏顺序呢?所以 这⾥是⽤了Order注解对他们的⼀个执⾏顺序进⾏指定,以后⽆论怎么加只要注意下Order中的value值 定义就好。~ Springboot中的Bean加载顺序也是⽤了这个同名注解进⾏Bean的加载顺序控制的~~