前言
这篇博文主要拓展Spring的配置。掌握更多配置数据源的方法,理解Bean的作用域,使用
Spring自动装配,会拆分Spring配置文件,下面我们来学习吧!
灵活配置DataSource
Spring与MyBatis的整合过程中,我们将数据源的配置拿到了Spring配置文件中。实际应用中,Spring还有很多灵活配置方式可以选择。
1. 使用属性文件配置
MyBatis可以引入外部属性文件,那么Spring肯定也是可以的。在Spring中,使用PropertyPlaceholderConfigurer类可以加载属性文件,在配置文件中采用${…}的方式引用属性文件中的键值对。
<!--引入外部文件properties文件-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:database.properties</value>
</property>
</bean>
引入属性文件之后,我们就可以在配置数据源的时候,应用属性文件中的键值对了,如下:
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassLoader" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8
user=root
password=521521
注意:经常有开发者在${…}的前后不小心输入一些空格,这些空格字符串将和变量合并后作为属性的值,最终将引发异常。
2. 使用JNDI数据源
应用部署在高性能的服务器上,我们可能更希望使用服务器本身提高的数据源。服务器的数据源使用JNDI方式供使用者调用。在Spring中,提供了专门引用JNDI资源的JndiObjectFactoryBean类。
使用JNDI资源,首先在服务器上要有这个资源。我们以Tomcat为例,在conf文件夹下的context.xml定义如下数据源:
步骤
- 在Tomcat中的lib文件夹下加入数据库驱动包
- 在tomcat下的context.xml中配置JDNI数据源,编写连接数据库配置
<Context>
<Resource name="jdbc/smbms" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" username="root"
password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/数据库?useUnicode=true&characterEncoding=utf-8"/>
</Context>
- 在Spring配置文件中通过JNDI配置DataSource
<!-- 通过JNDI配置DataSource -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<!--通过jndiName指定引用的JNDI数据源名称 -->
<property name="jndiName">
<value>java:comp/env/jdbc/smbms</value>
</property>
</bean>
注意:<Resource>
标签的name指定数据源的名称,要与Spring配置文件中的jndiName
值java:comp/env/
后的名称保持一致。
Spring中Bean的作用域问题
理解Bean的作用域
Spring中定义Bean,除了可以创建Bean实例并对其属性进行注入,还可以为其指定作用域。这个作用域的取值决定了Spring创建该组件实例时所采用的策略,进而影响程序的效率以及数据安全问题。目前Spring划分了5中Bean的作用域:
作用域 | 说明 |
---|---|
singleton | 默认值。Spring以单例模式创建Bean的实例,即容器中该Bean的实例只有一个 |
prototype | 每次从容器中获取Bean时,都会创建一个新的实例 |
request | 用于Web应用环境,针对每次HTTP请求都会创建一个实例 |
session | 用于Web应用环境,同一个会话共享同一个实例,不同的会话使用不同的实例 |
global session | Portlet的Web应用中使用,同一个全局会话共享一个实例。对于非Portlet环境,等同于session |
作用域详解:
- Singleton:单例作用域,是spring容器默认的作用域,使用singleton作用域生成的是单实例,在整个Bean容器中仅保留一个实例对象供所有调用者共享引用。单例模式一般适用于那些无会话状态的Bean(比如辅助工具类,DAO组件,业务逻辑组件等等)
- Prototype:原型模式,这是多实例作用域,针对每次不同请求,Bean容器均会生成一个全新的Bean实例以供调用者使用,他适用于那些需要保持会话状态的Bean实例,有一点值得注意:Spring不能对一个property bean的整个生命周期负责,容器在初始化、装配好一个property实例之后,就将他交给客户端,就不管了。因此客户端要负责property实例的生命周期管理
- Request:针对每次HTTP请求,spring容器都会根据bean的定义创建一个全新的bean实例,并且该bean实例仅在当前http request内有效,因此可以根据需要放心的更改实例内部状态,而其他请求中根据Bean定义创建的实例,不会看到这些特定于某些请求的状态变化,当处理请求结束,request作用域的Bean实例将被销毁,该作用域仅在基于web的spring ApplicationContext情形下才有效
- Session:针对某个HTTP session ,Spring容器会根据bean定义创建一个全新Bean实例,并且该Bean实例仅在当前的HTTP session内有效。该作用域仅在基于web的spring ApplicationContext情形下才有效
- Global session:该作用域类似于标准的http session 作用域,不过他仅仅在基于portlet的web应用中才有意义。。。。该作用域仅在基于web的spring ApplicationContext情形下才有效
singleton是默认采用的作用域,即默认情况下Spring为每一个Bean仅创建一个实例,对于不存在线程安全问题的组件,这种方式会大大减少创建对象的开销,提高运行效率。
对于特定的应用场景,比如我们需要每一个用户都相互隔离,使用默认的作用域肯定是实现不了需求的,这时候就需要我们指定Bean组件的作用域为prototype,即每一次获取该组件的实例时都去创建一个新的实例,指定组件的scope属性即可,如下:
<!--设置prototype作用域,作用:每次获取的时候,都会创建新的实例。-->
<bean id="..." class="..." scope="prototype">
</bean>
使用注解指定Bean的作用域
@Scope("prototype") //指定作用域为prototype
@Service("billService") //定义Bean组件
public class BillServiceImpl implements BillService {
//省略实现方法
}
Spring的自动装配
做了解即可(实战中,很少用),大型项目中不推荐使用自动装配,还是指定所依赖的组件好。因为虽然它能够简化配置,但是降低了依赖关系的清晰性和透明性,全都交给Spring自动装配组件,弄得我们都不知道谁依赖谁。而且依赖关系的装配仅依赖于源文件的属性名或者类型,导致Bean与Bean之间的耦合度上升,不利于高层次解耦。
autowrite属性值 | 说明 |
---|---|
no | 不适用自动装配,依赖关系必须由我们自己指定 |
byType | 根据属性类型自动装配。BeanFactory查找容器中的全部Bean,如果有一个与需要注入依赖的属性的类型相匹配的Bean,就将这个Bean装配给这个属性。如果有多个相匹配的Bean,Spring将抛出致命异常;如果没有相匹配的Bean,什么都不会发生,最多用的时候会出现空指针。 |
byName | 根据属性名自动装配。BeanFactory查找容器中所有的Bean,找出id与属性的setter方法相匹配的Bean,进行注入 |
constructor | 与byType的方式类似,不同之处在于它应用于构造器函数。如果容器中没有找到与构造器参数类型一致的Bean,那么将会抛出异常。 |
1. 采用传统的xml方式配置Bean组件
<!-- 配置UserMapper -->
<bean id="userMapper" class="cn.smbms.dao.user.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- 配置业务Bean并注入DAO实例 -->
<bean id="userService" class="cn.smbms.service.user.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
2 使用自动装配
<!--配置DAO 根据属性名称自动装配 -->
<bean id="userMapper" class="cn.smbms.dao.user.UserMapperImpl" autowire="byName"/>
<!-- 配置业务bean,根据属性名称自动装配 -->
<bean id="userService" class="cn.smbms.service.user.UserServiceImpl" autowire="byName"/>
3. 全局自动匹配
<beans default-autowire="byName"/>
<!--省略部分代码-->
</beans>
拆分Spring配置文件
使用XML方式进行配置Spring项目,如果项目很大,配置文件的可读性、可维护性较差,庞大的Spring配置文件难以维护、阅读。此外,在团队开发时,多人修改同一配置文件容易产生问题冲突。我们可以将一个大的配置文件进行拆分,每一个小的配置文件只配置功能相似的组件,比如application_dao.xml,application-service.xml,降低配置文件的修改难度。
为什么需要拆分配置文件
- 项目规模变大,配置文件可读性、可维护性差
- 团队开发时,多人修改同一配置文件,易发生冲突
拆分策略
-
公用配置+每个系统模块一个单独配置文件(包含DAO、Service、Web控制器)
-
公用配置+DAO Bean配置+业务逻辑Bean配置+Web控制器配置
启动项目的时候,如何指示Spring找到这些多个配置文件,将它们整合到一起呢?
根据ClassPathXmlApplicationContext类的构造方法的几种重载:
public ClassPathXmlApplicationContext(String configLocation);
public ClassPathXmlApplicationContext(String… configLocations);
方式一:
ApplicationContext context=new ClassPathXmlApplicationContext("application-mybatis.xml",
"application-dao.xml",
"application-service.xml");
方式二:
String[] locations={"application-mybatis.xml","application-dao.xml","application-service.xml"};
ApplicationContext context=new ClassPathXmlApplicationContext(locations);
方式三:
ApplicationContext context=new ClassPathXmlApplicationContext("application-*.xml");
使用通配符可以导入多个配置文件,很方便,但是命名规范一定要好,遵循一定的命名规律。
此外,Spring配置文件本身也可以通过import子元素导入外部配置文件,将多个配置文件整合到一起,最后只需要加载终极配置文件即可,例如在application-mybatis.xml中引入以下两个配置文件:
<import resource="application-dao.xml" />
<import resource="application-service.xml"/>
上面的方式用下面的方式加载
ApplicationContext context=new ClassPathXmlApplicationContext("application-mybatis.xml");
总结
这篇博文来自课本的总结,希望对大家有帮助