在项目中一个属性文件配置信息,提供给数据连接信息和主机信息,数据连接信息提供给spring框架使用,主机信息提供应用程序使用。不想写两个配置文件,特意对spring的读取属性的文件进行扩展。
下面为样例代码:
package com.easyway.ext.properties; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; /** * 采用Spring的加载属性信息 * 在应用中获取 * @author longgangbai * */ public class FilePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { private Map<String, String> resolvedProps;//将属性保存起来 @Override protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException { super.processProperties(beanFactoryToProcess, props); resolvedProps = new HashMap<String, String>(); for (Object key : props.keySet()) { String keyStr = key.toString(); resolvedProps.put(keyStr, parseStringValue(props.getProperty(keyStr), props, new HashSet())); } } public Map<String, String> getResolvedProps() { return resolvedProps; } public void setResolvedProps(Map<String, String> resolvedProps) { this.resolvedProps = resolvedProps; } }
spring的配置 如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="propertyBean" class="com.easyway.ext.properties.FilePropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:appContext.properties</value> </list> </property> </bean> </beans>
测试bean如下:
package com.easyway.ext.properties; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * * @author longgangbai * */ public class PropertyBeanMain { public static void main(String[] args) { ClassPathXmlApplicationContext appctx=new ClassPathXmlApplicationContext("classpath:appContext.xml"); FilePropertyPlaceholderConfigurer propertyBean=(FilePropertyPlaceholderConfigurer)appctx.getBean("propertyBean"); Map<String,String> maps=propertyBean.getResolvedProps(); Set<Entry<String,String>> entryset=maps.entrySet(); for (Entry<String, String> entry : entryset) { System.out.println(entry.getKey()+" ="+entry.getValue()); } } }
spring中classpath和classpath*的配置区别
- 同名资源存在时,classpath: 只从第一个符合条件的classpath中加载资源,而classpath*: 会从所有的classpath中加载符合条件的资源
- classpath*:需要遍历所有的classpath,效率肯定比不上classpath,因此在项目设计的初期就尽量规划好资源文件所在的路径,避免使用classpath*来加载
开发线程安全的Spring Web应用
前言
如果开发者正开发或维护基于Servlet的Web应用,则Servlet规范建议最好能够看看。因为它含有的内容对于Web应用开发者理解Servlet容器的工作机理很有帮助。
其中,规范给出了Servlet容器是如何处理客户请求的。Servlet容器将会根据web.xml配置文件中定义的各个Servet而创建相应的单例。因此,多个客户请求可能同时访问这些单例,即多个线程同时访问它们。在Web应用中保证线程安全是很重要的。开发者应该对这个问题保持警惕,而且必须确保各自的代码必须以线程安全的方式运行。
温习线程安全
大部分Java开发者都应该听过synchronized关键字。在不采用任何第三方库的前提下,Java本身对线程提供了原生支持,而且synchronized关键字往往是Java应用中实现线程安全最重要的因素。Java中的同步提供了互斥支持。通过同步一块代码或整个方法能够保证同时最多只有单个线程执行它,从而实现了线程安全。引入同步具有副作用,即阻塞。比如,大公司或律师办公室的前台小姐同时需要处理电话、邮件、受访客户等等。这使得她的工作很繁忙,而且导致一些事情不能够及时处理。
在Web应用中需要警惕阻塞。受同步保护的代码块使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态,除非某客户处理完成。而且互斥不仅会带来阻塞,还会带来死锁。通常,死锁是不可恢复的。如下条件将触发死锁的发生:线程A锁住了线程B等待的资源,而且线程B锁住了线程A等待的资源,即线程B一直在等待线程A释放锁,线程A也是如此。因此,对于多线程的应用而言,死锁的预防和处理通常都是很头疼的。
另外,synchronized关键字还使得大量的同步对象到处使用,从而引入了死锁的可能性。比如,java.util.Hashtable和java.util.Vector中提供的方法都是受互斥保护的,因此除非确实需要使用它们,否则尽量不用。开发者只需要使用java.util.HashMap和java.util.ArrayList即可。当然,java.util.Collections中的同步方法也使用了synchronized关键字。
尽管可重入更易于管理,但它引入了其他问题。可重入代码避免了线程间数据的共享。考虑如下代码(姑且认为Java中的方法是线程安全的):
public Double pi() { int a = 22; int b = 7; return new Double(a / b); } |
不管同时进入该方法的线程有多少,它总是线程安全的。各个线程都维护了属于各个线程的栈,并不同其他线程共享。其中,各个线程在当前方法(包括静态方法)中创建的方法变量仅属于当前线程,即存储在当前线程的栈中。因此,当线程A和B同时进入上述方法时,它们都将创建a和b。由于上述方法不存在数据共享,因此上述方法是线程安全的。请注意:22/7值同PI值较接近,但它们不相等。
接下来,看看如何优化上述代码吧。
private Double pi = null; public Double pi() { if (pi == null) { pi = new Double(22 / 7); } return pi; } |
尽管改进后的方法能够提高性能,但并不是线程安全的。比如:如果pi为null,而且线程A和B同时进入第4行。因此,线程A和B会同时测试pi是否为空,它们都将返回true。接下来,如果线程A继续执行(线程B由于某种原因被暂挂),然后返回对内存地址的引用。其中,该内存地址含有22/7的结果,即pi值。最后,线程A退出方法。当线程B再次进入第5行时,新的内存地址将覆盖原先的内存地址(线程A提供的)。这太危险了,而且这种问题往往难于调试。
如果使用ThreadLocal,则不仅能够保证pi()方法是线程安全,而且能够提供性能的改善。 private static ThreadLocal pi = new ThreadLocal();
public Double pi() { if (pi.get() == null) { pi.set(new Double(22 / 7)); } return (Double)pi.get(); } |
ThreadLocal类能够包裹任何对象,而且能够将对象绑定到当前线程,使得它仅仅供当前线程使用。当线程初次执行pi()方法时,由于没有对象绑定到ThreadLocal实例pi上,因此get()方法返回null。借助于set()方法能够将对象绑定到当前线程,而且不供其它线程使用。因此,如果不同线程需要经常访问pi()方法,则借助于ThreadLocal不仅能够保证线程安全,而且能够提高性能。
目前,存在很多关于如何使用ThreadLocal的资源。在Java 1.4之前,ThreadLocal的性能确实很差,但是现已解决了这个问题。另外,由于对ThreadLocal的错误理解,使得很多开发者对它的误用。注意,上述实例使用ThreadLocal的方式是绝对没问题的。在引入ThreadLocal后,上述方法的行为并未发生改变,但是方法已经是线程安全的了。
通过可重入的方式开发线程安全的代码要求开发者谨慎使用实例变量或静态变量,尤其对于修改那些其他线程需要使用的对象而言。某些场合,使用同步可能更为合适。然而,为识别由于同步而引起的应用性能瓶颈往往只能借助于专业的性能评测工具或负载测试完成。
Web应用中的线程安全
在温习线程安全的知识后,来研究Web应用中是如何线程安全的吧!开发者通过创建Web页面来操作数据库。比如,在Web层和业务逻辑层都能够操作RDBMS。本文使用Hibernate将业务模型持久化到数据库中。在Web层,开发者可以使用Tapestry、Wicket、Struts、WebWork、JSF、Spring MVC,或者其他运行在Web容器中的Web框架。
至于Web层的具体实现并不是本文的重点。本文将关注如何管理数据库连接,这也是Web应用中处理线程安全问题是经常要考虑的资源。数据库连接对象,比如连接、结果集、Statement、Hibernate Session,是有状态对象。当然,它们不是线程安全的,因此不能够同时供多个线程访问。在本文前面已经提到,开发者应尽量避免使用同步。无论是synchronized关键字,还是那些同步类(Hashtable或Vector),应尽量避免使用。因此,如果使用可重入,则不用处理阻塞或死锁。
当然,通过可重入实现线程安全以访问数据库并不是件简单的工作。比如,有些开发者可能会在Servlet容器配置中添加过滤器。因此,在客户请求到来时,过滤器将创建JDBC连接或Hibernate Session,并借助于ThreadLocal类将它们绑定到当前线程中,从而供业务逻辑使用。如果直接使用J2EE API,则开发者除了需要做很多同业务逻辑无关的操作外,还需要管理事务、DB错误等等开发内容。请注意,这些同业务逻辑无关的操作的维护工作往往很费时间。
Spring的闯入
一些Java开发者可能听说过Spring提供的DAO抽象。当然,一些开发者也有可能使用过它。借助于Spring提供的模板,开发者能够使用DAO代码的重用。借助于Spring AOP,开发者还能够使用声明式事务。因此,本文来研究Spring是如何实现以线程安全方式访问RDBMS的。比如,Spring允许以JDBC、Hibernate、JDO、iBATIS、TopLink等方式访问数据库。如下给出的实例是企业应用中很常见的情景。
首先,定义数据源和用于Hi
|
|||||||||
|
bernate SessionFactory。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>WEB-INF/jdbc.properties</value> </list> </property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"><value>${jdbc.driverClassName}</value></property> <property name="url"><value>${jdbc.url}</value></property> <property name="username"><value>${jdbc.username}</value></property> <property name="password"><value>${jdbc.password}</value></property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="mappingDirectoryLocations"> <list> <value>classpath:</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean> |
这是使用Hibernate的典型配置,即通过定义的数据源连接到数据库、通过本地SessionFactory创建Hibernate SessionFactory。接下来,需要定义业务对象(实现对DB的访问)和事务管理器(通过Hibernate Session管理本地事务)。其中,业务对象暴露的方法能够在数据库中添加新的纪录,而事务管理器能够将方法包裹在事务中。它们的定义如下。
public interface CustomerDAO { public void createCustomer(Customer customer); } public class HibernateCustomerDAO implements CustomerDAO { private HibernateTemplate hibernateTemplate = null; public void setSessionFactory(SessionFactory sessionFactory) { this.hibernateTemplate = new HibernateTemplate(sessionFactory, false); } public void createCustomer(Customer customer) { this.hibernateTemplate.save(customer); } } |
开发者应该已经看到,上述类使用了Spring提供的HibernateTemplate。注意,模板的开发遵循了业界最佳实践,并且将一些同业务不相关,但J2EE API规定要处理的那些代码处理掉了。与此同时,它通过DAO抽象将受查异常转换为非受查异常。当然,Spring不只是为使用Hibernate提供模板,它还为JDBC、iBATIS、SqlMap、JDO、TopLink提供类似模板。由于这些模板类及其实例变量实现了可重入,即都是线程安全的,因此允许并发线程同时使用模板。使用这些模板不仅能够实现代码的重用,还提供了最佳实践。除了以线程安全方式访问DB外,模板还提供了其他很多有意义的内容。好了,来看看如何定义业务对象和事务管理器吧!
<bean id="customerDAOTarget" class="test.usecase.HibernateCustomerDAO"> <property name="sessionFactory"><ref bean="sessionFactory"/></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory"><ref bean="sessionFactory"/></property> </bean> <bean id="customerDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="target"><ref bean="customerDAOTarget"/></property> <property name="transactionAttributes"> <props> <prop key="create*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> |
如果开发者对Spring中事务管理的配置不熟悉,则本文正好满足你们。首先,上述Spring配置片断定义了业务对象HibernateCustomerDAO,它包裹了Hibernate SessionFactory。注意,默认时,Spring中定义的JavaBean都是单例的,HibernateCustomerDAO也不例外。这意味:多个线程可能同时执行createCustomer()方法。
其次,配置了Hibernate事务管理器,它包裹了同一Hibernate SessionFactory实例。在事务管理器每次执行时,它都会完成如下几件事情。其一,检查Hibernate Session是否绑定到当前线程。如果已绑定,则直接使用它。如果还未绑定,事务管理器将告知Hibernate SessionFactory创建新的Session,然后将创建的Session绑定到当前线程。其二,如果当前没有处于活动的事务,则事务管理器将启动新的事务,并将Session包裹进来。否则,直接参与到活动事务中。
整个过程是通过使用Spring提供的TransactionProxyFactoryBean实现的。当然,这是一种以声明方式实现的事务管理过程。 TransactionProxyFactoryBean能够为业务对象创建代理对象,从而通过事务管理器管理事务。当每次通过代理对象调用createCustomer()方法时,事务管理器将根据事务属性管理事务。当前,Spring除了提供HibernateTransactionManager事务管理器外,还为JDBC数据源、JDO、TopLink提供了相应的事务管理器。
再来看看业务对象吧!当调用createCustomer()方法时,HibernateTemplate将查找绑定到当前线程的Hibernate Session。由于上述配置文件片断传入到HibernateTemplate构建器的第二个参数为false,因此如果没有绑定Hibernate Session,则将抛出未受查异常。这对于那些未正确配置事务管理功能的场和特别有用(注意,事务管理器很重要)。一旦事务管理配置好后,Hibernate Session将绑定到当前线程,从而启动事务。请注意,HibernateTemplate不会去检查事务是否激活,也不会显示地启动或终止事务。也请注意,如果在声明的方法(事务属性中给出的)中抛出了未受查异常,则当前活动事务将回滚。
结论
最后,来总结一下Spring以线程安全方式实现数据访问吧。通过使用事务管理和权衡ThreadLocal提供的功能,Spring将数据库连接(JDBC连接、Hibernate Session、JDO持久化管理器)绑定到当前线程,从而供DAO模板使用。本文在最开始研究了数据库连接并没有在线程间共享。Spring不仅提供了声明式事务管理、J2EE API抽象、最佳实践,而且其提供的模板是线程安全的。当使用Spring访问DB时,通过可重入实现应用的线程安全是最为可靠、常见的做法。
相关推荐
在IT行业中,动态加载属性文件和Spring Quartz的整合应用是一个重要的技术话题,特别是在构建大型、分布式和可扩展的系统时。下面将详细讲解这两个概念及其相互间的应用。 首先,我们来了解一下动态加载属性文件。...
Spring提供`PropertyPlaceholderConfigurer`或`@PropertySource`注解来加载属性文件。`PropertyPlaceholderConfigurer`是一个Bean定义后处理器,它会替换Bean定义中的占位符(如`${key}`)为属性文件中的值。而`@...
除了以上方法,Spring还支持通过环境变量或系统属性来指定配置文件的位置。例如,在启动时可以通过`-Dspring.config.location`命令行参数来指定。 此外,Spring Boot还提供了一种“profile”机制,可以根据不同环境...
### Spring框架中加载多个配置文件的方法 在Spring框架中,加载多个配置文件是常见的需求之一。这不仅可以帮助我们更好地组织代码结构,还可以提高程序的可维护性和可扩展性。本文将详细介绍Spring框架中如何加载多...
Spring支持根据激活的profile加载特定的属性文件,如`application-{profile}.properties`。这使得在不同环境中(例如开发、测试、生产)可以使用不同的配置。 6. **Cloud Config Server** 在分布式系统中,Spring...
### Spring启动时根据配置文件加载不同文件的知识点详解 #### 一、背景介绍 在实际的应用开发中,根据运行环境的不同(例如开发环境、测试环境、生产环境等),应用程序往往需要连接不同的数据库或其他资源。为了...
### Spring Bean 属性详解 Spring框架是Java平台上的一个开源框架,主要用来简化企业级应用程序的开发。...通过深入了解这些元素和属性,开发人员可以更有效地利用Spring框架的功能,构建更加健壮和可扩展的应用程序。
在Java Spring框架中,使用外部属性...在实际项目中,合理使用外部属性文件能显著提升项目的可维护性和扩展性。通过观看"尚硅谷_佟刚_Spring_使用外部属性文件.wmv"视频教程,你可以获得更具体的步骤指导和示例演示。
`@Import`可以导入其他配置类,`@PropertySource`加载属性文件,`@Value`注入属性值。 通过上述方式,我们可以看到Spring框架如何通过Java注解极大地简化了配置和编程模型。在实际项目中,理解并熟练掌握这些注解的...
4. **启动Web应用时加载Spring配置**:当Web应用启动时,`ContextLoaderListener`监听器会根据`contextConfigLocation`参数读取配置文件,并创建相应的ApplicationContext。 #### 二、Struts配置文件中的Spring集成...
8. `<context:property-placeholder>`:加载属性文件,使你可以在配置中使用占位符 `${...}`。 9. `<import>`:引入其他XML配置文件,允许配置模块化。 10. `<bean>`的`init-method`和`destroy-method`属性:指定...
3. **ConfigurableEnvironment或StandardEnvironment**:在Spring的初始化阶段,可以通过扩展`ApplicationContextInitializer`接口,直接修改`ConfigurableEnvironment`中的属性源。 4. **...
1. **配置加载顺序**:Spring加载配置文件时有默认的顺序,如果你同时使用了加密的.properties和YAML配置,需要合理安排加载顺序,以保证依赖关系正确。 2. **自定义`ApplicationContextInitializer`**:如果你需要...
3. 对于复杂的业务逻辑,可以利用Spring的Profile特性,根据环境(如开发、测试、生产)加载不同的配置。 4. 使用Spring的AOP和AspectJ进行切面编程,可以实现更灵活的代码组织和更少的重复代码。 5. 配合使用Maven...
总之,Spring Boot的多模块配置文件读取策略旨在提高项目的可扩展性和可维护性。通过合理的配置文件组织和使用`@ConfigurationProperties`,我们可以有效地管理各模块的配置,同时保持代码的清晰和整洁。理解并熟练...
在Spring中,`PropertyPlaceholderConfigurer`是一个非常重要的类,它用于处理属性文件中的占位符,将它们替换为实际的值。这在配置管理中起到了关键作用,特别是在大型分布式系统中,动态配置管理变得尤为重要。...
首先,Spring配置文件通常是以`.xml`为扩展名的文件,例如`beans.xml`。这些文件存储在项目的`src/main/resources`目录下,以便在运行时被自动加载。Spring容器(ApplicationContext)会解析这些配置文件,创建并...
为了提高代码的可维护性和灵活性,通常会将数据库连接信息存储在外部属性文件中,如`application.properties`或`application.yml`。Spring Boot支持自动配置,可以从这些文件中自动加载配置。以下是在`application....
标题“spring加载”涉及到的是Spring框架的初始化过程,这一过程对于理解Spring如何管理bean和依赖注入至关重要。在Spring框架中,加载是指从配置文件(如XML、Java配置或注解)中读取bean定义,并创建bean实例的...