spring
Spring基础
spring由,IOC控制反转,创建对象给Spring管理。AOP面向切面,不修改源代码进行功能的增强
特点
-
方便解耦度
-
Aop编程支持
-
方便测试
-
方便和其他框架整合
-
方便进行事务的操作
-
降低API的开发难度
IOC容器
底层原理
把对象创建和对象调用的过程交给Sring管理,IOC降低耦合度,底层原理(控制/反转)XML,工厂模式,反射
ioc过程
1.有xml配置文件,配置创建对象
<bean id = "对象名称" class = "对象文件地址"></bean> <bean id="User" class="Spring5.text.text1"/>
2.有service和dao类创建工厂类
class UserFactory{ public static Userdao getDao(){ String class value = class.属性值; //xml解析 //通过反射创建对象 Class class = Class.forName(Class.Value); return (UserDao)class.newInstance(); //强转得到 } }
IOC接口(容器)
思想基于IOC容器,IOC本质是对象工厂,Spring提供IOC的两种方式实现
(1)BeanFactory,IOC基本实现Spring内部使用接口,不提供开发人员使用
(2)ApplicationContext,BeanFactory的子接口提供更强大的接 口
区别:
BeanFactory加载时不会创建对象在获取时/使用时才创建对象,BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
ApplicationContext加载文件时就和把配置文件对象进行创建,ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能
(3)ApplicationContext接口实现类,FileSystem电脑盘加载,ClassPath编译src下文件
如何实现Ioc容器
-
配置文件中指定需要扫描的包的路径
-
定义注解,分别表示访问控制层,业务服务层,数据持久层,依赖注入注解,获取配置文件注解
-
从配置文件中获取需要扫描的包路径,获取到当前路径下文件信息及文件夹信息,我们将当前路径下所有以。class结尾文件添加到一个set集合中进行存储
-
变量这个set集合获取类上指定注解的类,并交给ioc容器,定义一个安全的map集合来存储这些对象
-
遍历这个ioc容器。获取到每一个类的实例,判断里面是否有依赖其他类的实例,然后进行递归注入
IOC操作bean管理(两个操作)
(1)spring创建对象(2)Spring注入属性
进行bean管理操作
(1)基于xml配置文件属性
(2)基于注解方式实现
-
基于xml方式。在标签里可以加响应属性bean有很多属性,id唯一标识,class类名路径,创建对象默认是无参构造方法,完成对象的创建
-
基于xml方式注入属性。DI依赖注入,注入属性,DI是IOC的一种实现,DI是IOC的具体实现
-
使用set方法注入,创建类,属性,对应的set方法,在Spring配置文件中进行配置对象,注入shuxing
-
使用有参数构造方法注入,创建类,对应属性,创建属性对应的有参构造方法,在Spring文件中进行配置
-
P名称空间注入,使用p名称空间注入,简化xml配置方式,添加p名称空间配置文件,进行属性注入在bean标签里
Spring的IOC有三种注入方式 :构造器注入、set方法注入、根据注解注入。
set方法注入
public class Book { private String bname; private String bauthor; public void setBname(String bname) { this.bname = bname; } public void setBauthor(String bauthor) { this.bauthor = bauthor; } }
(2)在spring文件中配置对象,配置属性注入
<!--set注入属性--> <bean id="book" class="com.Spring5.Book"> <!--使用property属性完成属性注入 name:类里面属性名称 value:向属性注入值 --> <property name="bname" value="易筋经"></property> <property name="bauthor" value="达摩老祖"></property> </bean>
(3)测试文件
@Test public void TextBook1(){ //加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //获取配置创建对象 Book book = context.getBean("book", Book.class); System.out.println(book); book.textDemo(); }
构造器注入
创建类,定义属性,创建属性对应有参数构造方法
public class Orders { private String oname; private String address; //有参数构造 public Orders(String oname, String address) { this.oname = oname; this.address = address; } }
<!--有参构造--> <bean id="orders" class="com.Spring5.Orders"> <constructor-arg name="oname" value="电脑"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
测试类
@Test public void TextOrders1(){ //加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //获取配置创建对象 Orders Orders = context.getBean("orders", Orders.class); System.out.println(Orders); Orders.OrdersText(); }
xml注入其他类型属性
-
字面量,设置属性的固定值
(1)null值
<property name="address"> <null></null> </property>
(2)属性值包含特殊符号
吧<>进行转义<,>
吧带特殊符号内容写到CDATA
<!--属性值中包含特殊符号--> <property name="bauthor"> <value> <![CDATA[<<南京>>]]> </value> </property>
外部bean
(1)创建两个类Service和Dao
(2)在sevice中调用Dao里的方法
(3)在spring文件中进行配置
public class UserService { //创建UserDao属性,生成set方法 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add(){ System.out.println("service>>>>>>>>>>>>"); userDao.update(); } } ------------------------------------------------------------------------------------------------ <!--service对象创建和Dao对象--> <bean id="userService" class="com.Spring5.service.UserService"> <!--注入UserDao对象 name属性值,类里面属性名称 ref 创建userDao对象bean标签id值--> <property name="userDao" ref="useDao"></property> </bean> <bean id="useDao" class="com.Spring5.dao.UserDaoImpl"></bean>
内部bean和级联赋值
(1)一对多,部门跟员工
(2)实体类表示一对多,员工表示所属部门,使用类对象进行表示
(3)在spring进行配置
//员工类 public class Emp { private String ename; private String gender; private Dept dept; public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } public void add(){ System.out.println(ename + "::" + gender + "::" + dept); } } -------------------------------------------------------------- //部门类 public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } @Override public String toString() { return "Dept{" + "dname='" + dname + '\'' + '}'; } } ----------------------------------------------------------------- <!--内部bean--> <bean id = "emp" class="com.Spring5.bean.Emp"> <!--设置普通属性类型--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--设置对象属性类型--> <property name="dept"> <bean name="dname" class="com.Spring5.bean.Dept"> <property name="dname" value="a保安部"></property> </bean> </property> </bean>
注入属性-级联赋值
<!--级联赋值--> <bean id = "emp" class="com.Spring5.bean.Emp"> <!--设置普通属性类型--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.Spring5.bean.Dept"> <property name="dname" value="财务部"></property> </bean> ------------------------------------------------ <bean id = "emp" class="com.Spring5.bean.Emp"> <!--级联赋值--> <property name="dept" ref="dept"></property> <property name="dept.dname" value="人事部"></property> </bean> <bean id="dept" class="com.Spring5.bean.Dept"> <property name="dname" value="财务部"></property> </bean>
xml属性注入
public class Stu { //数组类型 private String[] courses; //List集合类型 private List<String> list; //map集合类型 private Map<String,String> map; //set集合类型 private Set<String> set; public void setCourses(String[] courses) { this.courses = courses; } public void setList(List<String> list) { this.list = list; } public void setMap(Map<String, String> map) { this.map = map; } public void setSet(Set<String> set) { this.set = set; } } ------------------------------------------------------------------- <!--集合类型属性注入--> <bean id="stu" class="com.Spring5.stu.Stu"> <!--数组类型属性注入--> <property name="courses"> <array> <value> java </value> <value> 数据库 </value> </array> </property> <!--List集合类型属性注入--> <property name="list"> <array> <value>z展现</value> </array> </property> <!--map集合类型属性注入--> <property name="map"> <map> <entry key="JAVA" value="java"></entry> <entry key="PHP" value="php"></entry> </map> </property> <!--set集合类型属性注入--> <property name="set"> <set> <value>mySql</value> <value>Redis</value> </set> </property> </bean>
集合设置对象类型值
<bean id="stu" class="com.Spring5.stu.Stu"> <!--注入list集合类型,值是对象--> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> </bean> <bean id="course1" class="com.Spring5.stu.Course"> <property name="cname" value="Spring5框架"></property> </bean> <bean id="course2" class="com.Spring5.stu.Course"> <property name="cname" value="MyBatista框架"></property> </bean> ------------------------------------------------------------------- private List<Course> courseList; public void setCourseList(List<Course> courseList) { this.courseList = courseList; } ----------------------------------------------------- public class Course { private String cname; public void setCname(String cname) { this.cname = cname; } }
集合注入提取出来
<!--提取List集合类型注入提取--> <util:list id="bookList"> <value> 易筋经</value> <value> 九阴真经</value> <value> 九阳神功</value> </util:list> <!--提取list集合进行注入--> <bean id="book" class="com.Spring5.stu.Book"> <property name="list" ref="bookList"></property> </bean> --------------------------------------------------- public class Book { private List<String> list; public void setList(List<String> list) { this.list = list; } public void text() { System.out.println(list); } }
IOC操作bean管理
Spring有两种bea,普通bean和工厂bean。普通bean,定义什么类型返回什么类型。工厂Bean,定义类型和返回类型不同
bean的作用域
在Spring中设置bean是单实例还是多实例,默认bean是单实例,spring中有默认属性scope用于设置单实例/多实例
scope属性值有singleton表示单实例,prototype表示多实例,
设置singleton时加载Spring文件时对象就会被创建,设置Prototype时在调用getBean时创建对象,加载Spring文件时不会创建对象
bean的生命周期
-
通过构造器创建bean实例(无参构造)
-
为bean的属性值和对其他bean引用(调用set)
-
把bean实例传递到bean后置处理器方法postProcessBeforeInitialization
-
调用bean的初始化方法
-
把bean实例传递到bean后置处理器方法postProcessAfterInitialization
-
bean可以使用(对象获取到)
-
当容器关闭时,调用bean的销毁(需要配置方法)
@Test public void test2(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("创建bean实例"); //手动调用销毁 context.close(); } ----------------------------------------------------- public class Orders { public Orders() { System.out.println("执行无参构造方法创建bean实例"); } private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("调用set方法设置属性值"); } //创建执行初始化方法 public void initMethod(){ System.out.println("执行初始化方法"); } //创建执行销毁化方法 public void destroyMethod(){ System.out.println("销毁"); } } --------------------------------------------------------------- <bean id="orders" class="com.Spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean> --------------------------------------------------------------- //配置后置处理器 public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("初始化之前执行"); return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("初始化之后执行"); return null; } }
xml自动装配
根据指定的装配规则(属性名称,属性)Spring自动匹配属性注入
bean标签内有属性autowire,有两个常用的属性byName/byType,byName,bean内的id和属性名称一样,byType根据属性类型注入
<!--实现自动装配 bean标签属性autowire配置自动装配 byName,bean内的id和属性名称一样, byType根据属性类型注入--> <bean id="emp" class="com.Spring5.autowire.Emp" autowire="byName"> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.Spring5.autowire.Dept"></bean> ------------------------------------------------------- public class Emp { private Dept dept; public void setDept(Dept dept) { this.dept = dept; } @Override public String toString() { return "Emp{" + "dept=" + dept + '}'; } public void text() { System.out.println(dept); } } ------------------------------------------------- public class Dept { @Override public String toString() { return "Dept{}"; } }
外部属性
(1)配置数据库信息
<!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassLoader" value="com.mysql.jdbc.Driver"></property><!--驱动名称--> <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean>
(2)引入外部属性文件
prop.driverClass = com.mysql.jdbc.Driver prop.url = jdbc:mysql://localhost:3306/userDb prop.userName = root prop.password = root
把外部properties属性文件引入到spring文件中
引入contest
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd“ <!--引入外部文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassLoader" value="${prop.driverClass}"></property><!--驱动名称--> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean>
基于注解操作bean
1.什么是注解
-
是代码的特殊标记,格式 @注解名称( 属性名称 = 属性值 ,属性名称 = 属性值)
-
使用注解,可以作用在类,属性,方法上
-
使用注解目的,简化xml配置
2.bean管理创建对象提供注解
-
@Compoent,普通组件
-
@Service,一般用于业务逻辑层和service层
-
@Controller,一般用于web层
-
@Repository,用于持久层
-
四个功能都一样,都可以创建bean
3.基于注解实现对象创建
<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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--1.开启组件扫描 扫描多个包,多个包用逗号隔开 扫描上层目录 --> <context:component-scan base-package="com.Spring5"></context:component-scan> ------------------------------------------------------------------------------------------------------- //注解里面value可以不写默认名称为类名称首字母小写 @Component(value = "userService") public class UserService { public void add(){ System.out.println("service dao"); } } --------------------------------------------------------------------- //测试 @Test public void text01(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
(4)细节问题开启组件扫描
<!--示例1--> <context:component-scan base-package="com.Spring5" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
use-default-filters="false"表示不使用默认的filters,使用自己的配置的filter
context:include-filter 扫描设置那些内容,在注解里只扫描带这个注解的类
<!--示例2--> <context:component-scan base-package="com.Spring5"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
context:exclude-filter 意思是除了这个注解不扫描其他注解都扫描
(5)基于注解实现属性注入
-
@AoutWired 根据属性类型注入,自动装配
-
@Qualifier 根据属性名称进行注入,使用得和@AoutWired一起配合使用
-
@Resource 可以根据类型也可以根据属性名称注入
-
@Value 注入普通类型属性
吧service和dao对象创建在Service和Dao中添加对象注入
service注入dao,service添加dao属性,属性上使用注解
@Service public class UserService { /** * 定义dao属性 * 不需要添加set方法 * 添加注入属性注解 */ @Autowired private UserDao userDao; public void add(){ System.out.println("service dao"); userDao.add(); } } -------------------------------------------------- @Repository public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("dao add................."); } }
(6)完全注解开发
创建配置类
@Configuration @ComponentScan(basePackages = {"com.Spring5"}) public class SpringConfig { }
测试方法
@Test public void text02(){ ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
Aop概念
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。
不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,
而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用、
底层原理
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,创建接口实现类的代理对象增强类的方法
CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。创建当前的子类的代理对象,增强类的功能
JDK动态代理
-
使用jdk动态代理 , 使用Proxy类的方法可以实现动态代理
-
调用newProxyInstance方法
static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
-
方法有三个参数,第一个类加载器,第二个增强方法所在的类,这个类实现接口可以是多个,第三个这个接口InvocationHandler,创建代理对象写增强的部分
代码
创建接口,定义方法。创建接口实现类,使用Proxy创建接口代理对象
public class JDKProxy { public static void main(String[] args) { Class[] interfaces = {UserDao.class}; UserDaoImpl userDao = new UserDaoImpl(); //强转 UserDao dao =(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int result = dao.add(1, 2); System.out.println(result); } } class UserDaoProxy implements InvocationHandler{ //把创建是谁的代理对象,吧谁传递过来,多种方法 //有参构造 private Object obj; public UserDaoProxy(Object obj){ this.obj = obj; } //增强逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法之前执行" + method.getName() + "传递的参数。。" + Arrays.toString(args)); //被增强方法执行 Object res = method.invoke(obj , args); //方法之后 System.out.println("方法之后执行那个" + obj); return res; } } ----------------------------------------------------------------------- public class JDKProxy { public static void main(String[] args) { Class[] interfaces = {UserDao.class}; //匿名内部类 Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }); } public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a + b; } @Override public String update(String id) { return id; } } public interface UserDao { public int add( int a,int b); public String update(String id); }
Aop术语
-
连接点,类中的方法可以增强,这些方法称为连接点
-
切入点,实际被增强的方法,称为切入点
-
通知,实际增强逻辑部分称为通知(增强),通知有多种类型
前置通知 ,环绕通知 , 异常通知 ,后置通知 ,最终通知
-
切面(动作),吧通知应用到切入点的过程
Aop操作
-
Spring一般基于AspectJ实现Aop
AspectJ不是Spring部分,独立的Aop框架,吧AspectJ和Spring一起使用,进行Aop操作
-
基于AspectJ实现Aop操作
(1)基于Xml实现
(2)基于注解方式实现
-
引入相关jar包
-
切入点表达式
(1)切入点表达式作用,哪个类,那个方法进行增强
(2)语法结构execution([权限控制符] [返回类型] [类全路径] [方法名称] [参数列表])
对类增强
execution(* com.Spring5.aopanno.User.add(..))
所有方法增强
execution(* com.Spring5.aopanno.User.* (..))
AspectJ注解
-
创建类,在类中定义方法
-
创建增强类(编写增强逻辑)
(1)在增强类里创建方法,让不同的方法代表不同的通知
-
进行通知的配置
(1)在Spring文件中开启注解扫描
(2)使用注解创建User和Userproxy对象
(3)在增强类中添加注解@Aspect
(4)在Spring配置文件中开启生成代理对象
-
配置不同类型通知
(1)在增强类中,作为通知方法,上面添加通知类型注解,使用切入点表达式配置
-
相同切入点的抽取
-
多个增强类型,多个方法同时进行增强,设置增强优先级
(1)在增强类上添加注解@order(数字类型值),数字越小优先级越高。
-
完全使用注解开发
(1)创建配置类,不需要xml配置文件
@Configuration @ComponentScan(basePackages = {"com.Spring5"}) @EnableAspectJAutoProxy(proxyTargetClass = true)
@Component @Aspect //生成代理对象 //增强类 public class UserProxy { //相同切入点抽取 @Pointcut(value = "execution(* com.Spring5.aopanno.User.add(..))") public void PointDemo(){ } //前置通知 //Before表示作为前置通知 @Before(value = "PointDemo()") public void before1() { System.out.println("before1................"); } //前置通知 //Before表示作为前置通知 @Before(value = "execution(* com.Spring5.aopanno.User.add(..))") public void before() { System.out.println("before................"); } //最终通知 @AfterReturning(value = "execution(* com.Spring5.aopanno.User.add(..))") public void AfterReturning() { System.out.println("AfterReturning................"); } //后置通知 @After(value = "execution(* com.Spring5.aopanno.User.add(..))") public void after() { System.out.println("after........................"); } //异常通知 @AfterThrowing(value = "execution(* com.Spring5.aopanno.User.add(..))") public void AfterThrowing() { System.out.println("AfterThrowing........................"); } //环绕通知 @Around(value = "execution(* com.Spring5.aopanno.User.add(..))") public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("Around......................."); //被增强的方法 proceedingJoinPoint.proceed(); System.out.println("Around......................."); } } --------------------------------------------------------- <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" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.Spring5.aopanno"></context:component-scan> <!--开启AspectJ生成代理对象--> <aop:aspectj-autoproxy> </aop:aspectj-autoproxy> ------------------------------------------ @Component public class User { public void add(){ System.out.println("add................"); } }
配置文件
(1)创建两个类,增强类和被增强类
(2)在spring配置文件中创建两个;类对象
(3)在Spring配置文件中配置切入点
<!--创建对象--> <bean id="book" class="com.Spring5.AopXml.Book"></bean> <bean id="bookProxy" class="com.Spring5.AopXml.BookProxy"></bean> <!--配置Aop增强--> <aop:config> <!-- 切入点--> <aop:pointcut id="p" expression="execution(* com.Spring5.AopXml.Book.buy(..))"/> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增强作用在具体的方法上--> <aop:before method="Before" pointcut-ref="p"/> </aop:aspect> </aop:config>
JDBCTemplate
1.什么是jdbcTemplate
(1)Spring对JDBC进行封装,使用jdbcTemplate方便进行数据库操作
Spring的事务管理
通过aop管理实务,根据aop通过实现TransactionInterceptor调用invoke实现具体逻辑
1.先做准备工作,根据注解判断哪些事物需要执行
2.关闭自动提交,获取连接,开启事务
3.执行sql逻辑
4.若执行失败,获取连接对象,调用rollback方法回滚成功则commit提交
5.事务执行完成后清除相关事务信息
1.什么是事务
(1)事务是数据库操作最基本单元,逻辑上的一组操作,要么都成功要么都失败
事务的四个特性(ACID)
-
原子性
-
一致性
-
隔离性
-
持久性
事务环境搭建
-
创建数据库
-
创建service 搭建 dao 完成对象创建和注入关系
(1)service注入dao在dao中注入JDBCtemplate,在JDBC中注入DataSource
(2)xml配置
组件扫描
数据库连接池
jdbcTempalte对象,注入DataSource
-
在dao创建两个方法,多钱和少钱,在Service创建方法(转账方法)
-
正常执行无问题,若出现问题或异常则有问题
解决上面问题用事务解决
事务操作过程
1.开启事务 2.进行事务操作 3.没有发生异常 4.出现异常事务回滚
spring事务管理介绍
事务一般加到service层
在Spring中进行事务管理,有两种,编程式管理和声明式事务管理
声明式事务管理(1)基于注解方式(2)基于xml配置文件方式
Spring进行声明式事务管理,底层使用Aop原理
Spring事务管理API,提供了一个接口,代表事务管理针对不同的框架,实现不同的实现类
注解声明式事务管理
-
在spring配置文件事务管理器
-
在spring配置文件。开启事务注解
(1)在spring配置文件中引入名称空间tx
-
在service类上获得Service类上添加事务注解
(1)Transactional,可以添加到类上,也可以添加在方法上面
(2)如果吧这个注解添加到类上,这个类里所有方法都添加事务
(3)如果添加到方法上则为这个方法添加事务
<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" xmlns:aop="http://www.springframework.org/schema/aop" 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/context http://www.springframework.org/schema/context/spring-context.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"> <!--组件扫描--> <context:component-scan base-package="com.Spring5"/> <!--数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="jdbc:mysql:///user_db"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> </bean> <!--JDBCTemplate对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入DataSource--> <property name="dataSource" ref="dataSource"/> </bean> <!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"/> </bean> <!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans> ------------------------------------------------------------------------------------------------------- @Service @Transactional public class UserService { //注入dao private final UserDao userDao; public UserService(UserDao userDao) { this.userDao = userDao; } //转账方法 public void accountMoney(){ userDao.reduceMoney(); //模拟异常 int i = 10 / 0; userDao.addMoney(); } } ---------------------------------------------------------------------------------- @Repository public class UserDaoImpl implements UserDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public void addMoney() { String sql = "update t_account set money = money - ? where username = ?"; jdbcTemplate.update(sql,100,"lucy"); } @Override public void reduceMoney() { String sql = "update t_account set money = money + ? where username = ?"; jdbcTemplate.update(sql,100,"mary"); } }
声明式事务管理参数配置
-
service类上添加注解@Transactional,这注解可以配置事务相关参数
-
事务传播属性propagation
(1)多事务方法直接进行调用,这个过程中这个事务是如何进行管理的
事务传播行为,事务方法:对数据库表数据进行操作
spring事务传播行为有7种
REQURED 如果add方法本身有事务,调用update方法后,updata使用add的事务,若add方法,没有事务,调用update后创建新的事务 REQURED_NEW 使用add调用update方法,无论add是否有事务,都创建新的事务 SUPPORTS 如果有事务运行当前方法就在事务内运行,否则可以不运行在事务中 NOT_SUPPORTS 当前方法不运行在事务中,若有事务正在执行,则方法挂起 MANDATORY 当前事务必须执行在事务中,若没有事务正在运行,则抛出异常 NEVER 当前方法不应该运行在事务中,如果有正在运行的事务,抛出异常 NESTED 如果有事务在运当前方法,当前的方法就应该在事务的嵌套事务运行,否则就启动一个新的事务,并在自己的事务内运行。 @Transactional( propagation = Propagation.REQURED)
-
事务隔离级别ioslation
事务特性成为隔离性,多事务之间不会产生影响,不考虑隔离性,会产生很多问题
有三个读的问题 脏读,不可重复读,幻读
(1)脏读,一个未提交事务读取到另一个未提交事务的数据
(2)不可重复读,一个未提交事务读取到另一个提交事务,修改数据
(3)虚(幻)读,一个未提交事务读取到另一提交事务添加数据
设置事务隔离性解决读的问题
脏读 不可重复读 虚(幻)读 已读未提交 READ UNCOMMITTED × × × 未读已提交 READ COMMITTED √ × × 可重复读 REPEATABLE READ √ √ × 串行化 SERIALIZABLE √ √ √ -
超时时间timeOut
事务在一定时间内进行提交,如果不提交则进行回滚
默认是-1,以时间秒为单位进行计算
-
是否只读
(1)读查操作,写修改数据删除操作
(2)readOnly默认为false,可查询,可修改
(3)设置true,只能查询
-
回滚rollbackFor
设置事务回滚流
-
不回滚norollbackFor
设置出现那些异常不进行事务回滚