<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-34  評論-1965  文章-0  trackbacks-0

    好久沒有更新BLOG了,這幾天經常有朋友留言或EMAIL關心或鼓勵我。其實這篇文章我大約兩星期前就寫好了,本來想投稿IBM的 developerWork中國。算啦,還是不等IBM的回復啦,發到我的自己的博客吧。

    使用XML還是Annotation定義Bean

    自從Spring 2.5開始引入使用Annotation定義Bean的方式之后,業界時常會有一些關于“到底是應該使用XML還是Annotation定義Bean呢?”的討論。筆者本人就比較中庸,喜歡兩者結合使用——對于一些框架性的基礎型的Bean使用XML,對于業務性的Bean則使用Annotation。

    然而,什么是“框架性的基礎型的Bean”呢?這些Bean可以理解為由第三方開源組件提供的基礎Java類的、又或者開發者在其基礎上擴展而來的Bean,如數據源org.apache.commons.dbcp.BasicDataSource、事務管理器org.springframework.orm.hibernate3.HibernateTransactionManager等。這些Bean一般在應用程序中數量較少,卻起著框架性和全局性的作用,對于此類Bean使用XML的好處是必要時可以通過修改一個或幾個XML文件即可改變應用程序行為滿足實際的項目需求,如下清單1所示。

     1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
     2       destroy-method="close">
     3    <property name="driverClassName" value="${jdbc.driverClassName}" />
     4    <property name="url" value="${jdbc.url}" />
     5    <property name="username" value="${jdbc.username}" />
     6    <property name="password" value="${jdbc.password}" />
     7 </bean>
     8 <bean id="transactionManager" 
     9       class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    10    <property name="sessionFactory" ref="sessionFactory" />
    11 </bean>
    清單 1. 使用XML定義框架性的Bean

    此外,我們再來解釋一下什么是“業務性的Bean”。這些Bean相對比較容易理解,也就是開發者根據業務需求編寫的XxxDao、XxxManager或XxxService等。它們的特點是為數眾多,定義起來比較麻煩。Annotation方式的簡潔性可以最大程度地減少這方便的繁鎖,而且可以避免諸如打錯類型名稱等常見的小錯誤。對比清單2、3和4的代碼大家應該會有更為深刻的理解。

    1 <bean id="myService" class="net.blogjava.max.service.MyServiceImpl">
    2    <property name="myDao1" ref="myDao1" />
    3    <!-- 其它DAO引用  -->
    4    <property name="myDaoN" ref="myDaoN" />
    5 </bean>
    清單 2. 使用XML定義業務性的Bean

     1 public class MyServiceImpl implements MyService {
     2    private MyDao1 myDao1;
     3    // 其它DAO
     4    private MyDaoN myDaoN;
     5 
     6    public void setMyDao1(MyDao1 myDao1) {
     7       this.myDao1 = myDao1;
     8    }
     9 
    10    public void setMyDaoN(MyDaoN myDaoN) {
    11       this.myDaoN = myDaoN;
    12    }
    13    // 其它業務代碼
    14 }
    清單 3. 使用XML方式時Bean的代碼

     1 @Service("myService")
     2 public class MyServiceImpl implements MyService {
     3    @Resource
     4    private MyDao1 myDao1;
     5    // 其它DAO
     6    @Resource
     7    private MyDaoN myDaoN;
     8 
     9    // 其它業務代碼
    10 }
    清單 4. 使用Annotation方式的Bean代碼

    清單2、3實現的功能與清單4一樣,都是在Spring容器中定義一個MyServiceImpl類型的Bean。孰優孰劣?一目了然!

    在Spring中配置應用程序

    大家可以從清單1看到有${xxx.xxx}的寫法,有Spring開發經驗的朋友可能已經知道這是使用Spring框架時配置應用程序的方式之一。為了方便一些不甚了解的朋友,筆者在此也大概講述一下這種配置方式的步驟。

    首先,在工程中新建一個資源(Property)文件(筆者建議放在源代碼目錄下),通過“名稱=取值”的方式定義應用的配置,如下清單5所示。

    1 jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
    2 jdbc.url=jdbc\:oracle\:thin\:@localhost\:1521\:ORCL
    3 jdbc.username=max
    4 jdbc.password=secret
    清單 5. 配置代碼片段

    然后,定義一個 org.springframework.beans.factory.config.PropertyPlaceholderConfigurer類型 的Bean,id可以為propertyConfigurer。通常我們需要通過設定它的locations屬性指明應用程序配置文件的路徑。例如,以下清單6的代碼就是指明配置在構建路徑(Build Path)的根目錄下的config.properties文件里。

    1 <bean id="propertyConfigurer" 
    2       class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    3    <property name="locations">
    4    <list>
    5       <value>classpath:config.properties</value>
    6    </list>
    7    </property>
    8 </bean>
    清單 6. Spring配置代碼片段

    最后,在XML中定義Bean時,使用${xxx}引用配置資源來初始化對象,如清單1所示。然而這種配置方式僅限于XML,如果我們需要在通過Annotation定義的業務性的Bean中使用配置資源呢?

    實現通過Annotation向Bean注入配置資源

    解決上述問題的思路很簡單。首先,參考Spring注入Bean的Annotation(如@Resource等)編寫一個類似的Annotation類,如下清單7所示。

     1 package net.blogjava.max.spring;
     2 
     3 import java.lang.annotation.ElementType;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 @Retention(RetentionPolicy.RUNTIME)
     9 @Target(ElementType.FIELD)
    10 public @interface Config {
    11    String value() default "";
    12 }
    清單 7. Config.java

    上述Config類有只一個屬性,所以用默認的“value”作為名稱,而且此屬性是可選的,換而言之,開發者可以通過@Config("配置名稱")或簡單地直接使用@Config來注入配置資源。當程序發現@Config的value為空時,會使用變量域(Field)的名稱作為配置名稱獲取其值。

    然后,通過上節配置的propertyConfigurer對象獲取配置資源。不過通過閱讀Spring的API文檔或 org.springframework.beans.factory.config.PropertyPlaceholderConfigurer的源代碼,筆者發現此對象并沒有一個公共方法可以滿足以上需求,但是它有一個受保護的方法,protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException,作用是將從配置中讀入的配置資源應用到Bean的生產工廠對象中。因此,我們可以繼承此類,然后改寫該方法,將參數 props的引用放到類的全局變量里,接著通過它提供一個公共方法返回對應名稱的配置資源,如下清單8所示。

     1 package net.blogjava.max.spring;
     2 
     3 import java.util.Properties;
     4 
     5 import org.springframework.beans.BeansException;
     6 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
     7 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
     8 
     9 public class ExtendedPropertyPlaceholderConfigurer extends
    10       PropertyPlaceholderConfigurer {
    11    private Properties props;
    12 
    13    @Override
    14    protected void processProperties(
    15          ConfigurableListableBeanFactory beanFactory, Properties props)
    16          throws BeansException {
    17       super.processProperties(beanFactory, props);
    18       this.props = props;
    19    }
    20 
    21    public Object getProperty(String key) {
    22       return props.get(key);
    23    }
    24 }
    清單 8. ExtendedPropertyPlaceholderConfigurer.java

    最后,我們需要通過實現Spring的某此生命周期回調方法,在Bean實例化之后將配置資源注入到標記有@Config的變量域(Field)中。通過閱讀Spring的API文檔,筆者發現 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor 接口的方法boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException非常符合我們的需求,而且Spring的@Autowire就是通過實現此方法工作的。當然,在此大家已經可以著手編寫該接口的實現類了。不過,由于該接口還不少其它方法,而這些方法跟我們的目標是毫無瓜葛的,直接實現它就不得不被迫編寫一堆空的實現代碼,所以筆者選擇繼承 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter虛基類,改寫其postProcessAfterInstantiation方法。該虛基類是提供了一些接口(當然其中包括 InstantiationAwareBeanPostProcessor)的空實現,因此開發者只需改寫自己需要的方法即可,如下清單9所示。

     1 package net.blogjava.max.spring;
     2 
     3 import java.lang.reflect.Field;
     4 import java.lang.reflect.Modifier;
     5 
     6 import org.springframework.beans.BeansException;
     7 import org.springframework.beans.SimpleTypeConverter;
     8 import org.springframework.beans.factory.annotation.Autowired;
     9 import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
    10 import org.springframework.stereotype.Component;
    11 import org.springframework.util.ReflectionUtils;
    12 
    13  @Component //定義一個匿名Spring組件
    14 public class ConfigAnnotationBeanPostProcessor extends
    15       InstantiationAwareBeanPostProcessorAdapter {
    16    @Autowired //自動注入  ExtendedPropertyPlaceholderConfigurer對象,用于獲取配置資源
    17    private ExtendedPropertyPlaceholderConfigurer propertyConfigurer;
    18 
    19    //創建簡單類型轉換器
    20    private SimpleTypeConverter typeConverter = new SimpleTypeConverter();
    21 
    22    @Override
    23    public boolean postProcessAfterInstantiation(final Object bean, String beanName) 
    24          throws BeansException {
    25       ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
    26          public void doWith(Field field) throws IllegalArgumentException, 
    27                IllegalAccessException {
    28             Config cfg = field.getAnnotation(Config.class);
    29             if (cfg != null) {
    30                if (Modifier.isStatic(field.getModifiers())) {
    31                   throw new IllegalStateException("@Config annotation is not supported 
    32                            on static fields");
    33                }
    34 
    35             //如果開發者沒有設置@Config的 value,則使用變量域的名稱作為鍵查找配置資源
    36             String key = cfg.value().length() <= 0 ? field.getName() : cfg.value();
    37             Object value = propertyConfigurer.getProperty(key);
    38 
    39             if (value != null) {
    40                //轉換配置值成其它非String類型
    41                Object _value = typeConverter.convertIfNecessary(value, field.getType());
    42                //使變量域可用,并且轉換后的配置值注入其中
    43                ReflectionUtils.makeAccessible(field);
    44                field.set(bean, _value);
    45             }
    46          }
    47       }
    48    });
    49 
    50    //通常情況下返回true即可
    51    return true;
    52    }
    53 }
    清單 9. ConfigAnnotationBeanPostProcessor.java

    @Config使用示例

    完成了上述步驟之后,下面我們用一個完整的例子來演示一下@Config的使用。首先,創建配置文件,如下清單10所示。

    1 demo.config1=Demo Config \#1
    2 config2=314159
    清單 10. src/config.properties

    接著,編寫Demo類,它將演示通過XML和Annotation的方式獲取配置文件的資源。如下清單11所示。

     1 package net.blogjava.max.spring;
     2 
     3 import org.springframework.context.ApplicationContext;
     4 import org.springframework.context.support.ClassPathXmlApplicationContext;
     5 import org.springframework.stereotype.Service;
     6 
     7 @Service("demoAnn")//通過Annotation的方式定義Bean
     8 public class Demo {
     9    @Config("demo.config1"//演示最常見的用法
    10    private String config1;
    11 
    12    @Config //演示通過域變量名字獲取配置資源和數據類型轉換
    13    private Integer config2;
    14 
    15    //演示通過XML方式注入配置資源
    16    private String config3;
    17    private Integer config4;
    18 
    19    public void setConfig3(String config3) {
    20       this.config3 = config3;
    21    }
    22 
    23    public void setConfig4(Integer config4) {
    24       this.config4 = config4;
    25    }
    26 
    27    public void printConfigAnn() {
    28       System.out.println("{ config1 = " + config1 + ", config2 = " + config2
    29       + "}");
    30    }
    31 
    32    public void printConfigXML() {
    33       System.out.println("{ config3 = " + config3 + ", config4 = " + config4
    34       + "}");
    35    }
    36 
    37    public static void main(String[] args) {
    38       ApplicationContext appCtx = new ClassPathXmlApplicationContext(
    39             "applicationContext.xml");
    40 
    41       Demo demoAnn = (Demo) appCtx.getBean("demoAnn");
    42       demoAnn.printConfigAnn();
    43 
    44       Demo demoXML = (Demo) appCtx.getBean("demoXML");
    45       demoXML.printConfigXML();
    46    }
    47 }
    清單 11. Demo.java

    由于本示例的目的是演示@Config的使用,所以采取了最簡單編碼風格,而并非大家使用Spring時常用的基于接口的編碼風格。另外,本示例同時通過XML和Annotation的方式在Spring中定義Demo類型的Bean,前者通過類中的XML和兩個Setter注入配置資源,后者則是通過Annotation和兩個私有域變量。

    最后,編寫Spring的XML配置文件,如清單12所示。

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 
     3 <beans xmlns=http://www.springframework.org/schema/beans
     4    xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
     5    xmlns:context=http://www.springframework.org/schema/context
     6    xsi:schemaLocation="http://www.springframework.org/schema/beans 
     7       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     8       http://www.springframework.org/schema/context 
     9       http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    10 
    11    <!-- 指明需要進行Annotation掃描的包 -->
    12    <context:component-scan base-package="net.blogjava.max" />
    13 
    14    <!-- 讀入配置文件  -->
    15    <bean id="propertyConfigurer"
    16          class="net.blogjava.max.spring.ExtendedPropertyPlaceholderConfigurer">
    17       <property name="locations">
    18          <list>
    19             <value>classpath:config.properties</value>
    20          </list>
    21       </property>
    22    </bean>
    23 
    24    <!-- 通過XML配置Demo的Bean,并注入配置資源 -->
    25    <bean id="demoXML" class="net.blogjava.max.spring.Demo">
    26       <property name="config3" value="${demo.config1}" />
    27       <property name="config4" value="${config2}" />
    28    </bean>
    29 
    30 </beans>
    清單 12. src/applicationContext.xml

    完成了配置之后,大家可以運行Demo類的main方法,控制臺會有如清單13的輸出。這就證明了通過XML或Annotation可以注入相同配置資源,而且對比兩者的代碼,Annotation比XML更為簡便和快捷。

    1 { config1 = Demo Config #1, config2 = 314159}
    2 { config3 = Demo Config #1, config4 = 314159}
    清單 13. 示例控制臺輸出

    結束語

    本文再三強調定義Bean時Annotation對比XML的優越性,尤其是針對業務性的對象;而配置又是每個應用程序必不可少的一部分,通過擴展Spring框架,開發者可以輕松地使用Annotation的方式實現應用程序配置。同時,筆者也希望Spring社區能夠意識到這方面的需求,將其整合在以后發行的Spring版本之中。在此之前,大家可以通過文章后面的下載鏈接獲得本文Eclipse工程的壓縮包文件,運行示例或者將代碼應用到您的工程之中。該代碼不受任何版權保護,可以隨便修改或發布。

    下載代碼

    posted on 2009-11-20 22:27 Max 閱讀(24107) 評論(10)  編輯  收藏 所屬分類: 方法與技巧(Tips & tricks)

    評論:
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2009-11-20 23:36 | smildlzj
    終于看到好文章了...

    不知道Annotation能不能實現這樣?

    @Set
    @Get
    private String id;

    這樣的功能,這樣就不用寫Setter,Getter.  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2009-11-21 15:59 | 咖啡妝
    感覺還是配置文件好 自少東西不零散  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2009-11-22 15:07 | 99讀書人俱樂部
    四大皆空福建的  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2009-11-24 08:55 | aslan
    @smildlzj
    我也一直找這樣的框架 不知道有沒有  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2009-11-25 22:21 | smildlzj
    @aslan
    想了一下。這樣的做法也不怎么好。。

    setter,getter生成一下就好,如果用注入,那怎么調用,注入這玩意需要運行階段生成的。
    所以,需要調用的時候,連編譯都不通過。

    ==
    后來搞成接口
    public void setId(String id);
    public String getId();

    想用cglib生成,但是不會弄,讓這兩個方法關聯起來。
    而且cglib生成的父類必須是接口,不能是抽象類。  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2009-12-23 17:21 | koumei
    Merry Christmas buddy. What you been up to?  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2010-08-24 16:40 | sad
    The value of hands-on experience as compared to book learning is smaller in software development than in many other fields.   回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2011-06-14 09:10 | 墨龍
    @smildlzj
    使用Autowired 就可以自動注入 並且有get set方法  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2014-09-09 13:53 | xfan
    @smildlzj
    lombok  回復  更多評論
      
    # re: 擴展Spring——使用 Annotation將配置資源注入到Bean中 2015-08-19 14:50 | 紅色芒果
    這段代碼確實很酷, 但是不能不能講properties中的參數注入到@controller注解的類中.  回復  更多評論
      
    主站蜘蛛池模板: 精品亚洲成A人无码成A在线观看 | 国产92成人精品视频免费| 国产成人亚洲影院在线观看| 99亚洲男女激情在线观看| 日韩免费无码一区二区视频| 亚洲中文字幕一区精品自拍| 高清国语自产拍免费视频国产| 亚洲中文字幕无码久久2020| 午夜爱爱免费视频| 亚洲AV无码专区在线厂| 国产又大又长又粗又硬的免费视频| 亚洲AV无码AV吞精久久| 亚洲国产成人久久综合野外| 中国内地毛片免费高清| 久久综合图区亚洲综合图区| 中文字幕在线观看免费视频| 日本亚洲色大成网站www久久| 在线播放免费人成视频在线观看| 亚洲av色香蕉一区二区三区| 亚洲阿v天堂在线2017免费| 中文字幕免费在线观看动作大片| 亚洲人成在线影院| 久久WWW免费人成人片| 美女扒开屁股让男人桶爽免费| 亚洲夜夜欢A∨一区二区三区| 99re6在线精品视频免费播放| 亚洲www77777| 亚洲成a人片在线观看国产| 免费一区二区三区| 在线观看亚洲AV每日更新无码| 国产一区视频在线免费观看| 在线观看免费黄色网址| 亚洲一级在线观看| 国产福利电影一区二区三区,亚洲国模精品一区| 久久免费视频一区| 亚洲午夜久久久精品电影院| 亚洲成AV人在线观看网址| 蜜桃AV无码免费看永久| 国产天堂亚洲国产碰碰| 亚洲一区二区三区夜色| 国产三级免费观看|