自己实现的Spring父类注入

在开发过程如果使用全注解方式,难免会遇到一个类继承至某个父类,而这个父类的属性需要注入。

如果使用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是差不多的。

 


 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值