自分が実現したSpring父類注入

13088 ワード

開発過程において、全注釈方式を使うと、いずれかの親類に引き継がれることになりますが、この親類の属性は注入が必要です.
 
XMLを使用すれば、直接XMLプロファイルに属性setterを使用して親属性を注入することができます.
 
しかし、全注解を使うと、この問題は比較的難しいです.
 
方法1:
自分でプロジェクトの中で1つの種類を書いて父種類Aに継承して、直接にその父種類Aを受け継ぐことができません.この親タイプAは別のjarパッケージのクラスかもしれません.このクラスAはannotationによって修飾されていないかもしれません.注入が完了しません.自分で書いたのはこのタイプのサブクラスBに引き継がれます.プロジェクトでannotationを使って属性注入ができます.親属性の注入を完了しました.
 
この方法は親注入の問題を解決することができますが、父類を自分で書いて中間の橋渡しを実現します.
 
方法2:
二段階注入を実現するには、いわゆる二段階注入というのは、まずスプリングス自身の注入論理に依存してサブクラスの主な属性の注入を完了することであり、親類のいくつかの属性は、自分に依存して実現した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;
	}

}
 いくつかのカスタムコメントを使用しました.
Partent Inject:
/**
 *       
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ParentInject {
	
	/**
	 *    
	 * @return
	 */
	InjectItem[] items();

}
 Inject Item:
/**
 *                ,         ,     ,    
 * 
 *
 */
@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 "";

}
 Inject Style
public enum InjectStyle {
	
	/**
	 *   method invoke      
	 */
	METHOD_INVOKE,
	/**
	 *           
	 */
	ATTR_REFLECTION

}
 
public enum InjectType {
	
	BY_NAME,BY_TYPE,AUTO

}
 
XMLファイルにセットすればいいです.
テストクラス:
 
パーティー類は親類を表します.
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 hello 2
親注入が成功したことを示します.
 もちろんこのような方法は様々な変形がありますが、その主な意味と方法1は同じです.