第三章 Spring之AOP详解
第一节 AOP简介
AOP 简介: 面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是 Spring 框架中的一个重要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度 降低,提高程序的可重用性,同时提高了开发的效率。 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
第二节 SpringAOP实例
1.前置通知
可以获取调用的类名,方法名和传入的参数
2.后置通知
3.环绕通知
一般只用一个,用了环绕通知,前置和后置通知就可不用
4.返回通知(很少用)
5.异常通知
新建项目Spring403
->新建com.java1234.service
StudentService.java
package com.java1234.service;
public interface StudentService {
public void addStudent(String name);
}
->新建com.java1234.service.impl
StudentServiceImpl.java
package com.java1234.service.impl;
import com.java1234.service.StudentService;
public class StudentServiceImpl implements StudentService{
@Override
public void addStudent(String name) {
//写上我们的业务逻辑
System.out.println("添加学生"+name);
}
}
->beans.xml
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean>
->T.java
package com.java1234.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java1234.service.StudentService;
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void test1() {
StudentService studentService=(StudentService)ac.getBean("studentService");
studentService.addStudent("张三");
}
}
->启动程序
控制台上打印出“添加学生张三”
现在有一个需求,需要做一个日志记录
不是按照传统的一条直线的写代码
->com.java1234.service.impl
StudentServiceImpl.java
public void addStudent(String name) {
System.out.println("开始添加学生"+name);
System.out.println("添加学生"+name);
System.out.println("完成学生"+name+"的添加");
}
以前才是这样写的,按照顺序写下来
控制台上打印出
开始添加学生张三
添加学生张三
完成学生张三的添加
但是现在我们不想要日志代码侵入正常的业务逻辑代码,要用AOP来解决,用Spring把日志代码切进去
新建项目Spring403-02
->导入额外的几个与AOP有关的包
aopaliance.jar
aspectjweaver-1.6.6.jar
spring-aspects-4.0.6.RELEASE.jar
->新建com.java1234.advice
StudentServiceAspect.java
package com.java1234.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class StudentServiceAspect {
// 前置通知
public void doBefore(JoinPoint jp){
System.out.println("开始添加学生");
}
}
->beans.xml
// 引入下面这几个才能用xmlns:aop=“http://www.springframework.org/schema/aop”
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="studentServiceAspect" class="com.java1234.advice.StudentServiceAspect"></bean>
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl"></bean>
<aop:config>
//首先定义一个切面
<aop:aspect id="studentServiceAspect" ref="studentServiceAspect">
//定义切点,用到了表达式,来匹配addStudent这个方法,即我们的业务逻辑部分的代码
<aop:pointcut expression="execution(* com.java1234.service.*.*(..))" id="businessService"/>
//定义前置通知
<aop:before method="doBefore" pointcut-ref="businessService"/>
</aop:aspect>
</aop:config>
</beans>
->执行过程
让我们执行addStudent的方法时,通过配置before,去执行doBefore方法
->T.java
package com.java1234.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java1234.service.StudentService;
public class T {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
StudentService studentService=(StudentService)ac.getBean("studentService");
studentService.addStudent("张三");
}
}
->执行结果:
开始添加学生
添加学生张三
以上只是日志打印出了一句话,能不能在日志中获取类名方法名输入参数呀
通过joinpoint可以获取到
->StudentServiceAspect .java
public void doBefore(JoinPoint jp){
System.out.println("类名:"+jp.getTarget().getClass().getName());
System.out.println("方法名:"+jp.getSignature().getName());
System.out.println("开始添加学生:"+jp.getArgs()[0]);
}
->执行结果
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
开始添加学生:张三
添加学生张三
以上我们讲解了前置,再讲一个后置通知
以下都是在原先的代码中添加上方法、配置
->StudentServiceAspect .java
public void doAfter(JoinPoint jp){
System.out.println("类名:"+jp.getTarget().getClass().getName());
System.out.println("方法名:"+jp.getSignature().getName());
System.out.println("学生添加完成:"+jp.getArgs()[0]);
}
->beans.xml
//定义后置通知
<aop:after method="doAfter" pointcut-ref="businessService"/>
->执行结果
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
开始添加学生:张三
添加学生张三
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
学生添加完成:张三
以上我们讲解了前置、后置通知,再讲一个环绕
以下都是在原先的代码中添加上方法、配置
环绕方法是有返回值的,参数也不一样
->StudentServiceAspect .java
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("添加学生前");
Object retVal=pjp.proceed();
System.out.println(retVal);
System.out.println("添加学生后");
return retVal;
}
->StudentServiceImpl.java
package com.java1234.service.impl;
import com.java1234.service.StudentService;
public class StudentServiceImpl implements StudentService{
@Override
public String addStudent(String name) {
System.out.println("添加学生"+name);
return name;
}
}
->beans.xml
//定义环绕通知
<aop:around method="doAround" pointcut-ref="businessService"/>
->执行结果
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
开始添加学生:张三
添加学生前
添加学生张三
张三
添加学生后
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
学生添加完成:张三
发现环绕通知比前置后置通知的优先级要低
但是用了环绕通知、前置后置就不需要了
返回通知
->StudentServiceAspect .java
public void doAfterReturning(JoinPoint jp){
System.out.println("返回通知");
}
->beans.xml
<aop:after-returning method="doAfterReturning" pointcut-ref="businessService"/>
->执行结果
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
开始添加学生:张三
添加学生前
添加学生张三
返回通知
null
添加学生后
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
学生添加完成:张三
异常通知
->StudentServiceImpl.java
package com.java1234.service.impl;
import com.java1234.service.StudentService;
public class StudentServiceImpl implements StudentService{
@Override
public void addStudent(String name) {
System.out.println("添加学生"+name);
System.out.println(1/0);
}
}
->StudentServiceAspect .java
public void doAfterThrowing(JoinPoint jp,Throwable ex){
System.out.println("异常通知");
System.out.println("异常信息:"+ex.getMessage());
}
->beans.xml
<aop:after-throwing method="doAfterThrowing" pointcut-ref="businessService" throwing="ex"/>
->执行结果
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
开始添加学生:张三
添加学生前
添加学生张三
异常通知
异常信息:/by zero
类名:com.java1234.service.impl.StudentServiceImpl
方法名:addStudent
学生添加完成:张三
第四章 Spring对DAO的支持
第一节 Spring对JDBC的支持
JDBC是最原始的连接数据库的一种操作
1.配置数据源dbcp;
2.使用JdbcTemplate;
3.JdbcDaoSupport的使用;
daoimpl类不用写jdbcTemplate实例,直接设DataSource,getJdbcTemplate()即可.
4.NamedParameterJdbcTemplate的使用;支持命名参数变量;
org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
MapSqlParameterSource接受输入参数。
1.配置数据源dbcp;
2.使用JdbcTemplate;
新建项目Spring404
->新建文件夹dbcp,导入包
commons-dbcp-1.4
commons-pool
->新建属性文件jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_spring
jdbc.username=root
jdbc.password=123456
->新建数据库db_spring
新建表t_student( )
->引入jdbctemplate所用的jar包到spring包中
spring-jdbc-4.0.6.RELEASE.jar
spring-tx-4.0.6.RELEASE.jar
->先定义数据源
beans.xml中添加与数据源有关的包
xmlns:context=“http://www.springframework.org/schema/context”
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
beans.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
//定义数据源
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
//dbcp的数据源连接池
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
->新建com.java1234.model
Student.java
package com.java1234.model;
public class Student {
private int id;
private String name;
private int age;
public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student(int id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
->新建com.java1234.dao
StudentDao.java
package com.java1234.dao;
import java.util.List;
import com.java1234.model.Student;
public interface StudentDao {
public int addStudent(Student student);
public int updateStudent(Student student);
public int deleteStudent(int id);
public List<Student> findStudents();
}
->com.java1234.dao.impl
StudentDaoImpl.java
package com.java1234.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import com.java1234.dao.StudentDao;
import com.java1234.model.Student;
public class StudentDaoImpl implements StudentDao{
//将jdbcTemplate作为属性。后面的bean中也作为属性
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//用jdbcTemplate来实现以下操作
@Override
public int addStudent(Student student) {
String sql="insert into t_student values(null,?,?)";
Object []params=new Object[]{student.getName(),student.getAge()};
return jdbcTemplate.update(sql,params);
}
@Override
public int updateStudent(Student student) {
String sql="update t_student set name=?,age=? where id=?";
Object []params=new Object[]{student.getName(),student.getAge(),student.getId()};
return jdbcTemplate.update(sql,params);
}
@Override
public int deleteStudent(int id) {
String sql="delete from t_student where id=?";
Object []params=new Object[]{id};
return jdbcTemplate.update(sql,params);
}
@Override
public List<Student> findStudents() {
String sql="select * from t_student";
final List<Student> studentList=new ArrayList<Student>();
jdbcTemplate.query(sql, new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
Student student=new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
studentList.add(student);
}
});
return studentList;
}
}
->新建com.java1234.service
StudentService.java
package com.java1234.service;
import java.util.List;
import com.java1234.model.Student;
public interface StudentService {
public int addStudent(Student student);
public int updateStudent(Student student);
public int deleteStudent(int id);
public List<Student> findStudents();
}
->新建com.java1234.service.impl
StudentServiceImpl.java
package com.java1234.service.impl;
import java.util.List;
import com.java1234.dao.StudentDao;
import com.java1234.model.Student;
import com.java1234.service.StudentService;
public class StudentServiceImpl implements StudentService{
//将studentDao作为属性在后面bean中也作为属性注入
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int addStudent(Student student) {
return studentDao.addStudent(student);
}
@Override
public int updateStudent(Student student) {
return studentDao.updateStudent(student);
}
@Override
public int deleteStudent(int id) {
return studentDao.deleteStudent(id);
}
@Override
public List<Student> findStudents() {
return studentDao.findStudents();
}
}
->新建com.java1234.test
T.java
package com.java1234.test;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java1234.model.Student;
import com.java1234.service.StudentService;
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void addStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int addNums=studentService.addStudent(new Student("王五", 1));
if(addNums==1){
System.out.println("添加成功");
}
}
@Test
public void updateStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int updateNums=studentService.updateStudent(new Student(8,"王五2", 2));
if(updateNums==1){
System.out.println("更新成功");
}
}
@Test
public void deleteStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int deleteNums=studentService.deleteStudent(8);
if(deleteNums==1){
System.out.println("删除成功");
}
}
@Test
public void findStudents() {
StudentService studentService=(StudentService)ac.getBean("studentService");
List<Student> studentList=studentService.findStudents();
for(Student student:studentList){
System.out.println(student);
}
}
}
->beans.xml
//studentDao,注入jdbcTemplate
<bean id="studentDao" class="com.java1234.dao.impl.StudentDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
//studentService,注入studentDao
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
->执行程序
addStudent JunitTest 控制台上输出:添加成功
数据库中多了一条记录(0,王五,1)
updateStudent JunitTest 控制台上输出:更新成功
数据库中改成一条记录(8,王五2,2)
deleteStudent JunitTest 控制台上输出:删除成功
数据库中删除一条记录(8,王五2,2)
findStudents JunitTest 控制台上输出:查询成功
从数据库中遍历出记录
Student[id=2,name=张三,age=12]
3.JdbcDaoSupport的使用;
daoimpl类不用写jdbcTemplate实例,直接设DataSource,getJdbcTemplate()即可.
新建项目Spring404-02
->com.java1234.dao
StudentDao.java
package com.java1234.dao;
import java.util.List;
import com.java1234.model.Student;
public interface StudentDao {
public int addStudent(Student student);
public int updateStudent(Student student);
public int deleteStudent(int id);
public List<Student> findStudents();
}
->com.java1234.dao,impl
StudentDaoImpl.java
package com.java1234.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.java1234.dao.StudentDao;
import com.java1234.model.Student;
//extends JdbcDaoSupport
//它的源码里面有jdbctemplate就不需要我们自己定义了
public class StudentDaoImpl extends JdbcDaoSupport implements StudentDao{
@Override
public int addStudent(Student student) {
String sql="insert into t_student values(null,?,?)";
Object []params=new Object[]{student.getName(),student.getAge()};
return this.getJdbcTemplate().update(sql,params);
}
@Override
public int updateStudent(Student student) {
String sql="update t_student set name=?,age=? where id=?";
Object []params=new Object[]{student.getName(),student.getAge(),student.getId()};
return this.getJdbcTemplate().update(sql,params);
}
@Override
public int deleteStudent(int id) {
String sql="delete from t_student where id=?";
Object []params=new Object[]{id};
return this.getJdbcTemplate().update(sql,params);
}
@Override
public List<Student> findStudents() {
String sql="select * from t_student";
final List<Student> studentList=new ArrayList<Student>();
this.getJdbcTemplate().query(sql, new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
Student student=new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
studentList.add(student);
}
});
return studentList;
}
}
->beans.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
<bean id="studentDao" class="com.java1234.dao.impl.StudentDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
</beans>
->T.java
package com.java1234.test;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java1234.model.Student;
import com.java1234.service.StudentService;
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void addStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int addNums=studentService.addStudent(new Student("王五", 1));
if(addNums==1){
System.out.println("添加成功");
}
}
@Test
public void updateStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int updateNums=studentService.updateStudent(new Student(10,"王五2", 2));
if(updateNums==1){
System.out.println("更新成功");
}
}
@Test
public void deleteStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int deleteNums=studentService.deleteStudent(10);
if(deleteNums==1){
System.out.println("删除成功");
}
}
@Test
public void findStudents() {
StudentService studentService=(StudentService)ac.getBean("studentService");
List<Student> studentList=studentService.findStudents();
for(Student student:studentList){
System.out.println(student);
}
}
}
->运行结果
添加成功
更新成功
删除成功
查询成功
jdbcsupport方便了一些
4.NamedParameterJdbcTemplate的使用;支持命名参数变量;
新建项目Spring404-03
->beans.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
//org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
// 这里就不是属性了而是构造方法传入数据源
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<bean id="studentDao" class="com.java1234.dao.impl.StudentDaoImpl">
<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
</bean>
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
</beans>
->StudentDaoImpl.java
package com.java1234.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import com.java1234.dao.StudentDao;
import com.java1234.model.Student;
public class StudentDaoImpl implements StudentDao{
//NamedParameterJdbcTemplate
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setNamedParameterJdbcTemplate(
NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
// 有了这些以后,
@Override
public int addStudent(Student student) {
String sql="insert into t_student values(null,:name,:age)";
//MapSqlParameterSource专门来放参数
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("name", student.getName());
sps.addValue("age", student.getAge());
return namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public int updateStudent(Student student) {
String sql="update t_student set name=:name,age=:age where id=:id";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("name", student.getName());
sps.addValue("age", student.getAge());
sps.addValue("id", student.getId());
return namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public int deleteStudent(int id) {
String sql="delete from t_student where id=:id";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("id", id);
return namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public List<Student> findStudents() {
String sql="select * from t_student";
final List<Student> studentList=new ArrayList<Student>();
namedParameterJdbcTemplate.query(sql, new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
Student student=new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
studentList.add(student);
}
});
return studentList;
}
}
->执行结果
成功
第二节:Spring 对 Hibernate 的支持
后面 Spring 整合 Hibernate 的时候讲;
第五章 Spring对事务的支持
第一节 事务简介
要满足四个条件:
1.原子性
2.一致性
3.隔离性
4.持久性
第二节 编程式事务管理(用的较少,不是太好,会侵入业务代码)
Spring提供的事务模板类:org.springframework.transaction.support.TransactionTemplate
事务管理器:org.springframework.jdbc.datasource.DataSourceTransactionManager
银行转账例子
新建项目Spring405
->在数据库中建立db_bank
表t_count(id,userid,username,count)
->新建com.java1234.dao
BankDao.java
package com.java1234.dao;
public interface BankDao {
public void inMoney(int money,int userId);
public void outMoney(int money,int userId);
}
->com.java1234.dao.impl
BankDaoImpl.java
package com.java1234.dao.impl;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import com.java1234.dao.BankDao;
public class BankDaoImpl implements BankDao{
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setNamedParameterJdbcTemplate(
NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
@Override
public void inMoney(int money, int userId) {
// TODO Auto-generated method stub
String sql="update t_count2 set count=count+:money where userId=:userId";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("money", money);
sps.addValue("userId", userId);
namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public void outMoney(int money, int userId) {
// TODO Auto-generated method stub
String sql="update t_count set count=count-:money where userId=:userId";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("money", money);
sps.addValue("userId", userId);
namedParameterJdbcTemplate.update(sql,sps);
}
}
->com.java1234.service
事物层
BankService.java
package com.java1234.service;
public interface BankService {
/**
* A向B转账count元
* @param count
* @param userIdA
* @param userIdB
*/
public void transferAccounts(int count,int userIdA,int userIdB);
}
->com.java1234.service.impl
BankServiceImpl.java
package com.java1234.service.impl;
import com.java1234.dao.BankDao;
import com.java1234.service.BankService;
public class BankServiceImpl implements BankService{
//首先引入dao
private BankDao bankDao;
public void setBankDao(BankDao bankDao) {
this.bankDao = bankDao;
}
@Override
public void transferAccounts(int count,int userIdA,int userIdB) {
// TODO Auto-generated method stub
tbankDao.outMoney(count, userIdA);
bankDao.inMoney(count, userIdB);
}
}
}
->beans.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<bean id="bankDao" class="com.java1234.dao.impl.BankDaoImpl">
<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
</bean>
<bean id="bankService" class="com.java1234.service.impl.BankServiceImpl">
<property name="bankDao" ref="bankDao"></property>
</bean>
</beans>
->T.java
package com.java1234.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.java1234.service.BankService;
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void transferAccounts() {
BankService bankService=(BankService)ac.getBean("bankService");
bankService.transferAccounts(50, 1, 2);
}
}
->运行结果
转账成功
张三少了50李四多了50
->我们这边模拟一个异常
比如说,转出时没问题,转入时将String sqi="update t_count2…"设一个陷阱,t_count2根本就不存在
->运行结果
张三少了50但是李四没收到
->这时候必须加上事务,先讲编程式事务管理
->BankServiceImpl.java
package com.java1234.service.impl;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.java1234.dao.BankDao;
import com.java1234.service.BankService;
public class BankServiceImpl implements BankService{
private BankDao bankDao;
//引入TransactionTemplate
private TransactionTemplate transactionTemplate;
public void setBankDao(BankDao bankDao) {
this.bankDao = bankDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
//要把事务
@Override
public void transferAccounts(final int count, final int userIdA, final int userIdB) {
// TODO Auto-generated method stub
//execute实现的方法体里面,写入doInTransactionWithoutResult方法
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
// TODO Auto-generated method stub
bankDao.outMoney(count, userIdA);
bankDao.inMoney(count, userIdB);
}
});
}
}
->beans.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
//先配置事务管理器,注入数据源这个属性
<!-- jdbc事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
//注入transactionTemplate,注入属性事务管理器
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<context:property-placeholder location="jdbc.properties"/>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<bean id="bankDao" class="com.java1234.dao.impl.BankDaoImpl">
<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
</bean>
//在service层注入事务transactionTemplate
<bean id="bankService" class="com.java1234.service.impl.BankServiceImpl">
<property name="bankDao" ref="bankDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
</beans>
->运行结果
张三没扣钱李四也没收到钱
这个编程式事务管理不太好,因为事务的代码已经入侵到业务代码中了
第三节 声明式事务管理
1.使用XML配置声明式事务(一般情况下使用)
优点:只要配置一个xml文件,所有service都可以事务管理
新建项目Spring405-02
->BankServiceImpl.java
package com.java1234.service.impl;
import com.java1234.dao.BankDao;
import com.java1234.service.BankService;
public class BankServiceImpl implements BankService{
private BankDao bankDao;
public void setBankDao(BankDao bankDao) {
this.bankDao = bankDao;
}
@Override
public void transferAccounts(int count, int userIdA, int userIdB) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
bankDao.outMoney(count, userIdA);
bankDao.inMoney(count, userIdB);
}
}
->beans.xml
首先引入命名空间
xmlns:tx=“http://www.springframework.org/schema/tx”
和地址
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
然后就可以开始用了
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- jdbc事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
//切的都是方法
<tx:method name="insert*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 配置事务切面 -->
<aop:config>
<!-- 配置切点 -->
//切的都是service层的所有方法使用表达式标准写法
<aop:pointcut id="serviceMethod" expression="execution(* com.java1234.service.*.*(..))" />
<!-- 配置事务通知 -->
//已经切到里面后要增加通知
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
<context:property-placeholder location="jdbc.properties"/>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<bean id="bankDao" class="com.java1234.dao.impl.BankDaoImpl">
<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
</bean>
<bean id="bankService" class="com.java1234.service.impl.BankServiceImpl">
<property name="bankDao" ref="bankDao"></property>
</bean>
</beans>
->运行结果
事务
2.使用注解配置声明式样事务
缺点:每个service都要加注解
新建项目Spring405-03
->BankServiceImpl.java
package com.java1234.service.impl;
import org.springframework.transaction.annotation.Transactional;
import com.java1234.dao.BankDao;
import com.java1234.service.BankService;
//加注解
@Transactional
public class BankServiceImpl implements BankService{
private BankDao bankDao;
public void setBankDao(BankDao bankDao) {
this.bankDao = bankDao;
}
@Override
public void transferAccounts(int count, int userIdA, int userIdB) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
bankDao.outMoney(count, userIdA);
bankDao.inMoney(count, userIdB);
}
}
->beans.xml
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- jdbc事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
//要加入这个,表明是注解,把事务管理器加进去
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:property-placeholder location="jdbc.properties"/>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<bean id="bankDao" class="com.java1234.dao.impl.BankDaoImpl">
<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
</bean>
<bean id="bankService" class="com.java1234.service.impl.BankServiceImpl">
<property name="bankDao" ref="bankDao"></property>
</bean>
</beans>
->运行结果:
有效的
第四节 事务传播行为
事务传播行为:Spring 中,当一个 service 方法调用另外一个 service 方法的时候,因为每个 service 方法都有事务,这时候就出现了事务的嵌套;由此,就产生了事务传播行为; 在 Spring 中,通过配置 Propagation,来定义事务传播行为;
1PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 2PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
3PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。 4PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。 5PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。
beanx.xml
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="new*" propagation="REQUIRED" />
<tx:method name="set*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="change*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="load*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
<tx:attributes>
<tx:methodname="insert*" propagation="REQUIRED"/> <tx:methodname="update*"propagation="REQUIRED"/>
<tx:methodname="edit*" propagation="REQUIRED"/> <tx:methodname="save*"propagation="REQUIRED"/> <tx:methodname="add*"propagation="REQUIRED"/>
<tx:methodname="new*" propagation="REQUIRED" /> <tx:methodname="set*" propagation="REQUIRED"/> <tx:methodname="remove*"propagation="REQUIRED"/>
<tx:methodname="delete*" propagation="REQUIRED"/> <tx:methodname="change*" propagation="REQUIRED"/> <tx:methodname="get*" propagation="REQUIRED"read-only="true"/> <tx:methodname="find*" propagation="REQUIRED" read-only="true"/> <tx:methodname="load*" propagation="REQUIRED" read-only="true"/> <tx:methodname="*" propagation="REQUIRED"read-only="true"/>
</tx:attributes>