Java(Spring)当中利用反射去进行数组/集合的注入
在看到Spring当中是支持数组/集合/Map去进行注入的,但是我们如果自己要去使用,应该怎么做呢?以前自己确实没做过,这里记录一下。
1. 如何使用反射去进行数组字段的注入?
比如我们定义了如下这样一个字段
private User[] users;
我们用反射如何去进行实现对这个字段去进行赋值?首先我们可以通过Class.getField
去获取到这个users
字段。
Field field = App.class.getField("users");
接着呢,我们的想法是什么?反射创建对象对吧!
我们使用如下的代码可以获取到字段的类型(User[]
)
Class<?> fieldType = field.getType();
那么我们如何去创建一个User[]
类型的对象?因为这是个数组类型,我们自然可以拿到它的元素类型,使用如下的代码
Class<?> componentType = fieldType.getComponentType();
这个componentType
就可以拿到User
的类型Class
。接着怎么做?我们如何创建一个数组对象?可以使用JDK提供的Array
类给我们提供的相关方法:
Object userArray = Array.newInstance(componentType, 10);
使用上面的代码,userArray
就是一个长度为10的User[]
,那么我们如何对这个数组当中的元素去进行赋值?当然也是使用Array
类为我们提供的相关方法。
for (int i = 0; i < 10; i++) { Object user = componentType.getDeclaredConstructor().newInstance(); Array.set(userArray, i, user); }
使用Array.set
方法就可以很方便地为数组当中的元素进行赋值了!所以使用反射对User[]
类型的字段去进行赋值的完整的代码如下
Field field = App.class.getField("users"); Class<?> fieldType = field.getType(); if (fieldType.isArray()) { Class<?> componentType = fieldType.getComponentType(); Object userArray = Array.newInstance(componentType, 10); for (int i = 0; i < 10; i++) { Object user = componentType.getDeclaredConstructor().newInstance(); Array.set(userArray, i, user); } field.setAccessible(true); field.set(new App(), userArray); }
2. 如何使用反射去对集合的字段去进行注入?
比如我们定义了如下的字段
public List<User> users;
我们当然也可以使用反射拿到这个字段,以及拿到这个字段类型
Field field = App.class.getField("users"); Class<?> fieldType = field.getType();
但是问题来了,Java当中对于泛型的实现是使用的类型擦除,也就是所你List<User>
,这样一个集合,在底层仍然是使用的List<Object>
去进行实现的,对于泛型的检查都是在javac
对Java代码在编译层面去进行检查的。
这说明了什么呢?说明了我们通过fieldType
是拿不到我们定义的泛型信息的,因为类型被抹除成为Object
类型了。我们有什么办法获取到泛型信息吗?既然通过fieldType
获取不到,那么我们使用Filed
去进行获取嘛,在HotSpot VM
当中,Field
是有记录泛型的类型的!那么我们如何去进行获取?我们可以使用它的getGenericType
方法去进行获取。
Type genericType = field.getGenericType();
对于字段当中的泛型,实际上Type
是一个ParameterizedType
类型,我们可以将其进行强转试试!
if (genericType instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) genericType; Type[] actualTypeArguments = type.getActualTypeArguments(); }
我们发现强转之后,它就为我们提供了getActualTypeArguments
方法,翻译过来叫做获取真正的泛型参数,这里的Type[]
其实就是我们Field
的真正的泛型类型数组,为什么是个数组?别忘了还有Map<K,V>
这种。
我们这里既然是个Collection
,那么肯定只有一个泛型,我们直接获取它的0号元素即可。
Type typeArgument = actualTypeArguments[0];
实际上我们这里拿到的Type
就是一个真正的Class
对象,我们直接强转为Class
!接着就可以使用泛型的具体类型去创建对象了
Class typeArgument = (Class) actualTypeArguments[0]; Object o = typeArgument.getDeclaredConstructor().newInstance();
现在有个问题就是,我们不知道提供的集合是什么类型,比如List/Collection/Set
,还是ArrayList/LinkedList
等?这里貌似我们就没办法,只能做一层尽可能的枚举,毕竟用户还可以自定义Collection
等!
Collection collection; if (field.getType() == List.class || field.getType() == Collection.class) { collection = new ArrayList(); } else if (field.getType() == Set.class) { collection = new HashSet(); } else { collection = (Collection) field.getType().getDeclaredConstructor().newInstance(); }
既然得到了Collection
对象,也得到了泛型的User
对象了,差的自然就是往Collection
当中放元素了,这部分代码暂时忽略掉,您完全可以自己实现!
对于Map
类型中的泛型,以及方法参数中的泛型,其实完全类似的,和上述原理类似,这里就不再进行赘述。
3.下面是一个我写的比较详细的注入案例
主要功能就是实现类似Spring
当中的@Autowired
/@Inject
/@Resource
去进行注入的情况
/** * @author wanna * @version v1.0 */ public class InjectMetadataUtils { private final ConfigurableListableBeanFactory beanFactory; public InjectMetadataUtils(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } // 处理字段注入的情况 public Object handleFieldInject(Field field, boolean required) { Value value = AnnotatedElementUtils.getMergedAnnotation(field, Value.class); Qualifier qualifier = AnnotatedElementUtils.getMergedAnnotation(field, Qualifier.class); // 1.如果是一个@Value注解的话,那么,直接解析占位符就行了... if (!Objects.isNull(value)) { return handleValueInject(value.value()); } // 2.如果不是一个@Value注解,那么就是一个@Inject注解或者@Autowired注解...只需要解析@Qualifier注解就行了 Object[] injectBean = new Object[1]; // --2.1如果这个参数的类型是Map/Collection/Array,那么,需要去进行处理 if (ClassUtils.isAssignableFrom(Collection.class, field.getType()) || ClassUtils.isAssignableFrom(Map.class, field.getType()) || field.getType().isArray()) { injectBean[0] = handleMultiBeans(field.getType(), field.getGenericType()); // --2.2如果有Qualifier注解的话,那么按name去进行注入... } else if (!Objects.isNull(qualifier)) { injectBean[0] = beanFactory.getBean(qualifier.value(), field.getType()); // --2.3 如果没有Qualifier注解,那么type类型去进行注入 } else { injectBean[0] = beanFactory.getBean(field.getType()); } int checkedIndex = checkRequired(injectBean); // 如果没有找到合适的Bean,那么抛出异常... if (required && checkedIndex != -1) { throw new IllegalStateException("在处理字段" + field + "时遇到了没有容器中没有的Bean,字段类型为" + field.getType()); } return injectBean[0]; } // 处理方法去进行注入的情况,需要对每个参数都去进行注入... public Object handleMethodParametersInject(Method method, boolean required) { Type[] types = method.getGenericParameterTypes(); Parameter[] parameters = method.getParameters(); Class<?>[] parameterTypes = method.getParameterTypes(); // 要进行注入的参数列表... Object[] params = new Object[parameters.length]; // 获取方法上的@Qualifier注解... Qualifier methodQualifier = AnnotatedElementUtils.getMergedAnnotation(method, Qualifier.class); // 获取方法上的@Value注解... Value methodValue = AnnotatedElementUtils.getMergedAnnotation(method, Value.class); // 1.如果方法上有@Qualifier注解,那么优先去进行处理 if (!Objects.isNull(methodQualifier)) { if (parameterTypes.length != 1) { throw new NoSupportException("@Qualifier注解不支持标注在参数数量不是1个的方法上"); } // 如果@Qualifier注解标注在只有一个参数的方法上,那么...按照名字去解析 params[0] = beanFactory.getBean(methodQualifier.name(), parameterTypes[0]); // 2.如果方法上有@Value注解,那么也优先去进行处理 } else if (!Objects.isNull(methodValue)) { if (parameterTypes.length != 1) { throw new NoSupportException("@Value注解不支持标注在参数数量不是1个的方法上"); } // 如果@Value注解标注在只有一个参数的方法上,那么...直接进行解析占位符 params[0] = handleValueInject(methodValue.value()); // 3.如果方法上没标注@Value/@Qualifier注解,那么...遍历所有的参数去进行注入 } else { for (int i = 0; i < parameterTypes.length; i++) { handleMethodParameter(params, i, parameterTypes, parameters, types); } } // 如果required=true,就得去检查是否每个属性都存在,如果其中一个不存在(返回值不为-1),那么都会抛出异常... int checkedIndex = checkRequired(params); if (required && checkedIndex != -1) { throw new IllegalStateException("在处理方法" + method + "时遇到了没有容器中没有的Bean,参数类型为" + parameters[checkedIndex]); } return params; } private void handleMethodParameter(Object[] params, int i, Class<?>[] parameterTypes, Parameter[] parameters, Type[] types) { // 获取参数类型、泛型类型以及Parameter... Class<?> parameterType = parameterTypes[i]; Type type = types[i]; Parameter parameter = parameters[i]; // 获取参数上的@Value注解信息 Value value = AnnotatedElementUtils.getMergedAnnotation(parameter, Value.class); // 如果这个参数的类型是Map/Collection/Array,那么,需要去进行处理 if (ClassUtils.isAssignableFrom(Collection.class, parameterType) || ClassUtils.isAssignableFrom(Map.class, parameterType) || parameterType.isArray()) { params[i] = handleMultiBeans(parameterType, type); // 如果这个参数类型不是Map/Collection/Array类型的话,那么直接解析placeholder/getBean即可 // 处理方法参数上是@Value注解的话,那么... } else if (!Objects.isNull(value)) { params[i] = handleValueInject(value.value()); } else { // 如果方法上具体某个参数上标注了Qualifier注解的话...那么需要按照名字去进行注入 Qualifier qualifier = AnnotatedElementUtils.getMergedAnnotation(parameter, Qualifier.class); // 判断是根据name去注入还是根据type去进行注入 if (Objects.isNull(qualifier)) { params[i] = beanFactory.getBean(parameterType); } else { params[i] = beanFactory.getBean(qualifier.name(), parameterType); } } } // 处理要注入多个Bean的情况,支持Array/Collection/Map三种方式,其它的不支持 public Object handleMultiBeans(Class<?> type, Type genericType) { // 如果类型是个数组,那么从容器当中注入全部该类型的元素列表 if (type.isArray()) { return handleArrayBean(type, genericType); // 如果类型是个集合类型的话,也是从容器中注入全部该类型的元素列表 } else if (ClassUtils.isAssignableFrom(Collection.class, type)) { return handleCollectionBean(type, genericType); //如果类型是个Map类型,key是它的beanName,value是Bean } else if (ClassUtils.isAssignableFrom(Map.class, type)) { return handleMapBean(type, genericType); } return null; } // 处理要注入一个Array的情况,注入某种类型的全部Bean的数组 private Object handleArrayBean(Class<?> type, Type genericType) { Class<?> componentType = type.getComponentType(); // 这个api是获取数组的元素类型 List<?> beansForType = beanFactory.getBeansForType(componentType); // 使用Array类去创建一个目标类型的数组对象 Object targetArray = Array.newInstance(componentType, beansForType.size()); for (int i = 0; i < beansForType.size(); i++) { Array.set(targetArray, i, beansForType.get(i)); } return targetArray; } // 处理要注入一个Collection的情况...注入某种类型的全部Bean的Collection @SuppressWarnings({"unchecked", "rawtypes"}) private Object handleCollectionBean(Class<?> type, Type genericType) { Collection collection; if (type == Collection.class || type == List.class) { collection = new ArrayList(); } else if (type == Set.class) { collection = new HashSet(); } else { collection = (Collection) ClassUtils.newInstance(type); } AssertUtils.notNull(collection, "进行注入的Collection必须提供无参数构造器"); // 如果提供了泛型参数,那么...按照泛型参数去进行注入 if (genericType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); Class targetType = (Class) actualTypeArguments[0]; List<?> beanNamesForType = beanFactory.getBeansForType(targetType); collection.addAll(beanNamesForType); // 如果没有泛型参数,那么暂时不支持 } else { } return collection; } // 处理需要注入一个Map的情况,Key是beanName,value是beanName对应的Bean @SuppressWarnings({"unchecked", "rawtypes"}) private Object handleMapBean(Class<?> type, Type genericType) { Map map; if (type == Map.class) { map = new HashMap(); } else { map = (Map) ClassUtils.newInstance(type); } AssertUtils.notNull(map, "Map必须提供无参数构造器"); if (genericType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); Class keyType = (Class) actualTypeArguments[0]; AssertUtils.assertTrue(ClassUtils.isAssignableFrom(CharSequence.class, keyType), "要进行注入Map的Key必须是字符串(CharSequence)类型的"); Class valueType = (Class) actualTypeArguments[1]; // 拿到容器中所有该类型的Bean,加入到map当中去... for (String name : beanFactory.getBeanNamesForType(valueType)) { Object injectBean = beanFactory.getBean(name); map.put(name, injectBean); } // 如果Map没有泛型参数,那么...暂时不支持... } else { } return map; } // 对候选的参数去进行非空的检查,如果有一个为空,那么就会return 非空的参数所在的索引,如果全部都非空,那么return -1 private int checkRequired(Object[] params) { for (int i = 0; i < params.length; i++) { // 如果是字符串的话,并且为空串的话...那么 if (params[i] instanceof CharSequence && StringUtils.isNullOrEmpty(params[i].toString())) { return i; } if (Objects.isNull(params[i])) { return i; } } return -1; } // 处理@Value去进行注入的情况 private Object handleValueInject(String valueValue) { AssertUtils.assertTrue(!StringUtils.isNullOrEmpty(valueValue), "@Value注解不能不配置value属性"); // 如果必要的话,那么使用嵌入式的值解析器去解析占位符 return beanFactory.hasEmbeddedValueResolver() ? beanFactory.resolveEmbeddedValue(valueValue) : valueValue; } }#Java##学习路径#