• 模拟实现Spring中的注解装配


    本文原创,地址为http://www.cnblogs.com/fengzheng/p/5037359.html

    在Spring中,XML文件中的bean配置是实现Spring IOC的核心配置文件,在早版本的Spring中,只能基于XML配置文件,配置各个对象之间的依赖关系。在Spring 2.5以后出现了注解,使用注解结合XML的方式,简化了XML配置的复杂度。

    老版本中纯XML配置实现IOC

    在配置文件中配置如下:

    <bean id="userDao" class="com.springapp.mvc.dao.UserDao">
    </bean>
    <bean id="userService" class="com.springapp.mvc.service.impl.UserServiceImpl"> 
      <property name="userDao" ref="userDao"></property> 
    </bean>

    UserServiceImpl的实现如下:

    public class UserServiceImpl implements UserService {
        public UserDao getUserDao() {
            return userDao;
        }
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        private UserDao userDao;
    
        public User getUserById(int id){
    
            return userDao.getUserById(id);
        }
    
        public int getUserCount(){
            return userDao.getUserCount();
        }
    } 

    配置的意思是:<property name="userDao" ref="userDao"></property>这行配置是为UserServiceImpl类中的userDao指定userDao这个bean,这样在UserServiceImpl类中调用userDao的方法,其实就是调用com.springapp.mvc.dao.UserDao的方法。

    结合注解的实现方式

    配置文件简化如下:

    <bean id="userDao" class="com.springapp.mvc.dao.UserDao">
    </bean>
    <bean id="userService" class="com.springapp.mvc.service.impl.UserServiceImpl"> 
    </bean>

    UserServiceImpl的实现如下:

    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        public User getUserById(int id){
            return userDao.getUserById(id);
        }
    
        public int getUserCount(){
            return userDao.getUserCount();
        }
    }
    

    利用@Autowired注解,实现在xml中<property name="userDao" ref="userDao"></property>的配置,从而实现了自动注入。@Autowired自动注入的规则为byType,意思就是按照被注解字段的类型和xml中配置的bean的类型相匹配,即在UserServiceImpl 类中的userDao为UserDao类型,匹配的时候会在所有bean中查找类型同样为UserDao的bean。

    那么既然是按照类型匹配,如果存在两个相同类型的bean呢,这时候,就会启用第二个匹配规则ByName,即根据字段的名字来匹配相同id的bean。如下XML配置:

    <bean id="userDao1" class="com.springapp.mvc.dao.UserDao">
    </bean>
    
    <bean id="userDao2" class="com.springapp.mvc.dao.UserDao">
    </bean>
    
    <bean id="userService" class="com.springapp.mvc.service.impl.UserServiceImpl">
    </bean>
    

    那么在UserServiceImpl类中应该使用以下这种方式来自动注入:

    @Autowired
    private UserDao userDao1;
    
    @Autowired
    private UserDao userDao2;
    

    这样好像不是很灵活的样子,看起来有些不爽,有没有办法可以指定要匹配的bean呢?没错,是有的。可以使用这样的方式,通过@Autowired和@Qualifier相结合的方式,@Qualifier后跟的参数就是bean的名称:

    @Autowired
    @Qualifier("userDao1")
    private UserDao userDao;
    

    还有更常用的方式,@Resource:

    @Resource(name = "userDao1")
    private UserDao userDao;
    

    关于注解IOC的内容可以参看这篇文章,写的很详细。

    注解在Spring中的用法讲完了,下面来自己实习一个简单的类,来模拟Spring利用注解实现IOC的原理。

    Spring IOC实现原理

    1.首先Spring根据bean配置文件,收集所有bean的实例;

    2.Spring根据配置文件中的context:component-scan,扫描需要被注入的包(递归包中的所有待注入类);

    3.扫描待注入类时,判断是否有特定的注解(例如@Autowired、@Resource),如果有,则进行第4步,注入;

    4.注入:根据注解类型或参数,利用反射,为被注解的字段或属性等设置对应的bean实例。

    以上是我个人理解,可能和Spring真正的实现有些出入。

    模拟利用注解实现注入

    这里要定义一个类似于@Resource的注解,命名为@MyAutowired,定义如下:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE,ElementType.FIELD})
    @Documented
    public @interface MyAutowired {
    	public String name() default "";
    	
    	public String value() default "";
    }
    

    定义配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:context="http://www.springframework.org/schema/context">
    	<context:component-scan id="test" class="fengzheng.Test"/>
    	<bean id="tomoto" class="fengzheng.Tomoto"></bean>
    </beans>
    

    其中bean和Spring中bean的定义是一样的,而context:component-scan在Spring是定义属性base-package,之后根据这个属性,扫描这个包下的所有类,这里为做演示,也定义为一个类,之后会根据这个class属性,对这个类进行注入。

    配置文件中的tomoto bean的定义:

    package fengzheng;
    
    
    public class Tomoto {
    	public void SayHello(){
    		System.out.println("hello I'm tomoto");
    	}
    }
    

    配置文件中fengzheng.Test类定义,这个即为要被注入的类:

    package fengzheng;
    
    import fengzheng.fzAnnotation.MyAutowired;
    
    public class Test {
    	@MyAutowired(name = "tomoto")
    	private Tomoto tomoto; 
    	
    
    	public void Say(){
    		tomoto.SayHello();
    	}
    }
    

    核心注解分析并实现注入的类:

    import java.lang.reflect.Field;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import fengzheng.fzAnnotation.MyAutowired;
    
    import java.util.stream.*;
    
    public class FzClassPathXMLApplication {
    
    	//xml配置文件中 bean定义的集合
    	private List<BeanDefine> beanList = new ArrayList<BeanDefine>();
    
    	// 存储bean实例和bean的id的对应关系 以便可以根据注解名称找到对应的实例
    	Map<String, Object> beanInstanceList = new HashMap<String, Object>();
    
    	//xml配置文件中 被扫描类的定义的集合  在Spring框架中 直接扫描一个或多个包
    	List<ScanDefine> scanList = new ArrayList<ScanDefine>();
    	
    	// 存储被扫描的待注入的实体集合
    	Map<String, Object> annotationInstanceList = new HashMap<String, Object>();
    	
    	public FzClassPathXMLApplication(String xmlName) {
    		ReadXml(xmlName);
    
    		//实例化所有定义的bean
    		BeanInstance();
    		
    		//实例化所有的待注入类
    		ScanClassInstance();
    	
    		//开始根据注解实现依赖注入
    		InjectAnnotation();
    	}
    
    	/**
    	 * 读取配置文件  收集bean集合和待注入类的集合
    	 * @param xmlFileName
    	 */
    	public void ReadXml(String xmlFileName) {
    		URL xmlPath = this.getClass().getClassLoader().getResource(xmlFileName);
    		System.out.println(xmlPath);
    		SAXReader reader = new SAXReader();
    		try {
    			Document dom = reader.read(xmlPath);
    			Element root = dom.getRootElement();
    			List<Element> iters = root.elements();
    			Stream<Element> beans = iters.stream().filter(bean -> bean.getName().equals("bean"));
    			Iterator<Element> iterBeans = beans.iterator();
    
    			while (iterBeans.hasNext()) {
    				Element bean = iterBeans.next();
    				String id = bean.attributeValue("id");
    				String clsName = bean.attributeValue("class");
    				//System.out.println("id:" + id + "
    class:" + clsName);
    				BeanDefine bd = new BeanDefine();
    				bd.setId(id);
    				bd.setClsName(clsName);
    				beanList.add(bd);
    			}
    			
    			Stream<Element> scanClasses = iters.stream().filter(scan -> scan.getName().equals("component-scan"));
    			//iters.stream().forEach(scan -> System.out.println(scan.getName()));
    			Iterator<Element> iterScans = scanClasses.iterator();
    
    			while (iterScans.hasNext()) {
    				Element scan = iterScans.next();
    				String id = scan.attributeValue("id");
    				String clsName = scan.attributeValue("class");
    				ScanDefine sd = new ScanDefine();
    				sd.setId(id);
    				sd.setClassName(clsName);
    				scanList.add(sd);
    			}
    			System.out.println("scanList.size():"+scanList.size());
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * 收集bean实例
    	 */
    	private void BeanInstance() {
    		for (BeanDefine bd : beanList) {
    			try {
    				Object beanInstance = Class.forName(bd.getClsName()).newInstance();
    				System.out.println(beanInstance.getClass().getName());
    				beanInstanceList.put(bd.getId(), beanInstance);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	/**
    	 * 收集被扫描的待注入的类的实例
    	 */
    	private void ScanClassInstance(){
    		for(ScanDefine sd:scanList){
    			try {
    				Object scanInstance = Class.forName(sd.getClassName()).newInstance();
    				System.out.println(scanInstance.getClass().getName());
    				annotationInstanceList.put(sd.getId(), scanInstance);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	/**
    	 * 循环遍历待注入的类 
    	 */
    	public void InjectAnnotation() {
    		Iterator<Map.Entry<String, Object>> iters = annotationInstanceList.entrySet().iterator();
    		while (iters.hasNext()) {
    			Map.Entry<String, Object> iter = iters.next();
    			Object scanInstance = iter.getValue();
    			InjectField(scanInstance);
    		}
    		
    	}
    
    	/**
    	 * 注入:把需要注入类中的注解为MyAutowired的字段 注入bean实例
    	 * @param injectClass
    	 */
    	private void InjectField(Object injectClass) {
    		try {
    			Field[] fields = injectClass.getClass().getDeclaredFields();
    			
    			for (Field field : fields) {
    				
    				if (field != null && field.isAnnotationPresent(MyAutowired.class)) {
    					System.out.println(field.getName());
    					MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
    					String beanName = myAutowired.name();
    					
    					Object value = null;
    					if (beanName != null && !beanName.equals("")) {
    						value = beanInstanceList.get(beanName);
    					} else {
    						Class<?> fType = field.getType();
    						for (String key : beanInstanceList.keySet()) {
    							if (fType.isAssignableFrom(beanInstanceList.get(key).getClass())) {
    								value = beanInstanceList.get(key);
    								break;
    							}
    						}
    					}
    					field.setAccessible(true);
    					field.set(injectClass, value);
    				}
    				
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	public Object getScan(String scanName){
    		return this.annotationInstanceList.get(scanName);
    	}
    }

    注解处理类中用到的两个实体类:

    package fengzheng.simpleSpring;
    
    public class BeanDefine {
    	private String id;
    	
    	private String clsName;
    
    	public String getId() {
    		return id;
    	}
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getClsName() {
    		return clsName;
    	}
    
    	public void setClsName(String clsName) {
    		this.clsName = clsName;
    	}
    }
    
    
    package fengzheng.simpleSpring;
    
    public class ScanDefine {
    	public String id;
    	
    	public String className;
    
    	public String getId() {
    		return id;
    	}
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getClassName() {
    		return className;
    	}
    
    	public void setClassName(String className) {
    		this.className = className;
    	}
    }
    

    对这段程序逻辑的解释:

    1.首先通过ReadXml()方法读取配置文件beans.xml,找到其中的bean节点和context:component-scan节点,然后把bean节点实例化为BeanDefine,并加入beanList集合,把component-scan节点实例化为ScanDefine,并加入scanList集合;

    2.通过BeanInstance()方法,把配置文件中的bean都实例化存储到Map集合beanInstanceList中;

    3.通过ScanClassInstance()方法,把配置文件中的component-scan节点(即待注入的类)实例化并处处到Map集合annotationInstanceList中;

    4.通过InjectAnnotation()方法,遍历annotationInstanceList集合,为其中的被@MyAutowired注解的字段赋值(即对应的bean的实例)

    最后调用实现如下:

    FzClassPathXMLApplication ctx = new FzClassPathXMLApplication("beans.xml");
    Test test =(Test) ctx.getScan("test");
    test.Say();
    

    输出结果:

  • 相关阅读:
    Android 应用程序集成FaceBook 登录及二次封装
    Android MVP 设计模式
    java 接口的作用和好处
    Android版本和API Level对应关系
    Android 开源库和项目 2
    高效开发iOS系列 -- 那些不为人知的KVC
    HDU 1019 Least Common Multiple 数学题解
    程序猿喜欢如何的职位描写叙述?
    从零開始搭建微信硬件开发环境全过程——1小时掌握微信硬件开发流程
    Spring ORM数据訪问——Hibernate
  • 原文地址:https://www.cnblogs.com/fengzheng/p/5037359.html
Copyright © 2020-2023  润新知