在开发过程如果使用全注解方式,难免会遇到一个类继承至某个父类,而这个父类的属性需要注入。
如果使用XML可以直接在XML配置文件中使用属性setter进行注入父类属性。
但是如果使用的是全注解,这个问题会比较棘手。
方法1:
自己在工程项目中写一个类继承至该父类A,而不要直接继承该父类A。原因是该父类A可能是另外的jar包中的类,该类A可能没有被annotation修饰,无法完成注入,而你自己写的继承至该类的子类B可以在项目中使用annotation进行属性注入(要复写父类的setter方法,然后使用annotation注解),然后项目中其它类C可以继承至类B,完成父类属性的注入
这种方法的可以解决父类注入的问题,但是要自己写一个父类实现一个中间的桥接。
方法2:
实现两级注入,所谓的两级注入即是首先依赖spring自身的注入逻辑完成子类主要属性的注入,而父类的某些属性注入依赖自己实现的BeanPostProcessor完成二级注入。
我目前实现的方法:
主要实现一个BeanPostProcessor:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ReflectionUtils;
public class InjectParentBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext ctx;
private Set<String> alreadyInjected = new HashSet<String>();
private List<Class<?>> basicTypes = new ArrayList<Class<?>>() {
private static final long serialVersionUID = 1L;
{
add(char.class);
add(Character.class);
add(byte.class);
add(Byte.class);
add(short.class);
add(Short.class);
add(int.class);
add(Integer.class);
add(long.class);
add(Long.class);
add(String.class);
add(float.class);
add(Float.class);
add(double.class);
add(Double.class);
}
};
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ParentInject injectParent = bean.getClass().getAnnotation(ParentInject.class);
String[] aliases = ctx.getAliases(beanName);
if (injectParent != null) {
// 是否已经注入过
Object hasInjected = hasInjected(bean, beanName, aliases);
if (hasInjected != null) {
return hasInjected;
}
return doInject(bean, beanName, aliases, injectParent);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* 进行父类属性注入
*
* @param bean
* @param beanName
* @param injectParent
* @return
*/
private Object doInject(Object bean, String beanName, String[] aliases, ParentInject injectParent) {
InjectItem[] items = injectParent.items();
for (InjectItem item : items) {
InjectStyle injectStyle = item.injectStyle();
String attr = item.attr();
String injectRef = item.ref();
String injectValue = item.value();
Object injectObj = null;
if (injectRef != null && !injectRef.trim().equals("")) {
injectObj = ctx.getBean(injectRef);
}
if (injectObj == null) {
Field f = ReflectionUtils.findField(bean.getClass(), attr);
if (f == null) {
throw new RuntimeException("Class:" + bean.getClass() + " has not field:" + attr);
}
if (isBasicType(f.getType())) {
// 做基本类型转换
injectObj = basicTypeConvert(f.getType(), injectValue);
} else {
// 非基本类型,则猜测类型按类型注入
injectObj = ctx.getBean(f.getClass());
}
}
//注意如果是动态代理,属性注入会失败
if (InjectStyle.ATTR_REFLECTION.equals(injectStyle)) {
// 按属性直接注入
Field f = ReflectionUtils.findField(bean.getClass(), attr);
if (f == null) {
throw new RuntimeException("field does not exist:" + attr);
}
ReflectionUtils.makeAccessible(f);
ReflectionUtils.setField(f, bean, injectObj);
putInjected(aliases);
} else if (InjectStyle.METHOD_INVOKE.equals(injectStyle)) {
// 按方法调用注入
String injectMethod = item.injectMethod();
if (injectMethod == null || injectMethod.trim().equals("")) {
injectMethod = findSetterMethod(attr);
}
Method m = findInjectMethod(bean.getClass(), injectMethod, injectObj);
if (m == null) {
throw new RuntimeException("inject method does not exist:" + injectMethod);
}
ReflectionUtils.makeAccessible(m);
ReflectionUtils.invokeMethod(m, bean, injectObj);
putInjected(aliases);
} else {
throw new RuntimeException("not supported injectStyle:" + injectStyle);
}
}
return bean;
}
private Object basicTypeConvert(Class<?> class1, String injectValue) {
if (byte.class.equals(class1) || Byte.class.equals(class1)) {
return Byte.valueOf(injectValue);
} else if (short.class.equals(class1) || Short.class.equals(class1)) {
return Short.valueOf(injectValue);
} else if (int.class.equals(class1) || Integer.class.equals(class1)) {
return Integer.valueOf(injectValue);
} else if (long.class.equals(class1) || Long.class.equals(class1)) {
return Long.valueOf(injectValue);
} else if (float.class.equals(class1) || Float.class.equals(class1)) {
return Float.valueOf(injectValue);
} else if (double.class.equals(class1) || Double.class.equals(class1)) {
return Double.valueOf(injectValue);
}
return injectValue;
}
/**
* 是否是基本类型
*
* @param injectValue
* @return
*/
private boolean isBasicType(Class<?> cls) {
return basicTypes.contains(cls);
}
/**
* 通过迭代找出当前类或者父类方法
*
* @param injectMethod
* @return
*/
private Method findInjectMethod(Class<?> cls, String injectMethod, Object... args) {
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(injectMethod)) {
// 判断参数类型是否匹配
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length == 1 && isSuitableBasicType(paramTypes[0], args[0].getClass())) {
return method;
}
}
}
List<Class<?>> parentClasses = new ArrayList<Class<?>>();
if (cls.getSuperclass() != null) {
parentClasses.add(cls.getSuperclass());
}
parentClasses.addAll(Arrays.asList(cls.getInterfaces()));
for (Class<?> c : parentClasses) {
return findInjectMethod(c, injectMethod, args);
}
return null;
}
private boolean isSuitableBasicType(Class<?> class1, Class<?> class2) {
if (isByteTypeClass(class1) && isByteTypeClass(class2)) {
return true;
}
if (isShortTypeClass(class1) && isShortTypeClass(class2)) {
return true;
}
if (isIntTypeClass(class1) && isIntTypeClass(class2)) {
return true;
}
if (isLongTypeClass(class1) && isLongTypeClass(class2)) {
return true;
}
if (isFloatTypeClass(class1) && isFloatTypeClass(class2)) {
return true;
}
if (isDoubleTypeClass(class1) && isDoubleTypeClass(class2)) {
return true;
}
return class1.equals(class2);
}
private boolean isByteTypeClass(Class<?> cls) {
return byte.class.equals(cls) || Byte.class.equals(cls);
}
private boolean isShortTypeClass(Class<?> cls) {
return short.class.equals(cls) || Short.class.equals(cls);
}
private boolean isIntTypeClass(Class<?> cls) {
return int.class.equals(cls) || Integer.class.equals(cls);
}
private boolean isLongTypeClass(Class<?> cls) {
return long.class.equals(cls) || Long.class.equals(cls);
}
private boolean isFloatTypeClass(Class<?> cls) {
return float.class.equals(cls) || Float.class.equals(cls);
}
private boolean isDoubleTypeClass(Class<?> cls) {
return double.class.equals(cls) || Double.class.equals(cls);
}
/**
* 通过属性名找出setter方法
*
* @param attr
* @return
*/
private String findSetterMethod(String attr) {
StringBuilder sb = new StringBuilder("set");
sb.append(Character.toUpperCase(attr.charAt(0)));
sb.append(attr.substring(1));
return sb.toString();
}
/**
* 放置别名
*
* @param aliases
*/
private void putInjected(String[] aliases) {
alreadyInjected.addAll(Arrays.asList(aliases));
}
/**
* 判断是否已经注入
*
* @param bean
* @param beanName
* @return
*/
private Object hasInjected(Object bean, String beanName, String[] aliases) {
for (String aliase : aliases) {
if (alreadyInjected.contains(aliase)) {
return bean;
}
}
return null;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
}
使用到了几个自定义注解:
ParentInject:
/**
* 注入父类属性
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ParentInject {
/**
* 注入项
* @return
*/
InjectItem[] items();
}
InjectItem:
/**
* 注意注入引用和注入值同时存在时,先判断引用是否存在,如果不存在,则注入值
*
*
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectItem {
/**
* 允许按类型注入,该情况出现在ref和value同时不存在的情况下,此时允许通过attr属性类型进行按类型注入
* @return
*/
boolean allowByType() default false;
/**
* 属性名
* @return
*/
String attr() default "";
/**
* 注入引用
* @return
*/
String ref() default "";
/**
* 注入值
* @return
*/
String value() default "";
InjectStyle injectStyle() default InjectStyle.ATTR_REFLECTION;
/**
* 注入方法
* @return
*/
String injectMethod() default "";
}
InjectStyle
public enum InjectStyle {
/**
* 通过method invoke方法进行注入
*/
METHOD_INVOKE,
/**
* 通过反射方式直接注入
*/
ATTR_REFLECTION
}
public enum InjectType {
BY_NAME,BY_TYPE,AUTO
}
并在XML文件中配置一下就可以了。
测试类:
Parent类表示父类:
public class Parent {
private byte bStr;
private int age;
private String name = "hello";
private Parent2 p2;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void do1() {
System.out.println("Parent.do1");
}
public Parent2 getP2() {
return p2;
}
public void setP2(Parent2 p2) {
this.p2 = p2;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public byte getbStr() {
return bStr;
}
public void setbStr(byte bStr) {
this.bStr = bStr;
}
}
子类继承至该父类:子类继承至该父类:子类继承至该父类:
@Component
@ParentInject(items=@InjectItem(attr="name",value="test"))
public class Sub extends Parent {
@PostConstruct
public void init() {
System.out.println("PostConstruct:" + getName());
}
}
public class TestMain {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("test-context.xml");
Sub sub = ctx.getBean(Sub.class);
System.out.println(sub.getName());
}
}
PostConstruct:test
test
hello2
表明父类注入成功。
当然这种方式可以有多种变形,其主要意思和方法1是差不多的。