Spring基础巩固(此处专攻,也能成功)

本文详细介绍了Spring框架的核心特性,包括IOC(控制反转)和AOP(面向切面编程)。讨论了Bean的生命周期、配置方式以及依赖注入,讲解了AOP的切点表达式、通知类型和事务管理。同时,对比了XML配置和注解开发AOP的区别,并探讨了Spring的声明式事务控制,包括基于XML和注解的实现。此外,还提到了Spring整合Junit进行测试的方法。
摘要由CSDN通过智能技术生成

 

一.什么是Spring? 

     Spring是分层的 Java SE/EE应用 full-stack(全栈式) 轻量级开源框架。 提供了表现层 SpringMVC和持久层 Spring JDBC Template以及 业务层 事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

两大核心:以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

二.Spring相关API

1.API继承体系介绍

Spring的API体系异常庞大,我们现在只关注两个BeanFactory和ApplicationContext。

2.BeanFactory

 BeanFactory 是IOC容器的核心接口,它定了IOC的基本功能。

 特点:在第一次调用getBean()方法时,创建指定对象的实例

BeanFactory beanFactory=new

XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

3. ApplicationContext

代表应用上下文对象,可以获得spring中IOC容器的Bean对象。

特点:在spring容器启动时,加载并创建所有对象的实例

常用实现类

 

1. ClassPathXmlApplicationContext

   它是从类的根路径下加载配置文件 推荐使用这种

2. FileSystemXmlApplicationContext

   它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置

3. AnnotationConfigApplicationContext

   当使用注解配置容器对象时,需要使用此类来创建 spring 容器它用来读取注解

ApplicationContext app=

new ClassPathXmlApplicationContext("applicationContext.xml");

app.getBean("id");

app.getBean(Class);

常用方法

1. Object getBean(String name);

根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。

2. <T> T getBean(Class<T> requiredType);

根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。

3. <T> T getBean(String name,Class<T> requiredType);

根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况

三.Spring配置文件 (IOC)

1.Bean标签基本配置

<bean id="" class=""></bean>

* 用于配置对象交由Spring来创建

* 基本属性

idBean实例在Spring容器中的唯一标识

classBean的全限定名

* 默认情况下它调用的是类中的 无参构造函数如果没有无参构造函数则不能创建成功

2. Bean标签范围配置(Bean的作用域)

<bean id="" class="" scope=""></bean>

scope属性指对象的作用范围,取值如下:

取值范围

说明

Singleton

默认值,单例的.

Bean的实例化个数:1个

Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

Bean的生命周期

    对象创建:当应用加载,创建容器时,对象就被创建了

    对象运行:只要容器在,对象一直活着

    对象销毁:当应用卸载,销毁容器时,对象就被销毁了

Prototype

多例的。

  Bean的实例化个数:多个

   Bean的实例化时机:当调用getBean()方法时实例化Bean。

   Bean的生命周期

    对象创建:当使用对象时,创建新的对象实例。

    对象运行:只要对象在使用中,就一直活着。

    对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了。

Request

WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中

Session

WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中

globalsession

WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession 相当

于 session

3. Bean生命周期配置

<bean id="" class="" scope="" init-method="" destroy-method=""></bean>

* init-method指定类中的初始化方法名称

* destroy-method指定类中销毁方法名称

4.Bean实例化三种方式

4.1无参构造方法实例化。

它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败。

代码示例:

<bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"/>

 

4.2工厂静态方法实例化。

应用场景

      假如:依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

public class StaticFactoryBean {

  public static UserDao createUserDao(){  

 return new UserDaoImpl();

 }}

 <bean id="userDao" class="com.yang.factory.StaticFactoryBean"

   factory-method="createUserDao" />

 

4.3工厂普通方法实例化

应用场景

    例如:依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

public class DynamicFactoryBean {

public UserDao createUserDao(){    

return new UserDaoImpl();

}

}

<bean id="dynamicFactoryBean" class="com.yang.factory.DynamicFactoryBean"/>

<bean id="userDao" factory-bean="dynamicFactoryBean" factory-

method="createUserDao"/>

5. Bean依赖注入概述

依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。

    在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

    那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是通过框架把持久层对象传入业务层,而不用我们自己去获取。

6. Bean依赖注入方式

共三种方式如下:

6.1构造方法注入

在UserServiceImpl中创建有参构造

public class UserServiceImpl implements UserService {

  private UserDao userDao;

  public UserServiceImpl(UserDao userDao) {

    this.userDao = userDao;

 }

  @Override

  public void save() {

    userDao.save();

 }

}

配置Spring容器调用有参构造时进行注入

 <bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"/>

<bean id="userService" class="com.yang.service.impl.UserServiceImpl">

  <!--<constructor-arg index="0" type="com.yang.dao.UserDao" ref="userDao"/>-

->

  <constructor-arg name="userDao" ref="userDao"/>

</bean>

6.2 set方法注入

在UserServiceImpl中创建set方法

public class UserServiceImpl implements UserService {

  private UserDao userDao;

  public void setUserDao(UserDao userDao) {

    this.userDao = userDao;

 }

  @Override

  public void save() {

    userDao.save();

 }

}

配置Spring容器调用set方法进行注入

<bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"/>

<bean id="userService" class="com.yang.service.impl.UserServiceImpl">

  <property name="userDao" ref="userDao"/>

</bean>

6.3P命名空间注入

   P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件

中,如下:

首先,需要引入P命名空间:

xmlns:p="http://www.springframework.org/schema/p"

其次,需要修改注入方式:

 <bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"/>

<bean id="userService" class="com.yang.service.impl.UserServiceImpl"

   p:userDao-ref="userDao"/>

(上面操作,都是注入Bean对象,除了对象的引用可以注入,普通数据类型和集合都可以在容器中进行注入。注入数据的三种数据类型:1. 普通数据类型2. 引用数据类型3. 集合数据类型)

7. 配置文件模块化

    实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化。

  并列的多个配置文件:

ApplicationContext act =

 new

ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");

主从配置文件

<import resource="applicationContext-xxx.xml"/>

什么是同名bean ?它是如何产生的?

同一个xml中不能出现相同名称的bean,如果出现会报错

多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean

应该如何避免同名Bean?

8.重点配置总结

<bean>标签创建对象并放到springIOC容器

  id属性:在容器中Bean实例的唯一标识不允许重复

  class属性:要实例化的Bean的全限定名

  scope属性:Bean的作用范围常用是Singleton(默认)prototype

<constructor-arg>标签属性注入

name属性属性名称

  value属性:注入的普通属性值

  ref属性:注入的对象引用值

<property>标签属性注入

  name属性:属性名称

  value属性:注入的普通属性值

  ref属性:注入的对象引用值

<list>

  <set>

  <array>

  <map>

  <props>

<import>标签:导入其他的Spring的分文件

四.Spring注解开发

    Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。

1. Spring常用注解分类

注解

说明

@Component

使用在类上用于实例化Bean

@Controller

使用在web层类上用于实例化Bean

@Service

使用在service层类上用于实例化Bean

@Repository

使用在dao层类上用于实例化Bean

@Autowired

使用在字段上用于根据类型依赖注入

@Qualifier

结合@Autowired一起使用,根据名称进行依赖注入

@Resource

相当于@Autowired+@Qualifier,按照名称进行注入

@Value

注入普通属性

@Scope

标注Bean的作用范围

@PostConstruct

使用在方法上标注该方法是Bean的初始化方法

@PreDestroy

使用在方法上标注该方法是Bean的销毁方法

说明:

  JDK11以后完全移除了javax扩展导致不能使用@resource注解。

  需要maven引入依赖

<dependency>

   <groupId>javax.annotation</groupId>

    <artifactId>javax.annotation-api</artifactId>

   <version>1.3.2</version>

</dependency

注意:

   使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

<!--注解的组件扫描-->

<context:component-scan base-package="com.yang"></context:component-scan>

2.注解实现

 2.1 Bean实例化(IOC)

<bean id="userDao" class="com.yang.dao.impl.UserDaoImpl"></bean>

使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。

// @Component(value = "userDao")

@Repository // 如果没有写value属性值Beanid类名首字母小写

public class UserDaoImpl implements UserDao {

  }

2.2属性依赖注入(DI)

<bean id="userService" class="com.yang.service.impl.UserServiceImpl">

  <property name="userDao" ref="userDaoImpl"/>

</bean>

使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入

@Service

public class UserServiceImpl implements UserService {

  @Autowired

  private UserDao userDao;

  // <property name="userDao" ref="userDaoImpl"/>

  // @Autowired

  // @Qualifier("userDaoImpl")

  // @Resource(name = "userDaoImpl")

   public void setUserDao(UserDao userDao) {

    this.userDao = userDao;

 }

}

2.3 @Value

使用@Value进行字符串的注入,结合SPEL表达式获得配置参数

@Service

public class UserServiceImpl implements UserService {

 

  @Value("注入普通数据")

  private String str;

@Value("${jdbc.driver}")

  private String driver;

}

2.4 @Scope

<bean scope=""/>

使用@Scope标注Bean的范围

@Service

@Scope("singleton")

public class UserServiceImpl implements UserService {{

 

}

2.5 Bean生命周期

<bean init-method="init" destroy-method="destory" />

使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法

@PostConstruct

public void init(){

System.out.println("初始化方法....");

}

@PreDestroy

public void destroy(){

System.out.println("销毁方法.....");

}

五. Spring整合Junit

1. 普通Junit测试问题

    在普通的测试类中,需要开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得Bean实例;如果不这么做,那么无法从容器中获得对象。

ApplicationContext applicationContext =

 new ClassPathXmlApplicationContext("applicationContext.xml");

AccountService accountService =

applicationContext.getBean(AccountService.class);

我们可以让SpringJunit负责创建Spring容器来简化这个操作,开发者可以直接在测试类注入Bean实例;但是需要将配置文件的名称告诉它。

2.Spring整合Junit

步骤分析:

1. 导入spring集成Junit的坐标

2. 使用@Runwith注解替换原来的运行器

3. 使用@ContextConfiguration指定配置文件或配置类

4. 使用@Autowired注入需要测试的对象

5. 创建测试方法进行测试

2.1导入spring集成Junit的坐标

<!--此处需要注意的是,spring5 及以上版本要求junit 的版本必须是4.12 及以上-->

<dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-test</artifactId>

  <version>5.1.5.RELEASE</version>

</dependency>

<dependency>

  <groupId>junit</groupId>

  <artifactId>junit</artifactId>

  <version>4.12</version>

  <scope>test</scope>

</dependency>

2.2使用@Runwith注解替换原来的运行器

@RunWith(SpringJUnit4ClassRunner.class)

public class SpringJunitTest {

 

}

2.3使用@ContextConfiguration指定配置文件或配置类

@RunWith(SpringJUnit4ClassRunner.class)

//@ContextConfiguration(value = {"classpath:applicationContext.xml"}) 加载spring

核心配置文件

@ContextConfiguration(classes = {SpringConfig.class}) // 加载spring核心配置类

public class SpringJunitTest {

  }

2.4使用@Autowired注入需要测试的对象

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = {SpringConfig.class})

public class SpringJunitTest {

 

 @Autowired

  private AccountService accountService;

}

2.5创建测试方法进行测试

 @RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = {SpringConfig.class})

public class SpringJunitTest {

 

 @Autowired

  private AccountService accountService;

 

  //测试查询

  @Test

  public void testFindById() {

       Account account = accountService.findById(3);

       System.out.println(account);

  }

}

六.AOP的理解

1.什么是AOP

    AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程。

    AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内。容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

这样做的好处是:

一. 在程序运行期间,在不修改源码的情况下对方法进行功能增强

二. 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码

三.减少重复代码,提高开发效率,便于后期维护

2. AOP底层实现

    实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

3. AOP相关术语

  Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

  在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

* Target目标对象):代理的目标对象

* Proxy 代理):一个类被 AOP 织入增强后就产生一个结果代理类

* Joinpoint连接点):所谓连接点是指那些可以被拦截到的点spring,这些点指的是方法因为

spring只支持方法类型的连接点

* Pointcut切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义

* Advice通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知

分类前置通知后置通知异常通知最终通知环绕通知

* Aspect切面):是切入点和通知引介的结合

* Weaving织入):是指把增强应用到目标对象来创建新的代理对象的过程spring采用动态代理织

AspectJ采用编译期织入和类装载期织入

4.AOP开发明确事项

4.1.开发阶段(我们做的)

1. 编写核心业务代码(目标类的目标方法)切入点。

2. 把公用代码抽取出来,制作成通知(增强功能方法)通知。

3. 在配置文件中,声明切入点与通知间的关系,即切面。

4.2.运行阶段(Spring框架完成的)

   Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

4.3.底层代理实现

  在 Spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

当bean实现接口时,会用JDK代理模式

当bean没有实现接口,用cglib实现( 可以强制使用cglib(在spring配置中加入<aop:aspectj-

autoproxy proxyt-target-class=”true”/>)

5.总结

* aop面向切面编程

* aop底层实现基于JDK的动态代理 基于Cglib的动态代理

* aop的重点概念

Pointcut切入点):真正被增强的方法

Advice通知/ 增强):封装增强业务逻辑的方法

Aspect切面):切点+通知

Weaving织入):将切点与通知结合产生代理对象的过程

 

七. XML配置AOP详解

1.快速入门

1.1步骤分析: 

1. 创建java项目导入AOP相关坐标

2. 创建目标接口和目标实现类定义切入点

3. 创建通知类及方法定义通知

4. 将目标类和通知类对象创建权交给spring

5. 在核心配置文件中配置织入关系及切面

6. 编写测试代码

1.2. 创建java项目,导入AOP相关坐标

<dependencies>

  <!--导入springcontext坐标,context依赖aop-->

  <dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-context</artifactId>

    <version>5.1.5.RELEASE</version>

  </dependency>

  <!-- aspectj的织入(切点表达式需要用到该jar包)-->

  <dependency>

    <groupId>org.aspectj</groupId>

    <artifactId>aspectjweaver</artifactId>

    <version>1.8.13</version>

  </dependency>

  <!--spring整合junit-->

  <dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-test</artifactId>

<version>5.1.5.RELEASE</version>

  </dependency>

  <dependency>

    <groupId>junit</groupId>

    <artifactId>junit</artifactId>

    <version>4.12</version>

  </dependency>

</dependencies>

1.3. 创建目标接口和目标实现类

public interface AccountService {

  public void transfer();

}

 

 public class AccountServiceImpl implements AccountService {

 

  @Override

  public void transfer() {

    System.out.println("转账业务...");

 }

}

1.4. 创建通知类

public class MyAdvice {

  public void before() {

    System.out.println("前置通知...");

 }

}

 

1.5. 将目标类和通知类对象创建权交给spring

<!--目标类交给IOC容器-->

<bean id="accountService" class="com.yang.service.impl.AccountServiceImpl">

</bean>

<!--通知类交给IOC容器-->

<bean id="myAdvice" class="com.yang.advice.MyAdvice"></bean>

 

1.6. 在核心配置文件中配置织入关系,及切面

导入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"

   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">

  <!--目标类交给IOC容器-->

  <bean id="accountService" class="com.yang.service.impl.AccountServiceImpl">

</bean>

  <!--通知类交给IOC容器-->

  <bean id="myAdvice" class="com.yang.advice.MyAdvice"></bean>

  <aop:config>

    <!--引入通知类-->

    <aop:aspect ref="myAdvice">

      <!--配置目标类的transfer方法执行时,使用通知类的before方法进行前置增强-->

      <aop:before method="before"

            pointcut="execution(public void

com.yang.service.impl.AccountServiceImpl.transfer())"></aop:before>

    </aop:aspect>

  </aop:config>

</beans>

1.7. 编写测试代码

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:applicationContext.xml")

class AccountServiceTest {

  @Autowired

  private AccountService accountService;

  @Test

  public void testTransfer() throws Exception {

    accountService.transfer();

 }

}

2. XML配置AOP详解

2.1切点表达式

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

 

访问修饰符可以省略

返回值类型包名类名方法名可以使用星号 * 代替代表任意

包名与类名之间一个点 . 代表当前包下的类两个点 .. 表示当前包及其子包下的类

参数列表可以使用两个点 .. 表示任意个数任意类型的参数列表

例如:

execution(public void com.yang.service.impl.AccountServiceImpl.transfer())

execution(void com.yang.service.impl.AccountServiceImpl.*(..))

execution(* com.yang.service.impl.*.*(..))

execution(* com.yang.service..*.*(..))

切点表达式抽取

    当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替pointcut 属性来引用抽取后的切点表达式。

  

<aop:config>

  <!--抽取的切点表达式-->

<aop:pointcut id="myPointcut" expression="execution(* com.yang.service..*.*

(..))"> </aop:pointcut>

  <aop:aspect ref="myAdvice">

    <aop:before method="before" pointcut-ref="myPointcut"></aop:before>

  </aop:aspect>

</aop:config>

2.2通知类型

通知的配置语法:

<aop:通知类型 method=通知类中方法名 pointcut=切点表达式"></aop:通知类型>

 

名称

标签

说明

前置通知

<aop:before>

用于配置前置通知。指定增强的方法在切入点方法之前执

后置通知

<aop:afterReturning>

用于配置后置通知。指定增强的方法在切入点方法之后执

异常通知

<aop:afterThrowing>

用于配置异常通知。指定增强的方法出现异常后执行

最终通知

<aop:after>

用于配置最终通知。无论切入点方法执行时是否有异常,

都会执行

环绕通知

    <aop:around>

用于配置环绕通知。开发者可以手动控制增强代码在什么

时候执行

注意:通常情况下,环绕通知都是独立使用的

2.3总结

* aop织入的配置

<aop:config>

   <aop:aspect ref=通知类>

   <aop:before method=通知方法名称 pointcut=切点表达式"></aop:before>

    </aop:aspect>

  </aop:config>

                           

* 通知的类型

前置通知、后置通知、异常通知、最终通知

环绕通知

* 切点表达式

execution([修饰符] 返回值类型包名.类名.方法名(参数))

 

八.注解配置AOP详解

1. 快速入门

  步骤分析:

1. 创建java项目导入AOP相关坐标

2. 创建目标接口和目标实现类定义切入点

3. 创建通知类定义通知

4. 将目标类和通知类对象创建权交给spring

5. 在通知类中使用注解配置织入关系升级为切面类

6. 在配置文件中开启组件扫描和 AOP 的自动代理

7. 编写测试代码

1.1.创建java项目,导入AOP相关坐标

<dependencies>

  <!--导入springcontext坐标,context依赖aop-->

  <dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-context</artifactId>

    <version>5.1.5.RELEASE</version>

  </dependency>

  <!-- aspectj的织入-->

  <dependency>

    <groupId>org.aspectj</groupId>

    <artifactId>aspectjweaver</artifactId>

    <version>1.8.13</version>

  </dependency>

  <!--spring整合junit-->

  <dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-test</artifactId>

    <version>5.1.5.RELEASE</version>

  </dependency>

  <dependency>

    <groupId>junit</groupId>

<artifactId>junit</artifactId>

    <version>4.12</version>

  </dependency>

</dependencies>

1.2. 创建目标接口和目标实现类

public interface AccountService {

  public void transfer();

}

 

public class AccountServiceImpl implements AccountService {

 

  @Override

  public void transfer() {

    System.out.println("转账业务...");

 }

}

1.3.创建通知类

public class MyAdvice {

  public void before() {

    System.out.println("前置通知...");

 }

}

1.4. 将目标类和通知类对象创建权交给spring

 @Service

public class AccountServiceImpl implements AccountService {}

@Component

public class MyAdvice {}

1.5. 在通知类中使用注解配置织入关系,升级为切面类

@Component

@Aspect

public class MyAdvice {

  @Before("execution(* com.yang..*.*(..))")

  public void before() {

    System.out.println("前置通知...");

 }

}

1.6. 在配置文件中开启组件扫描和 AOP 的自动代理

<!--组件扫描-->

<context:component-scan base-package="com.yang"/>

<!--aop的自动代理-->

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

1.7. 编写测试代码

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:applicationContext.xml")

class AccountServiceTest {

  @Autowired

  private AccountService accountService;

  @Test

  public void testTransfer() throws Exception {

    accountService.transfer();

 }

}

2.注解配置AOP详解

  2.1切点表达式

切点表达式抽取

@Component

@Aspect

public class MyAdvice {

  @Pointcut("execution(* com.yang..*.*(..))")

  public void myPoint(){}

  @Before("MyAdvice.myPoint()")

  public void before() {

    System.out.println("前置通知...");

 }

2.2通知类型

通知的配置语法:@通知注解(“切点表达式")

名称

标签

说明

前置通知

@Before

用于配置前置通知。指定增强的方法在切入点方法之前执行

后置通知

@AfterReturning

用于配置后置通知。指定增强的方法在切入点方法之后执行

异常通知

@AfterThrowing

用于配置异常通知。指定增强的方法出现异常后执行

最终通知

@After

用于配置最终通知。无论切入点方法执行时是否有异常,都会

执行

环绕通知

@Around

用于配置环绕通知。开发者可以手动控制增强代码在什么时候

执行

注意:

当前四个通知组合在一起时,执行顺序如下:

@Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)

2.3纯注解配置

@Configuration

@ComponentScan("com.yang")

@EnableAspectJAutoProxy //替代 <aop:aspectj-autoproxy />

public class SpringConfig {

}

2.4知识小结

* 使用@Aspect注解标注切面类

* 使用@Before等注解标注通知方法

* 使用@Pointcut注解抽取切点表达式

* 配置aop自动代理 <aop:aspectj-autoproxy/> @EnableAspectJAutoProxy

 

九. Spring事务

1.Spring中的事务控制方式

Spring的事务控制可以分为编程式事务控制和声明式事务控制。

编程式

开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。

声明式

开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的AOP思想。

2.编程式事务控制相关对象(不常用)

2.1 PlatformTransactionManager

    PlatformTransactionManager接口,是spring的事务管理器,里面提供了我们常用的操作事务的方法。

方法

说明

TransactionStatus getTransaction(TransactionDefinition definition);

获取事务的状态信息

void commit(TransactionStatus status);

提交事务

void rollback(TransactionStatus status);

回滚事务

注意:

* PlatformTransactionManager 是接口类型不同的 Dao 层技术则有不同的实现类

* Dao层技术是jdbcTemplatemybatis

DataSourceTransactionManager

* Dao层技术是hibernate

HibernateTransactionManager

* Dao层技术是JPA

JpaTransactionManager

2.2 TransactionDefinition

TransactionDefinition接口提供事务的定义信息(事务隔离级别、事务传播行为等等)

方法

说明

int getIsolationLevel()

获得事务的隔离级别

int getPropogationBehavior()

获得事务的传播行为

int getTimeout()

获得超时时间

boolean isReadOnly()

是否只读

2.3代码实现

 2.3.1配置文件

<!--事务管理器交给IOC-->

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

  <property name="dataSource" ref="dataSource"/>

</bean

 

 2.3.2业务层代码

@Service

public class AccountServiceImpl implements AccountService {

  @Autowired

  private AccountDao accountDao;

  @Autowired

  private PlatformTransactionManager transactionManager;

  @Override

  public void transfer(String outUser, String inUser, Double money) {

    // 创建事务定义对象

   DefaultTransactionDefinition def = new DefaultTransactionDefinition();

  // 设置是否只读false支持事务

   def.setReadOnly(false);

  // 设置事务隔离级别可重复读mysql默认级别

   def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);

  // 设置事务传播行为必须有事务

   def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

   // 配置事务管理器

  TransactionStatus status = transactionManager.getTransaction(def);

   try {

  // 转账

   accountDao.out(outUser, money);

  accountDao.in(inUser, money);

   // 提交事务

  transactionManager.commit(status);

     } catch (Exception e) {

   e.printStackTrace();

   // 回滚事务

   transactionManager.rollback(status);

    }

  }

}

2.3.3小结

Spring中的事务控制主要就是通过这三个API实现的

* PlatformTransactionManager 负责事务的管理它是个接口其子类负责具体工作

* TransactionDefinition 定义了事务的一些相关参数

* TransactionStatus 代表事务运行的一个实时状态

理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态。

3. 基于XML的声明式事务控制(重中之中)

3.1介绍

   在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。

声明式事务控制明确事项:

    核心业务代码(目标对象) (切入点是谁?)

    事务增强代码(Spring已提供事务管理器))(通知是谁?)

    切面配置(切面如何配置?)

3.2快速入门

  需求:

     使用spring声明式事务控制转账业务。

步骤分析  

1. 引入tx命名空间

2. 事务管理器通知配置

3. 事务管理器AOP配置

4. 测试事务控制转账业务代码

3.2.1引入tx命名空间

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w2.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/s chema/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">

</beans>

3.2.2事务管理器通知配置

<!--事务管理器-->

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

  <property name="dataSource" ref="dataSource"></property>

</bean>

<!--通知增强-->

<tx:advice id="txAdvice" transaction-manager="transactionManager">

  <!--定义事务的属性-->

  <tx:attributes>

    <tx:method name="*"/>

  </tx:attributes>

</tx:advice>

3.2.3事务管理器AOP配置

<!--aop配置-->

<aop:config>

  <!--切面配置-->

  <aop:advisor advice-ref="txAdvice"

        pointcut="execution(* com.yang.serivce..*.*(..))">

  </aop:advisor>

</aop:config>

3.2.4测试事务控制转账业务代码

 @Override

public void transfer(String outUser, String inUser, Double money) {

  accountDao.out(outUser, money);

  // 制造异常

   int i = 1 / 0;

   accountDao.in(inUser, money);

}

3.3事务参数的配置详解

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>

* name切点方法名称

* isolation:事务的隔离级别

* propogation事务的传播行为

* timeout超时时间

* read-only是否只读

CRUD常用配置

<tx:attributes>

  <tx:method name="save*" propagation="REQUIRED"/>

  <tx:method name="delete*" propagation="REQUIRED"/>

  <tx:method name="update*" propagation="REQUIRED"/>

  <tx:method name="find*" read-only="true"/>

  <tx:method name="*"/>

</tx:attributes>

4. 基于注解的声明式事务控制(重中之中)

4.1注解(注:配置用的xml)

步骤分析:

1. 修改service增加事务注解

2. 修改spring核心配置文件开启事务注解支持

修改service层,增加事务注解

@Service

public class AccountServiceImpl implements AccountService {

  @Autowired

  private AccountDao accountDao;

  @Transactional(propagation = Propagation.REQUIRED, isolation =

Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)

  @Override

  public void transfer(String outUser, String inUser, Double money) {

    accountDao.out(outUser, money);

    int i = 1 / 0;

    accountDao.in(inUser, money);

 }

}

 修改spring核心配置文件,开启事务注解支持

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w2.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">

  <!--省略之前datsSourcejdbcTemplate、组件扫描配置-->

  <!--事务管理器-->

  <bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

  </bean>

 

  <!--事务的注解支持-->

  <tx:annotation-driven/>

</beans>

 

4.2纯注解

核心配置类

@Configuration  // 声明为spring配置类

@ComponentScan("com.yang") // 扫描包

@Import(DataSourceConfig.class) // 导入其他配置类

@EnableTransactionManagement // 事务的注解驱动

public class SpringConfig {

   @Bean

   public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource) {

   return new JdbcTemplate(dataSource);

   }

  @Bean("transactionManager")

   public PlatformTransactionManager getPlatformTransactionManager(@Autowired

DataSource dataSource) {

   return new DataSourceTransactionManager(dataSource);

  }

}

 

数据源配置类

 @PropertySource("classpath:jdbc.properties")

public class DataSourceConfig {

  @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

  public DataSource getDataSource() {

    DruidDataSource dataSource = new DruidDataSource();

    dataSource.setDriverClassName(driver);

    dataSource.setUrl(url);

    dataSource.setUsername(username);

    dataSource.setPassword(password);

    return dataSource;

 }

}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值