【java】 SpringFramework ———— 注入

我们这次的目的有三个:

1.组建Bean工厂(上下文,容器);

2.能够对Bean的成员进行注入;

3.能够取得Bean;

Bean,又称Java Bean,或者说POJO,平凡的Java类。

将这些平凡的Java类对象组织到一个容器中,就形成了所谓的“上下文”或“容器”,容器的形成需要配置手段,可以采用注解或者Xml文件的形式,在这里我们采用注解的形式。

@Component//该注解用来表明该类为容器中的类
public class Classone {
	@Autowired//该注解用于表示待注入的成员
	private Complex complex;
	private String str;
	@Autowired
	private Point point;
	
	public Classone() {
		
	}

    }

接下来我们要完成对Bean工厂的编写

public class BeanFactory {
	private static final Map<String,BeanDefinition> beanpool;//容器
	static {
		beanpool = new HashMap<String, BeanDefinition>();
	}
	
	public static void scanpackage(String pagename) {//包扫描
		new PackageScanner() {
			
                        //处理扫描到的类,如果不是带有Component注解的就直接跳过
			@Override
			public void dealClass(Class<?> klass) {
				if(klass.isPrimitive() 
                                || klass.isAnnotation()
				||klass.isEnum()
                                ||klass.isArray()
			        ||!klass.isAnnotationPresent(Component.class)) {
					return;
				}
				
				try {
					Object obj = klass.newInstance();
                                   /*BeanDefinition类是对我们扫描到的类进行进一步提炼,
                                        保留我们想要的值*/
					BeanDefinition bd = new BeanDefinition();
					bd.setKlass(klass);
					bd.setObj(obj);
					//将其储存到map中,键为该类的类名称
					beanpool.put(klass.getName(), bd);
				} catch (Exception e) {
					
					e.printStackTrace();
				}
				
			}
		}.packageScanner(pagename);
		
	}

        BeanDefinition getBeanDefinition(String klassname) {//通过类名称取得值
		return beanpool.get(klassname);
	}
	
	BeanDefinition getBeanDefinition(Class<?> klassname) {//通过类取得值
		return getBeanDefinition(klassname.getName());
		
	}

        public <T> T getBean(String klassname){//该方法用于直接在map获得对象值,返回值用泛型而不用Object类型是因为用泛型在调用时不需要强转;
        BeanDefinition bd = getBeanDefinition(klassname);
        if(bd = null){
            return null;
            }
        return (T) bd.getObject();
        }

 

通过传递Classone.class.getName()测试getBean我们发现得到的结果是:Classone [complex=null, str=null,Point=null],理应如此,因为map中存的对象是newInstance()的得到的,所以其成员值都为null,接下来我们就要对该对象的成员进行注入,在注入时我们要注意的问题(这里不做对循环注入的讨论,即A对象中的B成员待注入,而B中还有A成员待注入形成死循环):

1.我们采用的是“懒汉模式”,即等到相关的Bean被获取时才会注入,且Bean工厂中的Bean都是单例的,对同一个bean只用注入一次即可。

2.对成员注入时应该在map中通过该成员类名称进行查找,即注入的是map中的值。

3.在注入时应该深度注入,即在对一个对象的成员进行注入时,我们因该先查找该成员的类中有没有成员需要注入。

例如:A中有B成员,B中有C成员,对A中的B进行注入时,理所应当的先判断B中有没有也需要注入的,即先给B中的C注入

4.如果该对象中有被标记了Autowired的待注入成员,但是map中却没有该成员的Bean则应报异常并且终止运行。

在了解上面的信息后来看看我们的实现:

       //获得Bean的方法
         @SuppressWarnings("unchecked")
	public <T> T getBean(String klassname) throws RuntimeException {
		BeanDefinition bd = getBeanDefinition(klassname);
		if(bd == null) {
			return null;
		}
		Object res = bd.getObj();
		if(!bd.isInject()) {//判断该对象的成员是否已经被注入过了
		    injectPro(bd);//没注入则调用注入方法
		}
		
		return (T) res;
	}
        
        //给该对象待注入的成员注入
        private void injectPro(BeanDefinition bd) throws RuntimeException {
		Class<?> klass = bd.getKlass();
		Object obj = bd.getObj();
		
		Field[] field = klass.getDeclaredFields();//反射机制调用获取该对象所有成员
		
		for(int i = 0; i < field.length;i++) {
			if(!field[i].isAnnotationPresent(Autowired.class)) {
				continue;    //如果没有注入标记就跳过
			}
                        //有标记的话就先修改权限,然后在map中找对应的值
			field[i].setAccessible(true);
			
//			String klassname = field[i].getType().getName();
//			BeanDefinition bdm = getBeanDefinition(klassname);//在map中找

//			if(bdm == null) {//如果map中没找到,则说明有错那就抛异常
//				throw new Exception
//                    ("类{"+klass.getName()+"}中成员{"+field[i]+"}在map中没有对应的值");
//			}

//			field[i].set(obj, bdm.getObj());//给该成员注入
		
			Object value = getBean(field[i].getType());//递归
/*如果用上面的被注释的代码就只能对该成员进行查找注入(浅层次),但是万一该成员内还有需要注入的成员,就会出错,就会导致深度不够深。
    例如:A中有B成员,B中有C成员,对A中的B进行注入时,理所应当的先判断B中有没有也需要注入的,即先给B中的C注入,所以这里的递归调用没有问题 */
			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("成员["+field[i].getName()+"]以完成注入");
		}
		bd.setInject(true);//对所有成员注入完成之后一定要对对象的注入标记做更改,这样才能保证只注入一次
	}

	//该方法的重载,直接用类类型作为参数
	public <T> T getBean(Class<?> klassname) throws RuntimeException {
		return getBean(klassname.getName());
		
	}

以下是BeanDefinition.java:

public class BeanDefinition {
	private Class<?> klass;
	private Object obj;
	private boolean inject;
	
	 BeanDefinition() {
		this.inject = false;
	}

	
	boolean isInject() {
		return inject;
	}


	void setInject(boolean inject) {
		this.inject = inject;
	}


	Class<?> getKlass() {
		return klass;
	}


	void setKlass(Class<?> klass) {
		this.klass = klass;
	}


	Object getObj() {
		return obj;
	}


	void setObj(Object obj) {
		this.obj = obj;
	}


	@Override
	public String toString() {
		return klass.getSimpleName() + " : " + obj;
	}
	
	
}

以下是对我们注入和获取的测试:

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

		Classone co = bf.getBean(Classone.class);
		Classone cp = bf.getBean(Classone.class);
		System.out.println(co);
		System.out.println(cp);
	}
/*输出结果:
    成员[complex]以完成注入
    成员[point]以完成注入
    Classone [complex=(0.0,0.0), str=null,Point [a=0, b=0]]
    Classone [complex=(0.0,0.0), str=null,Point [a=0, b=0]]*/

顺便对我们的异常处理进行测试,我们将Point类上的标记去掉,让map中不会存在被标记了Autowired的待注入成员point:

 

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

		Classone co = bf.getBean(Classone.class);
		System.out.println(co);

	}
/*输出结果:
    Exception in thread "main" com.mec.spring.core.StopException: 类{com.mec.test.Classone}中成员{point}在map中没有对应的值
	at com.mec.spring.core.BeanFactory.injectPro(BeanFactory.java:77)
	at com.mec.spring.core.BeanFactory.getBean(BeanFactory.java:102)
	at com.mec.spring.core.BeanFactory.getBean(BeanFactory.java:110)
	at com.mec.test.text.main(text.java:13)*/

可见我们的异常处理也没有问题。

上一篇:[转]axios请求超时,设置重新请求的完美解决方法


下一篇:javascript实现网页分享至朋友圈功能