主要是看视频学习之后,做个笔记,可能有错误的地方
1 三层架构
表现层:用于展示数据
业务层:用于处理业务需求
持久层:用于和数据库交互
2 Spring介绍
Spring是一个业务层框架,以IoC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)为内核
3 Maven中配置Spring
<!-- 配置spring框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 配置spring整合junit测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 配置spring切面表达式的解析 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- 配置spring提供的jdbc模板 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 配置spring的事务管理 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
4 IoC控制反转
4.1 为什么要使用IoC
IoC可以降低程序间的依赖关系,降低程序间的耦合
耦合:简单理解为程序间的依赖关系,类间的依赖,方法间的依赖
解耦:降低程序之间的耦合关系
实际开发中应该做到,编译期间不依赖,运行时才依赖
解耦的思路;原先通过new创建对象是强耦合的,可以先创建一个工厂对象,使用对象的全限定类名通过反射,由工厂对象创建,这样可以使得编译期间不依赖,并且可以修改配置文件来修改创建的对象的全限定类名
4.2 IoC思想
最初的控制权是在这个类本身(可以自己new一个对象创建所需对象),也可以将控制权交给工厂对象(由工厂对象使用全限定类名来反射创建对象),这种将控制权交给工厂对象的思想成为控制反转
在spring中有一个核心容器来管理这些对象,控制反转就是将对象的控制权交给核心容器
4.3 DI依赖注入
Dependency Injection依赖注入,是spring框架IoC的具体实现
通过控制反转,把对象的创建交给了spring,IoC解耦只是降低他们的依赖关系,但依赖关系不会消除
依赖注入就是当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
如果是经常变化的数据,则不适用于依赖注入
4.4 获取IoC核心容器
核心容器的两个接口
- ApplicationContext:在构建核心容器时,创建对象采用的策略是立即加载,也就是说,只要一读取完配置文件,就创建配置文件里的对象,适用于单例对象,实际开发中,采用这个接口
- BeanFactory:在构建核心容器时,创建对象采取的策略时是延迟加载,也就是说,什么时候根据id获取对象,什么时候才创建对象,适用于多例对象
ApplicationContext的三个常用实现类
- ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,配置文件必须在类路径下
- FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
- AnnotationConfigApplicationContext:它是用于读取注解创建容器的
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
IAccountService as = (IAccountService) ac.getBean("accountService");
4.5 XML配置
在resources目录下创建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">
...
</beans>
配置Bean对象
- bean标签id属性:唯一标识符
- bean标签class属性:交给spring管理的某个接口的实现类
- property标签name属性:被注入对象属性的名称
- property标签ref属性:用于指定其他bean类型数据
<!-- 把对象的创建交给spring来管理 -->
<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl">
<!-- 常用的依赖注入方式 -->
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.zhq.dao.impl.AccountDaoImpl"></bean>
bean是可重用组件,JavaBean != 实体类,是java语言编写的可重用组件
4.5.1 bean对象的三种创建方式
第一种方式:使用默认构造函数创建对象并存入spring容器,此时如果类中没有默认构造函数,则对象无法创建
<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl"></bean>
第二种方式:使用普通工厂中的方法(某个类的方法)创建对象并存入spring容器
<bean id="instanceFactory" class="com.zhq.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
第三种方式:使用工厂中的静态方法(某个类的静态方法)创建对象并存入spring容器
<bean id="accountService" class="com.zhq.factory.StaticFactory" factory-method="getAccountService"></bean>
4.5.2 bean对象的作用范围
bean标签的scope属性用于指定bean的作用范围,取值:
- singleton:单例的(默认值)
- prototype:多例的
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的会话范围,当不是集群环境时,就是session
<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl" scope="singleton"></bean>
4.5.3 bean对象的生命周期
单例对象:
- 出生:当容器创建对象时出生
- 活着:只要容器还在,对象一直活着
- 死亡:容器销毁,对象死亡
- 总结:单例对象的生命周期和容器相同
多例对象:
- 出生:当我们使用对象时,spring创建对象
- 活着:对象只要在使用过程中就一直活着
- 死亡:当对象长时间不用且没有别的对象引用时,由java的垃圾回收器回收
init-method属性和destroy-method属性:指定对象创建和销毁时调用的方法
<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl" init-method="init" destroy-method="destroy"></bean>
4.5.4 依赖注入的两种方式
能注入的数据类型
- 基本类型和String
- 其他bean类型(xml配置或者注解过的bean)
- 复杂类型、集合类型
第一种方式:使用构造函数注入
- type属性:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个参数的类型
- index属性:用于指定要注入的数据在构造函数中索引的位置,索引从0开始
- name属性:用于指定要注入的数据在构造函数中的参数名称(常用)
- value属性:用于提供基本类型和String类型的数据
- ref属性:用于提供其他bean类型数据,就是spring在IoC核心容器中出现过的bean对象
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供
<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
第二种方式:set方法注入(常用)
- name属性:用于指定调用的set方法去除set后的名称
- value属性:用于提供基本类型和String类型的数据
- ref属性:用于提供其他bean类型数据,就是spring在IoC核心容器中出现过的bean对象
优势:创建对象时,不需要限制必须提供参数,可以直接使用默认构造函数
弊端:如果有某个成员必须有值,则获取对象时有可能set方法没有执行
<bean id="accountService2" class="com.zhq.service.impl.AccountServiceImpl2">
<property name="name" value="TEST"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
4.5.5 集合类型的注入
- 用于给List结构集合注入的标签:list、array、set通用
- 用于给Map结构集合注入的标签:map、props通用
<bean id="accountService3" class="com.zhq.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>AAA</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="AAA"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testC">CCC</prop>
</props>
</property>
</bean>
4.6 注解和XML混用
在resources目录下创建bean.xml
并告知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"
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.zhq"></context:component-scan>
</beans>
4.6.1 创建方式
- Component:用于把当前类对象存入spring容器中,value属性用于指定bean的id,当我们不写时,它的默认值是当前类名且首字母小写
- Controller:作用相同,一般用于表现层
- Service:作用相同,一般用于业务层
- Repository:作用相同,一般用于持久层
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
4.6.2 作用范围
Scope:用于指定bean的作用范围,value属性指定范围,常用取值:singleton、prototype
@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements IAccountService {
4.6.3 生命周期
PostConstruct:用于指定初始化方法
PreDestroy:用于指定销毁方法
@PostConstruct
public void init(){
System.out.println("init method");
}
@PreDestroy
public void destroy(){
System.out.println("destroy method");
}
4.6.4 依赖注入
使用注解方式注入时,不需要实现set方法
Autowired、Qualifier、Resource只能注入其他bean类型数据
Value用于注入基本类型和String类型
集合类型数据只能通过xml配置
- Autowired:自动按照类型注入,只要容器中有唯一一个bean对象类型和要注入的变量类型匹配成功,就可以注入成功,如果有多个类型相同的,首先按照类型匹配,接着按照变量名称在匹配的结果中继续查找,如果找不到则报错
- Qualifier:在按照类型注入的基础上再按照名称注入,它给类成员注入时不能单独使用,但是在给方法参数注入时可以,value属性用于指定注入bean的id,要和Autowired一起使用
- Resource:直接按照bean的id注入,它可以独立使用,name属性用于指定bean的id
- Value:用于注入基本类型和String类型的数据,value属性用于指定数据的值,它可以使用spring中EL表达式,${表达式}
// @Autowired
// @Qualifier("accountDaoImpl")
@Resource("accountDaoImpl")
private IAccountDao accountDao;
@Value("com.mysql.jdbc.Driver")
private String driver;
// 类上需要注解@PropertySource(value = "classpath:jdbcConfig.properties")
@Value("${jdbc.url}")
private String url;
4.7 纯注解
需要创建一个配置类SpringConfiguration,作用等同于bean.xml
- Configuration:用于指定当前类是一个配置类,当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写
- ComponentScan:用于指定spring在创建容器时要扫描的包
- Import:用于导入其他的配置类(也可以其他配置类加Configuration注解,或作为AnnotationConfigApplicationContext参数),使用Import注解之后,那么有Import注解的类就是父配置类,导入的就是子配置类
- PropertySource:用于指定properties文件的位置
- Bean:用于把当前方法的返回值作为Bean对象存入spring的ioc容器中,使用注解配置方法时,如果方法有参数,那么spring框架会去容器中查找有没有可用的bean对象,查找的方式和Autowired注解方式是一样的,先匹配类型,再匹配名称
@Configuration
@ComponentScan(value = "com.zhq")
@Import({JdbcConfig.class, TransactionConfig.class})
@PropertySource(value = "classpath:jdbcConfig.properties")
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
@Bean(name = "dataSource")
public DataSource createDataSource() {
...
}
}
5 AOP面向切面编程
5.1 AOP思想
例子:在业务层执行方法的时候,缺少事务的控制(事务提交、事务回滚等),如果在每一个方法中加入事务控制的代码将会很臃肿,所以可以采用动态代理的方式,对业务层代码进行增强,加入事务控制的内容
作用:在程序运行期间,不修改源码对已有方法增强,对业务逻辑各个部分进行隔离,提高程序重用性
优势:减少重复开发代码,提高开发效率,维护方便
实现方式:通过配置实现动态代理
5.2 AOP相关术语
Joinpoint(连接点):指那些被拦截到的点,在spring中,这些点指的是方法
Pointcut(切入点):指要对哪些Joinpoint进行拦截的定义
Advice(通知/增强):指拦截到Joinpoint之后所要做的事情就是通知,通知的类型有前置通知,后置通知,异常通知,最终通知,环绕通知
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
Target(目标对象):代理的目标对象
Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面):是切入点和通知(引介)的结合
5.3 Spring对AOP做的事
spring框架一旦监控到切入点方法被执行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整代理逻辑运行
5.4 XML配置
在resources目录下创建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: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
http://www.springframework.org/schema/aop/spring-aop.xsd">
...
</beans>
配置AOP
- aop:config标签:表明开始aop配置
- aop:aspect标签:表明配置切面,id属性是切面的唯一标识符,ref属性是执行通知的bean对象的id
- aop:before标签:配置前置通知,在切入点方法之前执行,method属性指定执行通知类的哪个方法,pointcut属性指定切入点表达式,该表达式指的是对哪些方法增强,pointcut-ref属性指定引用的切入点表达式id
- aop:after-returning标签:配置后置通知,在切入点方法正常执行之后执行
- aop:after-throwing标签:配置异常通知,在切入点方法执行产生异常后执行
- aop:after标签:配置最终通知,无论写入点方法是否正常执行都会在其后面执行
- aop:around标签:配置环绕通知,环绕通知包含了前置后置异常最终,无需再配其他通知标签
- aop:pointcut标签:配置切入点表达式,id属性用于指定表达式唯一标识符,expression属性用于指定表达式内容,此标签写在aspect标签内部使用则只能当前切面使用,配置在aspect标签外部则必须写在切面配置之前
- 切入点表达式:execution (访问修饰符 返回值 包名.包名…类名.方法名(参数列表))
- 标准写法:public void com.zhq.service.impl.AccountServiceImpl.saveAccount(),访问修饰符可以省略,返回值可以使用通配符*表示任意返回值,包名可以使用通配符*表示任意包,但是有几级包就要写几个通配符,包名可以使用…表示当前包及其子包,类名和方法名都可以使用通配符*,参数直接写数据类型,基本类型直接写名称,引用类型写包名.类名,通配符*表示任意类型但必须有参数,…表示有无参数均可,有参数则是任意类型
- 全通配写法:* *…*.*(…)
- 实际开发中通常写法:切到业务层实现类下所有方法,* com.zhq.service.impl.*.*(…)
<!-- 配置aop -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.zhq.service.impl.*.*(..))"/>
<!-- 配置切面 -->
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置通知的类型,并且建立通知方法和切入点方法的关联 -->
<!-- 配置前置通知 -->
<aop:before method="beforePrintLog" pointcut="execution(* com.zhq.service.impl.*.*(..))"/>
<!-- 配置后置通知 -->
<aop:after-returning method="afterRunningPrintLog" pointcut-ref="pt1"/>
<!-- 配置异常通知 -->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"/>
<!-- 配置最终通知 -->
<aop:after method="afterPrintLog" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<aop:around method="aroundPrintLog" pointcut-ref="pt1"/>
<aop:pointcut id="pt1" expression="execution(* com.zhq.service.impl.*.*(..))"/>
</aop:aspect>
</aop:config>
环绕通知:是spring提供的一种可以在代码中手动控制增强方法的执行方式
spring提供了一个接口ProceedingJoinPoint,该接口有一个方法proceed(),相当于执行切入点方法,该接口可以作为环绕通知的方法参数,在程序执行时spring会提供该接口的实现类
public Object aroundPrintLog(ProceedingJoinPoint pjt){
Object rtValue = null;
try{
Object[] args = pjt.getArgs();
System.out.println("前置");
rtValue = pjt.proceed(args); // 明确调用切入点方法
System.out.println("后置");
return rtValue;
}catch (Throwable e){
System.out.println("异常");
throw new RuntimeException(e);
}finally {
System.out.println("最终");
}
}
5.5 注解和XML混用
在resources目录下创建bean.xml
并告知spring在创建容器时要扫描的包,和开启注解aop的支持
<?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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.zhq"></context:component-scan>
<!-- 配置spring开启注解aop的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- Aspect注解:表示当前类是一个切面类
- Pointcut注解:配置切面表达式
- Before注解:配置前置通知
- AfterReturning注解:配置后置通知
- AfterThrowing注解:配置异常通知
- After注解:配置最终通知,spring在基于注解的aop开发中,最终通知会先于后置通知执行
- Around注解:配置环绕通知
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.zhq.service.impl.*.*(..))")
private void pt1(){}
@Before("execution(* com.zhq.service.impl.*.*(..))")
public void beforePrintLog(){
System.out.println("前置通知");
}
@AfterReturning("pt1()")
public void afterRunningPrintLog(){
System.out.println("后置通知");
}
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("异常通知");
}
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知");
}
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjt){
Object rtValue = null;
try{
Object[] args = pjt.getArgs();
System.out.println("前置");
rtValue = pjt.proceed(args); // 明确调用切入点方法
System.out.println("后置");
return rtValue;
}catch (Throwable e){
System.out.println("异常");
throw new RuntimeException(e);
}finally {
System.out.println("最终");
}
}
}
5.6 纯注解
在配置类上使用EnableAspectJAutoProxy注解,开启注解aop的支持
...
@EnableAspectJAutoProxy
public class SpringConfiguration {
6 事务控制
除了通过AOP自己实现事务的控制外,spring框架提供了一组事务控制的接口
6.1 XML配置
在resources目录下创建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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
...
</beans>
配置事务管理器,传入数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/java_study_spring"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
配置事务通知
- tx:advice标签:配置事务通知,id属性是唯一标识符,transaction-manager属性指定事务管理器id
- tx:attributes标签:配置事务属性
- tx:method标签:配置事务需要增强的业务层方法,name属性是方法名,可以使用通配符*,propagation属性指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择,查询方法可以选择SUPPORTS,isolation属性指定事务的隔离级别,默认值是DEFAULT表示使用数据库级别,read-only属性指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写,timeout属性指定事务的超时时间,默认值是-1,表示永不超时,以秒为单位,rollback-for属性指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚,没有默认值,表示任何异常都回滚,no-rollback-for属性指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚,没有默认值,表示任何异常都回滚
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
建立事务通知和切入点的联系
aop:advisor标签:建立事务通知和切入点的联系,advice-ref属性用于引用一个事务通知对象
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.zhq.service.impl.*.*(..))"/>
<!-- 建立切入点表达式和事务通知的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
6.2 注解和XML混用
在resources目录下创建bean.xml
并告知spring在创建容器时要扫描的包,和开启注解aop的支持,和开启注解事务的支持
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.zhq"></context:component-scan>
<!-- 配置spring开启注解aop的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 开启spring对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
同样需要在bean.xml中配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/java_study_spring"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
Transactional注解:需要事务控制的切入点类或方法,如果每个方法的注解参数不同,则需要每个方法独立注解
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void transfer(String sourceName, String targetName, float money) {
...
}
}
6.3 纯注解
在配置类上使用EnableTransactionManagement注解,开启注解事务的支持,并且需要创建事务管理器
...
@EnableTransactionManagement
public class SpringConfiguration {
@Bean(name = "transactionManager")
public PlatformTransactionManager createPlatformTransactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
7 Spring中的JDBC操作
7.1 JdbcTemplate
在bean.xml中配置jdbcTemplate
<!-- 配置dao -->
<bean id="accountDao" class="com.zhq.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 配置jdbcTemplate -->
<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.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/java_study_spring"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
dao层的使用方法
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Account findAccountById(Integer accountId) {
List<Account> accounts = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
return accounts.isEmpty() ? null : accounts.get(0);
}
public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?, money=? where id=?", account.getName(), account.getMoney(), account.getId());
}
}
7.2 JdbcDaoSupport
在bean.xml中配置数据源即可
<!-- 配置dao -->
<bean id="accountDao" class="com.zhq.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/java_study_spring"></property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>
dao层的使用方法
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
public Account findAccountById(Integer accountId) {
List<Account> accounts = super.getJdbcTemplate().query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
return accounts.isEmpty() ? null : accounts.get(0);
}
public void updateAccount(Account account) {
super.getJdbcTemplate().update("update account set name=?, money=? where id=?", account.getName(), account.getMoney(), account.getId());
}
}
8 Spring整合Junit
- Runwith:把原有的main方法替换成spring提供的
- ContextConfiguration:告知spring的运行器,spring和ioc创建时基于xml还是注解的,location属性用于指定xml文件的位置,加上classpath关键字表示在类路径下,classes属性用于指定注解类所在的位置
- 当使用spring 5.x版本的时候,要求junit的jar必须时4.12及以上
@RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration(locations = "classpath:bean.xml")
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
@Autowired
private IAccountService as;