在开发过程如果使用全注解方式,难免会遇到一个类继承至某个父类,而这个父类的属性需要注入。
如果使用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是差不多的。