Spring学习笔记

主要是看视频学习之后,做个笔记,可能有错误的地方

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核心容器

核心容器的两个接口

  1. ApplicationContext:在构建核心容器时,创建对象采用的策略是立即加载,也就是说,只要一读取完配置文件,就创建配置文件里的对象,适用于单例对象,实际开发中,采用这个接口
  2. BeanFactory:在构建核心容器时,创建对象采取的策略时是延迟加载,也就是说,什么时候根据id获取对象,什么时候才创建对象,适用于多例对象

ApplicationContext的三个常用实现类

  1. ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,配置文件必须在类路径下
  2. FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
  3. 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对象

  1. bean标签id属性:唯一标识符
  2. bean标签class属性:交给spring管理的某个接口的实现类
  3. property标签name属性:被注入对象属性的名称
  4. 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的作用范围,取值:

  1. singleton:单例的(默认值)
  2. prototype:多例的
  3. request:作用于web应用的请求范围
  4. session:作用于web应用的会话范围
  5. global-session:作用于集群环境的会话范围,当不是集群环境时,就是session
<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl" scope="singleton"></bean>
4.5.3 bean对象的生命周期

单例对象:

  1. 出生:当容器创建对象时出生
  2. 活着:只要容器还在,对象一直活着
  3. 死亡:容器销毁,对象死亡
  4. 总结:单例对象的生命周期和容器相同

多例对象:

  1. 出生:当我们使用对象时,spring创建对象
  2. 活着:对象只要在使用过程中就一直活着
  3. 死亡:当对象长时间不用且没有别的对象引用时,由java的垃圾回收器回收

init-method属性和destroy-method属性:指定对象创建和销毁时调用的方法

<bean id="accountService" class="com.zhq.service.impl.AccountServiceImpl" init-method="init" destroy-method="destroy"></bean>
4.5.4 依赖注入的两种方式

能注入的数据类型

  1. 基本类型和String
  2. 其他bean类型(xml配置或者注解过的bean)
  3. 复杂类型、集合类型

第一种方式:使用构造函数注入

  1. type属性:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个参数的类型
  2. index属性:用于指定要注入的数据在构造函数中索引的位置,索引从0开始
  3. name属性:用于指定要注入的数据在构造函数中的参数名称(常用)
  4. value属性:用于提供基本类型和String类型的数据
  5. 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方法注入(常用)

  1. name属性:用于指定调用的set方法去除set后的名称
  2. value属性:用于提供基本类型和String类型的数据
  3. 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 集合类型的注入
  1. 用于给List结构集合注入的标签:list、array、set通用
  2. 用于给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 创建方式
  1. Component:用于把当前类对象存入spring容器中,value属性用于指定bean的id,当我们不写时,它的默认值是当前类名且首字母小写
  2. Controller:作用相同,一般用于表现层
  3. Service:作用相同,一般用于业务层
  4. 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配置

  1. Autowired:自动按照类型注入,只要容器中有唯一一个bean对象类型和要注入的变量类型匹配成功,就可以注入成功,如果有多个类型相同的,首先按照类型匹配,接着按照变量名称在匹配的结果中继续查找,如果找不到则报错
  2. Qualifier:在按照类型注入的基础上再按照名称注入,它给类成员注入时不能单独使用,但是在给方法参数注入时可以,value属性用于指定注入bean的id,要和Autowired一起使用
  3. Resource:直接按照bean的id注入,它可以独立使用,name属性用于指定bean的id
  4. 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

  1. Configuration:用于指定当前类是一个配置类,当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写
  2. ComponentScan:用于指定spring在创建容器时要扫描的包
  3. Import:用于导入其他的配置类(也可以其他配置类加Configuration注解,或作为AnnotationConfigApplicationContext参数),使用Import注解之后,那么有Import注解的类就是父配置类,导入的就是子配置类
  4. PropertySource:用于指定properties文件的位置
  5. 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

  1. aop:config标签:表明开始aop配置
  2. aop:aspect标签:表明配置切面,id属性是切面的唯一标识符,ref属性是执行通知的bean对象的id
  3. aop:before标签:配置前置通知,在切入点方法之前执行,method属性指定执行通知类的哪个方法,pointcut属性指定切入点表达式,该表达式指的是对哪些方法增强,pointcut-ref属性指定引用的切入点表达式id
  4. aop:after-returning标签:配置后置通知,在切入点方法正常执行之后执行
  5. aop:after-throwing标签:配置异常通知,在切入点方法执行产生异常后执行
  6. aop:after标签:配置最终通知,无论写入点方法是否正常执行都会在其后面执行
  7. aop:around标签:配置环绕通知,环绕通知包含了前置后置异常最终,无需再配其他通知标签
  8. aop:pointcut标签:配置切入点表达式,id属性用于指定表达式唯一标识符,expression属性用于指定表达式内容,此标签写在aspect标签内部使用则只能当前切面使用,配置在aspect标签外部则必须写在切面配置之前
  9. 切入点表达式:execution (访问修饰符 返回值 包名.包名…类名.方法名(参数列表))
  10. 标准写法:public void com.zhq.service.impl.AccountServiceImpl.saveAccount(),访问修饰符可以省略,返回值可以使用通配符*表示任意返回值,包名可以使用通配符*表示任意包,但是有几级包就要写几个通配符,包名可以使用…表示当前包及其子包,类名和方法名都可以使用通配符*,参数直接写数据类型,基本类型直接写名称,引用类型写包名.类名,通配符*表示任意类型但必须有参数,…表示有无参数均可,有参数则是任意类型
  11. 全通配写法:* *…*.*(…)
  12. 实际开发中通常写法:切到业务层实现类下所有方法,* 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>
  1. Aspect注解:表示当前类是一个切面类
  2. Pointcut注解:配置切面表达式
  3. Before注解:配置前置通知
  4. AfterReturning注解:配置后置通知
  5. AfterThrowing注解:配置异常通知
  6. After注解:配置最终通知,spring在基于注解的aop开发中,最终通知会先于后置通知执行
  7. 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>

配置事务通知

  1. tx:advice标签:配置事务通知,id属性是唯一标识符,transaction-manager属性指定事务管理器id
  2. tx:attributes标签:配置事务属性
  3. 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

  1. Runwith:把原有的main方法替换成spring提供的
  2. ContextConfiguration:告知spring的运行器,spring和ioc创建时基于xml还是注解的,location属性用于指定xml文件的位置,加上classpath关键字表示在类路径下,classes属性用于指定注解类所在的位置
  3. 当使用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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值