来源:http://www.blogjava.net/max/archive/2009/11/20/303112.html
使用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所示。
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的代码大家应该会有更为深刻的理解。
2 <property name="myDao1" ref="myDao1" />
3 <!-- 其它DAO引用 -->
4 <property name="myDaoN" ref="myDaoN" />
5 </bean>
清单 2. 使用XML定义业务性的Bean
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的代码
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所示。
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文件里。
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所示。
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所示。
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所示。
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所示。
2 config2=314159
清单 10. src/config.properties
接着,编写Demo类,它将演示通过XML和Annotation的方式获取配置文件的资源。如下清单11所示。
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所示。
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更为简便和快捷。
2 { config3 = Demo Config #1, config4 = 314159}
清单 13. 示例控制台输出
结束语
本文再三强调定义Bean时Annotation对比XML的优越性,尤其是针对业务性的对象;而配置又是每个应用程序必不可少的一部分,通过扩展 Spring框架,开发者可以轻松地使用Annotation的方式实现应用程序配置。同时,笔者也希望Spring社区能够意识到这方面的需求,将其整 合在以后发行的Spring版本之中。在此之前,大家可以通过文章后面的下载链接获得本文Eclipse工程的压缩包文件,运行示例或者将代码应用到您的 工程之中。该代码不受任何版权保护,可以随便修改或发布。
相关推荐
在本文中,我们将深入探讨SSH笔记中的一个关键概念——基于注解的配置注入关系,特别是在Spring框架4.x版本中的应用。SSH(Struts、Spring、Hibernate)是Java Web开发中的经典组合,而Spring框架以其强大的依赖注入...
当我们在一个切面类中使用`@DeclareParents`时,Spring会为匹配到的bean创建代理,并将指定的接口实现注入到这些bean中。 首先,我们需要了解`@Aspect`的使用。在Spring Boot项目中,创建一个带有`@Aspect`注解的类...
本教程将通过三个部分——SpringHelloWorld、SpringXmlIOC和SpringAnnotationIOC,深入探讨Spring的IOC机制。 首先,我们从SpringHelloWorld开始。这是一个简单的Spring入门示例,展示了如何使用Spring来创建和管理...
在本文中,我们将深入探讨Spring框架的核心特性——控制反转(Inversion of Control,简称IoC)和依赖注入(Dependency Injection,简称DI),以及如何通过注解配置和Maven项目构建来实现这一概念。Spring框架是Java...
这种方式使得配置文件中的属性能被方便地注入到其他Bean中,增强了配置的灵活性。 4. **Spring_2900_Registration_7可能的含义**:这个标识可能指的是一个特定版本或迭代的Spring注册相关教程或案例。"2900"可能是...
要使用BeanPostProcessor,开发者需要编写一个实现了该接口的类,并且将该类的实例作为一个Bean注册到Spring容器中。当Spring容器启动时,它会检测到实现了BeanPostProcessor接口的Bean,并将其特殊处理。这些...
12.2 在Spring中使用Hibernate 12.2.1 配置SessionFactory 12.2.2 使用HibernateTemplate 12.2.3 处理LOB类型数据 12.2.4 添加Hibernate事件监听器 12.2.5 使用原生Hibernate API 12.2.6 使用注解配置 12.2.7 事务...
12.2 在Spring中使用Hibernate 12.2.1 配置SessionFactory 12.2.2 使用HibernateTemplate 12.2.3 处理LOB类型数据 12.2.4 添加Hibernate事件监听器 12.2.5 使用原生Hibernate API 12.2.6 使用注解配置 12.2.7 事务...
Spring 支持多种配置方式,包括 XML、注解(Annotation)和基于 Java 的配置。 - **实例化一个容器**:为了使用 Spring 容器,首先需要实例化一个容器。可以通过多种方式完成此操作,例如使用 `new ...
【CXF集成Spring Hibernate】是将三个核心的Java技术——CXF、Spring和Hibernate整合到一个应用程序中的实践。这样的集成可以构建出强大的企业级服务,其中CXF用于提供Web服务,Spring作为应用的管理和依赖注入容器...
5. **排序与比较**:`org.springframework.core.order`和`org.springframework.core.type`包提供了排序算法和类型比较机制,对于Spring容器中bean的排序和类型检查起到了关键作用。 6. **事件驱动**:`org.spring...
MyBatis 使用简单的 XML 或注解进行配置和原始映射,将接口和 Java POJOs 映射到数据库中的记录。 - **SQL Maps**: 提供 SQL 查询语句和 Java 类型之间的映射关系。 - **Data Access Objects (DAO)**: 提供对数据的...
3. **@Autowired**:自动装配依赖,Spring会根据类型或名称自动将Bean注入到需要的地方,如: ```java @Autowired private MyService myService; ``` 4. **@Qualifier**:当有多个相同类型的Bean需要选择注入...
- **将DI容器宿主到Web容器中**:讲解了如何将Spring容器集成到Web容器中,使得Spring管理的Bean可以在Web环境中使用。 - **外在化配置应用参数**:介绍了如何将应用程序的配置信息从代码中分离出来,放置在外部...
- **XML配置**:可以在Spring的配置文件中使用`<aop:config>`等标签来配置AOP。 #### 第九课:DataSource - **配置**:在Spring中可以通过`<bean>`标签配置数据源。 - **使用**:通常会使用C3P0或DBCP等连接池来...
- **机制**:通过简单的XML配置文件或注解将接口和Java POJOs映射到数据库记录。 #### 三、开发环境搭建 - **环境要求**:Spring 4.0.2 RELEASE、Spring MVC 4.0.2 RELEASE。 - **工具选择**:采用Maven进行项目...
这篇博客是关于Java 5.0新特性——Annotation的系统学习与整理笔记,通过讲解与实例,深入探讨了注解在Junit、Spring和Hibernate中的应用。 首先,注解是一种声明式的编程元素,它可以附加到类、接口、方法、变量等...
### SSM框架——详细整合教程(Spring+SpringMVC+MyBatis) #### 1、基本概念 **1.1、Spring** Spring 是一个开源框架,最初由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的...
Spring 框架的核心特性包括依赖注入(DI)和面向切面编程(AOP),这些特性极大地简化了 Bean 的装配和事务管理等工作。Spring 还为不同的数据访问技术提供了一个统一的接口,这意味着开发者可以在不改变业务逻辑的...