spring
一个大容器,用来管理项目中的所有对象
spring不排斥其他框架,帮其他框架管理对象
Spring中的主要组件如图:
Core Container是spring框架的核心控制器,有四个部分,核心组件是其中的Beans、Core和Context组件。
-
Beans组件可以说是比较核心的,Spring框架将对象之间的依赖关系转用配置文件、注解来配置,注入关系通过IOC容器来管理,而IOC容器中管理的实体就是被Bean包裹的对象。Bean就是被包装之后的Java对象,由Spring框架创建和维护。
-
Context组件是Bean的上下文,是Bean的生存环境,用于建立和维护Bean之间的关系,所以说Context其实是Bean关系的集合,这个关系的集合就叫做IOC容器。
-
Core组件,就是发现、建立、维护Bean之间的关系所需要的一系列的工具类(Util)。
spring搭建
1.导包
(1)基础包,即Core Container的四个包
(2)日志包
2.创建一个对象
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3.书写配置注册对象到容器
配置文件名任意,位置任意(一般叫applicationContext.xml,放在src目录下)
第一步:导入约束
(1)window–preferences–xml catalog–add–
(2)applicationContext.xml写好根元素
<?xml version="1.0" encoding="UTF-8"?>
<beans></beans>
(3)applicationContext.xml切换到design视图
选中beans右键edit namespaces–add–
(4)再add–
(5)导入约束后applicationContext.xml中自动变成
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd ">
</beans>
第二步:将对象交给spring管理
4.测试
package com.xdh.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.xdh.bean.User;
public class HelloDemo {
@Test
public void fun1(){
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user");
//3 打印user对象
System.out.println(u);
}
}
IOC和DI
IOC:Inverse of control:反转控制
以前对象的创建以及依赖关系的注入都是由开发人员自己维护,使用了spring之后,对象的创建和依赖关系的注入都可以由spring完成。反转控制就是反转了对象的创建方式,从我们自己创建反转给了程序(spring)
DI:dependency injection:依赖注入
实现IOC思想需要DI做支持
注入方式:
- set注入
- 构造方法注入
- 字段注入
注入类型:
1)值类型注入 --8大基本数据类型
2)引用类型注入 --将依赖对象注入
BeanFactory和ApplicationContext
BeanFactory:
spring原始接口,功能较为单一
BeanFactory接口实现类的容器,每次在获得对象时才会创建对象
ApplicationContext:
每次容器启动时就会创建容器中配置的所有对象,并提供更多功能
加载配置文件方式:
- 从类路径下加载配置文件:ClassPathXmlApplicationContext
- 从硬盘绝对路径下加载配置文件:FileSystemXmlApplicatinContext(d:/xxx/xxx.xml)
总结:web开发中,使用ApplicationContext,在资源匮乏的环境可以使用BeanFactory
配置详解
1.bean元素
<!-- 将User对象交给spring容器管理 -->
<!-- Bean元素:使用该元素描述需要spring容器管理的对象
class属性:被管理对象的完整类名.
name属性:给被管理的对象起个名字,获得对象时根据该名称获得对象.
可以重复.可以使用特殊字符.
id属性: 与name属性一模一样.
名称不可重复,不能使用特殊字符.
小结: 尽量使用name属性.
scope属性:
singleton(默认值):单例对象,被标识为单例的对象在spring容器中只会存在一个实例
prototype:多例,标识为多例的对象,每次在获得时才会创建对象,每次创建都是新的对象
request(了解):web环境下,对象与request生命周期一致
session(了解):web环境下,对象与session生命周期一致
小结:一般使用singleton
spring整合struts2时,把Action交给spring框架管理,每访问一次Action要创建一个Action对象, 所以配成多例
生命周期属性:
配置一个方法作为生命周期初始化方法,spring会在对象创建之后立即调用
init-method=""
配置一个方法作为生命周期的销毁方法,spring容器在关闭并销毁容器中的对象之前调用
destroy-method=""
-->
<bean name="user" class="com.xdh.bean.User" scope="singleton" init-method="init" destroy-method="destroy" ></bean>
2.三种对象创建方式
<!-- 创建方式1:空参构造创建(常用) -->
<bean name="user" class="com.xdh.bean.User" ></bean>
<!-- 创建方式2:静态工厂创建 (了解)
调用UserFactory的createUser方法创建名为user2的对象,放入容器
createUser是静态方法,直接类名.方法名
-->
<bean name="user2"
class="com.xdh.createbean.UserFactory"
factory-method="createUser" ></bean>
<!-- 创建方式3:实例工厂创建 (了解)
调用UserFactory对象的createUser2方法创建名为user3的对象,放入容器
createUser2是普通方法,需先创建UserFactory实例,再调方法
-->
<bean name="user3"
factory-bean="userFactory"
factory-method="createUser2" ></bean>
<bean name="userFactory"
class="com.xdh.createbean.UserFactory" ></bean>
public class UserFactory {
public static User createUser(){
System.out.println("静态工厂创建User");
return new User();
}
public User createUser2(){
System.out.println("实例工厂创建User");
return new User();
}
}
方式一是spring创建对象,方式二三是手动创建对象,spring调创建对象的方法
3.spring的分模块配置
即引入别的spring配置文件
<!-- 导入其他spring配置文件 -->
<import resource="com/xdh/createdemo/applicationContext.xml"/>
4.spring属性注入
(1).set方法注入(最主要)
(2).构造函数注入
<bean name="user2" class="com.xdh.bean.User" >
<!-- name属性: 构造函数的参数名 -->
<!-- index属性: 构造函数的参数索引 -->
<!-- type属性: 构造函数的参数类型-->
<constructor-arg name="name" index="0" type="java.lang.Integer" value="999" ></constructor-arg>
<constructor-arg name="car" ref="car" index="1" ></constructor-arg>
</bean>
(3).p名称空间注入(了解)
在使用p名称空间注入时要先引入P名称空间:
<!-- p名称空间注入, 走set方法
1.导入P名称空间 xmlns:p="http://www.springframework.org/schema/p"
2.使用p:属性完成注入
|-值类型: p:属性名="值"
|-对象类型: p:属性名-ref="bean名称"
-->
<bean name="user3" class="com.xdh.bean.User" p:name="jack" p:age="20" p:car-ref="car" >
</bean>
(4).spel注入(了解)
spel:spring expression language(spring表达式语言),不支持引用类型的注入
<bean name="user4" class="com.xdh.bean.User" >
<property name="name" value="#{user.name}" ></property>
<property name="age" value="#{user3.age}" ></property>
<property name="car" ref="car" ></property>
</bean>
(5).复杂类型的注入
例如集合、数组
public class CollectionBean {
private Object[] arr;//数组类型注入
private List list;//list/set 类型注入
private Map map;//map类型注入
private Properties prop;//properties类型注入
...
}
<!-- 复杂类型注入 -->
<bean name="cb" class="com.xdh.bean.CollectionBean" >
<!-- array注入 -->
<!-- 如果数组中只准备注入一个值(对象),直接使用value|ref即可
<property name="arr" value="tom" ></property>
-->
<!-- 多个元素注入 -->
<property name="arr">
<array>
<value>tom</value>
<value>jerry</value>
<ref bean="user4" />
</array>
</property>
<!-- 如果List中只准备注入一个值(对象),直接使用value|ref即可
<property name="list" value="jack" ></property>-->
<property name="list" >
<list>
<value>jack</value>
<value>rose</value>
<ref bean="user3" />
</list>
</property>
<!-- map类型注入 -->
<property name="map" >
<map>
<entry key="url" value="jdbc:mysql:///crm" ></entry>
<entry key="user" value-ref="user4" ></entry>
<entry key-ref="user3" value-ref="user2" ></entry>
</map>
</property>
<!-- prperties 类型注入 -->
<property name="prop" >
<props>
<prop key="driverClass">com.jdbc.mysql.Driver</prop>
<prop key="userName">root</prop>
<prop key="password">1234</prop>
</props>
</property>
</bean>
补充
1、spring在web项目中的生命周期
spring是一个存放了大量对象的容器,所以不可能每访问Action中的一个方法,就创建一个ApplicationContext容器对象,即不能通过
//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user");
创建容器对象,ApplicationContext容器对象在整个项目中只创建和销毁一次。
ServletContextListner监听器,用来监听ServletContext的创建与销毁,spring内置了ContextLoaderListener监听器,底层也是ServletContextListner,ServletContextListner监听器创建好后,把容器存在ServletContext中,可以让spring容器随项目的启动而创建,随项目的关闭而销毁。
监听器:监听web服务器的运行,当发生特定的事件时采取预先设定的处理措施的组件
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
- 配置ContextLoaderListener监听器需先导包:
- 再配置:
<!-- 可以让spring容器随项目的启动而创建,随项目的关闭而销毁 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- 获得容器,再从容器里拿对象
//获得spring容器=>从Application域获得即可
//1 获得servletContext对象
ServletContext sc = ServletActionContext.getServletContext();
//2.从Sc中获得ac容器
WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc);
//3.从容器中获得CustomerService
CustomerService cs = (CustomerService) ac.getBean("customerService");
2、指定配置文件的位置
spring配置文件名字任意,位置任意,创建spring容器时需指定名字和位置,用以下方式指定配置文件的名字和位置:
<!-- 指定加载spring配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
spring中使用注解代替xml配置
准备工作
1.导包
2.为主配置文件导入context命名空间(约束)
成果:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
3.开启使用注解代替配置文件
<!-- 指定扫描com.xdh.bean包下的所有类中的注解(包含子包) -->
<context:component-scan base-package="com.xdh.bean"></context:component-scan>
component-scan:组件扫描,在这里,component理解为对象
具体使用
1.在类中使用注解
@Component("user")
public class User {
}
@Component注解:将对象注册到容器,参数是对象在容器中的名称
以下三个注解和@Component作用一样,只是为了区分包方便,一般用以下三个注解
@Service(“user”) // service层
@Controller(“user”) // web层
@Repository(“user”)// dao层
相当于:<bean name="user" class="com.xdh.bean.User"></bean>
2.修改对象的作用范围
@Component("user")
//指定对象的作用范围
@Scope(scopeName="singleton")
//singleton单例(默认值)
//prototype多例
public class User {
}
3.属性注入
(1)值类型注入
- 加在成员变量上
@Value(value="my")
private String name;
通过反射field赋值,破坏了封装性
注解中只有一个属性需要赋值,并且属性名为value时,键可省略,即@Value(“my”)
- 或set方法上
@Value("my")
public void setName(String name) {
this.name = name;
}
通过set方法赋值,推荐使用
(2)引用类型注入
user对象中注入car步骤:
- 先将car注入到spring容器中
@Component("car2")
public class Car {
- 把car对象注入到user中:
方式一:自动装配
@Autowired
@Qualifier("car2")
private Car car;
问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.
@Qualifier(“car2”)//使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
方式二:手动装配,推荐使用
@Resource(name="car2")
private Car car;
手动注入,指定注入哪个名称的对象
4.初始化和销毁方法
@PostConstruct //在对象被创建后调用.init-method
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy //在销毁之前调用.destory-method
public void destroy(){
System.out.println("我是销毁方法!");
}
spring整合Junit
不需要再在测试demo里每个方法都创建容器和获取对象了
1.导包
2.配置注解
import com.xdh.bean.User;
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
//将名为user的对象注入到u变量中
@Resource(name="user")
private User u;
@Test
public void fun1(){
System.out.println(u);
}
}
spring中的aop
aop思想:横向重复,纵向抽取,即面向切面编程
例如,过滤器,动态代理,拦截器
动态代理:
Proxy.newProxyInstance(classload,interface[],InvocationHandler)
classload:告诉虚拟机用哪个字节码加载器加载内存中创建出的字节码文件
interface:被代理对象实现的接口
InvocationHandler:存放需要被加到被代理对象中的代码
1.spring中的aop概念
spring能够为容器中管理的对象生成动态代理对象
以前要使用动态代理,需要手动调用Proxy.newProxyInstance(classload,interface[],InvocationHandler),生成代理对象
spring能帮我们生成代理对象
2.spring实现aop的原理
(1)动态代理技术
被代理对象必须要实现接口,才能产生代理对象,如果没有接口,将不能使用动态代理技术
(2)cglib代理技术
第三方代理技术,可以对任何类生成代理,代理的原理是对目标对象进行继承代理,如果目标对象被final修饰,那么该类无法被cglib代理
手动实现动态代理的demo:
public interface UserService {
void save();
void delete();
void update();
void find();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户!");
//int i = 1/0;
}
@Override
public void delete() {
System.out.println("删除用户!");
}
@Override
public void update() {
System.out.println("更新用户!");
}
@Override
public void find() {
System.out.println("查找用户!");
}
}
package com.xdh.proxydemo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.xdh.service.UserService;
import com.xdh.service.UserServiceImpl;
public class UserServiceProxyFactory implements InvocationHandler {
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
private UserService us;
public UserService getUserServiceProxy(){
//生成动态代理
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
this);
//返回
return usProxy;
}
@Override
//arg0当前代理对象
//method当前方法
//arg2当前方法执行的参数
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
System.out.println("打开事务!");
//执行业务方法
Object invoke = method.invoke(us, arg2);
System.out.println("提交事务!");
return invoke;
}
}
@Test
//动态代理
public void fun1(){
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//代理对象与被代理对象实现了相同的接口
//代理对象 与 被代理对象没有继承关系
System.out.println(usProxy instanceof UserServiceImpl );//false
}
手动实现cglib代理的demo:
package com.xdh.proxydemo;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.xdh.service.UserService;
import com.xdh.service.UserServiceImpl;
public class UserServiceProxyFactory2 implements MethodInterceptor {
public UserService getUserServiceProxy(){
Enhancer en = new Enhancer();//帮我们生成代理对象
en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
en.setCallback(this);//代理要做什么
UserService us = (UserService) en.create();//创建代理对象
return us;
}
@Override
public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
//打开事务
System.out.println("打开事务!");
//调用原有方法
Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
//提交事务
System.out.println("提交事务!");
return returnValue;
}
}
@Test
public void fun2(){
UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//判断代理对象是否属于被代理对象类型
//代理对象继承了被代理对象=>true
System.out.println(usProxy instanceof UserServiceImpl );//true
}
总结:
(1)优先使用动态代理,如果没有接口,则使用cglib代理
(2)动态代理里,代理对象与被代理对象实现了相同的接口,代理对象与被代理对象 没有继承关系
cglib代理里,代理对象继承了被代理对象
3.spring的aop名词解释
Jointpoint(连接点):目标对象中,所有可以增强的方法
比如UserServiceImpl中的增删改查方法
Pointcut(切入点):目标对象中已经增强的方法
比如UserServiceImpl中的增删改方法
Advice(通知/增强):增强的代理
比如开启事务和提交/回滚事务的方法
Target(目标对象):被代理对象UserServiceImpl
Weaving(织入):将通知应用到切入点的过程
Proxy(代理):将通知织入到目标对象之后,形成代理对象
aspect(切面):切入点+通知
4.spring的aop准备工作
(1)xml配置
1)导包
2)准备目标对象
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存用户!");
//int i = 1/0;
}
@Override
public void delete() {
System.out.println("删除用户!");
}
@Override
public void update() {
System.out.println("更新用户!");
}
@Override
public void find() {
System.out.println("查找用户!");
}
}
3)准备通知
package com.xdh.aopdemo;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
//前置通知
public void before(){
System.out.println("这是前置通知!!");
}
//后置通知
public void afterReturning(){
System.out.println("这是后置通知(如果出现异常不会调用)!!");
}
//环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分!!");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("这是环绕通知之后的部分!!");
return proceed;
}
//异常通知
public void afterException(){
System.out.println("出事啦!出现异常了!!");
}
//后置通知
public void after(){
System.out.println("这是后置通知(出现异常也会调用)!!");
}
}
4)配置进行织入,将通知织入到目标对象中
先导入aop命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 准备工作: 导入aop(约束)命名空间 -->
<!-- 1.配置目标对象 -->
<bean name="userService" class="com.xdh.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
<bean name="myAdvice" class="com.xdh.aopdemo.MyAdvice" ></bean>
<!-- 3.配置将通知织入目标对象 -->
<aop:config>
<!-- 配置切入点
id:切入点的名字
-->
<aop:pointcut expression="execution(* com.xdh.service.*ServiceImpl.*(..))" id="pc"/>
<!-- ref属性:通知对象名称 -->
<aop:aspect ref="myAdvice" >
<!-- 指定名为before方法作为前置通知 -->
<aop:before method="before" pointcut-ref="pc" />
<!-- 后置 -->
<aop:after-returning method="afterReturning" pointcut-ref="pc" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pc" />
<!-- 异常拦截通知 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!-- 后置 -->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
切入点表达式:
public void com.xdh.service.UserServiceImpl.save()
一般public可省略,public是默认值
切入点对返回值没有要求
不仅对save()方法增强
方法的参数不做要求
还可对service的子包下的方法进行增强
即 * com.xdh.service…ServiceImpl.(…)
测试:
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:com/xdh/aopdemo/applicationContext.xml")
public class TestAop {
//将名为userService的对象注入到us变量中
//这里userService是代理对象,获取不到被代理对象了
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){
us.save();
}
}
(2)注解配置
1)导包
2)准备目标对象
3)准备通知
4)配置进行织入,将通知织入到目标对象中
<!-- 1.配置目标对象 -->
<bean name="userService" class="com.xdh.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
<bean name="myAdvice" class="com.xdh.annotationaop.MyAdvice" ></bean>
<!-- 3.开启使用注解完成织入 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.xdh.annotationaop;
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;
//通知类
@Aspect
//表示该类是一个通知类
public class MyAdvice {
@Pointcut("execution(* com.xdh.service.*ServiceImpl.*(..))")
public void pc(){}
//前置通知
//指定该方法是前置通知,并制定切入点
//找MyAdvice下的pc()方法,自动找方法上的注解
@Before("MyAdvice.pc()")
public void before(){
System.out.println("这是前置通知!!");
}
//后置通知
@AfterReturning("execution(* com.xdh.service.*ServiceImpl.*(..))")
public void afterReturning(){
System.out.println("这是后置通知(如果出现异常不会调用)!!");
}
//环绕通知
@Around("execution(* com.xdh.service.*ServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分!!");
Object proceed = pjp.proceed();//调用目标方法
System.out.println("这是环绕通知之后的部分!!");
return proceed;
}
//异常通知
@AfterThrowing("execution(* com.xdh.service.*ServiceImpl.*(..))")
public void afterException(){
System.out.println("出事啦!出现异常了!!");
}
//后置通知
@After("execution(* com.xdh.service.*ServiceImpl.*(..))")
public void after(){
System.out.println("这是后置通知(出现异常也会调用)!!");
}
}
spring-jdbc
spring中提供了一个可以操作数据库的对象,即JDBCTemplate,该对象封装了JDBC技术
JDBCTemplate:JDBC模板对象
与DBUtils中的QueryRunner非常相似
1.步骤
(1) 导包
- 4个spring核心包
- 2个日志包
- test包
- c3p0连接池
- JDBC驱动包
- spring-jdbc(JDBCTemplate在这个包里)
- spring-tx事务包
JDBCTemplate操作数据库的demo:
//0 准备连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///springtest");
dataSource.setUser("root");
dataSource.setPassword("root");
//1 创建JDBC模板对象
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
//2 书写sql,并执行
String sql = "insert into user(id,name,psw) values(null,'rose','123456') ";
jt.update(sql);
以上是自己手动new JdbcTemplate,现将dao,JdbcTemplate,datasorce都交由spring容器管理!
(2) 书写Dao
public interface UserDao {
//增
void save(User u);
//删
void delete(Integer id);
//改
void update(User u);
//查
User getById(Integer id);
//查
int getTotalCount();
//查
List<User> getAll();
}
package com.xdh.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import com.xdh.bean.User;
public class UserDaoImpl implements UserDao{
private JdbcTemplate jt;
@Override
public void save(User u) {
String sql = "insert into user values(null,?,?) ";
jt.update(sql, u.getName(),u.getPsw());
}
@Override
public void delete(Integer id) {
String sql = "delete from user where id = ? ";
jt.update(sql,id);
}
@Override
public void update(User u) {
String sql = "update user set name = ?,psw = ? where id=? ";
jt.update(sql, u.getName(),u.getPsw(),u.getId());
}
@Override
public User getById(Integer id) {
String sql = "select * from user where id = ? ";
//查询结果返回单个对象
//RowMapper<T>:手动封装结果集,底层是遍历ResultSet
return jt.queryForObject(sql, new RowMapper<User>(){
@Override
//mapRow()方法已经对结果进行非空判断了,非空才会执行该方法
public User mapRow(ResultSet rs, int arg1) throws SQLException {
// TODO Auto-generated method stub
User u = new User();
u.setId(rs.getInt("id"));
u.setName(rs.getString("name"));
return u;
}}, id);
}
@Override
public int getTotalCount() {
String sql = "select count(*) from user";
//当查询结果返回值类型,Class<T> requiredType参数直接是想封装成哪种类型的值类型
Integer count = jt.queryForObject(sql, Integer.class);
return count;
}
@Override
public List<User> getAll() {
String sql = "select * from t_user ";
List<User> list = jt.query(sql, new RowMapper<User>(){
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User u = new User();
u.setId(rs.getInt("id"));
u.setName(rs.getString("name"));
u.setPsw(rs.getString("psw"));
return u;
}});
return list;
}
public JdbcTemplate getJt() {
return jt;
}
public void setJt(JdbcTemplate jt) {
this.jt = jt;
}
}
(3) 将对象配置到spring容器中让容器进行管理
dao依赖JdbcTemplate,JdbcTemplate依赖datasource
<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="jdbcUrl" value="jdbc:mysql:///springtest" ></property>
<property name="driverClass" value="com.mysql.jdbc.Driver" ></property>
<property name="user" value="root" ></property>
<property name="password" value="root" ></property>
</bean>
<!-- 2.将JDBCTemplate放入spring容器 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
<property name="dataSource" ref="dataSource" ></property>
</bean>
<!-- 3.将UserDao放入spring容器 -->
<bean name="userDao" class="com.xdh.dao.UserDaoImpl" >
<property name="jt" ref="jdbcTemplate" ></property>
</bean>
扩展1:
在UserDaoImpl中手动生成private JdbcTemplate jt;对象,
可让UserDaoImpl extends JdbcDaoSupport代替手动生成JdbcTemplate 对象
在JdbcDaoSupport里,会根据连接池帮忙创建JdbcTemplate 对象
因此上述代码可改成:
public class UserDaoImpl extends JdbcDaoSupport implements UserDao{
@Override
public void save(User u) {
String sql = "insert into user values(null,?,?) ";
super.getJdbcTemplate().update(sql, u.getName(),u.getPsw());
}
<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="jdbcUrl" value="jdbc:mysql:///springtest" ></property>
<property name="driverClass" value="com.mysql.jdbc.Driver" ></property>
<property name="user" value="root" ></property>
<property name="password" value="root" ></property>
</bean>
<!-- 2.将UserDao放入spring容器 -->
<bean name="userDao" class="com.xdh.dao.UserDaoImpl" >
<property name="dataSource" ref="dataSource" ></property>
</bean>
扩展2:
读取外部文件配置datasoure
<!-- 指定spring读取db.properties配置 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
<property name="driverClass" value="${jdbc.driverClass}" ></property>
<property name="user" value="${jdbc.user}" ></property>
<property name="password" value="${jdbc.password}" ></property>
</bean>
2.spring中的aop事务
(1)事务操作对象
因为在不同平台,操作事务的代码各不相同,因此spring提供了一个接口:PlatformTransactionManager
各平台自己提供实现类,例如:
JDBC:DataSourceTransactionManager
Hibernate:HibernateTransactionManager
spring中的事务管理,最核心的对象就是TransactionManager
(2)spring管理事务的属性
1)事务的隔离级别
- 读未提交:1
- 读已提交:2
- 可重复读:4
- 串行化:8
2)是否只读
- 只读:true
- 可操作:false
3)事务的传播行为
即页面方法之间调用,事务应该如何处理
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。(默认,且最常用)
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
(3)spring管理事务方式
1)编码式(在方法里写事务,不常用)
步骤:
- 将核心事务管理器DataSourceTransactionManager配置到spring容器
<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" ></property>
</bean>
- 配置事务模板对象TransactionTemplate
<!-- 事务模板对象 -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
<property name="transactionManager" ref="transactionManager" ></property>
</bean>
- 将事务模板注入到service
<bean name="accountService" class="com.xdh.service.AccountServiceImpl" >
<property name="ad" ref="accountDao" ></property>
<property name="tt" ref="transactionTemplate" ></property>
</bean>
- 在service中调用模板
package com.xdh.service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.xdh.dao.AccountDao;
public class AccountServiceImpl implements AccountService {
private AccountDao ad ;
private TransactionTemplate tt;
@Override
public void transfer(final Integer from,final Integer to,final Double money) {
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
//减钱
ad.decreaseMoney(from, money);
int i = 1/0;
//加钱
ad.increaseMoney(to, money);
}
});
}
public void setAd(AccountDao ad) {
this.ad = ad;
}
public void setTt(TransactionTemplate tt) {
this.tt = tt;
}
}
2)xml配置(aop)
aop工作流程:准备目标对象,准备通知,将通知织入到目标对象中
这里目标对象AccountServiceImpl,事务通知由spring准备,配置进行织入
步骤:
- 导包(即aop需要的包:aop、aspect、aop联盟、weaving织入包)
- 导入tx命名空间
- 配置通知
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<!-- 以方法为单位,指定方法应用什么事务属性
isolation:隔离级别
propagation:传播行为
read-only:是否只读
-->
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
</tx:attributes>
</tx:advice>
实际开发中用通配符:
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
<tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
<tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
<tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
<tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
<tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
<tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
- 配置将通知织入目标对象
<!-- 配置织入 -->
<aop:config >
<!-- 配置切点表达式 -->
<aop:pointcut expression="execution(* com.xdh.service.*ServiceImpl.*(..))" id="txPc"/>
<!-- 配置切面 : 通知+切点
advice-ref:通知的名称
pointcut-ref:切点的名称
-->
<!-- aop:aspect手动配置通知方法 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" />
</aop:config>
3)注解配置(aop)
步骤:导包、约束和xml配置一样,其他步骤:
- 开启注解管理事务
<!-- 开启使用注解管理aop事务 -->
<tx:annotation-driven/>
- 使用注解
1)加在方法上
@Override
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
public void transfer(final Integer from,final Integer to,final Double money) {
//减钱
ad.decreaseMoney(from, money);
int i = 1/0;
//加钱
ad.increaseMoney(to, money);
}
2)加在类上
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
public class AccountServiceImpl implements AccountService {
}