Spring入门笔记

本文详细介绍了Spring框架的核心特性,包括IoC容器的bean管理,如依赖注入的XML配置与注解方式,以及AOP面向切面编程的原理和应用。此外,还讲解了Spring的事务管理,包括事务的隔离级别、传播行为和回滚规则,以及如何通过注解和配置文件实现事务控制。最后,讨论了Spring与MyBatis的集成,展示了如何配置数据源、事务管理器以及使用注解驱动的事务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring入门笔记

一、Spring概述

1. Spring框架是什么?

Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。以 IoC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为核心,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

2. spring的优势

  • 方便解耦,简化开发

    通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring容器进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

  • AOP编程的支持

    通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以通过 AOP 轻松应付。

  • 声明式事务的支持

    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

  • 方便集成各种优秀框架

    Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring 提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持,简化框架的使用。

  • 降低 JavaEE API的使用难度

    Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

  • Java源码是经典学习范例

    Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

3. spring的体系结构

image-20211121215828554

Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、 Web、面向切面编程(AOP, Aspects)、提供JVM的代理(Instrumentation)、消息发送(Messaging)、 核心容器(Core Container)和测试(Test)。

二、Ioc的简介

1. 耦合和内聚

  • 耦合(Coupling):代码书写过程中所使用技术的结合紧密度,用于衡量软件中各个模块之间的互联程度

    耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。

  • 内聚(Cohesion):代码书写过程中单个模块内部各组成部分间的联系,用于衡量软件中各个功能模块内部的功能联系

    内聚标志一个模块内各个元素彼此结合的紧密程度,内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。

  • 程序书写的目标:高内聚,低耦合。

    就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却不要那么紧密

2. Ioc 控制反转

百度百科给出的解释:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代 码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

ioc的体现: 
servlet 1: 创建类继承HttpServelt 
		2:  在web.xml 注册servlet
            <servlet-name> myservlet </servlet-name>
            <servelt-class>com.bjpwernode.controller.MyServlet1</servlet-class>
        3. 没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()
		4. ServletTomcat服务器它能你创建的。 Tomcat也称为容器
		   Tomcat作为容器:里面存放的有Servlet对象, ListenerFilter对象

3. DI 依赖注入

依赖注入DI (Dependency Injection),是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的

那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

Spring 框架使用依赖注入(DI)实现 IoC。

Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称 为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式 来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

三、基于xml的依赖注入

1.前期准备

  1. 创建 maven 项目
  2. 引入 maven 依赖 pom.xml
<dependencies>
        <!--spring-context依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.12</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
  1. 定义接口与实体类
  2. 创建 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--告诉spring创建对象
    声明bean , 就是告诉spring要创建某个类的对象
        id:对象的自定义名称,唯一值。 spring通过 id 属性访问 BeanBeanBean 间的依赖关系也是通过 id 属性关联的。
        class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

    spring就完成 SomeService someService = new SomeServiceImpl();
    spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
        springMap.put(id的值, 对象);
        例如 springMap.put("someService", new SomeServiceImpl());
        一个bean标签声明一个对象。
    -->

    <!--非自定义类的对象-->
    <bean id="myDate" class="java.util.Date"/>

    <bean id="myService" class="com.hang.service.impl.MyServiceImpl"/>
</beans>
<!--
   spring的配置文件
   1.beans : 是根标签,spring把java对象成为bean。
   2.spring-beans.xsd 是约束文件,和mybatis指定  dtd是一样的。
-->

  1. 定义测试类
/**
* spring默认创建对象的时间:在创建spring的容器时,会创建配置文件中的所有的对象。
* spring创建对象:默认调用的是无参数构造方法
*/
@Test
public void beanTest(){
    //使用spring容器创建的对象
    //1.指定spring配置文件的名称
    String config = "beans.xml";

    // 2.创建表示spring容器的对象, ApplicationContext
    // ApplicationContext就是表示Spring容器,通过容器获取对象了
    // ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
    ApplicationContext  applicationContext = new ClassPathXmlApplicationContext(config);

    // 从容器中获取某个对象, 你要调用对象的方法
    // getBean("配置文件中的bean的id值")
    MyService myService = (MyService) applicationContext.getBean("myService");

    //使用spring创建好的对象调用对象的方法
    myService.doSome();
}
  1. 容器接口和实现类

ApplicationContext 接口(容器)

ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色。其实现 类有两个

image-20211122130952803

若 Spring 配置文件存放在项目的类路径下,则使用 ClassPathXmlApplicationContext 实现 类进行加载。

ApplicationContext 容器中对象的装配时机

ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。 以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。

使用 spring 容器创建的 java 对象

image-20211122131144861

2. 注入分类

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化 是由容器自动完成的,称为注入。 根据注入方式的不同,常用的有两类:set 注入、构造注入。

1. set注入(掌握)

set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、 直观,因而在 Spring 的依赖注入中大量使用。

  • 名称:property

  • 类型:标签

  • 归属:bean标签

  • 作用:使用set方法的形式为bean提供资源

  • 格式:

    <bean>
    	<property />
    </bean>
    
  • 基本属性:

    <property name="propertyName" value="propertyValue" ref="beanId"/>
    

​ name:对应bean中的属性名,要求该属性必须提供可访问的set方法(严格规范为此名称是set方法对应名称)

​ value:设定非引用类型属性对应的值,不能与ref同时使用

​ ref:设定引用类型属性对应bean的id ,不能与value同时使用

  • 注意:一个bean可以有多个property标签
2. 构造注入(理解)
  • 名称:constructor-arg

  • 类型:标签

  • 归属:bean标签

  • 作用:使用构造方法的形式为bean提供资源,兼容早期遗留系统的升级工作

  • 格式:

    <bean>
    	<constructor-arg />
    </bean>
    
  • 基本属性:

    <constructor-arg name="argsName" value="argsValue />
    

​ name:对应bean中的构造方法所携带的参数名

​ value:设定非引用类型构造方法参数对应的值,不能与ref同时使用

其他属性:

<constructor-arg index="arg-index" type="arg-type" ref="beanId"/>

​ ref:设定引用类型构造方法参数对应bean的id ,不能与value同时使用

​ type :设定构造方法参数的类型,用于按类型匹配参数或进行类型校验

​ index :设定构造方法参数的位置,用于按位置匹配参数,参数index值从0开始计数

  • 注意:一个bean可以有多个constructor-arg标签
3. 集合类型数据注入
  • 名称:array,list,set,map,props
  • 类型:标签
  • 归属:property标签 或 constructor-arg标签
  • 作用:注入集合数据类型属性
  • 格式:
<property>	<list></list></property>

(1)集合类型数据注入——list

<property name="al">    <list>        <value>zh</value>        <value>66666</value>    </list></property>

(2)集合类型数据注入——props

<property name="properties">    <props>        <prop key="name">zh</prop>        <prop key="value">666666</prop>    </props></property>

(3)集合类型数据注入——array (了解)

<property name="arr">    <array>        <value>123456</value>        <value>66666</value>    </array></property>

(4)集合类型数据注入——set(了解)

<property name="hs">     <set>         <value>zh</value>         <value>66666</value>     </set></property>

(5)集合类型数据注入——map(了解)

<property name="hm">    <map>        <entry key="name" value="zh66666"/>        <entry key="value" value="6666666666"/>    </map></property>

3. 引用类型属性自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。

根据自动注入判断标准的不同,可以分为两种:

byName:根据名称自动注入

byType: 根据类型自动注入

1. byName 方式自动注入

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用 byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean 类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。

image-20211122102844055

2. byType 方式自动注入

使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子 类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

image-20211122103052159

4. 为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变 得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将 Spring 配置文件分解成多个配置文件。

包含关系的配置文件: 多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java 代码中只需要使用总配置文件对容器进行初始化即可。

image-20211122103725295

5. spring 中工厂的类结构图

image-20211122101351426

BeanFactory和 ApplicationContext 的区别

BeanFactory 才是 Spring 容器中的顶层接口。

ApplicationContext 是它的子接口。

BeanFactory 和 ApplicationContext 的区别:

  • 创建对象的时间点不一样。
    • ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
    • BeanFactory:什么使用什么时候创建对象。
ApplicationContext 接口的实现类
  • ClassPathXmlApplicationContext:
    它是从类的根路径下加载配置文件 推荐使用这种
  • FileSystemXmlApplicationContext:
    它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:
    当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

6. Ioc中 bean 标签和管理对象细节

bean标签

作用:

  • 用于配置对象让 spring 来创建的。
  • 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

属性:

  • id: 给对象在容器中提供一个唯一标识。用于获取对象。
  • class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
  • scope:指定对象的作用范围。
    • singleton :默认值,单例的。
    • prototype :多例的。
    • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中。
    • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中。
    • global session :WEB 项目中,应用在 Portlet 环境。如果没有 Portlet 环境那么 globalSession 相当于 session。
bean 的作用范围和生命周期

单例对象:scope=“singleton”

一个应用只有一个对象的实例。它的作用范围就是整个引用。

生命周期:

  • 对象出生:当应用加载,创建容器时,对象就被创建了。
  • 对象活着:只要容器在,对象一直活着。
  • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

多例对象:scope=“prototype”

每次访问对象时,都会重新创建对象实例。

生命周期:

  • 对象出生:当使用对象时,创建新的对象实例。
  • 对象活着:只要对象在使用中,就一直活着。
  • 对象死亡:当对象长时间不用时,被 Java 的垃圾回收器回收了。

实例化 Bean 的三种方式

第一种方式:使用默认无参构造函数

 <!--在默认情况下:   它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。   --> <bean id="accountService" class="com.service.impl.AccountServiceImpl"/> 

第二种方式:spring管理静态工厂-使用静态工厂的方法创建对象

//模拟一个静态工厂,创建业务层实现类public class StaticFactory {	public static AccountService createAccountService() {		return new AccountServiceImpl();	}}<!-- 此种方式是:   使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器   id 属性:指定 bean 的 id,用于从容器中获取   class 属性:指定静态工厂的全限定类名   factory-method 属性:指定生产对象的静态方法 --> <bean id="accountService"     class="com.factory.StaticFactory"     factory-method="createAccountService"></bean> 

第三种方式:spring管理实例工厂-使用实例工厂的方法创建对象

/** 
 * 模拟一个实例工厂,创建业务层实现类 
 * 此工厂创建对象,必须现有工厂实例对象,再调用方法  
 */
public class InstanceFactory {
	public AccountService createAccountSerivce() {
		return new AccountServiceImpl();
	}
}

<!-- 此种方式是: 
     先把工厂的创建交给 spring 来管理。 
     然后在使用工厂的 bean 来调用里面的方法 
     factory-bean 属性:用于指定实例工厂 bean 的 id。 
     factory-method 属性:用于指定实例工厂中创建对象的方法。 
--> 
 <bean id="instancFactory" class="com.factory.InstanceFactory"></bean>
 <bean id="accountService"  
    factory-bean="instancFactory"  
    factory-method="createAccountService"></bean> 

7. xml加载properties文件

  • Spring提供了读取外部properties文件的机制,使用读取到的数据为bean的属性赋值

  • 操作步骤

    1.准备外部properties文件

    2.开启context命名空间支持

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

​ 3.加载指定的properties文件

<context:property-placeholder location="classpath:filename.properties">

​ 4.使用加载的数据

<property name="propertyName" value="${propertiesName}"/>
  • 注意:如果需要加载所有的properties文件,可以使用*.properties表示加载所有的properties文件
  • 注意:读取数据使用**${propertiesName}格式进行,其中propertiesName**指properties文件中的属性名

四、基于注解的依赖注入

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解, 需要在原有 Spring 运行环境基础上再做一些改变。

需要在 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       https://www.springframework.org/schema/context/spring-context.xsd">    <!--声明组件扫描器(component-scan),组件就是java对象        base-package:指定注解在你的项目中的包名。        component-scan工作方式: spring会扫描遍历base-package指定的包,           把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。       加入了component-scan标签,配置文件的变化:        1.加入一个新的约束文件spring-context.xsd        2.给这个新的约束文件起个命名空间的名称    -->    <context:component-scan base-package="com.hang.ba01"/></beans>

image-20211122114003165

1. 定义 Bean 的注解@Component(掌握)

需要在类上使用注解@Component,该注解的 value 属性用于指定该 bean 的 id 值。

/** * @Component: 创建对象的, 等同于<bean>的功能 *     属性:value 就是对象的名称,也就是bean的id值, *          value的值是唯一的,创建的对象在整个spring容器中就一个 *     位置:在类的上面 * *  @Component(value = "myStudent")等同于 *   <bean id="myStudent" class="com.bjpowernode.ba01.Student" /> * *  spring中和@Component功能一致,创建对象的注解还有: *  1.@Repository(用在持久层类的上面) : 放在dao的实现类上面, *               表示创建dao对象,dao对象是能访问数据库的。 *  2.@Service(用在业务层类的上面):放在service的实现类上面, *              创建service对象,service对象是做业务处理,可以有事务等功能的。 *  3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的, *              控制器对象,能够接受用户提交的参数,显示请求的处理结果。 *  以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。 *  @Repository,@Service,@Controller是给项目的对象分层的。 * *///使用value属性,指定对象名称//@Component(value = "myStudent")//省略value@Component("myStudent")//不指定对象名称,由spring提供默认名称: 类名的首字母小写//@Componentpublic class Student {    private String name;    private Integer age;    public Student() {        System.out.println("==student无参数构造方法===");    }    public void setName(String name) {        this.name = name;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Student{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}

image-20211122114727630

2. 简单类型属性注入@Value(掌握)

需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。

使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

/*** @Value: 简单类型的属性赋值*   属性: value 是String类型的,表示简单类型的属性值*   位置: 1.在属性定义的上面,无需set方法,推荐使用。*         2.在set方法的上面*/@Value("李四" )private String name;@Value("18")private Integer age;

3. byType 自动注入@Autowired(掌握)

需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。

使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加 到 setter 上。

image-20211122142908780

4. byName 自动注入@Autowired 与@Qualifier(掌握)

需要在引用属性上联合使用注解@Autowired 与@Qualifier。

@Qualifier 的 value 属性用 于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。

image-20211122143007102

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运 行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

image-20211122143024069

5. JDK 注解@Resource 自动注入(掌握)

Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean, 也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。 @Resource 可在属性上,也可在 set 方法上。

byType 注入引用类型属性

@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean, 则会按照类型进行 Bean 的匹配注入。

image-20211122144750336

byName 注入引用类型属性

@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

image-20211122144815199

6. 注解与 XML 的对比

注解优点是:方便、直观、高效(代码少,没有配置文件的书写那么复杂)。

其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。

XML 方式优点是: 配置和代码是分离的、在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

五、AOP 面向切面编程

1. AOP 简介

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。AOP 是 Spring 框架中的一个重要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理。

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到 主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、 事务、日志、缓存等。

若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样, 会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占 比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大 大干扰了主业务逻辑—转账。

动态代理

jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口

cglib动态代理:第三方的工具库,创建代理对象。原理是继承,通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final,方法也不能是final

动态代理的作用

1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。

⭐️Aop:面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。
Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了, 让开发人员用一种统一的方式,使用动态代理。

怎么理解面向切面编程 ?

1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

2. AOP相关术语

1)Aspect: 切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。

2)JoinPoint: 连接点 ,连接业务方法和切面的位置。通常业务接口中的方法均为连接点。

3)Pointcut : 切入点 ,指多个连接点方法的集合。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

4)Target:目标对象,给哪个类的方法增加功能, 这个类就是目标对象。即 包含主业务逻辑的类的对象 。

5)Advice: 通知表示切面的执行时间,Advice 也叫增强。切入点定义切入的位置,通知定义切入的时间。

3. AspectJ 对 AOP 的实现(掌握)

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框 架中。

在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。

AspectJ 简介

AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。

AspectJ 的通知类型(理解)

AspectJ 中常用的通知有五种类型:

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知
AspectJ 的切入点表达式(掌握)

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern		  declaring-type-pattern?name-pattern(param-pattern)          throws-pattern?)	

解释:

modifiers-pattern 访问权限类型

ret-type-pattern 返回值类型

declaring-type-pattern 包名类名

name-pattern(param-pattern) 方法名(参数类型和参数个数)

throws-pattern 抛出异常类型

?表示可选的部分

以上表达式共 4 个部分。 execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。表达式中的访问权限和异常类型是可省略部分,各部分间用空格分开。在其中可以使用以下符号:

符号意义
*0至多个任意字符
用在方法参数中,表示任意多个参数 用在包名后,表示当前及其子包路径
+用在类名后,表示当前类及其子类 用在接口后,表示当前接口及其实现类

举例:

execution ( public * *(…) )

指定切入点为:任意公共方法。

execution ( * set * (…) )

指定切入点为:任何一个以“set”开始的方法。

execution ( * com.xyz.service..(…) )

指定切入点为:定义在 service 包里的任意类的任意方法。

execution( * com.xyz.service….(…) )

指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后 面必须跟“*”,表示包、子包下的所有类。

execution(* *…service.*.*(…))

指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

AspectJ 的开发环境(掌握)

maven 依赖

<dependency>    <groupId>junit</groupId>    <artifactId>junit</artifactId>    <version>4.11</version>    <scope>test</scope></dependency><!-- spring-context --><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-context</artifactId>    <version>5.3.12</version></dependency><!-- spring-aspects --><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-aspects</artifactId>    <version>5.3.12</version></dependency>

引入 AOP 约束

在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签, 均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。

AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。

AspectJ 基于注解的 AOP 实现(掌握)

AspectJ 提供了以注解方式对于 AOP 的实现。

实现步骤:

Step1:定义业务接口与实现类

image-20211124151630172

Step2:定义切面类:类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。

image-20211124151640897

Step3:声明目标对象切面类对象

image-20211124151649335

Step4:注册 AspectJ 的自动代理

在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的 自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。

image-20211124151736901

<aop:aspectj-autoproxy />的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。 从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。 其工作原理是,通过扫描找到@Aspect 定义的切面类,再由切 面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。

其工作原理是,通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。

Step5:测试类中使用目标对象的 id

image-20211124222519111

@Before 前置通知-方法有 JoinPoint 参数

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参 数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。

不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该 参数。

image-20211124222914514

@AfterReturning 后置通知-注解有 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

image-20211124223442287

@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并 且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法 的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

image-20211124223529932

@AfterThrowing 异常通知-注解中有 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。 当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的 名称,表示发生的异常对象。

image-20211124223616128

image-20211124223623693

@After 最终通知

无论目标方法是否抛出异常,该增强均会被执行。

image-20211124223650012

@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。 AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。

其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均 可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解 的方法一般使用 private 的标识方法,即没有实际作用的方法。

image-20211124223732496

六、Spring 集成 MyBatis

这里主要介绍Spring的配置文件

数据源的配置(掌握)

使用 JDBC 模板,首先需要配置好数据源,数据源直接以 Bean 的形式配置在 Spring 配置文件中。

Druid 数据源 DruidDataSource

Druid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能 够提供强大的监控和扩展功能。Druid 与其他数据库连接池的最大区别是提供数据库的配置连接池:

DruidDataSource大部分属性都是参考DBCP的,如果你原来就是使用DBCP,迁移是十分方便的。

 <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
     <property name="url" value="${jdbc_url}" />
     <property name="username" value="${jdbc_user}" />
     <property name="password" value="${jdbc_password}" />

     <property name="filters" value="stat" />

     <property name="maxActive" value="20" />
     <property name="initialSize" value="1" />
     <property name="maxWait" value="6000" />
     <property name="minIdle" value="1" />

     <property name="timeBetweenEvictionRunsMillis" value="60000" />
     <property name="minEvictableIdleTimeMillis" value="300000" />

     <property name="testWhileIdle" value="true" />
     <property name="testOnBorrow" value="false" />
     <property name="testOnReturn" value="false" />

     <property name="poolPreparedStatements" value="true" />
     <property name="maxOpenPreparedStatements" value="20" />

     <property name="asyncInit" value="true" />
 </bean>
  • 在上面的配置中,通常你需要配置url、username、password,maxActive这三项。
  • Druid会自动跟url识别驱动类名,如果连接的数据库非常见数据库,配置属性driverClassName
  • asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间
从属性文件读取数据库连接信息

为了便于维护,可以将数据库连接信息写入到属性文件中,使 Spring 配置文件从中读取 数据。

image-20211127191715171

image-20211127191730185

Spring 配置文件从属性文件中读取数据时,需要在的 value 属性中使用${ }, 将在属性文件中定义的 key 括起来,以引用指定属性的值。

该属性文件若要被 Spring 配置文件读取,其必须在配置文件中进行注册。

使用<conext:property-placeholder location="classpath:jdbc.properties"/>标签。

标签中有一个属性 location,用于指定属性文件的位置。

注册 SqlSessionFactoryBean
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
	SqlSessionFactory  sqlSessionFactory = new ..-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--set注入,把数据库连接池付给了dataSource属性-->
    <property name="dataSource" ref="myDataSource"/>
    <!--mybatis主配置文件的位置
       configLocation属性是Resource类型,读取配置文件
       它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
定义 Mapper 扫描配置器 MapperScannerConfigurer

Mapper 扫描配置器 MapperScannerConfigurer 会自动生成指定的基本包中 mapper 的代理对象。该 Bean 无需设置 id 属性。basePackage 使用分号或逗号设置多个包。

<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
        MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--指定SqlSessionFactory对象的id-->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <!--指定包名, 包名是dao接口所在的包名。
           MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
           一次getMapper()方法,得到每个接口的dao对象。
           创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写 -->
    <property name="basePackage" value="com.hang.dao"/>
</bean>
向 Service 注入接口名

向 Service 注入 Mapper 代理对象时需要注意,由于通过 Mapper 扫描配置器 MapperScannerConfigurer 生成的 Mapper 代理对象没有名称,所以在向 Service 注入 Mapper 代理时,无法通过名称注入。但可通过接口的简单类名注入,因为生成的是这个 Dao 接口 的对象。

<!--声明service-->
<bean id="studentService" class="com.hang.service.impl.StudentServiceImpl">
   <property name="studentDao" ref="studentDao"/>
</bean>
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:conext="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 https://www.springframework.org/schema/context/spring-context.xsd">

    <conext:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxActive" value="20"/>
    </bean>

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
    SqlSessionFactory  sqlSessionFactory = new ..-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDataSource"/>
        <!--mybatis主配置文件的位置
           configLocation属性是Resource类型,读取配置文件
           它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>


    <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
        MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--指定包名, 包名是dao接口所在的包名。
           MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
           一次getMapper()方法,得到每个接口的dao对象。
           创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
       -->
        <property name="basePackage" value="com.hang.dao"/>
    </bean>

    <!--声明service-->
    <bean id="studentService" class="com.hang.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

</beans>

七、Spring事务

1. Spring 的事务管理

什么是事务

事务是指一组sql语句的集合, 集合中有多条sql语句可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。

事务是一个不可分割的工作逻辑单元。

什么时候会用到事务

当我们的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。

关于Spring事务

spring提供一种处理事务的统一模型, 能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。

使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理

使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

在 Spring 中通常可以通过以下两种方式来实现对事务的管理:

(1)使用 Spring 的事务注解管理事务

(2)使用 AspectJ 的 AOP 配置管理事务

2. Spring 事务管理 API

Spring 的事务管理,主要用到两个事务相关的接口。

事务管理器接口(重点)

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。

image-20211127205835908

常用的两个实现类

PlatformTransactionManager 接口有两个常用的实现类:

DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。

HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

Spring 的回滚方式(理解)

Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。

事务定义接口

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、 事务传播行为、事务默认超时时限,及对它们的操作。

定义了五个事务隔离级别常量

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

DEFAULT:采用 DB 默认的事务隔离级别。

MySql 的默认为 REPEATABLE_READ;Oracle 默认为 READ_COMMITTED。

  • READ_UNCOMMITTED:读未提交。未解决任何并发问题。

  • READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。

  • REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读

  • SERIALIZABLE:串行化。不存在并发问题。

定义了七个事务传播行为常量

  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  • NEVER:以非事务方式运行,如果当前存在事务,抛出异常
  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作

定义了默认事务超时时限

默认值是-1,没有超时限制。如果有,以秒为单位进行设置。

3. 使用 Spring 的事务注解管理事务(掌握)

通过@Transactional注解方式,可将事务织入到相应 public 方法中,实现事务管理。

@Transactional 的所有可选属性如下所示:

isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。

propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。

read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。

timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。

rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。

no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。

实现注解的事务步骤:
使用@Transactional的步骤:
1.需要声明事务管理器对象
  <bean id="xx" class="DataSourceTransactionManager">

2.开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。
  spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
  spring给业务方法加入事务:
    在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
     
   @Around("你要增加的事务功能的业务方法名称")
   Object myAround(){
         开启事务,spring给你开启
      try{
         buy(1001,10);
        spring的事务管理器.commit();
      }catch(Exception e){
           spring的事务管理器.rollback();
      }
     
   }

3.在你的方法的上面加入@Trancational
  1. 声明事务管理器
<!--使用spring的事务处理-->
<!--1. 声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--连接的数据库, 指定数据源-->
    <property name="dataSource" ref="myDataSource"/>
</bean>
  1. 开启注解驱动
<!--2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
    transaction-manager:事务管理器对象的id-->
<tx:annotation-driven transaction-manager="transactionManager"/>
  1. 业务层 public 方法加入事务属性
@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.DEFAULT,
    readOnly = false,
    rollbackFor = {
        NullPointerException.class,NotEnoughException.class
            }
)
@Override
public void buy(Integer goodsId, Integer amount) {
    Sale sale = new Sale(goodsId,amount);

    saleDao.insertSale(sale);
    Goods goods = goodsDao.selectGoodsById(goodsId);
    if (goods==null){
        throw new NullPointerException("无此商品");
    }
    if (goods.getAmount() < amount){
        throw new NotEnoughException("库存不足");
    }

    goods = new Goods(goodsId,amount);
    goodsDao.updateGoods(goods);
}

4. 使用 AspectJ 的 AOP 配置管理事务(掌握)

适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。 
要使用的是aspectj框架,需要加入依赖
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>

使用AspectJ 的 AOP 配置管理事务步骤
    1、配置事务管理器
    2、配置事务的通知
            此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
            使用tx:advice标签配置事务通知
                属性:
                    id:给事务通知起一个唯一标识
                    transaction-manager:给事务通知提供一个事务管理器引用
    3、配置AOP中的通用切入点表达式
    4、建立事务通知和切入点表达式的对应关系
    5、配置事务的属性
           是在事务的通知tx:advice标签的内部
<!--使用spring的事务处理-->
1. 在容器中添加事务管理器
<!--1. 声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--连接的数据库, 指定数据源-->
    <property name="dataSource" ref="myDataSource"/>
</bean>

2. 配置事务通知
<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
      id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
      transaction-manager:事务管理器对象的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <!--tx:attributes:配置事务属性-->
    <tx:attributes>
        <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
               name:方法名称,1)完整的方法名称,不带有包和类。
                             2)方法可以使用通配符,* 表示任意字符
               propagation:传播行为,枚举值
               isolation:隔离级别
               rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
           -->
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false"
                   rollback-for="java.lang.NullPointerException,com.hang.expec.NotEnoughException"/>
        <!--使用通配符,指定很多的方法-->
        <tx:method name="*" propagation="SUPPORTS"/>
    </tx:attributes>

</tx:advice>

3.配置增强器
<aop:config>
    <!--配置切入点表达式:指定哪些包中类,要使用事务
            id:切入点表达式的名称,唯一值
            expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象

            com.bjpowernode.service
            com.crm.service
            com.service
        -->
    <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
    <!--配置切入点表达式:指定哪些包中类,要使用事务
            id:切入点表达式的名称,唯一值
            expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
            com.bjpowernode.service
            com.crm.service
            com.service
        -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值