類比實現Spring中的註解裝配,spring註解

來源:互聯網
上載者:User

類比實現Spring中的註解裝配,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類中應該使用以下這種方式來自動注入:

@Autowiredprivate UserDao userDao1;@Autowiredprivate 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})@Documentedpublic @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);//執行個體化所有定義的beanBeanInstance();//執行個體化所有的待注入類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 + "\nclass:" + 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();

輸出結果:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.