赞
踩
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,DL(依赖查找)也是实现IOC的一种方法;
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
Spring容器是一个超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。Spring 容器管理着容器中Bean之间的依赖关系,Spring 使用**DI(依赖注入)**的方式来管理Bean之间的依赖关系。使用loC实现对象之间的解耦合。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
实现步骤:
1.创建maven项目
2.加入maven的依赖
spring的依赖
junit依赖
lombok依赖
3.创建Student类,和没有使用框架一样,就是普通的类。
4.创建spring需要使用的配置文件
声明类的信息。这些类由spring创建和管理
5.测试spring创建的对象。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--告诉spring创建对象 声明bean,告诉spring要创建某个类的对象 id是对象的自定义名称(唯一值)。spring通过这个名称找到对象 class:类的全限定名称(不能是接口,因为spring是反射机制) spring就完成Student student = new Student(); spring是把创建好的对象放到了map中,spring框架中有一个map存放对象的。 springMap.put(id的值,对象) 例如 springMap.put("student",new Student()); 一个bean标签只声明一个对象 --> <bean id="student" class="com.test.dyh.Student"> <property name="id" value="1"/> <property name="age" value="12"/> <property name="name" value="段玉"/> </bean> </beans> <!--spring的配置文件 1.beans是根标签,spring中把Java对象成为bean 2.spring-beans.xsd是约束文件,和mybatis指定的类似 -->
package com.test.dyh; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.text.MessageFormat; public class AppTest { /*spring默认创建对象的时间:在创建spring的容器时,他会创建配置文件中的所有对象*/ @Test public void shouldAnswerWithTrue() { //使用spring容器创建的对象 //1.指定spring配置文件的名称 String config="bean.xml"; //2.创建表示spring容器的对象,ApplicationContext //ClassPathXmlApplicationContext;表示从类路径中加载spring的配置文件 ApplicationContext st = new ClassPathXmlApplicationContext(config); //3.从容器中获取某个对象,你要调用对象的方法, //getBean("配置文件中的bena的id值"); Student student = (Student) st.getBean("student"); //使用spring创建好的对象 System.err.println(MessageFormat.format("id={0}, age={1}, name={2}", student.getId(), student.getAge(), student.getName())); //输出:id=1, age=12, name=段玉 } }
使用无参构造创建对象,默认!
假设我们要使用有参构造创建对象。
<bean id="student" class="com.test.dyh.Student">
<constructor-arg index="0" value="1"></constructor-arg>
<constructor-arg index="1" value="14"></constructor-arg>
<constructor-arg index="2" value="name"></constructor-arg>
</bean>
<bean id="student" class="com.test.dyh.Student">
<constructor-arg type="int" value="1"></constructor-arg>
<constructor-arg type="java.lang.Integer" value="14"></constructor-arg>
<constructor-arg type="java.lang.String" value="段"></constructor-arg>
</bean>
<bean id="student" class="com.test.dyh.Student">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="age" value="14"></constructor-arg>
<constructor-arg name="name" value="段"></constructor-arg>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="student" alias="student2"></alias>
<!--bean就是java对象,由Spring创建和管理-->
<!--
id : bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class : bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个;
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
<import resource="{path}/beans.xml"/>
依赖注入:Set注入
2个实体类
package com.test.dyh; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * @author dyh * @date 2021/6/1 - 16:41 * @description: */ @Data @AllArgsConstructor @NoArgsConstructor public class Student { private int id; private Integer age; private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String, String> card; private Set<String> games; private String wife; private Properties info; }
package com.test.dyh; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author dyh * @date 2021/6/1 - 17:52 * @description: */ @Data @AllArgsConstructor @NoArgsConstructor public class Address { private String address; }
配置 applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="address" class="com.test.dyh.Address"> <property name="address" value="广东深圳"></property> </bean> <!--声明student对象 注入:就是赋值的意思 简单类型:spring中规定Java的基本数据类型和string类型都是简单类型。 di:给属性赋值 1.set注入(设置注入):spring调用类的set方法,你可以在set方法中完成属性赋值 利用property --> <bean id="student" class="com.test.dyh.Student"> <!--第一种,普通值注入,value--> <property name="id" value="1"></property> <property name="age" value="14"></property>认准set方法 <property name="name" value="断浪"></property><!--调用setName...--> <!--第二种,Bean注入--> <property name="address" ref="address"></property> <!--数组--> <property name="books"> <array> <value>水浒传</value> <value>西游记</value> </array> </property> <!--List--> <property name="hobbys"> <list> <value>music</value> <value>swimming</value> <value>coding</value> </list> </property> <!--Map--> <property name="card"> <map> <entry key="身份证" value="12312121212"/> <entry key="银行卡" value="678112121111000"/> </map> </property> <!--Set--> <property name="games"> <set> <value>CF</value> <value>LOL</value> <value>GTA</value> </set> </property> <!--null--> <property name="wife"> <null/> </property> <!--Properties--> <property name="info"> <props> <prop key="学号">20190526</prop> <prop key="username">root</prop> <prop key="password">root</prop> </props> </property> </bean> </beans>
测试
package com.test.dyh; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AppTest { /*spring默认创建对象的时间:在创建spring的容器时,他会创建配置文件中的所有对象*/ @Test public void shouldAnswerWithTrue() { //使用spring容器创建的对象 //1.指定spring配置文件的名称 String config = "bean.xml"; //2.创建表示spring容器的对象,ApplicationContext //ClassPathXmlApplicationContext;表示从类路径中加载spring的配置文件 ApplicationContext st = new ClassPathXmlApplicationContext(config); //3.从容器中获取某个对象,你要调用对象的方法, //getBean("配置文件中的bena的id值"); Student student = (Student) st.getBean("student"); //使用spring创建好的对象 System.err.println(student); //输出:Student(id=1, age=14, name=断浪, address=Address(address=广东深圳), // books=[水浒传, 西游记], hobbys=[music, swimming, coding], // card={身份证=12312121212, 银行卡=678112121111000}, games=[CF, LOL, GTA], // wife=null, info={password=root, 学号=20190526, username=root}) } }
我们可以使用p命名空间和c命名空间进行注入
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="student" class="com.test.dyh.Student" p:name="张三" p:age="18"/> <!--c命名空间注入,通过构造器注入:construt-args--> <bean id="student2" class="com.test.dyh.Student" c:name="李四" c:age="11"/> </beans>
注意点:p命名和c命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
通过注解完成Java对象的创建,属性赋值
加入maven的依赖spring-context,在你加入spring-context的同时,间接加入spring-aop的依赖。
使用注解必须使用spring-aop依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.14.RELEASE</version>
</dependency>
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--声明组件扫描器(component-scan), 组件就是Java对象,所以就是找Java对象 base-package:指定注解在你的项目中的包名 component-scan工作方式:spring会扫描遍历base-package指定的包 把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象和给属性赋值。 --> <context:component-scan base-package="com.test.dyh"></context:component-scan> </beans>
1.@Component 2.@Repository 3.@Service 4.@Controller 5.@Value 6.@Autowired 7.@Resource
package com.test.dyh; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * @author dyh * @date 2021/6/1 - 16:41 * @description: */ @Data @AllArgsConstructor @NoArgsConstructor /* * @Component:创建对象的,等同于<bean>的功能 * 属性value: 就是对象的名称,也就是bean的id值,value值是唯一的,创建的对象在整个spring容器中就一个 * 位置:在类的上面写注解 * */ //等同于<bean id="myStudent" class="com.test.dyh.Student"/> //@Component(value = "myStudent") //省略value //@Component("myStudent") //不指定对象名称,由spring提供默认名称(首字母小写类名) @Component public class Student { private int id; private Integer age; //相当于 <property name="name" value="凯斯"></property> @Value("凯斯") private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String, String> card; private Set<String> games; private String wife; private Properties info; }
测试
package com.test.dyh; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AppTest { @Test public void shouldAnswerWithTrue() { ApplicationContext ac=new ClassPathXmlApplicationContext ("bean.xml"); //从容器中获取对象 Student student=(Student) ac.getBean ("student"); System.out.println (student.getName()); //输出:Student(id=0, age=null, name=凯斯, //address=null, books=null, hobbys=null, //card=null, games=null, wife=null, info=null) } }
* spring中和@Component功能一致,创建对象的注解还有:
* @Repository(用在持久层上):放在dao的实现类上面,
* 表示创建dao对象,dao对象是能访问数据库的。(持久层注解)
*
* @Service(用在业务层类的上面):放在service的实现类上面,
* 创建service对象,service对象是做业务处理的,可以有事物等功能的。
*
* @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,
* 控制器对象可以接收用户提交的参数和显示请求的处理结果。
*
* 以上三个注解的使用语法和@Component是一样的,都能够创建对象,但是这三个注解还有额外的功能----给项目分层
使用多次组件扫描器标签,指定不同的包
使用分隔符(分号或者逗号)分隔多个包名
指定父包
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="address" class="com.test.dyh.Address"> <property name="address" value="深圳"></property> </bean> <!-- byName(按名称注入): 会自动在容器上下文中查找,和自己对象set方法参数名对应的bean_id 且数据类型一致,这样的容器中的bean,spring能够自动注入 byType(按类型注入) : 会自动在容器上下文中查找,和自己对象属性类型相同的bean --> <bean id="student" class="com.test.dyh.Student" autowire="byName"> <property name="id" value="1"></property> <property name="age" value="14"></property> </bean> </beans>
注意
byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
package com.test.dyh; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AppTest { /*spring默认创建对象的时间:在创建spring的容器时,他会创建配置文件中的所有对象*/ @Test public void shouldAnswerWithTrue() { //使用spring容器创建的对象 //1.指定spring配置文件的名称 String config = "bean.xml"; //2.创建表示spring容器的对象,ApplicationContext //ClassPathXmlApplicationContext;表示从类路径中加载spring的配置文件 ApplicationContext st = new ClassPathXmlApplicationContext(config); //3.从容器中获取某个对象,你要调用对象的方法, //getBean("配置文件中的bena的id值"); Student student = (Student) st.getBean("student"); //使用spring创建好的对象 System.err.println(student.getAddress().getAddress()); //输出:深圳 } }
要使用注解须知:
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 比较少用的扫描标签,它只会扫描属性(即变量) <context:annotation-config></context:annotation-config> 仅作了解即可。 --> <context:annotation-config></context:annotation-config> <bean id="address" class="com.test.dyh.Address"> <property name="address" value="深圳"></property> </bean> <bean id="student" class="com.test.dyh.Student"></bean> </beans>
package com.test.dyh; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; /** * @author dyh * @date 2021/6/1 - 16:41 * @description: */ @Data @AllArgsConstructor @NoArgsConstructor public class Student { private int id; private Integer age; private String name; //如果显示定义了Autowired的required属性为false,说明这个对象可以为Null,否则不允许为空 //@Autowired(required = false) //如果@Autowired自动装配的环境比较复杂, //自动装配无法通过一个注解【@Autowired】完成的时候, //我们可以使用==@Qualifier(value = “xxx”)==去 //配合@Autowire的使用,指定一个唯一的bean对象注入! //@Autowired //@Qualifier(value = "address") @Resource( name = "address") private Address address; private String[] books; private List<String> hobbys; private Map<String, String> card; private Set<String> games; private String wife; private Properties info; }
package com.test.dyh; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AppTest { @Test public void shouldAnswerWithTrue() { ApplicationContext ac=new ClassPathXmlApplicationContext ("bean.xml"); //从容器中获取对象 Student student=(Student) ac.getBean ("student"); System.out.println (student.getAddress().getAddress()); //输出:深圳 } }
@Resource和@Autowired的区别:
不再使用spring的xml配置,全权交给java
JavaConfig是Spring的一个子项目
实体类
package com.test.dyh; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Value; /** * @author dyh * @date 2021/6/1 - 16:41 * @description: */ @Data @AllArgsConstructor @NoArgsConstructor public class Student { private int id; private Integer age; @Value("李四") private String name; }
JavaConfig类
package com.test.dyh; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * @author dyh * @date 2021/6/2 - 17:32 * @description: */ //这个也会被Spring容器托管,注册到容器中,因为它本身就是一个@Component //@Configuration代表这是一个配置类。就跟之前bean.xml是一样的 @Configuration @ComponentScan("com.test.dyh") @Import(JavaConfig2.class) public class JavaConfig { //注册一个bean,就相当于一个bean标签 //这个方法的名字,就相当于bean标签的id属性 //这个方法的返回值,就相当于bean标签的class属性 @Bean public Student student() { return new Student(); } }
测试类
package com.test.dyh; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class AppTest { @Test public void shouldAnswerWithTrue() { //如果完全使用配置类做,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载 ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class); //从容器中获取对象 Student student = (Student) ac.getBean("student"); System.out.println(student.getName()); //输出李四 } }
代理模式的分类:
动态代理是指:程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理只是由代理生成工具(不是真实定义的类)在程序运行时由JVM根据反射等机制动态生成,代理对象与目标对象的代理关系在程序运行时确定。
实现方式
jdk动态代理, 使用jdk中的Proxy, Method, InvocationHanderl创建代理对象。jdk动态代理要求目标类必须实现接口。
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的。
动态代理的作用
AOP (Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
AOP底层,就是采用动态代理模式实现的。采用了两种代理: JDK的动态代理,与CGLIB的动态代理。
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方法,使用动态代理)。
切面:给你的目标类增加的功能,就是切面,什么日志等(切面的特点:一般都是非业务方法,独立使用的。)
面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。
若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。
如何理解面向切面编程?
一个切面有三个关键的要素
aop的实现
aop是一个规范,是一个动态的一个规范化,一个标准。
aop的技术实现框架:
spring:spring在内部实现了aop规范,能做aop的工作。
aspectJ:一个开源的专门做aop的框架。spring框架中集成了aspectJ框架,通过spring就可以使用aspectJ的功能了。
aspectJ框架实现有两种方式:1.使用xml的配置文件(配置全局事务) 2.使用注解,一般都用注解
如何表示切面的执行时间?
注解表示:1.@Before 2.@AfterReturning 3.@Around 4.@AfterThrowing 5.@After
如何表示切面执行的位置?
使用的是切入点表达式。
表达式原型为:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
解释:
modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选部分
使用aspectJ框架实现aop 使用aop:目的是给已经存在的一些类和方法,增加额外的功能,前提是不改变原来的类的代码。 框架的使用步骤: 1.新建maven项目 2.加入依赖 spring依赖 aspectJ依赖 junit单元测试 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> 3.创建目标类:接口和他的实现类 要做的是给类中的方法去增加功能 4.创建切面类:普通类 1.在类的上面加入@Aspect 2.在类中去定义方法---要执行的功能代码 3.在方法的上面加入aspectJ中的通知注解,例如@Before 还需要指定切入点表达式execution() 5.创建spring的配置文件,在文件中声明对象,把对象交给容器统一管理 声明对象可以使用注解或者xml配置文件 声明目标对象 声明切面类对象 声明aspectJ框架中的自动代理生成器标签(用来完成代理对象的自动创建功能的) 6.创建测试类,从spring容器中获取目标对象(实际上是代理对象)。 通过代理执行方法,实现aop的功能增强
接口
package cqutlc.ba01;
public interface SomeSevice {
void doSome(String name,Integer age);
}
目标类
package cqutlc.ba01;
//目标类
public class SomeSeviceImpl implements SomeSevice {
@Override
public void doSome (String name, Integer age) {
//给doSome方法增加一个功能,在doSome方法执行之前,输出方法的执行时间
System.out.println ("目标方法执行");
}
}
切面类
package cqutlc.ba01; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import java.util.Date; /* * @Aspect:aspectJ框架中的注解,用来表示当前类是切面类 * 位置:类定义的上面 * */ @Aspect public class MyAspect { /* * 定义方法:实现切面功能的、 * 方法的定义要求: * 1.公共方法 * 2.没有返回值3.方法名自定义4.方法可以有参数也可以没有参数,有几个参数类型可以使用() * */ /*@Before 前置通知注解 * 属性:value,切入点表达式,表示切面的功能执行的位置 * 特点:在目标方法之前先执行,不会改变目标方法的执行结果,不会影响目标方法的执行 * */ @Before (value = "execution(public void *..SomeSeviceImpl.doSome(..))") public void myBefore(){ //功能代码 System.out.println ("时间:"+new Date ()); } }
Spring配置文件
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--把对象交给spring容器,由容器统一创建和管理对象--> <!--声明目标对象--> <bean id="someService" class="cqutlc.ba01.SomeSeviceImpl"/> <!--声明切面类对象--> <bean id="myAspect" class="cqutlc.ba01.MyAspect"/> <!--声明自动代理生成器:使用aspectJ框架内部的功能,创建目标对象的代理对象 创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象 所以目标对象就是被修改后的代理对象 aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。 --> <aop:aspectj-autoproxy/> </beans>
测试类
package cqutlc.ba01; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class test1 { @Test public void test1(){ String config="applicationContext.xml"; ApplicationContext ac=new ClassPathXmlApplicationContext (config); SomeSevice proxy=(SomeSevice)ac.getBean ("someService"); //通过代理的对象执行方法,实现目标方法执行时,增强了功能。 proxy.doSome ("lisi",20); } }
指定通知方法中的参数
joinpoint:业务方法,要加入切面功能的业务方法
作用是:可以在通知方法中获取方法执行时的信息,例如,方法名称,方法的实参。
如果你的切面功能中需要用到方法的信息,就加入joinpoint
这个joinpoint参数的值是由框架赋予,必须是第一个位置的参数。
@Aspect public class MyAspect { @Before (value = "execution(public void *..SomeSeviceImpl.doSome(..))") public void myBefore(JoinPoint jp){ //获取方法的完整定义 System.out.println("方法的定义:"+jp.getSignature()); System.out.println("方法的名称:"+jp.getSignature().getName()); //获取方法的实参 Object[] args= jp.getArgs(); for(Object arg: args){ System.out.println("参数="+arg); } //功能代码 System.out.println ("时间:"+new Date ()); } }
package cqutlc.ba02; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import java.util.Date; /* * @Aspect:aspectJ框架中的注解,用来表示当前类是切面类 * 位置:类定义的上面 * */ @Aspect public class MyAspect { /* * 后置通知定义方法:实现切面功能的、 * 方法的定义要求: * 1.公共方法 * 2.没有返回值3.方法名自定义4.方法有参数,推荐使用object,参数名自定义 * */ /* * @AfterReturning * 属性: * 1.value:切入点表达式 * 2.returning:自定义的变量,用来表示目标方法的返回值,自定义变量名必须和通知方法的形参名一样 * 特点: * 1.在目标方法之后执行的 * 2.能够获得目标方法的返回值,可以根据这个返回值做不同的处理功能 * Object res = doOther(); * 3.可以修改这个返回值。 * */ @AfterReturning(value = "execution(* *..SomeSeviceImpl.doOther(..))", returning = "res") public void myAfterReturing(Object res){ //Object res :是目标方法执行后的返回值,根据返回值做你的切面的功能处理 System.out.println ("后置通知,获取的返回值是"+res); if (res.equals ("abc")){ res="hi"; }else { } } }
增强方法有ProceedingJoinPoint参数
package cqutlc.ba03; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import java.util.Date; /* * @Aspect:aspectJ框架中的注解,用来表示当前类是切面类 * 位置:类定义的上面 * */ @Aspect public class MyAspect { /* * 环绕通知定义方法的格式 * 1.public * 2.有一个返回值 object * 3.方法名称自定义 * 4.方法有参数,固定的 ProceedingJoinPoint * * */ /*@Around 特点: 1.功能最强的通知 2.在目标方法前和后都可以加入功能 3.控制目标方法是否被调用实行 4.修改原来的目标方法的执行结果,影响最后的调用结果 环绕通知等同于jdk动态代理,InvocationHandler接口 参数:ProceedingJoinPoint 就等同于Method 作用:执行目标方法的执行结果,可以被修改。 环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务 * */ @Around (value = "execution(* *..SomeServiceImpl.doFirst(..))") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { String name=""; Object[] args =pjp.getArgs (); if (args!=null&&args.length>1){ Object arg= args[0]; name=(String)arg; } //在目标方法前或者后加功能 //实现环绕通知 Object result=null; System.out.println ("环绕通知在目标方法之前"+new Date ()); if ("zhangsan".equals (name)){ //1.实现目标方法的调用 result= pjp.proceed ();//method.invoke,object result=doFirst(); } System.out.println ("环绕通知在目标方法之后,提交事务"); return result; } }
测试
package cqutlc.ba03; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class test1 { @Test public void test1(){ String config="applicationContext.xml"; ApplicationContext ac=new ClassPathXmlApplicationContext (config); SomeService proxy=(SomeService)ac.getBean ("someService"); String str=proxy.doFirst ("lc",19); } }
当较多的通知增强方法使用相同的execution切入点表达式时,编写、维护均较为麻烦。
AspectJ提供了@Pointcut注解,用于定义execution切入点表达式。其用法是,将@Pointcut注解在一个方法之上,以后所有的execution的value属性值均可使用该方法名作为切入点。代表的就是@Pointcut定义的切入点。这个使用@Pointcut注解的方法一般使用private 的标识方法,即没有实际作用的方法。
属性 :value切入点表达式
位置:自定义的方法的上面
特点:当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名,其他的通知中,value属性就可以使用这个方法名称代替切入点表达式。
@Pointcut(value="execution(* *..SomeServiceImpl.doOther(..))")
private void mypt(){
//无需代码
}
有接口也可以使用cglib代理
在xml中配置<aop:aspectj-autoproxy proxy-target-class=“true”/>表示告诉框架,你要使用cglib代理。JDK(默认 <aop:aspectj-autoproxy proxy-target-class=“false”/>)
spring和mybatis集成
步骤
1.新建maven项目
2.加入依赖 spring依赖 mybatis依赖 mysql驱动 spring的事务依赖 mybatis和spring集成的依赖
3.创建实体类
4.创建mybatis主配置文件
5.创建接口和mapper文件
6.创建测试类
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test.dyh</groupId> <artifactId>spring</artifactId> <version>1.0-SNAPSHOT</version> <name>spring</name> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> <!--mybatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
package com.test.dyh.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author dyh * @date 2021/6/3 - 15:55 * @description: */ @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--日志--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--设置别名--> <typeAliases> <!--name所在包的路径--> <package name="com.test.dyh.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis ?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--sql mapper映射文件的位置--> <mappers> <mapper class="com.test.dyh.mapper.UserMapper"></mapper> </mappers> </configuration>
package com.test.dyh.mapper; import com.test.dyh.pojo.User; import java.util.List; /** * @author dyh * @date 2021/6/3 - 16:02 * @description: */ public interface UserMapper { public List<User> listUser(); }
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.dyh.mapper.UserMapper">
<select id="listUser" resultType="user">
select * from mybatis.user;
</select>
</mapper>
package com.test.dyh; import com.test.dyh.mapper.UserMapper; import com.test.dyh.pojo.User; import lombok.val; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class AppTest { @Test public void shouldAnswerWithTrue() throws IOException { String resources = "mybatis.xml"; InputStream resourceAsStream = Resources.getResourceAsStream(resources); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = build.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.listUser(); for (User u : users) { System.err.println(u); } } }
User(id=1, name=张三, pwd=124)
编程式和声明式事务的区别
Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
事务有四个特性:ACID
事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 |
PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 |
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读 |
ISOLATION_READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 |
ISOLATION_REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager
采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。代码片段:
TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplate
Object result = tt.execute(
new TransactionCallback(){
public Object doTransaction(TransactionStatus status){
updateOperation();
return resultOfUpdateOperation();
}
}); // 执行execute方法进行事务管理
使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。
示例代码如下:
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
try {
// 数据库操作
dataSourceTransactionManager.commit(status);// 提交
} catch (Exception e) {
dataSourceTransactionManager.rollback(status);// 回滚
}
根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoTarget" /> <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> <!-- 配置事务管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" parent="transactionBase" > <property name="target" ref="userDaoTarget" /> </bean> </beans>
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事务属性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> <!-- 配置DAO --> <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
<?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" 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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" /> </aop:config> </beans>
<?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" 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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
此时在DAO上需加上@Transactional注解,如下:
package com.bluesky.spring.dao; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; import com.bluesky.spring.domain.User; @Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List<User> listUsers() { return this.getSession().createQuery("from User").list(); } }
首先是数据库表
book(isbn, book_name, price)
account(username, balance)
book_stock(isbn, stock)
然后是XML配置
<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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <import resource="applicationContext-db.xml" /> <context:component-scan base-package="com.springinaction.transaction"> </context:component-scan> <tx:annotation-driven transaction-manager="txManager"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
使用的类
BookShopDao
package com.springinaction.transaction;
public interface BookShopDao {
// 根据书号获取书的单价
public int findBookPriceByIsbn(String isbn);
// 更新书的库存,使书号对应的库存-1
public void updateBookStock(String isbn);
// 更新用户的账户余额:account的balance-price
public void updateUserAccount(String username, int price);
}
BookShopDaoImpl
package com.springinaction.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("bookShopDao") public class BookShopDaoImpl implements BookShopDao { @Autowired private JdbcTemplate JdbcTemplate; @Override public int findBookPriceByIsbn(String isbn) { String sql = "SELECT price FROM book WHERE isbn = ?"; return JdbcTemplate.queryForObject(sql, Integer.class, isbn); } @Override public void updateBookStock(String isbn) { //检查书的库存是否足够,若不够,则抛出异常 String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?"; int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn); if (stock == 0) { throw new BookStockException("库存不足!"); } String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?"; JdbcTemplate.update(sql, isbn); } @Override public void updateUserAccount(String username, int price) { //检查余额是否不足,若不足,则抛出异常 String sql2 = "SELECT balance FROM account WHERE username = ?"; int balance = JdbcTemplate.queryForObject(sql2, Integer.class, username); if (balance < price) { throw new UserAccountException("余额不足!"); } String sql = "UPDATE account SET balance = balance - ? WHERE username = ?"; JdbcTemplate.update(sql, price, username); } }
BookShopService
package com.springinaction.transaction;
public interface BookShopService {
public void purchase(String username, String isbn);
}
BookShopServiceImpl
package com.springinaction.transaction; 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 指定事务的传播行为,即当前的事务方法被另外一个事务方法调用时如何使用事务。 * 默认取值为REQUIRED,即使用调用方法的事务 * REQUIRES_NEW:使用自己的事务,调用的事务方法的事务被挂起。 * * 2.使用isolation 指定事务的隔离级别,最常用的取值为READ_COMMITTED * 3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下,默认值即可。 * 4.使用readOnly 指定事务是否为只读。 表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。若真的是一个只读取数据库值得方法,应设置readOnly=true * 5.使用timeOut 指定强制回滚之前事务可以占用的时间。 */ @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, noRollbackFor={UserAccountException.class}, readOnly=true, timeout=3) @Override public void purchase(String username, String isbn) { //1.获取书的单价 int price = bookShopDao.findBookPriceByIsbn(isbn); //2.更新书的库存 bookShopDao.updateBookStock(isbn); //3.更新用户余额 bookShopDao.updateUserAccount(username, price); } }
Cashier
package com.springinaction.transaction;
import java.util.List;
public interface Cashier {
public void checkout(String username, List<String>isbns);
}
CashierImpl:CashierImpl.checkout和bookShopService.purchase联合测试了事务的传播行为
package com.springinaction.transaction; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("cashier") public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional @Override public void checkout(String username, List<String> isbns) { for(String isbn : isbns) { bookShopService.purchase(username, isbn); } } }
BookStockException
package com.springinaction.transaction; public class BookStockException extends RuntimeException { private static final long serialVersionUID = 1L; public BookStockException() { super(); // TODO Auto-generated constructor stub } public BookStockException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { super(arg0, arg1, arg2, arg3); // TODO Auto-generated constructor stub } public BookStockException(String arg0, Throwable arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } public BookStockException(String arg0) { super(arg0); // TODO Auto-generated constructor stub } public BookStockException(Throwable arg0) { super(arg0); // TODO Auto-generated constructor stub } }
UserAccountException
package com.springinaction.transaction; public class UserAccountException extends RuntimeException { private static final long serialVersionUID = 1L; public UserAccountException() { super(); // TODO Auto-generated constructor stub } public UserAccountException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { super(arg0, arg1, arg2, arg3); // TODO Auto-generated constructor stub } public UserAccountException(String arg0, Throwable arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } public UserAccountException(String arg0) { super(arg0); // TODO Auto-generated constructor stub } public UserAccountException(Throwable arg0) { super(arg0); // TODO Auto-generated constructor stub } }
测试类
package com.springinaction.transaction; import java.util.Arrays; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTransitionTest { private ApplicationContext ctx = null; private BookShopDao bookShopDao = null; private BookShopService bookShopService = null; private Cashier cashier = null; { ctx = new ClassPathXmlApplicationContext("config/transaction.xml"); bookShopDao = ctx.getBean(BookShopDao.class); bookShopService = ctx.getBean(BookShopService.class); cashier = ctx.getBean(Cashier.class); } @Test public void testBookShopDaoFindPriceByIsbn() { System.out.println(bookShopDao.findBookPriceByIsbn("1001")); } @Test public void testBookShopDaoUpdateBookStock(){ bookShopDao.updateBookStock("1001"); } @Test public void testBookShopDaoUpdateUserAccount(){ bookShopDao.updateUserAccount("AA", 100); } @Test public void testBookShopService(){ bookShopService.purchase("AA", "1001"); } @Test public void testTransactionPropagation(){ cashier.checkout("AA", Arrays.asList("1001", "1002")); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。