`
ahuaxuan
  • 浏览: 640686 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

别装了,难道你们不想把properties直接注入到object中去(spring-plugin)?

阅读更多
[size=small]/**
*作者:张荣华(ahuaxuan)
*日期:2008-4-9
**/



1背景
Spring2.5支持使用annotation来配置我们的service,比如如下代码:
@Service("userService")
public class UserServiceImpl extends BaseServiceSupport implements UserService {

	public void xxx() {

	}
}


这样就表示这个service需要被spring管理,不过只是这样做是不够的,我们还需要在applicationcontext***.xml中加入这么一段:
<context:component-scan base-package="xxxxxxx"/>

这么一来这个xxxxxxx包下所有的使用@Service这个注释的对象都会自动的被spring管理。

虽然这样看上去很美好,但是却是不满足我们的需求的,因为我们的service中,或者其他被管理的bean中有时候需要一些配置,比如说String,Integer等等,而且这些配置的值一般都来自Properties文件,一般情况下我们会使用如下这段代码:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:jdbc.properties</value>
			</list>
		</property>
	</bean>	

这样我们就可以通过${}来引用到properties文件中的值。

不过使用了@service之后,我们就无法通过${}来得到properties中的值了。downpour是spring2.5使用的先行者,他很早就意识到这个问题,通过我们的讨论,确定了解决问题的方向。下面我把这个方案拿出来和大家共享。

2目标:
我们的目标是实现一个Annotation,代码如下:
@Service
public class ImageFileUpload implements Serializable {

    @Properties(name="pic.address" )
    private String picAddress;

    @Properties(name="pic.url" )
    private String picUrl;

    private String picServerUrl;
}


pic.address和pic.url是properties文件中的两个属性

以上代码中的@Properties就是我们要实现的Annotation,通过name的值作为key去对应的properties中寻找对应的value,并且主动赋值给ImageFileUpload的对应属性。

3步骤:
我们知道,spring在初始化完bean之后我们可以对这些bean进行一定的操作,这里就是一个扩展点,我决定使用BeanPostProcessor这个接口,这个接口中有一个postProcessAfterInitialization方法就是用来做bean的后处理的,一旦一个bean被初始化完成之后,我们就可以对这个bean进行赋值了。

但是考虑到我们项目中不是所有的bean都使用Annotation来注册到spring中的,这些普通的,配置在xml文件中的bean也有用到${}的需求,所以我考虑扩展PropertyPlaceholderConfigurer这个类。我们来分析一下具体的代码。

首先建立一个Annotation,如下:
/**
 * @author ahuaxuan(aaron zhang)
 * @since 2008-4-7
 * @version $Id: Properties.java 261 2008-04-07 07:03:41Z aaron $
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) 
public @interface Properties {

//	String bundle();
	
	String name();
}


接着我们实现我们的扩展主类:
/**
 * @author ahuaxuan(aaron zhang)
 * @since 2008-4-7
 * @version $Id: AnnotationBeanPostProcessor.java 260 2008-04-07 07:03:35Z aaron $
 */
public class AnnotationBeanPostProcessor extends PropertyPlaceholderConfigurer implements BeanPostProcessor, InitializingBean {

	private static transient Log logger = LogFactory.getLog(AnnotationBeanPostProcessor.class);
	
	private java.util.Properties pros;
	
	@SuppressWarnings("unchecked")
	private Class[] enableClassList = {String.class};
	
	@SuppressWarnings("unchecked")
	public void setEnableClassList(Class[] enableClassList) {
		this.enableClassList = enableClassList;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		
		Field [] fields = bean.getClass().getDeclaredFields();
		
		for (Field field : fields) {
			if (logger.isDebugEnabled()) {
				StringBuilder sb = new StringBuilder();
				sb.append(" ========= ")
					.append(field.getType())
					.append(" ============ ")
					.append(field.getName())
					.append(" ============ ")
					.append(field.isAnnotationPresent(Properties.class));
				
				logger.debug(sb.toString());
			}
			
			if (field.isAnnotationPresent(Properties.class)) {
				if (filterType(field.getType().toString())) {
					Properties p = field.getAnnotation(Properties.class);
					try {
//						StringBuilder sb = new StringBuilder();
//						sb.append("set").append(StringUtils.upperCase(field.getName().substring(0, 1)))
//										.append(field.getName().substring(1, field.getName().length()));
//						
//						Method method = bean.getClass().getMethod(sb.toString(), String.class);
//						method.invoke(bean, pros.getProperty(p.name()));
本来我是通过set方法来把properties文件中的值注入到对应的属性上去的,后来downpour提供了更好的方案,就是下面这两行代码,虽然这样做破坏了private的功能,同时破坏了封装,但是确实节省了很多代码,建议大家在业务代码中不要这样做,如果做框架代码可以考虑一下。

						ReflectionUtils.makeAccessible(field);
						field.set(bean, pros.getProperty(p.name()));
					} catch (Exception e) {
						logger.error(" --- ", e);
					} 
				}
			}
		}
		
		
		return bean;
	}
	
	@SuppressWarnings("unchecked")
	private boolean filterType(String type) {
		
		if (type != null) {
			for (Class c : enableClassList) {
				if (c.toString().equals(type)) {
					return true;
				}
			}
			
			return false;
		} else {
			return true;
		}
	}

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public void afterPropertiesSet() throws Exception {
		pros = mergeProperties();
	}
}


最后我们需要在xml文件中配置一下:
<bean id="propertyConfigurer"
		class="xx.service.AnnotationBeanPostProcessor">
		<property name="locations">
			<list>
				<value>classpath:jdbc.properties</value>
			</list>
		</property>
	</bean>


这样任何一个bean,不管它是使用annotation注册的,还是直接配置在xml文件中的都可以使用这种方式来注入properties中的值。

下面看一下我在项目中的一个真实的例子,这个类是一个value object,它代表一组配置:
@Component
public class Config implements Serializable{

	/**  */
	private static final long serialVersionUID = 8737228049639915113L;

	@Properties(name = " online.pay.accounts")
	private String accounts;
	
	@Properties(name = " online.pay.user")
	private String user;
	
	@Properties(name = " online.pay.password")
	private String password;
	
	@Properties(name = " online.transurl")
	private String transUrl;
	
	@Properties(name = " online.refundurl")
	private String refundUrl;
	
	@Properties(name = " online.query")
	private String queryUrl;

	```setter and getter method
}

那么在需要用到该vo的地方比如:
@Service(“userService”)
public class UserServiceImpl implements UserService {
	@autowired
	private Config config;

	public void setConfig(Config config) {
		This.config = config;
	}
}

就这么多内容就ok了,如果按照原来的办法,我们就需要在xml配置以上两个bean,然后在里面写一堆又一堆的${},肯定能让你看了之后崩溃,至少我差点崩溃,因为它看上去实在是太丑陋了。而现在,我的心情好多了,因为我用这个@Properties(name = "")用的很爽,呵呵,而且即使有些bean是配置在xml文件中的,比如datasource等等,我们还是可以通过${}来进行设值,也就是说这个方案既支持annotation,也支持${},很好,很强大。

结语:
很显然,在spring2.5的时代,以上这个需求是非常平常的,我相信在spring3.0中一定会提供这样的功能,而且我觉得spring2.5应该是一个过渡版本,虽然上面的方案中代码行数并不多,但是我觉得很有价值,应该很有市场才对,也许我们可以把这个东东叫做spring-properties2object-plugin。

题外话:
说点题外话吧,目前在我的项目里,我使用了struts2.0+spring2.5+hibernate3.2,使用struts2.0的时候我使用struts2.0的zero configuration和codebehind,基本上实现了真正意义零配置,剩下的都是一些common的配置,而且很少,不超过150行。在使用spring的时候,我也基本上是使用annotation来注册我的bean,同时使用上面的方案来作为补充,所以applicationContext-xxx.xml中的也是一些common的配置,也是非常少,应该只有200行左右。而hibernate我是使用annotation来配置我的PO,基本没有配置文件。所以整个项目的xml文件中配置的总行数大大下降。


[/size]
分享到:
评论
15 楼 ahuaxuan 2008-04-12  
yizhuo 写道
Guice作同样的事情简单很多

读取properties
Injector injector = Guice.createInjector(new AbstractModule() {
	@Override
	protected void configure() {
		Properties properties = new Properties();
		properties.setProperty("name", "Guice");
		properties.setProperty("description", "Value");
		
		Names.bindProperties(binder(), properties);
	}
});


我怎么就没有看出来这方面guice简单很多呢,上面的设置代码在spring只是一段配置,我在原文中已经给出,这个功能上guice并没有什么突出的特点。

而下面这段代码和我原文中的注入代码没有什么本质的区别,要说区别,那么就是,我只要一个annotation(@Properties),而guice需要两个,哪里有什么guice在这方面简单多了之说????
class DummyPropertyClass {
	@Inject
	@Named("name")
	String name;

	@Inject
	@Named("description")
	String description;
}



14 楼 yizhuo 2008-04-12  
Guice作同样的事情简单很多

读取properties
Injector injector = Guice.createInjector(new AbstractModule() {
	@Override
	protected void configure() {
		Properties properties = new Properties();
		properties.setProperty("name", "Guice");
		properties.setProperty("description", "Value");
		
		Names.bindProperties(binder(), properties);
	}
});


设置对象
class DummyPropertyClass {
	@Inject
	@Named("name")
	String name;

	@Inject
	@Named("description")
	String description;
}



调用对象
DummyPropertyClass instance = injector.getInstance(DummyPropertyClass.class);
assert instance.name.equals("Guice");
assert instance.description.equals("Value");


个人认为,Guice在DI方面比Spring Javaconfig简单很多
13 楼 温柔一刀 2008-04-11  
downpour 写道
Quake Wang果然是经验丰富啊。这里依稀看到了Webwork中类型转化的影子。用在这里真是恰到好处。

呵呵,Quake Wang是习惯了rails,同时更习惯了rails的默认优于配置的原则
12 楼 downpour 2008-04-10  
Quake Wang果然是经验丰富啊。这里依稀看到了Webwork中类型转化的影子。用在这里真是恰到好处。
11 楼 fujohnwang 2008-04-10  
Quake Wang 写道
这样做会不会更好一些?
在Config这个class所在的package下面,有一个同名的Config.properties文件,里面有设置同名field的值:
accounts=xxx
user=xxx
password=xxx

然后实现一个BeanPostProcessor来根据这个约定来注入值。
这样就省了在Config.java文件写一堆的@Properties annotation,如果你觉得在某些情况下properties文件无法保证和field同名这个约定,你也可以再以@Properties annotation为优先设定。


Convention Over Configuration的理念用的cool啊,呵呵
其实总的理念就是用BeanPostProcessor对bean定义做后处理,至于通过何种方式获取后处理用的数据,那就各家自显神通了,呵呵
10 楼 ahuaxuan 2008-04-10  
Quake Wang 写道
这样做会不会更好一些?
在Config这个class所在的package下面,有一个同名的Config.properties文件,里面有设置同名field的值:
accounts=xxx
user=xxx
password=xxx

然后实现一个BeanPostProcessor来根据这个约定来注入值。
这样就省了在Config.java文件写一堆的@Properties annotation,如果你觉得在某些情况下properties文件无法保证和field同名这个约定,你也可以再以@Properties annotation为优先设定。



有道理,尤其是用在vo上很方便,那么也就是说可以提供两个功能,一个是基于规则的(这个规则就是properties文件要和vo名称对应),一个是基于annotation的,如果没有annotation就代表这个是基于规则的。

9 楼 QuakeWang 2008-04-10  
这样做会不会更好一些?
在Config这个class所在的package下面,有一个同名的Config.properties文件,里面有设置同名field的值:
accounts=xxx
user=xxx
password=xxx

然后实现一个BeanPostProcessor来根据这个约定来注入值。
这样就省了在Config.java文件写一堆的@Properties annotation,如果你觉得在某些情况下properties文件无法保证和field同名这个约定,你也可以再以@Properties annotation为优先设定。
8 楼 downpour 2008-04-10  
Frederick 写道
一直使用xml配置,前段时间对spring2.5的annotation做了点研究,在一个私人项目(ext2.0 + spring mvc + db4o)里面尝试使用annotation来完成整个项目,结果发现很多不如意的地方,感觉spring2.5的annotation还很不完善。
就目前spring 2.5的annotation,不敢在正式的项目中使用。所以还在沿用xml的配置。
希望spring 3.0可以完善它的annotation,可以使得一个项目可以完整的使用annotation配置完成,至少要可以使用annotation完成绝大部分的配置才行。不然,一半annotation一半xml,反倒不如完全使用详细划分的xml配置来得清爽


一个项目完整使用Annotation是不现实的,也不可能。

XML需要解决全局的,纲领性的配置,例如:dataSource,Transaction等等。而Annotation需要解决的是局部的,业务相关性非常强的配置,例如:Bean定义等等。

事实上,全局的,纲领性的配置可能在项目中永远不会改变,所以基本上XML文件变动的机会不大,可以考虑使用ant或者其他的生成工具生成。
7 楼 downpour 2008-04-10  
一个非常优雅的实现方案。

这里对注入的限制就是要注意,你所配置的bean,都是单例的,而且这种需求基本对应于某种配置是全局的,却又要在各处使用的那种情况。

额外提一点,这里的实现是可以扩展的,当前ahuaxuan的实现对Properties文件中的类型做了限制,只支持String类型,对于想进一步扩展的朋友,可以在这个基础上,利用反射进行其他类型的扩展,比如Integer,BigDecimal等等。
6 楼 zl584521 2008-04-10  
看了看感觉有点晕,哈哈
入行不久就这样吧~
5 楼 Frederick 2008-04-10  
一直使用xml配置,前段时间对spring2.5的annotation做了点研究,在一个私人项目(ext2.0 + spring mvc + db4o)里面尝试使用annotation来完成整个项目,结果发现很多不如意的地方,感觉spring2.5的annotation还很不完善。
就目前spring 2.5的annotation,不敢在正式的项目中使用。所以还在沿用xml的配置。
希望spring 3.0可以完善它的annotation,可以使得一个项目可以完整的使用annotation配置完成,至少要可以使用annotation完成绝大部分的配置才行。不然,一半annotation一半xml,反倒不如完全使用详细划分的xml配置来得清爽
4 楼 ahuaxuan 2008-04-10  
bulargy 写道
本来我和楼主一样,所参与的项目也是struts2.0+spring2.5+hibernate3.2的,也是追求尽量少的xml的。可以进展到一定程度,发现很多问题,。现在除action中注入service保留了annotation,其他地方又改回为xml的配置了。并且对xml进行了教细粒度的划分。一样看着很舒服。。。我对annotation的一些困惑在我的blog里也有提到,如果你有好的建议,我很乐意学习一下。http://bulargy.iteye.com/blog/179781


xml和annotation各有各的用处,有些地方不该用annotation就不要用,比如说事务,不要为了annotation而annotation,其实这个的观点在这个帖子里都有明确的阐述。
http://www.iteye.com/topic/178725
3 楼 linliangyi2007 2008-04-10  
自从有了Ioc和依赖注入以后,大家开始变得慵懒啦,暴力了!!
想没有Spring的时代,也许为解决这些事情,需要更多的设计,比较累,也比较有趣啊!
万能且万恶的Spring啊~~~:)
2 楼 bulargy 2008-04-10  
本来我和楼主一样,所参与的项目也是struts2.0+spring2.5+hibernate3.2的,也是追求尽量少的xml的。可以进展到一定程度,发现很多问题,。现在除action中注入service保留了annotation,其他地方又改回为xml的配置了。并且对xml进行了教细粒度的划分。一样看着很舒服。。。我对annotation的一些困惑在我的blog里也有提到,如果你有好的建议,我很乐意学习一下。http://bulargy.iteye.com/blog/179781
1 楼 ahuaxuan 2008-04-09  
无意中发现,我解决了这个帖子所提出来的问题:http://www.iteye.com/topic/154121

相关推荐

    springboot 基础简易实例, maven项目

    &lt;artifactId&gt;spring-boot-maven-plugin &lt;/plugin&gt; --------------------------- src/main/resources/application.yml --------------------------- spring: # 指定静态资源的路径 resources: static-...

    springboot+maven打包demo【将依赖与配置文件打包到jar包外部】

    在本文中,我们将深入探讨如何使用Spring Boot和Maven来构建一个项目,使得依赖和配置文件被打包到jar包外部,以实现更加灵活的项目管理。这个方法对于那些需要根据不同环境进行定制配置或者频繁更新配置的应用来说...

    spring boot入门教程

    3. **内嵌式服务器**:Spring Boot 内嵌了 Tomcat、Jetty 或 Undertow 服务器,这意味着你可以直接在应用中运行 Spring Boot 应用程序,而无需单独安装 Web 服务器。 4. **自动配置**:Spring Boot 可以根据添加到...

    spring_annotation maven 的配置

    在IT行业中,Spring框架是Java开发中的一个核心组件,它极大地简化了企业级应用的构建。Spring Annotation和Maven的结合使用是现代Java项目中常见的配置方式,它们为开发者提供了高效、灵活的开发环境。本篇文章将...

    springmybatis

    查询出列表,也就是返回list, 在我们这个例子中也就是 List&lt;User&gt; , 这种方式返回数据,需要在User.xml 里面配置返回的类型 resultMap, 注意不是 resultType, 而这个resultMap 所对应的应该是我们自己配置的 ...

    struts2.0+spring2.5+JPA整合框架

    5. 将Struts2和Spring整合:使用Spring的Struts2插件,将Spring的bean注入到Struts2的Action中。 在实际开发中,为了使项目运行,你需要将ZYC_SSH_JPA_DEMO导入Eclipse,并添加所有必要的jar包到项目的类路径中,...

    SSH框架lib Struts2,Spring4,Hibernate4

    SSH框架,全称为Struts2、Spring4和Hibernate4,是Java Web开发中常见的三大开源框架集成,它们各自负责Web应用程序的不同层面,协同工作以构建高效、可维护的系统。 Struts2作为MVC(Model-View-Controller)框架...

    struts2+spring+hibernate框架jar包

    5. 整合Struts2和Spring:使用Spring插件(struts2-spring-plugin)实现Action的依赖注入,将业务逻辑层(Service)和数据访问层(DAO)的bean注入到Action中。 6. 整合Spring和Hibernate:通过Spring的...

    pom_POM_will2ef_源码

    Spring Boot项目的构建通常依赖于Maven或Gradle,其中Maven使用POM(Project Object Model)文件来管理项目依赖和构建过程。本篇文章将深入探讨在Spring Boot项目中,`pom.xml`文件的基本配置及其重要性。 `pom.xml...

    struts2-jar

    - `struts2-spring-plugin.jar`:用于整合Spring框架,便于管理Action及依赖注入。 3. **视图技术**: - `freemarker.jar`:FreeMarker是一个模板引擎,用于生成动态内容,常用于Struts2的视图层。 - `ognl.jar`...

    struts2.3 jar包

    9. **依赖注入**:Struts2支持Spring等依赖注入框架,可以方便地管理Action的依赖关系。 10. **RESTful支持**:虽然Struts2最初是为传统HTTP请求设计的,但随着RESTful风格的流行,Struts2也提供了相应的支持,可以...

    struts2 lib包

    9. **其他支持库**: 包括但不限于ognl(Object-Graph Navigation Language)用于表达式语言,xwork(提供动作和结果处理)等,都是Struts2框架不可或缺的部分。 通过理解并熟练掌握这些库的功能和用法,开发者能够...

    自己写Spring2.5 Hibernate3 Struts2的整合例子

    4. 配置Struts2与Spring的整合:在struts.properties或struts-plugin.xml中指定Spring的ApplicationContext路径。 5. 编写Action类:Action类通常由Spring管理,通过@Autowired注入Service层。 6. 实现Service和DAO...

    struts-2.3.4.1-lib

    5. **安全相关**:Struts2会包含一些针对常见Web安全问题的防护措施,例如struts2-spring-plugin.jar如果存在,表明它与Spring框架集成,提供了依赖注入,也有助于减轻XSS、CSRF等攻击。 6. **测试与调试**:Struts...

    开发struts2+spring

    通过Action的构造函数或setter方法,可以将请求参数直接注入到Action对象中,简化了参数获取的过程。 1. **配置注入规则**:在`struts.xml`中配置参数注入的规则。 2. **在Action中使用参数**:在Action类中直接...

    struts 2.1.6 类库

    9. **整合Spring**:通过`struts2-spring-plugin.jar`,Struts 2可以与Spring无缝集成,实现Action的依赖注入,方便管理Bean。 10. **安全考虑**:Struts 2.1.6版本虽然功能强大,但需要注意安全问题,如著名的...

    itcast-springboot.zip

    在Maven的生命周期中,`install`阶段会把项目打包成可执行的JAR或WAR文件。SpringBoot项目默认生成的是可执行JAR,包含嵌入式的Tomcat服务器,可以直接运行。我们可以在`pom.xml`中配置`maven-jar-plugin`,生成启动...

    java springboot redisson mybatis maven.zip

    标题中的"java springboot redisson mybatis maven.zip"表明这是一个使用Java编程语言,基于Spring Boot框架,并结合Redisson、MyBatis以及Maven构建的项目。这个项目可能是一个Web应用,利用了Spring Boot的便利性...

    struts2 jar 包

    - `struts2-spring-plugin.jar`:与Spring框架的整合,实现依赖注入和Action的管理。 总的来说,Struts2是一个功能丰富的MVC框架,通过灵活的配置和强大的拦截器机制,能够帮助开发者构建高效、可维护的Java web...

Global site tag (gtag.js) - Google Analytics