Spring
1 Spring概述
是一个免费的开源的容器
是一个轻量级、非入侵式的框架
核心:控制反转(IOC)、面向切面编程(AOP)
支持事务的处理,对框架整合的支持
特点
- 方便解耦,简化开发
- AOP编程的支持
- 方便程序的测试
- 方便整合其他框架
- 方便进行事务操作
- 降低API的开发难度
2 IOC
控制反转lOC(Inversion of Control),是一种设计思想。
在Java开发中,IOC指把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是有其它外部资源完成。
控制:创建对象,对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象。创建对象,给属性赋值
正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象。(目前为止使用的都是正转)
为什么使用IOC?
- 减少对代码的改动,也能实现不同的功能,实现解耦
IOC的技术实现:
- DI(依赖注入):只需再程序中提供要使用的对象名就行了,至于对象如何在容器中创建、赋值、查找、都交给容器。
- spring是使用的DI实现了IOC的功能,Spring底层创建对象,使用的是反射机制。
IOC接口
1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2、Spring提供IOC容器实现两种方式: (两个接口)
- BeanFactory: IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。
- 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
- ApplicationContext: BeanFactory的子接口,提供更多更强大的功能,一般由开发人员进行使用
- 加载配置文件时会把配置文件对象进行创建
3、
FileSystem通过绝对路径加载
ClassPath通过src下全路径加载
XML方式注入
bean实例在调用无参构造器创建对象后,就要对bean对象的属性进行初始化。初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类: set注入、构造注入。
在pom.xml
中导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
set注入
编写一个类交给spring管理
public class User {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
public String getAge() {
return age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
在resource目录下创建beans.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
使用Springl来创建对象,在Spring这些称为Bean
bean = 对象
id = 变量名
class = 要new的对象
property 相当于给对象中的属性赋值
name 类中的属性名
value 具体的值,基本数据类型
ref 引用Spring容器中的对象
底层:spring是把创建好的对象放入到map中, spring框架有一个map存放对象的,springMap.put ( id的值,对象);
-->
<bean id="User" class="com.wei.User"> <!--将User这个对象交给spring容器来创建和赋值-->
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
</beans>
测试
public class MyTest01 {
@Test
public void test01(){
// 1、 加载xml文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2、 获取spring管理的对象
User user1 = context.getBean("User",User.class);
User user2 = context.getBean("User",User.class);
System.out.println(user1);
System.out.println("user1 == user2 ? " + (user1 == user2));
}
}
结果
User{name=‘张三’, age=‘18’}
user1 == user2 ? true
结论:
上面方式是spring自动创建对象,并调用set方法进行属性赋值
创建的对象只有一份
有参构造注入
public class Order {
private String name;
private String address;
public Order(String name, String address) {
this.name = name;
this.address = address;
}
@Override
public String toString() {
return "Order{" + "name='"+name+ '\'' +
", address='" + + address + '\'' + + '}';
}
}
bean.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="User" class="com.wei.User">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
<!--使用constructor-arg进行构造注入-->
<bean id="Order" class="com.wei.Order">
<constructor-arg name="name" value="手机"/>
<constructor-arg name="address" value="北京"/>
</bean>
</beans>
结果
User{name=‘张三’, age=‘18’}
user1 == user2 ? true
Order{name=‘手机’, address=‘北京’}
order1== order2 ? true
p命名空间注入(了解)
在配置文件中添加p命名空间
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
使用Springl来创建对象,在Spring这些称为Bean
bean = 对象
id = 变量名
class = 要new的对象
property 相当于给对象中的属性赋值
name 类中的属性名
value 具体的值,基本数据类型
ref 引用Spring容器中的对象
底层:spring是把创建好的对象放入到map中, spring框架有一个map存放对象的,springMap.put ( id的值,对象);
-->
<bean id="User" class="com.wei.User"> <!--将User这个对象交给spring容器来创建和赋值-->
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
<bean id="Order" class="com.wei.Order">
<constructor-arg name="name" value="手机"/>
<constructor-arg name="address" value="北京"/>
</bean>
<!-- p命名空间注入:set注入 -->
<bean id="Dog" class="com.wei.Dog" p:name="旺财" p:age="3">
</bean>
</beans>
注入字面量
1 null值
<bean id="User" class="com.wei.User"> <!--将User这个对象交给spring容器来创建和赋值-->
<!-- name属性设置为null值 -->
<property name="name">
<null/>
</property>
<property name="age" value="18"/>
</bean>
测试
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("User", User.class);
System.out.println(user);
}
结果
User{name=‘null’, age=‘18’}
2 属性值包含特殊符号
<bean id="User" class="com.wei.User"> <!--将User这个对象交给spring 容器来创建和赋值-->
<!-- name属性包含特殊符号:
方式一:将<> 进行转义
方式二:将特殊符号写进CDATA
-->
<!-- <property name="name" value="<<张三>>"/> -->
<property name="name">
<value><![CDATA[<<张三>>]]]> </value>
</property>
<property name="age" value="18"/>
</bean>
@Testpublic void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("User", User.class);
System.out.println(user);
}
结果
User{name='<<张三>>] ', age=‘18’}
注入外部bean
要创建两个类service和dao类,然后在service中调用dao类的方法
UserDao类
public interface UserDao {
public void update();
}
UserImpl类
public class UserDaoImpl implements UserDao{
public void update() {
System.out.println("dao update...");
}
}
UserService类
public class UserService {
private UserDao userDao;
// set函数让spring进行set注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("service 的 add方法被调用");
userDao.update();
}
}
bean2.xml
<!-- 将dao和service交给spring管理 -->
<bean id="userService" class="com.wei.service.UserService">
<!--对userService的userDao属性进行注入,他是一个对象,需要用到ref属性,将下面的id为userDao1的对象给它-->
<property name="userDao" ref="userDao1"></property>
</bean>
<bean id="userDao1" class="com.wei.dao.UserDaoImpl"/>
测试
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
UserService us = context.getBean("userService",UserService.class);
us.add();
}
结果
service 的 add方法被调用
dao update…
注入内部bean
一个员工属于一个部门
创建部门类和员工类
部门类
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
员工类
// 员工类
public class Emp {
private String ename;
private String gender;
private Dept dept;
//员工所属的部门
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
bean3.xml
<bean id="emp" class="com.wei.bean.Emp">
<property name="ename" value="张三"/>
<property name="gender" value="男"/>
<!--设置对象类型属性-->
<property name="dept">
<!--内部bean-->
<bean id="dept" class="com.wei.bean.Dept">
<property name="dname" value="财务部"/>
</bean>
</property>
</bean>
测试
@Test
public void test04(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
结果
Emp{ename=‘张三’, gender=‘男’, dept=Dept{dname=‘财务部’}}
级联赋值
部门类
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
员工类
// 员工类
public class Emp {
private String ename;
private String gender;
private Dept dept;
//员工所属的部门
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
bean3.xml
<bean id="emp" class="com.wei.bean.Emp">
<property name="ename" value="张三"/>
<property name="gender" value="男"/>
<!--设置对象类型属性-->
<property name="dept" ref="dept"/>
</bean>
<bean id="dept" class="com.wei.bean.Dept">
<property name="dname" value="财务部"/>
</bean>
测试
@Test
public void test04(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
注入集合属性
public class Student {
//1 数组类型属性
private String[] courses;
//2 list集合属性
private List<String> list;
//3 map集合属性
private Map<String,String> maps;
//4 set集合属性
private Set<String> sets;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
}
bean.xml
<bean id="student" class="com.wei.Student">
<!--1 数组类型注入-->
<property name="courses">
<array>
<value>java课程</value>
<value>mysql课程</value>
<value>数据结构课程</value>
</array>
</property>
<!--2 list类型注入-->
<property name="list">
<list>
<value>吃早餐</value>
<value>吃午餐</value>
<value>吃晚餐</value>
</list>
</property>
<!--3 map类型注入-->
<property name="maps">
<map>
<entry key="java" value="48学时"/>
<entry key="数据结构" value="32学时"/>
</map>
</property>
<!--4 set注入-->
<property name="sets">
<set>
<value>Redis</value>
<value>web</value>
</set>
</property>
</bean>
测试
@Test
public
void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student);
}
结果
Student{courses=[java课程, mysql课程, 数据结构课程],
list=[吃早餐, 吃午餐, 吃晚餐],
maps={java=48学时, 数据结构=32学时},
sets=[Redis, web]}
注入集合属性2
集合中的类型是一个自定义对象
// 学生类
public class Student {
private List<Course> course;
public void setCourse(List<Course> course) {
this.course = course;
}
}
// 课程类
public class Course {
private String name;
public void setName(String name) {
this.name = name;
}
}
bean.xml
<bean id="student" class="com.wei.Student">
<!--注入list类型,但值为对象类型-->
<property name="course" >
<list>
<ref bean="course1"/> <!--注入course1-->
<ref bean="course2"/> <!--注入course2-->
</list>
</property>
</bean>
<bean id="course1" class="com.wei.Course">
<property name="name" value="数据结构"/>
</bean>
<bean id="course2" class="com.wei.Course">
<property name="name" value="计算机网络"/>
</bean>
也可以将list中注入course提取出去,需要使用util命名空间
bean.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
">
<bean id="student" class="com.wei.Student">
<properties name="course" ref="courseList">
</properties>
</bean>
<util:list id="courseList">
<value>计算机组成原理</value>
<value>操作系统</value>
</util:list>
FactoryBean
1、Spring有两种类型bean,一种普通bean,另外一种工厂bean (FactoryBean)
2、普通bean: 在配置文件中定义bean类型就是返回类型
3、工厂bean: 在配置文件定义bean类型可以和返回类型不一样(可以自定义bean)
- 第一步创建类,让这个类作为工厂bean,实现接口FactoryBean
- 第二步实现接口里面的方法,在实现的方法中定义返回的bean类型
import org.springframework.beans.factory.FactoryBean;
public class MyBean implements FactoryBean<Course> {
// 定义返回bean类型
public Course getObject() throws Exception {
Course course = new Course();
course.setName("aaaabbbb");
return course;
}
public Class<?> getObjectType() {
return null;
}
public boolean isSingleton() {
return false; //是否位单例
}
}
bean的作用域
1 spring中创建bean的实例是单实例对象
2 在spring配置文件中可利用scope属性进行调整
- scope属性的值:
- sgleton:单实例,默认值,在加载spring配置文件时创建对象
- prototype:多实例,只有在调用getBean()方法时才回去创建对象
bean生命周期
生命周期:从创建到销毁的过程
1、通过无参构造器创建bean实例(构造注入调用有参)
2、为bean的属性进行赋值,调用每个属性的set方法(构造注入在第一步赋值)
3、调用bean的初始化方法(需要进行配置)
4、获取到bean对象并进行使用
5、当容器关闭时,调用bean的销毁方法(需要进行配置)
public class User {
private String name;
public void setName(String name) {
this.name = name;
System.out.println("set方法被调用...................");
}
public User() {
System.out.println("user无参构造被调用............");
}
// 创建初始化方法,名字随便起
public void initMethod(){
System.out.println("执行了初始化方法............");
}
// 创建销毁时调用的方法,名字随便起
public void destroyMethod(){
System.out.println("执行了销毁的方法............");
}
}
bean.xml
<!--
init-method 设置初始化方法
destroy-method 设置销毁时调用方法
-->
<bean id="user" class="com.wei.bean.User"
init-method="initMethod"
destroy-method="destroyMethod">
<property name="name" value="李四"/>
</bean>
测试
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
// 手动销毁bean实例
((ClassPathXmlApplicationContext)context).close();
}
完整的bean声明周期一共有7步:
1、通过无参构造器创建bean实例(无参构造)
2、为bean的属性进行赋值(调用set方法)
3、把bean实例传递给bean后置处理器的方法postProcessBeforeInitialization
4、调用bean的初始化方法(需要进行配置)
5、把bean实例传递给bean后置处理器的方法postProcessAfterInitialization
6、获取到bean对象并进行使用
7、当容器关闭时,调用beand额销毁方法(需要进行配置)
添加后置处理器步骤:
1、创建类,实现接口BeanPostProcessor,创建后置处理器
2、在spring配置文件中使用后置处理器
后置处理器类单独创建
public class MyBean implements BeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化执行之前执行的方法");
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化执行之后执行的方法");
return bean;
}
}
bean.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
init-method 设置初始化方法
destroy-method 设置销毁时调用方法
-->
<bean id="user" class="com.wei.bean.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="李四"/>
</bean>
<!--为所有类配置后置处理器-->
<bean id="myBeanPost" class="com.wei.bean.MyBean"/>
</beans>
测试
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
// 手动销毁bean实例
((ClassPathXmlApplicationContext)context).close();
}
自动装配
根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
在spring配置文件中使用bean的autowire属性,他有两个值
- byName:根据属性名自动注入,bean的id和要注入的属性名一致
- byType:根据类型自动注入,不能有一样类型的bean存在,不然会报错
// 员工类
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
}
// 部门类
public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}
bean.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--实现自动装配
autowire属性:
byName:根据属性名自动注入,bean的id和要注入的属性名一致
byType:根据类型自动注入,bean的类型是
-->
<bean id="emp" class="com.wei.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.wei.autowire.Dept"></bean>
</beans>
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
注解方式注入
什么是注解?
(1)注解是代码特殊标记,格式 : @注解名称(属性名=属性值,属性名称=属性值…)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化xml配置
Spring针对Bean管理中创建对象提供注解
1、@Component:不属于一下三层
2、@Service:用于service层
3、@Controllere:用于controller层
4、@Repository:用于dao层
- 上面四个注解功能是一样的,都可以用来创建bean实例
使用注解步骤:
1 导入aop依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2 开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描
如果要扫描多个包,包与包之间用,隔开
-->
<context:component-scan base-package="com.wei"></context:component-scan>
</beans>
3 创建类,使用注解
//以下代码相当于<bean id="myService" class="com.wei.service"/>
//value不写默认类名首字母小写
@Service(value = "myService")
public class UserService {
public void add(){
System.out.println("调用了add方法....");
}
}
4 测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService us = context.getBean("userService", UserService.class);
us.add();
}
再谈组件扫描
<!--
use-default-filters="false":表示不使用默认的filter,使用自己配置的filter
context:include-filter:设置扫描内容
-->
<context:component-scan base-package="com.wei" use-default-filters="false">
<!--只扫描com.wei中带有@Controller注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
加了use-default-filters="false"之后要自己设置需要扫描哪些内容
需要扫描的内容用include-filter设置
<!--
use-default-filters="false":表示不使用默认的filter,使用自己配置的filter
context:exclude-filter:设置不扫描的内容
-->
<context:component-scan base-package="com.wei" >
<!--不扫描com.wei中带有@Controller注解的类-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
exclude-filter设置不扫描的内容
使用注解注入属性
@AutoWired:根据属性类型进行自动注入
@Qualifier:根据属性名进行自动注入,需要跟@AutoWired一起使用
@Resource:可以根据类型注入也可以根据名称注入
@Value:注入普通类型属性
使用@AutoWired
// dao接口
public interface UserDao {
public void add();
}
// impl实现类
@Repository
public class UserImpl implements UserDao{
public void add() {
System.out.println("userImpl 的add方法被调用.......");
}
}
// userService类
@Service
public class UserService {
@Autowired //根据属性类型自动注入,不需要set方法
private UserDao userDao;
public void add(){
System.out.println("service的add方法..........");
userDao.add();
}
}
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService myService = context.getBean("userService", UserService.class);
myService.add();
}
使用@Qualifier
// dao接口
public interface UserDao {
public void add();
}
// impl实现类
@Repository
public class UserImpl implements UserDao{
public void add() {
System.out.println("userImpl 的add方法被调用.......");
}
}
// userService类
@Service
public class UserService {
@Qualifier(value = "userimpl01")
@Autowired
private UserDao userDao;
public void add(){
System.out.println("service的add方法..........");
userDao.add();
}
}
使用@Resource
// dao接口
public interface UserDao {
public void add();
}
// impl实现类
@Repository
public class UserImpl implements UserDao{
public void add() {
System.out.println("userImpl 的add方法被调用.......");
}
}
// userService类
@Service
public class UserService {
//@Resource //根据类型进行注入
@Resource(name = "userimpl01") //根据名称进行注入
private UserDao userDao;
public void add(){
System.out.println("service的add方法..........");
userDao.add();
}
}
使用@Value注解
@Service
public class UserService {
@Value(value = "张三")
private String name;
public void add(){
System.out.println("service的add方法.........." + name);
}
}
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService myService = context.getBean("userService", UserService.class);
myService.add();
}
完全注解开发
创建一个配置类,用来替代xml配置文件
@Configuration //将这个类作为配置类,替代xml文件
@ComponentScan(basePackages = {"com.wei"}) //替代xml中的组件扫描
public class SpringConf { }
测试
@Test
public void test01(){
//没有配置文件了 不需要加载
//ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConf.class);
UserService userService = context.getBean("userService",UserService.class);
userService.add();
}
正常输出
3 AOP
概念
面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
底层原理
1、AOP使用了动态代理,有以下两种情况:
1)有接口情况,使用JDK动态代理
2)无接口情况,使用CGLIB动态代理
实现JDK的动态代理
步骤:
1、使用Proxy类来创建代理对象
2、调用newProxyInstance方法
参数一:类加载器
参数二:增强方法的类所实现的接口
参数三:实现这个接口InvocationHandler,创建代理对象,写增强的方法
//dao接口
public interface UserDao {
public int add(int a,int b);
public String update(String id );
}
//实现类
public class UserImpl implements UserDao{
public int add(int a, int b) {
return a + b;
}
public String update(String id) {
return id;
}
}
编写代理类
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDao userDao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),
interfaces, new UserDaoProxy());
int res = userDao.add(10, 20);
System.out.println(res);
userDao.update("001");
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler{
//增强的逻辑代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行...方法名:" + method + "\n传递的参数:" + Arrays.toString(args));
//被增强的方法
Object invoke = method.invoke(new UserImpl(), args);
//方法之后
System.out.println("方法之后执行...");
return invoke;
}
}
AOP相关术语
1、连接点
可以被增强的方法称为连接点
2、切入点
实际真正被增强的方法
3、通知(增强)
实际增强的逻辑部分,有5中类型
1、前置通知
2、后置通知
3、环绕通知
4、异常通知
5、最终通知
4、切面
把通知应用到切入点的过程
进行AOP操作准备
Aspectj不是Spring的组成部分,是独立的框架,一般将其与spring一起使用,进行AOP操作
导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
切入点表达式
作用:对指定类中的具体方法进行增强
语法结构:execution([权限修饰符] [返回值类型] [类全路径] [方法名] ([参数列表]))
如:
1 对com.wei.UserImpl类中add方法进行增强
execution(* com.wei.userImpl.add(..))
2 对com.wei.UserImpl类中所有方法进行增强
execution(* com.wei.userImpl.*(..))
3 对com.wei包下的所有类的所有方法进行增强
execution(* com.wei.*.*(..))
使用注解方式进行AOP操作
创建目标类
@Component
public class User {
public void add(){
System.out.println("User的add方法被调用");
}
}
创建代理类:代理类增强目标类的add方法
@Component
@Aspect //声明为切面类
public class UserProxy {
//前置通知
@Before(value = "execution(* com.wei.User.add(..))")
public void before(){
System.out.println("before方法被执行");
}
}
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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">
<!-- 1、开启组件扫描 -->
<context:component-scan base-package="com.wei"></context:component-scan>
<!-- 2、自动为spring容器中那些配置@aspectJ切面的bean创建代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试
public class MyTest01 {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.add();
}
}
全部通知的注解类型:
1、前置通知:@Before
2、后置通知:@AfterReturning
3、环绕通知:@Around
4、异常通知:@AfterThrowing
5、最终通知:@After
抽取切入点
修改代理对象,其他不用改
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
@Before(value = "pointTest()") //value里面写函数名
public void before(){
System.out.println("before方法被执行");
}
//相同切入点抽取出来,放在这个函数上
@Pointcut(value = "execution(* com.wei.User.add(..))")
public void pointTest(){}
}
设置优先级
如果多个增强类对同一个方法进行增强,可以设置增强类之间的优先级
只需在各个增强类上添加@Order(数值)
注解数字越小优先级越高
//被增强类
@Component
public class User {
public void add(){
System.out.println("User的add方法被调用");
}
}
//增强类1
@Order(1)
@Component
@Aspect //生成代理对象
public class UserProxy1 {
//前置通知
@Before(value = "pointTest()")
public void before(){
System.out.println("UserProxy1的before方法被执行");
}
//相同切入点抽取
@Pointcut(value = "execution(* com.wei.User.add(..))")
public void pointTest(){}
}
//增强类2
@Order(2)
@Component
@Aspect //生成代理对象
public class UserProxy2 {
//前置通知
@Before(value = "pointTest()")
public void before(){
System.out.println("UserProxy2的before方法被执行");
}
//相同切入点抽取
@Pointcut(value = "execution(* com.wei.User.add(..))")
public void pointTest(){}
}
测试
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.add();
}
使用配置文件进行AOP操作
//被增强类
public class User {
public void add(){
System.out.println("User的add方法被调用");
}
}
//增强类
public class UserProxy {
public void before(){
System.out.println("UserProxy的before方法被执行");
}
}
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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">
<!--被增强对象-->
<bean id="user" class="com.wei.User"></bean>
<!--增强类对象-->
<bean id="userproxy" class="com.wei.UserProxy"></bean>
<!--配置aop增强-->
<aop:config>
<!--切入点:作用在User类中的add方法上-->
<aop:pointcut id="point1" expression="execution(* com.wei.User.add(..))"/>
<!--配置切面-->
<aop:aspect ref="userproxy">
<!--增强的逻辑在那个方法上?-->
<aop:before method="before" pointcut-ref="point1"/>
</aop:aspect>
</aop:config>
</beans>
测试
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.add();
}
xml通知的类型
通知配置语法:
<aop:通知类型 method="切面类中的方法名" pointcut="切点表达式"></aop:通知类型>
通知名 | 标签 | 说明 |
---|---|---|
前置通知 | <aop:befor> | 用于配置前置通知,指定增强的方法在切入点方法之前执行 |
后置通知 | <aop:after-returnning> | 用于配置后置通知,指定增强的方法在切入点方法之后执行 |
环绕通知 | <aop:around> | 用于配置环绕通知,指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 | <aop:throwing> | 用于配置异常抛出通知,指定增强的方法在出现异常时执行 |
最终通知 | <aop:after> | 用于配置最终通知,无论增强方式执行是否有异常都会执行 |
注解通知的类型
通知配置语法:
@通知注解("切点表达式")
名称 | 注解 | 说明 |
---|---|---|
前置通知 | @Befor | 用于配置前置通知,指定增强的方法在切入点方法之前执行 |
后置通知 | @AfterReturnning | 用于配置后置通知,指定增强的方法在切入点方法之后执行 |
环绕通知 | @Around | 用于配置环绕通知,指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 | @AfterThrowing | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
最终通知 | @After | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
4、jdbcTemplate
概念
什么是jdbcTemplate?
- 是Spring对jdbc的封装,可以更方便对数据库进行操作
准备工作
需要导入相关依赖
<!--mysqsl驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!--spring封装jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.14</version>
</dependency>
<!--spring core-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.14</version>
</dependency>
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.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">
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql:///demo"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--jdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启组件扫描-->
<context:component-scan base-package="com.wei"></context:component-scan>
</beans>
mysql中user表信息为
利用jdbcTemplate对该表进行增删改查
创建User实体类
public interface UserDao {
//添加
public void addUser(User user);
//修改(按id)
public void updateUserInfo(User user);
//删除(按id)
public void deleteUser(int id);
//1 查找全部用户
public List<User> findAll();
//2 查询表中的记录数
public int findCount();
//3 查询某个user(按id)
public User findUserById(int id);
}
创建实现类
@Repository
public class UserDaoImpl implements UserDao{
//注入jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
//实现添加一个user
public void addUser(User user) {
//创建sql语句
String sql = "insert into user value(?,?,?)";
int count = jdbcTemplate.update(sql, user.getId(), user.getUsername(), user.getPassword());
if(count != 0){
System.out.println("添加成功");
}
}
//实现修改user信息
public void updateUserInfo(User user) {
String sql = "update user set username=?,password=? where id=?";
int count = jdbcTemplate.update(sql,user.getUsername(),user.getPassword(),user.getId());
if (count != 0){
System.out.println("修改成功");
}
}
//实现删除user
public void deleteUser(int id) {
String sql = "delete from user where id = ?";
int count = jdbcTemplate.update(sql, id);
if (count != 0){
System.out.println("删除成功");
}
}
//1 查找所有user
public List<User> findAll() {
List<User> list;
String sql = "select * from user";
list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
return list;
}
//2 查找表中的记录数
public int findCount() {
String sql = "select count(*) from user";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count; //返回查询到的记录
}
//3 查询某个user(按id)
public User findUserById(int id) {
String sql = "select * from user where id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
}
创建service类
@Service
public class UserService {
//注入UserDao
@Autowired
private UserDao userDao;
//添加
public void addUser(User user){
userDao.addUser(user);
}
//修改(按id)
public void updateUserInfo(User user){
userDao.updateUserInfo(user);
}
//删除(按id)
public void deleteUser(int id){
userDao.deleteUser(id);
}
//1 查找全部用户
public List<User> findAll(){
return userDao.findAll();
}
//2 查找表中记录数
public int findCount(){
return userDao.findCount();
}
//3 查找某个user(按id)
public User findUserById(int id){
return userDao.findUserById(id);
}
}
测试
public class MyTest01 {
//添加
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
User user = new User();
user.setId(2);
user.setUsername("张三");
user.setPassword("123456");
userService.addUser(user);
}
//修改
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
User user = new User();
user.setId(2);
user.setUsername("李四");
user.setPassword("654321");
userService.updateUserInfo(user);
}
//删除
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
int id = 2;
userService.deleteUser(id);
}
//1 查询所有user
@Test
public void test04(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
List<User> users = userService.findAll();
System.out.println(users);
}
// 2 查询表中记录数
@Test
public void test05(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
int count = userService.findCount();
System.out.println("目前表中有" + count + "条记录");
}
// 3 查询表中某个user
@Test
public void test06(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
User user = userService.findUserById(1);
System.out.println(user);
}
}
5事务
概念
事务是数据库操作最近本的单元,逻辑上一组操作,要么都成功,要么都失败。
事务一般在service层进行处理
- Spring中有两种方式进行事务操作,分别为编程式事务管理和声明式事务管理,一般使用声明式事务管理
- 声明式事务管理有两种实现方式,基于xml文件,基于注解方式
- Spring中进行声明式事务管理,底层使用的是AOP原理
- Spring中事务管理的API
需求:数据库中有一个account表
,接下来,张三需要向李四转账100元,张三账户-100,李四账户+100
基于注解实现
步骤:
1、先创建dao接口和impl类
2、在Spring配置文件中创建事务管理器
3、在service类(或方法)上面添加事务注解@Transational
1、先创建dao接口和impl类
//接口
public interface AccountDao {
//多钱
void add();
//少钱
void reduce();
}
//实现类
@Repository
public class AccountDaoImpl implements AccountDao{
//按类型自动注入jdbcTemplate
@Autowired
JdbcTemplate jdbcTemplate;
public void add() {
String sql = "update account set money = money + ? where username = ?";
int count = jdbcTemplate.update(sql, 100, "张三");
System.out.println(count);
}
public void reduce() {
String sql = "update account set money = money - ? where username = ?";
int count = jdbcTemplate.update(sql, 100, "李四");
System.out.println(count);
}
}
2、在Spring配置文件中创建事务管理器
开启事务注解:1)引入名称空间tx 2)开启事务注解
<?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.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.wei"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&
characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8">
</property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--jdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--1、创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源,指明对那个数据库进行操作-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2、开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
3、在service类(或方法)上面添加事务注解@Transational
加在类上面表示类中的所有方法都添加了事务,此时方法执行中出现异常,那么事务会自动回滚(方法要么执行完,要么不执
@Service
public class AccountService {
//注入dao
@Autowired
private AccountDao accountDao;
//转账 :李四给张三转100
@Transactional
public void transfer(){
//李四少100元
accountDao.reduce();
//张三多100元
accountDao.add();
}
}
@Transational配置
注解中有跟事务相关的属性配置
propagation属性:
事务的传播行为
事务方法:指让数据库中数据发生变化的操作(增、删、改)
多事务方法直接调用,这个过程中事务是如何管理的
Propagation类的属性 | 说明 |
---|---|
REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务(默认) |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
NESTED | 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务 |
使用@Transational(propagation = Propagation.REQUIRED)
ioslation属性
事务的隔离级别
事务有特性称为隔离性,多事务操作之间不会影响,如果不考虑隔离性会产生很多问题,如:脏读、不可重复读、虚读(幻读)
脏读:事务A将某一值修改,然后事务B读取该值,此后A因为某种原因撤销对该值的修改,这就导致了B所读取到的数据是无效的
不可重读:事务A读取某一数据,事务B读取并修改了该数据,A为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户内存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。
虚读:在一个事务的两次相同查询中(两次查询有事件间隔),查询的结果不同
解决方法:设置事务的隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
Ioslation类的属性 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED(读未提交) | 有 | 有 | 有 |
READ COMMITTED(读已提交) | 无 | 有 | 有 |
REPEATABLE READ(可重复读) | 无 | 无 | 有 |
SERIALIZABLE(串行化) | 无 | 无 | 无 |
使用@Transational(ioslation = Ioslation.SERIALIZABLE)
@timeout属性
超时时间
事务需要在一定时间内提交,如果不能提交则进行回滚,默认值为-1,单位:秒
使用@Transation(timeout = 3600)
@readOnly
是否只读?
false,表示可以查询也可以增、删、改(默认值)
true,只能查询
@rollbackFor
回滚
设置出现哪些异常时进行事务的回滚
@noRollbackFor
不回滚
设置出现哪些异常就不进行事务的回滚
基于XML实现
步骤:
1、在Spring配置文件中创建事务管理器
2、配置通知
3、配置切入点和切面
创建dao和impl和service
//接口
public interface AccountDao {
//多钱
void add();
//少钱
void reduce();
}
//实现类
@Repository
public class AccountDaoImpl implements AccountDao{
//按类型自动注入jdbcTemplate
@Autowired
JdbcTemplate jdbcTemplate;
public void add() {
String sql = "update account set money = money + ? where username = ?";
int count = jdbcTemplate.update(sql, 100, "张三");
System.out.println(count);
}
public void reduce() {
String sql = "update account set money = money - ? where username = ?";
int count = jdbcTemplate.update(sql, 100, "李四");
System.out.println(count);
}
}
//service
@Service
public class AccountService {
//注入dao
@Autowired
private AccountDao accountDao;
//转账 :李四给张三转100
@Transactional
public void transfer(){
//李四少100元
accountDao.reduce();
//张三多100元
accountDao.add();
}
}
1、在Spring配置文件中创建事务管理器
2、配置通知
3、配置切入点和切面
<?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.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.wei"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&
characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8">
</property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--jdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--1、创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源,指明对那个数据库进行操作-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2、配置通知-->
<tx:advice id="txAdvice">
<!--配置事务的参数-->
<tx:attributes>
<!--指定在哪些方法上添加事务-->
<tx:method name="account" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--3、配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="point1" expression="execution(* com.wei.service.AccountService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="point1"/>
</aop:config>
</beans>
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
AccountService as = context.getBean("accountService", AccountService.class);
as.transfer();
}
完全注解开发
编写一个配置类
@Configuration
@ComponentScan(basePackages = "com.wei") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
//以下属性跟xml中property一一对应
dataSource.setDriverClassName("com.mysql.jdbc.Driver"); //驱动名
dataSource.setUrl("jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
//创建jdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){ //根据类型到IOC容器中找到对象,自动注入
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
这个配置类是下面的bean.xml的替代
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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.wei"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&
characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8">
</property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--jdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--1、创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源,指明对那个数据库进行操作-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2、开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>