spring是什么?
Spring是分层的Java SE/EE应用 full-stack 轻量级开源框架,以IOC(Inverse Of Control :反转控制)和AOP(Aspect Oriented Programming :面向切面编程)为内核,提供了展现层Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三框架和类库。
spring的两大核心?
IOC 和 AOP
spring的发展历程和优势
优势:方便解耦,简化开发(利用了IOC容器)
AOP编程的支持
声明事务的支持
方便程序的测试
方便集成各种优秀框架
降低 JavaEE API 的使用难度
Java 源码是经典学习典范
耦合:程序间的依赖关系
包括:
类之间的依赖
方法间的依赖
解耦:
降低程序间的依赖关系
实际开发中:
应该做到:编译期不依赖,运行期才依赖
解耦的思路:
第一步:使用反射来创建对象,而避免使用new 关键字。
第二步:通过读取配置文件来获取要创建的对象的全限定类名。
//1.获取核心容器对象
//ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\bean.xml");(绝对)
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象
UserService service = (UserService) ac.getBean("userService");
UserDao dao = ac.getBean("userDao",UserDao.class);
System.out.println(service);
System.out.println(dao);
创建bean对象的三种方式:
<?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 id="userService" class="com.itheima.service.Imp.UserServiceImpl"></bean>
<bean id="userDao" class="com.itheima.dao.Imp.UserDaoImpl"></bean>
<!-- 把对象的创建交给spring管理 -->
<!-- spring对bean的管理细节
1.创建bean的三种方式
2.bean对象的作用范围
3.bean对象的生命周期
-->
<!-- 创建bean的三种方式 -->
<!-- 第一种方式:使用默认的无参构造函数创建
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认无参构造函数创建bean对象,此时如果类中没有没有默认的无参构造,则对象无法创建。-->
<bean id="userService" class="com.lucky.service.Impl.UserServiceImpl"></bean>
<!-- 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器中) -->
<bean id="instanceFactory" class="com.lucky.factory.InstanceFactory"></bean> 得到InstanceFactory对象
<bean id="userService" factory-bean="instanceFactory" factory-method="getUserService"></bean> 调用里面的方法
<!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器中)--> <!-- class创建 StaticFactory实体类,在通过静态方法创建实体类-->
<bean id="userService" class="com.lucky.factory.StaticFactory"
factory-method="getUserService"></bean>
</beans>
bean对象的作用范围:
<!-- bean的作用范围:
bean标签的scope属性:
作用:用于指定bean的作用范围
取值:常用的就是单例的和多例的
singleton:单例的(默认值)(产生的对象为一个)
prototype:多例的
request:作用域于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用与集群环境的会话范围(全局会话范围),当不是集群环境时,他就是session
-->
<bean id="userService" class="com.lucky.service.Impl.UserServiceImpl" scope="prototype"></bean>
bean对象的生命周期:
<!-- bean对象的生命周期:
单例对象:
出生:当容器创建时产生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象灭亡 (销毁时,还没来得及调用销毁对象就已经消失)
总结:单例对象的声明周期和容器相同
多例对象:
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象时间不用,且没有别的对象引用时,由java的垃圾回收机制回收
-->
<bean id="userService" class="com.lucky.service.Impl.UserServiceImpl" scope="singleton"
init-method="init" destroy-method="destroy"></bean>
<!-- 构造函数依赖 :
<--1. 构造函数的注入:
使用的标签:constructor-arg (bean标签内部)
标签中的属性:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
type遇到的问题:不知道是否还有其他相同的类型。
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。
index遇到的问题:不知道具体位置的类型是什么。
name:用于指定给构造函数中指定名称的参数赋值 (最常用)。
========================================================================
value:用于提供基本类型和String类型的数据。
ref:用于指定其他的bean类型的数据。它指的就是在spring的ioc核心容器中出现过的bean对象。
优势:
在获取bean对象,注入数据是必须的操作,否则对象无法创建成功。
弊端:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.lucky.service.Impl.AccountServiceImpl">
<!-- <constructor-arg type="java.lang.String" value="显示"></constructor-arg>
<constructor-arg index="1" value="18"></constructor-arg>-->
<constructor-arg name="name" value="范德萨"></constructor-arg>
<constructor-arg name="age" value="16"></constructor-arg>
<constructor-arg name="birthday" ref="date"></constructor-arg>
</bean>
<bean id="date" class="java.util.Date"></bean>
<!-- set方法注入-->
<!--使用标签:property (在bean标签内部)
标签属性:
name:用于指定给构造函数中指定名称的参数赋值 (最常用)。
value:用于提供基本类型和String类型的数据。
ref:用于指定其他的bean类型的数据。它指的就是在spring的ioc核心容器中出现过的bean对象。
优势:
创建对象时没有明确的限制,可以直接使用默认构造函数。
弊端:
如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
-->
<bean id="accountService" class="com.lucky.service.Impl.AccountServiceImpl2">
<property name="name" value="大数"></property>
<property name="age" value="48"></property>
<property name="birthday" ref="date"></property>
</bean>
spring中ioc的常用注解
/* 用于创建对象的:
他们的作用域就和在XML配置文件中编写一个 标签实现的功能是一样的
@Component:
作用:用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id。当我们不写时,它的默认值就是当前类名,且首字母改小写
@Controller:一般用在表现层
@Service:一般用在业务层
@Repository:一般用在持久层
以上三个注解它们的作用和属性与Component是一模一样的。
它们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。
用于注入数据的:
他们的作用域就和在xml配置文件中的bean标签中写一个 标签的作用是一样的
@Autowired:
作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果ioc容器中有多个类型匹配时,则会按照创建UserDao的变量名称和注入时的名称匹配
出现位置:可以是变量上,也可以是方法上
细节:在使用注解注入时,set方法就不是必须的了
@Qualifier:
作用:在按照指定类中注入的基础之上在按照名称注入。他在给成员注入时不能单独使用(必须依赖与Autowired)。但是在给方法参数注入可以单独使用
属性:value:用于指定注入bean的id。
@Resource:
作用:直接按照bean的id注入。它可以独立使用
属性:
name:用于指定bean的id
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过XML来实现
@Value:
作用:用于注入基本数据类型和String类型的数据
属性:
value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
SpEL的写法:${表达式} */
@Component(value = "userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao2 = null;
}
@Repository
public class UserDaoImpl implements UserDao {
}
@Repository(value = "userDao2")
public class UserDaoImpl2 implements UserDao {
}
//使用Qualifier时,两个必须一起使用
//@Autowired
//@Qualifier("userDaoImpl")
@Resource(name = "userDaoImpl")
private UserDao userDao2 = null;
/* 用于改变作用范围的:
* 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* @Scope:
* 作用:用于指定bean的作用范围
* 属性:value:指定范围的取值。常用取值:singleton prototype
* 和生命周期相关
* 他们的作用就和在bean标签中使用 init-method 和 destroy-method 的作用是一样的
* @PreDestroy:
* 作用:用于指定销毁方法
* @PostConstruct:
* 作用:用于指定初始化方法
* */
@Component(value = "userService")
@Scope("prototype")
public class UserServiceImpl implements UserService {
@Override
@PostConstruct
public void init() {
System.out.println("我初始化了...");
}
@Override
@PreDestroy
public void destroy() {
System.out.println("我销毁了...");
}
}
@Value(value = "${driver}")
private String driver;
@Value("username")
private String username;
改造基于注解的ioc案例,使用纯注解的方式实现
/**
* 该类是一个配置类,它的作用和bean.xml是一样的:
* spring中的新注解:
* @Configuration:
* 作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
* @ComponentScan:
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
* 等同于这个配置:
*/
@Configuration
@ComponentScan("com.lucky") //要扫描的包
public class ConfigReplaceXml {
}
注意:ComponentScan扫描时,只会扫描有 @Configuration 注解的类
Configuration细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
/*
@Bean:
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中。
属性:
name:用于指定bean的id。当不写时,默认值是当前方法的名称,
细节: 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
查找的方式和Autowired注解的作用是一样的
*/
@Bean
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
@Bean
public DataSource createDataSource(){
try{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("root");
return dataSource;
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Test
public void test2(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class); //创建方式改变,基于注解创建
QQUserService service = ac.getBean("userService",QQUserService.class);
QQUser one = service.findOne(2);
System.out.println(one);
}
@Bean注解的细节:细节: 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,没有就会报错
(1).实现配置类调用另一配置类的方法:
/*
@Import:
作用:用于导入其它的配置类
属性:value:用于指定其他配置类的字节码。
当使用Import的注解之后,有Import注解的类就是父配置类,而导入的都是子配置类
*/
//第一种
@ComponentScan({"com.lucky"})
public class ConfigReplaceXml {
}
@Configuration
public class JDBCConfig {
}
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class);
//第二种
@ComponentScan({"com.lucky"})
public class ConfigReplaceXml {
}
public class JDBCConfig {
}
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class, JDBCConfig.class);//两个类都加上,为兄弟关系
//第三种(具有父子关系)
@ComponentScan({"com.lucky"})
@Import(value = {JDBCConfig.class})
public class ConfigReplaceXml {
}
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class);
(2)配置连接数据库的资源文件
/*
PropertySource:
* 作用:用于指定properties文件的位置。
* 属性:value:指定文件的的名称和路径。
关键字:classpath:表示类路径下
*/
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=root
@ComponentScan({"com.lucky"})
@Import(value = {JDBCConfig.class})
@PropertySource("classpath:jdbcConfig.properties")
public class ConfigReplaceXml {
}
@Value(value = "${driver}")
private String driver;
@Value(value = "${url}")
private String url;
@Value("${user}")
private String username;
@Value("${password}")
private String password;
(3)在注解中springIOC容器中同一个对象有多个实现方法时
/*当方法重载时,用 @Qualifier 注解可以实现使用的是哪一个方法 */
@Bean
@Scope("prototype")
public QueryRunner createQueryRunner(@Qualifier(value = "ds1") DataSource dataSource){
return new QueryRunner(dataSource);
}
@Bean(name = "ds1")
public DataSource createDataSource(){}
@Bean(name = "ds2")
public DataSource createDataSource2(){}
1、动态代理(基于接口实现)
/*基于接口的动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于接口的动态代理:
* 涉及的类:Proxy
* 提供者:JDK官方
* 如何创建代理对象:
* 使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:
* 被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:
* 1.类加载器:它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。真实对象.getClass().getClassLoader()
* 2.接口数组:字节码数组,它是用于让代理对象和被代理对象有相同的方法。真实对象.getClass().getInterfaces()
* 3.处理器:new InvocationHandler() */
Huawei huawei = new HuaWei();
Producer proxyProducer = (Producer) Proxy.newProxyInstance(huaWei.getClass().getClassLoader(), huiWei.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强代码
Object returnValue = null;
//1.获取方法执行的参数
double money = (double) args[0];
//2.判断当前方法是不是销售
if("saleProduce".equals(method.getName())){
//若是销售的方法名,则要进行处理,代理商要拿到 20%,生产商只能拿到 80%
returnValue = method.invoke(huaWei, money * 0.8);
}
return returnValue;
}
});
proxyProducer.saleProduce(10000);
/*基于子类的动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于子类的动态代理:
* 涉及的类:Enhancer
* 提供者:第三方cglib库
* 如何创建代理对象:
* 使用Enhancer类中的create方法
* 创建代理对象的要求:
* 被代理类不能时最终类
* create方法的参数:
* 1.Class:字节码,用于指定被代理对象的字节码,
* 2.Callback:用于指定提供增强的代码,
* 它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
* 此接口的实现类都是谁用谁写
* 我们一般写的都是该接口的子接口的实现类:MethodInterceptor
*/
Huawei huawei = new HuaWei();
Producer cglibProducer = (Producer) Enhancer.create(huaWei.getClass(), new MethodInterceptor() {
/**
* 执行被代理对象的任何方法都会经过该方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] ags, MethodProxy methodProxy) throws Throwable {
//增强代码
Object returnValue = null;
//1.获取方法执行的参数
double money = (double) ags[0];
//2.判断当前方法是不是销售
if ("saleProduce".equals(method.getName())) {
//若是销售的方法名,则要进行处理,代理商要拿到 20%,生产商只能拿到 80%
returnValue = method.invoke(huaWei, money * 0.8);
}
return returnValue;
}
});
cglibProducer.saleProduce(12000);
AOP:Aspect Oriented Programing(面向切面编程)。
spring中基于XML的 AOP 配置步骤 1.把通知 bean 也交给 spring 来管理 2.使用 aop:config 标签表明开始aop配置 3.使用 aop:aspect 标签表明配置切面 id属性 : 是给切面提供一个唯一标识 ref属性 : 是指定通知类的 id 4.在 aop:aspect 标签内部使用对应标签来配置通知的类型 我们现在实例是让 printLog 方法在切入点方法执行之前执行,所以是前置通知 aop:before : 表示配置前置通知 method属性:用于指定Logger类中哪个方法是前置通知 pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法的增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名.(参数列表)
举例:
public void com.lucky.service.impl.AccountServiceImpl.save()
修饰符可以不写:
void com.lucky.service.impl.AccountServiceImpl.save()
返回值可以使用通配符,表示任意返回值:
* com.lucky.service.impl.AccountServiceImpl.save()
包名可以使用通配符,表示任意包。但是有几级包,就需要写几个 *.
void *.*.*.*.AccountServiceImpl.save()
包名可以使用 .. 表示当前包及其子包:
void *..AccountServiceImpl.save()
类名和方法名都可以使用 * 来通配
* *..*.*()
参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意的类型,但是必须有参数
可以使用 .. 表示有无参数均可,有参数可以是任意类型
全通配符写法:
* *..*.*(..)
实际开发中切入点表达式的通常写法 :
切到业务层实现类下的所有方法
* com.lucky.service.impl.*.*(..)
<!-- 所有方法都可 -->
<aop:before method="printLog" pointcut="execution(* *..*.*(..))"></aop:before>
<!-- 省略修饰符 -->
<aop:before method="printLog" pointcut="execution(void com.lucky.service.impl.AccountServiceImpl.save())"></aop:before>
<!-- 包名改 *. -->
<aop:before method="printLog" pointcut="execution(void *.*.*.*.AccountServiceImpl.save())"></aop:before>
<!-- 任意包下或子包下有AccountServiceImpl类的 -->
<aop:before method="printLog" pointcut="execution(void *..AccountServiceImpl.save())"></aop:before>
<!-- 任意包下或子包下的任意类、任意方法,只要是无参就执行 -->
<aop:before method="printLog" pointcut="execution(void *..*.*())"></aop:before>
<!-- 参数通配符(可..) -->
<aop:before method="printLog" pointcut="execution(void *..*.*(..))"></aop:before>
<!-- 实际开发需要写的 -->
<aop:before method="printLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:before>
method:指定一个类中的方法 pointcut:指定切入点表达式,该表达式的含义指的是对业务层中哪些方法的增强 <!-- 配置前置通知,在切入点方法执行之前执行 --> <aop:before method="beforePrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:before> <!-- 配置后置通知 : 在切入点方法正常之后执行。它和异常通知永远只能执行一个 --> <aop:after-returning method="afterPrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:after-returning> <!-- 配置异常通知 : 在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个 --> <aop:after-throwing method="afterExceptionPrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:after-throwing> <!-- 配置最终通知 : 无论切入点方法是否正常执行它都会在最后执行--> <aop:after method="finallyPrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:after>
<!-- 配置通用切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容 此标签写在 aop:aspect 标签内部,只能当切面使用。 他还可以写在 aop:aspect 外面。此时就变成了所有切面可用 --> <!-- 要配置在 aop:aspect 的外面,必须是在 aop:aspect 的前面 --> <aop:pointcut id="pt1" expression="execution(* com.lucky.service.impl.*.*(..))"/>
//invoke方法执行的就是环绕通知 /** * 环绕通知 * 问题:当我们配置了环绕 之后,切入点方法没有执行,而通知方法执行了 * 分析:通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。 * 解决:Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法 proceed() ,此方法就相当于明确调用切入点方法。 * 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用 */ public Object aroundPrintLog(MethodInvocationProceedingJoinPoint pjp){ System.out.println("环绕通知:打印aroundPrintLog..."); return null; }
使用注解时:
XML中要配置:
@Aspect放在类上:表示当前类是一个切面类
<!-- spring中基于XML的声明式事务控制配置步骤 1.配置事务管理器 2.配置事务的通知 此时我们需要导入事务的约束 tx 名称空间和约束,同时也需要aop的 使用 tx:advice 标签配置事务通知 属性: id : 给事务通知起一个唯一标识 transaction-manager : 给事务通知提供一个事务管理器引用 3.配置AOP中的切入点表达式 4.建立事务通知和切入点表达式的对应关系 5.配置事务的属性: 是在事务的通知 tx:advice 标签内部 --> <!-- 1.配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 2.配置事务的通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 5.配置事务的属性 isolation :用于指定事务的隔离级别。默认方法时DEFAULT,表示使用数据库的默认隔离级别。 propagation :用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。 read-only :用于指定事务的超时时间,默认值是 -1,表示永不超时。如果指定了数值,以秒为单位。 rollback-for :用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。 no-rollback-for:用于指定一个异常,当产生异常时,事务不回滚,产生其他异常时事务回滚。没有默认值,表示任何异常都回滚。 --> <tx:attributes> <!-- 写的两个属性,优先级是 2>1 因为下面的只是一部分(查询的)适用,且只能为只读方式;第一个是所有的都适用 --> <tx:method name="*" propagation="REQUIRED" read-only="false"/> <tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method> </tx:attributes> </tx:advice> <!-- 配置AOP --> <aop:config> <!-- 3.配置切入点表达式 --> <aop:pointcut id="pt1" expression="execution(* com.smile.service.impl.*.*(..))"/> <!-- 4.建立切入点表达式和事务通知的关系 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor> </aop:config>
<!--配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.smile"></context:component-scan>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- spring中基于 注解 的声明式事务控制配置步骤
1.配置事务管理器
2.开启spring对注解事务的支持
3.在需要事务支持的地方使用 @Transactional
-->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.开启spring对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
@Service("accountService")
//@Transactional //需要事务支持
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) //对里面的全部方法配置针对查询的只读形式
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public Account findAccountById(int id) {
return accountDao.findAccountById(id);
}
@Override
@Transactional(propagation = Propagation.REQUIRED, readOnly = false) //只对该方法针对增删改查的读写形式
public void transfer(String sourceName, String targetName, double money) {}