【java】 SpringFramework ———— 方法的注入

继上次对象的成员注入后,当我们对某个类对象进行注入时,如果该类对象中有一个待注入的成员是jar包里的类对象,由于我们无法对jar包进行@Component的注解,所以就得采用在一个方法中实例化该对象再注入的手段。

        @Bean
	public Point getjar(Complex c) {
		
		Point p = new Point();
		return p;
		
	}

上图该方法就是为了处理当jar包里的Point类对象作为待注入成员,我们可以通过反射机制调用将该方法,将其返回值打包成BeanDefinition然后存到BeanFactory的beanpool里面等待以后注入,这便是解决思路。

但在实际处理时我们要注意以下问题:

1.@Bean注解的方法有可能是无参的,我们把他保存下来以供后面直接反射机制调用处理就行。但如果@Bean注解的方法如果有参数且参数类是@Component注解的类(上图的方法便是如此),那我们就得考虑先后顺序问题,即应该先处理@Component注解的普通类并将其存入beanpool中,然后在处理方法的注入。

解决办法:包扫描时扫描到方法带有@Bean注解时我们先将该方法打包分类保存下来,等扫描完所有的@Component类后在进行处理

new PackageScanner() {//
			
			@Override
			public void dealClass(Class<?> klass) {
				
				if(klass.isPrimitive() || klass.isAnnotation()
					||klass.isEnum()||klass.isArray()
					||! klass.isAnnotationPresent(Component.class)) {
					return;
				}
				Object obj = null;
				try {
					 obj = klass.newInstance();
					BeanDefinition bd = new BeanDefinition();
					bd.setKlass(klass);
					bd.setObj(obj);
					
					beanpool.put(klass.getName(), bd);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				collentmethod(obj,klass,on);
                //没扫描一个类就直接调用处理保存方法
				
			}
		}.packageScanner(pagename);
private static void collentmethod(Object obj,Class<?>klass,Onreday on) {
//对扫面到的类的方法进行处理
			ParamterDependce pa = new ParamterDependce();
			Method[] method = klass.getMethods();
			for(int i = 0; i < method.length;i++ ) {
				if(!method[i].isAnnotationPresent(Bean.class)) {
//看该方法有么有Bean注解
					continue;		
				}
				//BeanMessageDefinnation便是保存方法的类
				BeanMessageDefinnation bmd = new     BeanMessageDefinnation();//有的话就直接newBeanMessageDefinnation对象保存
				bmd.setKlass(klass);
				bmd.setMethod(method[i]);//处理方法
				bmd.setObj(obj);
				
				if(!pa.addDependce(bmd)) {//判断有参无参
					on.in(bmd);//无参直接保存
				}
			}

2.该如何处理带参数的方法,这是整套体系的核心所在因为有些特殊情况我们不得不重视:

<a>多个方法参数相同或包含同一个参数;也就是说我们每处理一个方法是否应该都要先遍历一遍beanpool。

否,因为他的时间复杂度实在太高了,这里我们的解决办法就是用另外一个容器储存map,将参数的类类型作为键,将所有需求参数的方法组成一个list作为值,这样只需遍历一遍beanpool得到可供使用的参数。

//用于储存带参方法的容器
private static final Map<Class<?>,List<BeanMessageDefinnation>> parameterDependce;
	//用参数类型为键,需要该参数值的所有方法形成list作为值;
	
	static {
		parameterDependce = new HashMap<Class<?>,List<BeanMessageDefinnation>>();
	}

//该方法用于给parameterDependce容器添加内容,每次传递过来一个@bean注解方法
 boolean addDependce(BeanMessageDefinnation bmd){

		 Method method	= bmd.getMethod();//得到该方法名
		 int methodcount = bmd.getParacount();//取得参数类型个数
		 
		 if(methodcount <= 0) {//无参方法处理
			 return false;
		 }

		 Parameter[] paras = method.getParameters();//取得所有参数类型成员
		 for(int i = 0; i < paras.length; i++) {//遍历参数成员
			 Class<?> paratype = paras[i].getType();//得到该参数类型
			
			 if(!parameterDependce.containsKey(paratype)) {
                    //判断容器里是否包含该键
				 parameterDependce.put(paratype,
                         new ArrayList<BeanMessageDefinnation>());//不包含则生成一条新记录
			 }
			 List<BeanMessageDefinnation> list =   parameterDependce.get(paratype);//包含则将自己加入需求参数的数组中
			 list.add(bmd);
		 }
		 return true;
	}

<b> 如何得知某个方法参数已经全部准备妥当了可以进行处理并存入map以供其他情况使用(例如下面的<c>情况);这里我们采用标记计数法,即当扫描到该方法在保存时,将他的参数类型个数保存下来,在每满足该方法的一个参数后我们便给标记减一。当标记减到零时将该方法从parameterDependce 容器中的list捞出来放到ready容器中以供处理,无参方法则直接放入ready容器中以供处理,parameterDependce 容器中每处理完一个参数键对应的list就该销毁整条记录。

 void matchDependance(Class<?> klass, Onreday onReady) {
//该方法用于对以注入的参数的方法list进行操作
		 List<BeanMessageDefinnation> list = parameterDependce.get(klass);
//先得到这个已经满足的list
		 if(list == null) {
			 return;
		 }
		 for(int i = 0 ; i < list.size(); i++) {//遍历
			int count  = list.get(i).increase();//给每个方法的参数个数进行--
			if(count == 0) {//如果为0则证明该方法已完毕
				onReady.in(list.get(i));
			}
			list.remove(i);//无论是否完毕都要删除掉该方法
			if(list.isEmpty()) {
				parameterDependce.remove(klass);
//若该参数对应的数组已经空了,则删除整个记录
			}
		 }
	 }

 

public void setMethod(Method method) {//计数的方法
		this.method = method;
		Parameter[] par = method.getParameters();
		
		if(par.length == 0) {
			this.paracount = 0;
			return;
		}
		
		Map<Class<?>,Object> Methmap = new HashMap<Class<?>,Object>();
		for(Parameter parameter : par) {
			Methmap.put(parameter.getType(), null);
		}
		this.paracount = Methmap.size();
		this.fcount = Methmap.size();
	}

<c>   一个方法的返回值是另一个方法的参数;例如:A方法的返回值是B方法的参数,那就应该在每处理完一个方法后检查beanpool看是否又满足新的需求。

void check(Onreday orbmd) {//检查已经可用的依赖
		BeanFactory bf = new BeanFactory();
		
		for(Class<?> klass : parameterDependce.keySet()) {
//遍历依赖关系的map,得到键集合在beanfactor里的map找是否满足
		
			if( bf.getBeanDefinition(klass) != null) {
				matchDependance(klass, orbmd);//满足了就调用下面方法
			}
		}
		 
	 }
private static void processBeanMethod(Onreday orbm,ParamterDependce pa) {
//处理ready的列表里准备好的方法
			while(orbm.hasNext()) {//如果准备好的list里有下一成员
			BeanMessageDefinnation bm	= orbm.next();
			deallist( bm);//调用该方法给beanpool里添加新的bean
			pa.check(orbm);
//每次执行完后就在检查是否有有新的关系满足了,实现处理和生成同时操作
			}
			
		}
		
		
		private static void deallist(BeanMessageDefinnation bm) {
			Parameter[]  pa	=bm.getMethod().getParameters();
			Object[] op = null;
			if(pa.length == 0) {//判断有参无参
				op =  new Object[] {};
			}else {
				op = new Object[pa.length];
			}
			for(int i = 0; i<pa.length;i++) {

				op[i] = beanpool.get(pa[i].getType().getName()).getObj();	//从map里得到参数具体值
			}
			
			try {
				Object ob = bm.getMethod().invoke(bm.getObj(), op);
				BeanDefinition bd = new BeanDefinition();
				bd.setKlass(ob.getClass());
				bd.setObj(ob);
				bd.setInject(false);
				beanpool.put(ob.getClass().getName(),bd);		
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}

在处理完问题后,我们的模型就成了,在这里我在次简述下大体思路:先扫描,扫描到普通类时处理完存入map;扫描到方法时先分情况保存下来,无参的直接加入到ready容器中,带参的先保存到parameterDependce容器中等加工完成后在加入到ready容器中,然后对reday容器中准备好的方法进行处理在保存到map中以供使用。

以下是源代码和测试结果,感兴趣的小伙伴可以了解下(解决了上次遗留的循环依赖,并还有些其他处理过程)。

 BeanFactory类:

public class BeanFactory {
	private static boolean firstGetBean = true;
	
	private static final Map<String,BeanDefinition> beanpool;
//注入时应该在map里找,即注入的是map中存在的值
	static {
		beanpool = new HashMap<String, BeanDefinition>();
	}
	
	boolean isFirstGetBean() {
		return firstGetBean;
	}

	void setFirstGetBean(boolean firstGetBean) {
		BeanFactory.firstGetBean = firstGetBean;
	}
	
	public static void scanpackage(String pagename) {
		ParamterDependce pa = new ParamterDependce();
		Onreday on = new Onreday();
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				
				if(klass.isPrimitive() || klass.isAnnotation()
						||klass.isEnum()||klass.isArray()
						||!klass.isAnnotationPresent(Component.class)) {
					return;
				}
				Object obj = null;
				try {
					 obj = klass.newInstance();
					BeanDefinition bd = new BeanDefinition();
					bd.setKlass(klass);
					bd.setObj(obj);
					
					beanpool.put(klass.getName(), bd);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				collentmethod(obj,klass,on);//没扫描一个类就直接调用处理保存方法
				
			}
		}.packageScanner(pagename);
		
		
		pa.check(on);//在扫面并装入完所有类对象后先检查以满足的方法
		processBeanMethod (on,pa);//处理方法
	}
	
		private static void processBeanMethod(Onreday orbm,ParamterDependce pa) {//处理ready的列表里准备好的方法
			while(orbm.hasNext()) {//如果准备好的list里有下一成员
			BeanMessageDefinnation bm	= orbm.next();
			deallist( bm);//调用该方法给beanpool里添加新的bean
			pa.check(orbm)//每次执行完后就在检查是否有有新的关系满足了,实现处理和生成同时操作
			}
			
		}
		
		
		private static void deallist(BeanMessageDefinnation bm) {
			Parameter[]  pa	=bm.getMethod().getParameters();
			Object[] op = null;
			if(pa.length == 0) {//判断有参无参
				op =  new Object[] {};
			}else {
				op = new Object[pa.length];
			}
			for(int i = 0; i<pa.length;i++) {

				op[i] = beanpool.get(pa[i].getType().getName()).getObj();	//从map里得到参数具体值
			}
			
			try {
				Object ob = bm.getMethod().invoke(bm.getObj(), op);
				BeanDefinition bd = new BeanDefinition();
				bd.setKlass(ob.getClass());
				bd.setObj(ob);
				bd.setInject(false);
				beanpool.put(ob.getClass().getName(),bd);		
				
			} catch (Exception e) {
				
				e.printStackTrace();
			}
			
		}
	
		private static void collentmethod(Object obj,Class<?>klass,Onreday on) {//对扫面到的类的方法进行处理
			ParamterDependce pa = new ParamterDependce();
			Method[] method = klass.getMethods();
			for(int i = 0; i < method.length;i++ ) {
				if(!method[i].isAnnotationPresent(Bean.class)) {
//看该方法有么有Bean注解
					continue;		
				}
				//BeanMessageDefinnation
				BeanMessageDefinnation bmd = new BeanMessageDefinnation();//有的话就直接new对象
				bmd.setKlass(klass);
				bmd.setMethod(method[i]);//处理方法
				bmd.setObj(obj);
				
				if(!pa.addDependce(bmd)) {
					on.in(bmd);
				}
			}
		
		}
	//通过键取出definnation
	BeanDefinition getBeanDefinition(String klassname) {
		return beanpool.get(klassname);
	}
	
	BeanDefinition getBeanDefinition(Class<?> klassname) {
		return getBeanDefinition(klassname.getName());
		
	}
	
	private void injectPro(BeanDefinition bd) throws RuntimeException {//给该对象待注入的成员注入
	
		Class<?> klass = bd.getKlass();
	//	System.out.println(klass.getName());
		Object obj = bd.getObj();
		
		Field[] field = klass.getDeclaredFields();
		
		for(int i = 0; i < field.length;i++) {
			if(!field[i].isAnnotationPresent(Autowired.class)) {
//如果没有注入标记就跳过
				continue;
			}
		
			field[i].setAccessible(true);
//有标记的话就先修改权限,然后在map中通过class找对应的值
		
			Object value = getBean(field[i].getType());
			if(value == null) {
//如果value == null,就说明该成员待注入却没在map中没找到,则说明有错那就抛异常
				throw new StopException("类{"+klass.getName()+"}中成员{"+field[i].getName()+"}在map中没有对应的值");
			}
			try {
				field[i].set(obj,value);
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}//给该对象的成员赋值
			
			System.out.println("成员["+klass.getName()+"."+field[i].getName()+"]以完成注入");
		}
		bd.setInject(true);
//对所有成员注入完成之后一定要对对象的注入标记做更改,这样才能保证只注入一次
	}
	
	
	//通过键直接取得对象
	@SuppressWarnings("unchecked")
	public <T> T getBean(String klassname) throws RuntimeException {
//也可以将Objet作为返回类型,不过在使用时得要强转下,所以泛型好用些
		if (firstGetBean) { // 这里并没有考虑线程安全性的问题
			ParamterDependce parameterDependance = new ParamterDependce();
			if (!parameterDependance.isEmpty()) {
				throw new RuntimeException(
						parameterDependance.getDependanceMessage());
			}
		}
		BeanDefinition bd = getBeanDefinition(klassname);
	
		if(bd == null) {
			return null;
		}
		
		Object res = bd.getObj();
	//	System.out.println(!bd.isInject());
		if(!bd.isInject()) {//判断该对象的成员是否已经被注入过了
			
			bd.setInject(true);
				injectPro(bd);
			
		}
	
		return (T) res;
	}
	
	public <T> T getBean(Class<?> klassname) throws RuntimeException {
		return getBean(klassname.getName());
		
	}
	
	
}

 

 BeanMessageDefinnation类:

public class BeanMessageDefinnation {
	private Class<?> klass;
	private Object obj;
	private Method method;
	private int paracount;
	private int fcount;
	
	

	public BeanMessageDefinnation(){
		
	}
	
	
	public int getFcount() {
		return fcount;
	}

	public int getParacount() {
		return paracount;
	}


	int increase() {
		return --this.paracount;
	}
	
	public Class<?> getKlass() {
		return klass;
	}
	public void setKlass(Class<?> klass) {
		this.klass = klass;
	}
	public Object getObj() {
		return obj;
	}
	public void setObj(Object obj) {
		this.obj = obj;
	}
	public Method getMethod() {
		return method;
	}
	
	public void setMethod(Method method) {
		this.method = method;
		Parameter[] par = method.getParameters();
		
		if(par.length == 0) {
			this.paracount = 0;
			return;
		}
		
		Map<Class<?>,Object> Methmap = new HashMap<Class<?>,Object>();
		for(Parameter parameter : par) {
			Methmap.put(parameter.getType(), null);
		}
		this.paracount = Methmap.size();
		this.fcount = Methmap.size();
	}

}

ParamterDependce类:

public class ParamterDependce {
	public static boolean sign = true;
	private static final Map<Class<?>,List<BeanMessageDefinnation>> parameterDependce;//用于从处理待注入的方法
	//用参数类型为键,需要该参数作为注入值的所有方法形成list作为值;
	
	static {
		parameterDependce = new HashMap<Class<?>,List<BeanMessageDefinnation>>();
	}
	
	public ParamterDependce() {
		
	}
	
	boolean isEmpty() {
		return parameterDependce.isEmpty();
	}
	
	String getDependanceMessage() {
		StringBuffer res = new StringBuffer();
		
		for (Class<?> klass : parameterDependce.keySet()) {
			List<BeanMessageDefinnation> bmdList = parameterDependce.get(klass);
			for (BeanMessageDefinnation bmd : bmdList) {
				res.append('\n').append("类:"+bmd.getKlass().getName()+"中的:")
				.append(bmd.getMethod().getName()).append("方法")
				.append(" 缺少对应参数: ")
				.append(klass.getName());
			}
		}
		res.append('\n');
		
		return res.toString();
	}
	
	 boolean addDependce(BeanMessageDefinnation bmd){
//该方法用于给parameterDependce容器添加内容,每次传递过来一个bean注解方法
		 Method method	= bmd.getMethod();//得到该方法名
		 int methodcount = bmd.getParacount();//取得参数类型个数
		 
		 if(methodcount <= 0) {//无参方法
			 return false;
		 }

		 Parameter[] paras = method.getParameters();//取得所有参数类型成员
		 for(int i = 0; i < paras.length; i++) {//遍历参数成员
			 Class<?> paratype = paras[i].getType();//得到该参数类型
			
			 if(!parameterDependce.containsKey(paratype)) {
//判断容器里是否包含该键
				 parameterDependce.put(paratype, new ArrayList<BeanMessageDefinnation>());//不包含则生成一条记录
			 }
			 List<BeanMessageDefinnation> list = parameterDependce.get(paratype);//将自己加入需求参数的数组中
			 list.add(bmd);
		 }
		 return true;
	}
	 
	 void check(Onreday orbmd) {//检查已经可用的依赖
		BeanFactory bf = new BeanFactory();
		
		for(Class<?> klass : parameterDependce.keySet()) {
//遍历依赖关系的map,得到键集合在beanfactor里的map找是否满足
		
			if( bf.getBeanDefinition(klass) != null) {
				matchDependance(klass, orbmd);//满足了就调用下面方法
			}
		}
		 
	 }
	 
	 void matchDependance(Class<?> klass, Onreday onReady) {
//该方法用于对以注入的参数的方法list进行操作
		 List<BeanMessageDefinnation> list = parameterDependce.get(klass);
//先得到这个已经满足的list
		 if(list == null) {
			 return;
		 }
		 for(int i = 0 ; i < list.size(); i++) {//遍历
			int count  = list.get(i).increase();//给每个方法的参数个数进行--
			if(count == 0) {//如果为0则证明该方法已完毕
				onReady.in(list.get(i));
			}
			list.remove(i);//无论是否完毕都要删除掉该方法
			if(list.isEmpty()) {
				parameterDependce.remove(klass);
//若该参数对应的数组已经空了,则删除整个记录
			}
		 }
	 }
	 
	 
}

	

Oneready类:

public class Onreday {
	private List<BeanMessageDefinnation> onreadlist;
	
	public Onreday() {
		onreadlist = new LinkedList<BeanMessageDefinnation>();
	}
	
	void in(BeanMessageDefinnation bmd) {
		onreadlist.add(bmd);
	}
	
	BeanMessageDefinnation next() {
		return onreadlist.remove(0);
	}
	
	boolean hasNext() {
		return !onreadlist.isEmpty();
	}
}

Point类:

public class Point {
  
	private int a;
	private int b;
	@Autowired
	private Complex c;
	
	public Point() {
		
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}

	public int getB() {
		return b;
	}

	public void setB(int b) {
		this.b = b;
	}

	@Override
	public String toString() {
		return "Point [a=" + a + ", b=" + b + "]"+c
				;
	}
	
	

}

测试结果:

 public static void main(String[] args) {
		BeanFactory bf = new BeanFactory();
		BeanFactory.scanpackage("com.mec.test");

		
		 Point a =  bf.getBean(Point.class);
		 System.out.println(a);
/*output:
成员[com.mec.test.Point.c]以完成注入
Point [a=0, b=0](0.0,0.0)
*/
		
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值