Spring
Spring 是一个 IOC(DI) 和 AOP 容器框架.
一、浅谈 Spring
二、搭建 Spring 开发环境:
三、IOC & DI 概述
四、获取 Bean 实例
1.从 IOC 容器中获取 Bean
在 spring 配置文件中配置:
<bean id="helloword" class="cn.edu.pzhu.cg.helloword.HelloWord">
<property name="name" value="Spring"></property>
</bean>
获取 bean 实例:
public static void main(String[] args) {
//创建 Spring 的 IOC 对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//从 IOC 对象中获取 Bean 实例
HelloWord helloWord = (HelloWord) ctx.getBean("helloword");
helloWord.hello();
}
2.通过 P 命名空间为 bean 赋值:
<!-- 可以通过 p 命名空间为 bean 赋值,需要先导入 p 的 命名空间 -->
<bean id="helloword2" class="cn.edu.pzhu.cg.helloword.HelloWord" p:name="CG"></bean>
3.抽象 bean:
<!-- 抽象bean:bean 的 abstract 属性为 true 的bean。这样的 bean 不能被 IOC 容器实例化,只能用来被继承。 -->
<bean id="abstracPerson" class="cn.edu.pzhu.cg.test.Person"
p:name="CG" p:age="22" abstract="true"></bean>
<!-- bean 配置的继承:使用 bean 的 parent 属性,指定继承那个 bean 的配置 -->
<bean id="person2" p:name="Tom" parent="person1"></bean>
4.引用其他 bean:
5.bean 的作用域:
使用 bean 的scope属性来配置 bean 的作用域:
- singleton:默认的。容器初始化时创建 bean 实例,在整个容器的生命周期内,只创建这一个 bean,单例的。
- prototype:原型的。容器初始化时不创建 bean 实例,而在每次请求时都创建一个新的 bean 实例,并返回。
<bean id="person3" class="cn.edu.pzhu.cg.test.Person" scope="prototype"></bean>
6.使用外部属性文件:
外部属性文件 db.properties:
user=root
password=123456
driverclass=com.mysql.jdbc.Driver
jdbcurl=jdbc:mysql:///test2
在配置文件中引用属性文件:
<!-- 导入属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 使用外部化属性文件 的属性值 -->
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="driverClass" value="${driverclass}"></property>
<property name="jdbcUrl" value="${jdbcurl}"></property>
</bean>
7.Spring表达式语言:spel
<bean id="address" class="cn.edu.pzhu.cg.spel.Address">
<property name="province" value="#{'四川'}"/>
<property name="city" value="#{'攀枝花'}"/>
</bean>
<bean id="car" class="cn.edu.pzhu.cg.spel.Car">
<property name="name" value="#{'Jeep'}"></property>
<property name="price" value="#{'400000'}"></property>
<!-- 使用 spel 引用类的静态属性 -->
<property name="tyrePerimeter" value="#{T(java.lang.Math).PI * 80}"></property>
</bean>
<bean id="person" class="cn.edu.pzhu.cg.spel.Person">
<property name="name" value="张超"></property>
<property name="age" value="22"></property>
<!-- 使用 spel 来引用其他 Bean 的属性 -->
<property name="city" value="#{address.city}"></property>
<!-- 使用 spel 来引用其他 Bean -->
<property name="car" value="#{car}"></property>
<!-- 在 spel 中使用运算符 -->
<property name="info" value="#{car.price > 500000 ? '金领':'白领'}"></property>
</bean>
8.IOC 容器中 Bean 的生命周期方法:
<bean id="car" class="cn.edu.pzhu.cg.cycle.Car"
init-method="init"
destroy-method="destroy">
<property name="brand" value="Jeep"></property>
</bean>
public class Car {
private String brand;
public Car() {
super();
System.out.println("Car's constructor");
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void init() {
System.out.println("Init method");
}
public void destroy() {
System.out.println("Destroy method");
}
}
9.创建 Bean 后置处理器:
BeanPostProcessor 实现类:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization : " + bean + ", " + beanName);
Car car = new Car();
car.setBrand("BWM");
return car;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization : " + bean + ", " + beanName);
return bean;
}
}
bean.xml:
<!--
实现BeanPostProcessor 接口,并具体提供:
1.Object postProcessBeforeInitialization(Object bean, String beanName):在 init-method 之前被调用
2.Object postProcessAfterInitialization(Object bean, String beanName):在 init-method 之后被调用
的实现。
参数:
bean: bean 实例本身。
beanName: IOC 容器配置的 bean 的名字。
返回值:是实际上返回给用户的那个 bean,注意:可以在以上两个方法中修改返回的 bean,甚至返回一个新的 bean。
-->
<!-- 配置 Bean 的后置处理器 :不需要配置 id,IOC 容器自动识别 BeanPostProcessor -->
<bean class="cn.edu.pzhu.cg.cycle.MyBeanPostProcessor"></bean>
10.通过调用静态工厂方法创建 Bean:
通过访问静态的工厂方法来获得一个 bean 实例
静态工厂方法:
public class StaticCarFactory {
private static Map<String, Car> cars = new HashMap();
static {
cars.put("Audi", new Car("Audi", 300000));
cars.put("Jeep", new Car("Jeep", 400000));
//....
}
public static Car getCar(String name) {
return cars.get(name);
}
}
bean.xml:
<!-- 通过静态工厂方法来配置 bean,注意不是配置静态工厂方法实例,而是配置 bean 实例 -->
<!--
class : 指向静态工厂方法的全类名。
factory-method : 指向静态工厂方法的名字(方法是静态的)。
constructor-arg : 如果工厂方法需要传入参数,则使用 constructor-arg 来配置参数。
-->
<bean id="car1" class="cn.edu.pzhu.cg.factory.StaticCarFactory"
factory-method="getCar">
<constructor-arg value="Jeep"></constructor-arg>
</bean>
11.实现 FactoryBean 接口在 Spring IOC 容器中配置 Bean:
工厂方法:
/*
* 自定义的 factorybean 需要实现 FactoryBean 接口
*/
public class CarFactoryBean implements FactoryBean<Car>{
private String brand;
public void setBrand(String brand) {
this.brand = brand;
}
//返回 bean 的对象
@Override
public Car getObject() throws Exception {
return new Car(brand, 300000);
}
//返回 bean 的类型
@Override
public Class<?> getObjectType() {
return Car.class;
}
// 返回bean 是否是单例
@Override
public boolean isSingleton() {
return true;
}
}
bean.xml:
<!--
通过 FactoryBean 来配置 bean 的实例。
class : 指向 FactoryBean 的全类名。
property : 配置 FactoryBean 的属性。
但实际上返回是 FactoryBean 中 getObject() 方法所返回的实例!
-->
<bean id="car" class="cn.edu.pzhu.cg.factorybean.CarFactoryBean">
<property name="brand" value="Jeep"></property>
</bean>
12.在 classpath 中扫描组件
<!-- 指定 Spring IOC 容器扫描的包 -->
<!-- resource-pattern:过滤特定的类 -->
<context:component-scan base-package="cn.edu.pzhu.cg.annotation" resource-pattern="controller/*.class">
</context:component-scan>
<!--
context:exclude-filter:子节点指定排除哪些指定表达式的组件
context:include-filter:子节点指定包含哪些表达式的组件,该子节点需要和 use-default-filters 配合使用
-->
<context:component-scan base-package="cn.edu.pzhu.cg.annotation" use-default-filters="false">
<!--基于注解-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--基于类名-->
<context:exclude-filter type="assignable" expression="cn.edu.pzhu.cg.annotation.service.UserService"/>
<context:include-filter type="assignable" expression="cn.edu.pzhu.cg.annotation.service.UserService"/>
</context:component-scan>
13.使用注解自动装配bean:
为类加上注解,不需要在 bean.xml 中配置也可以被 IOC 装配
五、Spring AOP
1.AOP 简介
2.AOP 术语
3.Spring AOP
- AspectJ:Java 社区里最完整最流行的 AOP 框架.
- 在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP
4.在 Spring 中启用 AspectJ 注解支持
5.用 AspectJ 注解声明切面
6.AOP 日志
(1).前置通知
(2).后置通知
(3).返回通知
(4).异常通知
(5).环绕通知
7.指定切面的优先级
8.重用切入点定义
实例代码:
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/*
* 1.先加入 IOC 容器
* 2.在声明为切面
* 3.使用 @Order() 注解,可以指定切面的优先级,值越小优先级越高。
*/
@Order(2)
@Component
@Aspect
public class LoggingAspect {
/*
* 定义一个方法,用于声明切入点表达式,一般情况,该方法中不需要添加其他代码。(根据不同的位置,可以在前面指定包名、类名)
* 使用 @Pointcut 来声明切入点表达式
*/
@Pointcut("execution(public int cn.edu.pzhu.cg.aop.impl.ArithmeticCalculatorImpl.*(int, int))")
public void declareJointPointExpression(){}
//前置通知:在目标方法执行前通知
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
Object[] args = joinpoint.getArgs();
System.out.println("The method "+methodName +" begin with " + Arrays.asList(args));
}
//后置通知:在目标方法执行后通知,无论目标方法是否发生异常,都会通知
//注意:在后置通知中,不能访问目标方法执行的结果
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinpoint) {
String methodName = joinpoint.getSignature().getName();
System.out.println("The method "+methodName +" end ");
}
/*
* 在方法正常结束后执行的;并且可以访问到方法的返回值。
*/
@AfterReturning(value="declareJointPointExpression()" ,returning="result")
public void afterReturning(JoinPoint joinpoint,Object result) {
String methodName = joinpoint.getSignature().getName();
System.out.println("The method "+methodName +" end with " + result);
}
/*
* 目标出现异常后进行通知;可以访问到异常对象;且可以指定在出现特定的异常时执行通知。
*/
@AfterThrowing(value="declareJointPointExpression()",throwing="ex")
public void afterException(JoinPoint joinpoint,Exception ex) {
//可以改变形参来指定某种异常的通知,比如 NullPointerException,出现空指针时才通知。
String methodName = joinpoint.getSignature().getName();
System.out.println("The method "+methodName +" occurs: " + ex);
}
/*
* 1.环绕通知需要携带 ProceedingJoinPoint 类型的参数
* 2.环绕通知类似于动态代理的全过程:ProceedingJoinPoint 参数可以决定是否执行目标方法
* 3.环绕通知必须有返回值,返回值即为目标方法的返回值
*/
@Around("execution(public int cn.edu.pzhu.cg.aop.impl.ArithmeticCalculatorImpl.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint pjp) {
Object result = null;
String methodName = pjp.getSignature().getName();
try {
//前置通知
System.out.println("The method "+methodName +" begin with " + Arrays.asList(pjp.getArgs()));
//执行方法
result = pjp.proceed();
//结果通知
System.out.println("The method "+methodName +" end with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method "+methodName +" occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method "+methodName +" end ");
return result;
}
}
六、Spring 对 JDBC 的支持
1.JdbcTemplate 简介
- 为了使 JDBC 更加易于使用, Spring 在 JDBC API 上定义了一个抽象层, 以此建立一个 JDBC 存取框架.
- 作为 Spring JDBC 框架的核心, JDBC 模板的设计目的是为不同类型的 JDBC 操作提供模板方法. 每个模板方法都能控制整个过程, 并允许覆盖过程中的特定任务. 通过这种方式, 可以在尽可能保留灵活性的情况下, 将数据库存取的工作量降到最低.
2.NamedParameterJdbcTemplate 简介
- 在经典的 JDBC 用法中, SQL 参数是用占位符 ? 表示,并且受到位置的限制. 定位参数的问题在于, 一旦参数的顺序发生变化, 就必须改变参数绑定.
- 在 Spring JDBC 框架中, 绑定 SQL 参数的另一种选择是使用具名参数(named parameter).
- 具名参数: SQL 按名称(以冒号开头)而不是按位置进行指定. 具名参数更易于维护, 也提升了可读性. 具名参数由框架类在运行时用占位符取代
- 具名参数只在 NamedParameterJdbcTemplate 中得到支持
在 XML 文件中配置 JdbcTemplate :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 导入资源文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置 C3P0 数据源 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="jdbcUrl" value="${jdbcurl}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="initialPoolSize" value="${initialPoolSize}"></property>
<property name="maxPoolSize" value="${maxPoolSize}"></property>
</bean>
<!-- 配置 Spring 的 JdbcTemplate -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置能使用具名参数的 NamedParameterJdbcTemplate -->
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
</beans>
实例代码:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
public class JDBCTest {
private ApplicationContext ctx = null;
private JdbcTemplate jdbcTemplate = null;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate = null;
{
ctx = new ClassPathXmlApplicationContext("bean_jdbc.xml");
jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");
namedParameterJdbcTemplate = ctx.getBean(NamedParameterJdbcTemplate.class);
}
/*
* 使用具名参数时,可以用 update(String sql, SqlParameterSource paramSource) 方法进行更新操作
* 1.SQL 参数的参数名和类的属性名一致。
* 2.使用 SqlParameterSource 的 BeanPropertySqlParameterSource 实现类作为参数。
*/
@Test
public void testNamedParameterJdbcTemplate2() {
String sql = "INSERT INTO student(name,age,sno) VALUES(:name,:age,:sno)";
Student student = new Student();
student.setName("张学友");
student.setAge(50);
student.setSno("10002");
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(student);
namedParameterJdbcTemplate.update(sql, paramSource);
}
/*
* NamedParameterJdbcTemplate 可以使用具名参数
* 可为参数取别名:
* 1.好处:若有多个参数,则不用再去对应位置,直接对应参数名,便于维护。
* 2.缺点:较为麻烦。
*/
@Test
public void testNamedParameterJdbcTemolate() {
String sql = "INSERT INTO student(name,age,sno) VALUES(:name,:age,:sno)";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "刘德华");
paramMap.put("age", 50);
paramMap.put("sno", "10001");
namedParameterJdbcTemplate.update(sql, paramMap);
}
/*
* 获取单个列的值
*/
@Test
public void testQueryForObject2() {
String sql = "SELECT count(id) FROM student";
long count = jdbcTemplate.queryForObject(sql, long.class);
System.out.println(count);
}
/*
* 查到实体类的集合
*/
@Test
public void testQueryForList() {
String sql = "SELECT id,name,age,sno FROM student WHERE id > ?";
RowMapper<Student> rowMapper = new BeanPropertyRowMapper<>(Student.class);
List<Student> students = jdbcTemplate.query(sql, rowMapper,6);
System.out.println(students);
}
/*
* 从数据库中获取一条记录,实际上是得到对应的一个对象。
* 需要调用 queryForObject(String sql, RowMapper<Student> rowMapper, Object... args) 方法
* 1.其中 RowMapper 指定如何去映射结果集的行,常用的实现类为 BeanPropertyRowMapper。
* 2.使用 SQL 中列的别名完成列名和类中属性名的映射,比如在 表中列名 为 last_name,在类 中属性名为 lastname
* 在写 SQL 语句时,应该这样: SELECT last_name lastname FROM student WHERE id = ?
*
* 3.不支持级联属性。JdbcTemplate 是一个 JDBC 小工具,而不是 ORM 框架。
*
*/
@Test
public void testQueryForObject() {
String sql = "SELECT id,name,age,sno FROM student WHERE id = ?";
RowMapper<Student> rowMapper = new BeanPropertyRowMapper<>(Student.class);
Student student = jdbcTemplate.queryForObject(sql, rowMapper, 6);
System.out.println(student);
}
/*
* 进行批量的 INSERT UPDATE DELETE
* batchUpdate 中第二个参数是 Object[] 的 List ,因为一条记录对应一个 Object 数组,多条记录就是一个 List.
*/
@Test
public void testBatchUpdate() {
String sql = "INSERT INTO student(name,age,sno) VALUES(?,?,?)";
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[] {"张三",18,"201510803001"});
batchArgs.add(new Object[] {"李四",25,"201510803002"});
batchArgs.add(new Object[] {"翠花",20,"201510803003"});
batchArgs.add(new Object[] {"小华",21,"201510803004"});
batchArgs.add(new Object[] {"小明",20,"201510803005"});
jdbcTemplate.batchUpdate(sql, batchArgs);
}
/*
* 执行 UPDATE INSERT DELETE
*/
@Test
public void testUpdate() {
String sql = "UPDATE student SET age = ? WHERE id = ?";
jdbcTemplate.update(sql, 20,8);
}
@Test
public void testDataSource() throws Exception {
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
}
七、Spring 中的事务管理
1. Spring 中的事务
- 作为企业级应用程序框架, Spring 在不同的事务管理 API 之上定义了一个抽象层. 而应用程序开发人员不必了解底层的事务管理 API, 就可以使用 Spring 的事务管理机制.
- Spring 既支持编程式事务管理, 也支持声明式的事务管理.
- 编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚. 在编程式管理事务时, 必须在每个事务操作中包含额外的事务管理代码.
- 声明式事务管理: 大多数情况下比编程式事务管理更好用. 它将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. 事务管理作为一种横切关注点, 可以通过 AOP 方法模块化. Spring 通过 Spring AOP 框架支持声明式事务管理.
2.用 @Transactional 注解声明式地管理事务
3.事务传播属性
- 当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.
- 事务的传播行为可以由传播属性指定. Spring 定义了 7 种类传播行为.
4.事务的隔离级别
- 从理论上来说, 事务应该彼此完全隔离, 以避免并发事务所导致的问题. 然而, 那样会对性能产生极大的影响, 因为事务必须按顺序运行.
- 在实际开发中, 为了提升性能, 事务会以较低的隔离级别运行.
- 事务的隔离级别可以通过隔离事务属性指定
Spring 支持的事务隔离级别:
- 事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持.
- Oracle 支持的 2 种事务隔离级别:READ_COMMITED , SERIALIZABLE
- Mysql 支持 4 中事务隔离级别
5.设置隔离事务属性
6.设置回滚事务属性
7.超时和只读属性
- 由于事务可以在行和表上获得锁, 因此长事务会占用资源, 并对整体性能产生影响.
- 如果一个事物只读取数据但不做修改, 数据库引擎可以对这个事务进行优化.
- 超时事务属性: 事务在强制回滚之前可以保持多久. 这样可以防止长期运行的事务占用资源.
- 只读事务属性: 表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务.
实例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
/*
* 1.使用 propagation 指定事务的传播行为,即当前的事务方法在被另一个事务方法调用时,如何使用事务。
* 2.默认,REQUIRED:使用调用方法的事务;
* REQUIRES_NEW:使用自己的事务,调用方法的事务被挂起。
* 3.使用 isolation 指定事务的隔离级别,最常用的是 READ_COMMITTED(读已提交)
* 4.使用 timeout 可以指定强制回滚前事务可以占用的最长时间,单位为秒
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,
timeout=2)
@Override
public void purchase(int userId, int bookId) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
//1.获取书的单价
int price = bookShopDao.getBookPriceByBookId(bookId);
//2.修改书的库存
bookShopDao.updateBookStock(bookId);
//3.修改用户余额
bookShopDao.updateUserAccount(userId, price);
}
}
八、Spring 整合 Hibernate
需要导入 Spring 和 Hibernate 所需的 jar 包。
1.在 Spring 中配置 SessionFactory
- 对于 Hibernate 而言, 必须从原生的 Hibernate API 中构建 SessionFactory. 此外, 应用程序也无法利用 Spring 提供的数据存储机制(例如: Spring 的事务管理机制)
- Spring 提供了对应的工厂 Bean, 可以用单实例的形式在 IOC 容器中创建 SessionFactory 实例.
2.各种 xml 文件的配置
(1).hibernate.cfg.xml:在 hibernate 的配置文件中只配置 hibernate 基本信息。
<hibernate-configuration>
<session-factory>
<!-- 配置 Hibernate 的基本信息 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
(2).Spring 配置文件:
如果在 Spring IOC 容器中配置数据源. 可以将该数据源注入到 LocalSessionFactoryBean 的 dataSource 属性中. 该属性可以指定的数据源会覆盖掉 Hibernate 配置文件里的数据库配置.
<!-- 配置 C3P0 数据源 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="driverClass" value="${driverclass}"></property>
<property name="jdbcUrl" value="jdbc:mysql:///test2?useUnicode=true&characterEncoding=UTF-8"></property>
<property name="initialPoolSize" value="${initialPoolSize}"></property>
<property name="maxPoolSize" value="${maxPoolSize}"></property>
</bean>
<!-- 配置 SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- 关联 Hibernate 的配置文件 -->
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<!-- 关联 Hibernate 的映射配置文件 -->
<property name="mappingLocations" value="classpath:cn/edu/pzhu/entities/*.hbm.xml"></property>
</bean>
可以将 Hibernate 配置信息放在 Spring 的配置文件中,这样可以少维护一个配置文件,只需利用 hibernateProperties 属性即可
(3).配置事务:
为了让 Hibernate 使用上 Spring 的声明式事务(也可以使用注解),需要配置事务。需要在事务管理器中引用前面的配置的 sessionFactory。
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!--配置事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置切点信息,并与事务关联-->
<aop:config>
<aop:pointcut expression="execution(* cn.edu.pzhu.dao.*.*(..))" id="txPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
通过以上配置就可以在 Spring 中使用 Hibernate,同时可以为 Hibernate 使用上 Spring 的声明式事务。
3.使用 SessionFactory
在hibernate和spring集成之后,如果我们使用spring配置注入的sessionFactory生成session,这个时候我们一定要注意使用getCurrentSession()而不要使用openSession,这是因为spring去管理事务,getCurrentSession()始终返回的是同一个对象,当出问题的时候,事务会自动回滚,而openSession()则返回不同的对象,出了问题,由于是不同的session,就会发生不能自动回滚的现象,所以使用的时候一定要注意!
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.edu.pzhu.entities.Person;
@Service("personDao")
public class PersonDaoImpl implements PersonDao {
@Autowired
private SessionFactory sessionFactory;
private Session getSession() {
//从当前线程中去获取 session
return sessionFactory.getCurrentSession();
}
@Override
public String getPersonById(int id) {
String hql = "SELECT personName FROM Person where id = ?";
return getSession().createQuery(hql).setParameter(0, id).uniqueResult().toString();
}
@Transactional
@Override
public void save(Person person){
getSession().save(person);
//手动抛出异常测试事务配置是否成功
throw new RuntimeException("异常");
}
}
九、Spring 整合 Struts2
1.导入 jar 包:
在 web 环境下使用 Spring 需要导入一下两个 jar 包:
spring-web-4.3.3.RELEASE.jar
spring-webmvc-4.3.3.RELEASE.jar
除了 Struts 和 Spring 的 jar 包,需要额外导入:
struts2-spring-plugin-2.3.24.1.jar
2.在通用的 web 应用中访问 Spring
(1).需要额外的加入以下 jar 包:
spring-web-4.3.3.RELEASE.jar
spring-webmvc-4.3.3.RELEASE.jar
(2).Spring 的配置文件和非 WEB 环境下没有什么不同
(3).需要在 web.xml 中加入如下配置:
<!-- 配置 Spring 配置文件的名称和位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置启动 IOC 容器的 ServletContextListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3.web.xml 文件示例代码
<!-- 配置 spring 配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.在 web 应用程序中访问 Spring 的 ApplicationContext 对象
5.将 Struts 的 Action 交由 Spring 掌管,通过 IOC 实现自动注入。
在 Spring 的配置文件中配置 Action 时要加上 scope=”prototype”,非单例模式。
示例代码:
<!-- 在配置 Action 时候应该 scope="prototype" 配置为非单例模式-->
<bean id="springAction"
class="cn.edu.pzhu.action.SpringAction" scope="prototype"></bean>
在 Struts 的配置文件配置 Action 时,class 应该指向在 Spring 配置文件中当前 Action 的 id,而不再是 Action 的全类名。
示例代码:
<struts>
<constant name="struts.action.extension" value="action,do"></constant>
<package name="default" namespace="/" extends="struts-default">
//注意:这里 class 不再指向 Action 的全类名,因为 Action 已经交由 Spring 掌管了。
<action name="SpringAction" class="springAction" method="Hello">
<result name="success">success.jsp</result>
</action>
</package>
</struts>
通过以上配置就可以在 Spring 中使用 Struts 了,将 Action 交由 Spring 掌管,实现自动注入。