Spring的攀登之旅

书上有路勤为径,学海无涯苦作舟

链接地址: 哔哩哔哩视频地址

Spring

一.为什么需要Spring

image-20240928142752660

问题一.

​ 根据以上代码,可以发现层与层之间的耦合度是很大的,为了使用userDao的方法我们需要在业务层去手动去new一个M层的对象,当底层代码发生改动会影响到上层代码为了解决这种耦合,可以使用工厂模式,即把创建对象的工作交给第三方,由第三方根据要求为程序提供所需的Bean.

问题二.

​ 程序除了自己的主要功能以为还有类似Log这种通用功能,如果每编写一个功能都要重复的写入,会使代码变的臃肿,也增加了耦合度.为了解决这种问题我们通常使用 代理模式 ,即第三方返回的bean对象不再是普通的对象而是经过增强的代理对象.

image-20240928150816581

解决:Spring框架就是集合了工厂模式和代理模式( IOC,DI和AOP )

一.IOC思想:

​ 控制反转(IoC)是一种编程思想,旨在降低代码之间的耦合度、简化对象的创建和管理过程。以下是对IOC思想的详细介绍:

  1. 基本概念:控制反转(Inversion of Control, IoC)是一种设计原则,用于实现低耦合、高内聚的系统架构。其核心思想是将对象的创建和依赖关系的管理交给外部容器或框架来处理,而不是由对象自身来管理。
  2. 实现方式:在IoC模式中,通常使用工厂模式来实现对象的创建,并通过XML配置文件或注解等方式来描述对象之间的依赖关系。这样,当需要获取某个对象时,只需要从容器中获取即可,无需手动创建和管理对象。
  3. 优点:IoC模式可以有效地降低代码之间的耦合度,提高系统的可维护性和可扩展性。同时,由于对象的创建和依赖关系都由外部容器来管理,因此也更容易进行单元测试和集成测试。
  4. 应用:IoC模式广泛应用于各种软件开发领域,特别是在Java EE和Spring等框架中得到了广泛应用。在这些框架中,IoC模式被用于实现依赖注入、AOP等功能,从而简化了开发过程并提高了代码质量。
  5. 与依赖注入的区别:IoC和DI是两个相关但不同的概念。IoC是一种设计思想,而DI则是实现IoC的一种方法。DI通过将对象的依赖关系以参数的形式传递给对象,从而实现了对象之间的解耦。
  6. 在Spring中的实现:在Spring框架中,IoC容器负责创建、配置和管理Bean的生命周期。开发者只需定义好Bean的配置信息,Spring容器就会自动完成Bean的实例化、属性注入等工作。这种方式不仅简化了开发过程,还提高了代码的可读性和可维护性。
  7. 与其他设计模式的关系:IoC模式与工厂模式、单例模式等其他设计模式有着密切的关系。工厂模式通常用于创建复杂对象,而单例模式则确保一个类只有一个实例。IoC模式将这些模式的思想融合在一起,形成了一种更为灵活和强大的设计原则。
二.DI思想:

​ 依赖注入(DI)是一种设计模式,用于实现控制反转(IoC)的具体方法之一。 以下是对DI思想的详细介绍:

  1. 基本概念:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,用于减少代码之间的耦合度。它的核心思想是将对象所依赖的资源(如服务、数据访问对象等)通过外部注入的方式提供给对象,而不是由对象自己创建或查找这些资源。
  2. 实现方式:在DI模式中,通常使用构造函数注入、设值注入(Setter注入)或接口注入等方式来实现对象的依赖关系管理。以构造函数注入为例,当一个类需要另一个类的实例时,可以通过构造函数将所需的实例传递给该类,从而实现了依赖关系的注入。
  3. 与IoC的关系:IoC是一种编程思想,而DI是实现IoC的一种具体技术手段。可以说,IoC是目的,而DI是实现这一目的的手段之一。通过DI,我们可以更容易地实现IoC的思想,从而降低代码之间的耦合度并提高系统的可维护性和可扩展性。
  4. 优点:DI模式可以有效地降低代码之间的耦合度,使得系统更加灵活和易于扩展。同时,由于依赖关系都由外部容器来管理,因此也更容易进行单元测试和集成测试。
  5. 应用:DI模式广泛应用于各种软件开发领域,特别是在Java EE和Spring等框架中得到了广泛应用。在这些框架中,DI模式被用于实现依赖注入、AOP等功能,从而简化了开发过程并提高了代码质量。
  6. 注意事项:在使用DI模式时,需要注意避免过度依赖容器或框架而导致的紧耦合问题。同时,也需要合理规划和管理依赖关系以确保系统的稳定性和可维护性。
  7. 与其他设计模式的关系:DI模式与其他设计模式如工厂模式、单例模式等有着密切的关系。工厂模式通常用于创建复杂对象,而单例模式则确保一个类只有一个实例。DI模式将这些模式的思想融合在一起形成了一种更为灵活和强大的设计原则。
三.AOP思想

​ 面向切面编程(AOP)是一种软件开发范式,旨在通过横切关注点的方式解耦系 统中的各个模块。以下是对AOP思想的详细介绍:

  1. 基本概念:AOP即Aspect Oriented Programming的缩写,意为面向切面编程,是一种通过预编译方式和运行期动态代理实现程序功能统一维护的技术。它是OOP(面向对象编程)的延续,利用AOP可以对业务逻辑的各个部分进行隔离,从而降低业务逻辑各部分之间的耦合度,提高程序的可重用性,同时提高了开发效率。
  2. 编程思想的发展路线:从POP(面向过程编程)到OOP(面向对象编程),再到AOP,反映了编程思想的发展与演变。POP以过程为中心,OOP则将问题分解为各个对象,而AOP则是对OOP的补充,它专注于处理业务逻辑之外的通用需求功能。
  3. 实现方式:AOP的实现主要依赖于静态代理和动态代理两种方式。静态代理需要使用装饰器模式和代理模式,而动态代理则在程序运行时生成代理对象,通过反射机制或字节码操作来实现。
  4. 优势:AOP的优势在于它可以将通用功能从业务逻辑中抽离出来,提高代码复用性,有利于后期的维护和扩展。同时,由于横切关注点被集中到切面中,对于这部分代码的维护变得更加容易,不需要在各个模块中进行重复的修改。
  5. 应用场景:AOP广泛应用于日志记录、事务管理、安全性等横切关注点的实现中。通过定义切面,可以在不修改原始类的情况下给程序动态添加统一功能,从而实现了与业务逻辑的解耦。
  6. 与其他编程思想的关系:AOP与OOP是互补关系,AOP关注的是横切关注点,而OOP关注的是将需求功能划分为不同的并且相对独立、封装良好的类。在实际开发中,两者往往结合使用,以实现更高效、更灵活的软件设计。

二.快速入门

BeanFactory版

image-20240928151726110

一.创建一个空的maven项目并在pom.xml导入依赖坐标
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.11</version>
        </dependency>
二.创建一个service,dao层,并创建spring.xml

image-20240928153118219

public class UserServiceImpl implements UserService {
    //BeanFactory 调用该方法,从容器中获得userDao设置到此处
public void setUserDao(UserDao userDao){
    System.out.println("BeanFactory 调用该方法"+userDao);
}

}
三.配置XML文件
3.1配置userServiceimpl
<?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">
        <!--配置userServiceimpl-->
        <bean id="userService" class="com.tuy.service.impl.UserServiceImpl"></bean>
</beans>
3.2配置userDaoimpl
<?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">
        <!--配置userServiceimpl-->
        <bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
        </bean>
        <!--配置userDaoimpl-->
        <bean id="userDao" class="com.tuy.dao.impl.UserDaoImpl"></bean>
</beans>
3.3配置Service层引用Dao层
<?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">
        <!--配置userServiceimpl-->
        <bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
                <property name="userDao"  ref="userDao"> </property>
        </bean>
        <!--userDaoimpl-->
        <bean id="userDao" class="com.tuy.dao.impl.UserDaoImpl"></bean>
</beans>

bean标签通常用于Spring框架的XML配置文件中,用于定义一个bean实例。id属性用于指定bean的唯一标识符,而class属性用于指定bean的完全限定类名。<property>标签用于在Spring配置文件中设置bean的属性。在这个例子中,name="userDao"表示要设置的属性名为"userDao",而ref="userDao"表示这个属性的值是一个已经定义的bean,其id为"userDao"。这样,当前bean就可以引用到"userDao"这个bean实例。

四.测试
4.1控制反转的实例
  public static void main(String[] args) {
        //创建工厂对象
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //创建一个读取器并和工厂绑定
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        //读取配置文件给工厂
        reader.loadBeanDefinitions("beans.xml");
        //工厂根据ID获取bean的实例对象
        UserService userService = (UserService) beanFactory.getBean("userService");
        //打印userService若有地址则代表工厂的确创建了实例对象
        System.out.println(userService);

    }

image-20240928161115463

package com.tuy.test;

import com.tuy.dao.UserDao;
import com.tuy.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

public class BeanFactoryTest {
    public static void main(String[] args) {
        //创建工厂对象
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //创建一个读取器并和工厂绑定
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        //读取配置文件给工厂
        reader.loadBeanDefinitions("beans.xml");
        //工厂根据ID获取bean的实例对象
        UserService userService = (UserService) beanFactory.getBean("userService");
        //打印userService若有地址则代表工厂的确创建了实例对象
        System.out.println(userService);

        //工厂根据ID获取bean的实例对象
        Object userDao = beanFactory.getBean("userDao");
        //打印userDao若有地址则代表工厂的确创建了实例对象
        System.out.println(userDao);

    }
}

image-20240928162633402

4.2依赖注入的实例
package com.tuy.test;

import com.tuy.dao.UserDao;
import com.tuy.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

public class BeanFactoryTest {
    public static void main(String[] args) {
        //创建工厂对象
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //创建一个读取器并和工厂绑定
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        //读取配置文件给工厂
        reader.loadBeanDefinitions("beans.xml");
        //工厂根据ID获取bean的实例对象
        UserService userService = (UserService) beanFactory.getBean("userService");
        //打印userService若有地址则代表工厂的确创建了实例对象
        System.out.println(userService);

//        //工厂根据ID获取bean的实例对象
//        Object userDao = beanFactory.getBean("userDao");
//        //打印userService若有地址则代表工厂的确创建了实例对象
//        System.out.println(userDao);

    }
}

image-20240929170958270

ApplicationContext版

​ ApplicationContext称为应用上下文,它是Spring框架的核心接口之一。内部封装了BeanFactory和一些额外的功能。BeanFactory是Spring IoC容器的实际实现,负责实例化、配置和组装bean。而ApplicationContext在BeanFactory的基础上增加了更多企业特定的功能,例如事件传播、资源加载、透明地处理消息资源等。

package com.tuy.test;

import com.tuy.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        System.out.println(userService);
    }
}

image-20241002153111544

BeanFactory和ApplicationContext的关系

  1. 基础与高级

    • BeanFactory:作为Spring框架中最基础的容器,提供了延迟初始化Bean的机制。它主要负责加载Bean配置信息、实例化Bean、维护Bean生命周期以及注入Bean之间的依赖关系。
    • ApplicationContext:是BeanFactory的子接口,提供了更多的企业级功能,如国际化、事件传播、资源加载等,并预先实例化所有的Bean,适用于更复杂的应用场景。
  2. 实现原理与区别

    • BeanFactory:采用懒加载策略,即只有在首次请求时才进行Bean的创建和实例化。
    • ApplicationContext:则在配置文件加载和容器创建时就将所有的Bean实例化并初始化好,支持基于注解的自动装配等功能。
  3. 性能与应用场景

    • BeanFactory:因其延迟加载的特点,适合对启动速度和资源消耗敏感的大型应用程序。
    • ApplicationContext:由于其预先加载所有Bean的特性,适合需要尽早初始化Bean或执行一些启动任务的场景,如Web应用。
  4. 功能与扩展

    • BeanFactory:主要提供基本的IoC容器功能,如Bean的定义、实例化、配置及依赖注入等。

    • ApplicationContext:除了继承BeanFactory的所有功能外,还通过集成MessageSource、ResourceLoader等接口,增加了许多高级特性。

    BeanFactory的继承体系

image-20241002151656391

ApplicationContext的继承体系

image-20241002153223995

三.基于xml的Spring应用

一. SpringBean的配置详解

image-20241002154234316

1.1 id,class,name的小keys

image-20241002155415124

image-20241002155650284

image-20241002160205603

如果没有id且没有name别名那么SingletonObjects中的key就是class,如有别名则是第一个别名.别名存储在beanFactory下的aliasMap中.

1.2 Bean的范围配置(scope)
  1. Singleton(单例)

    • 定义:默认情况下,Spring容器中的Bean是单例的。这意味着在整个Spring容器中,每个Bean定义只有一个共享的实例。当一个Bean被定义为单例时,无论从Spring容器中获取多少次这个Bean的引用,都将得到相同的实例。

    • 示例

        <bean id="singletonBean" class="com.example.SingletonBean" scope="singleton"/>
      
    • 应用场景:适用于无状态的服务、工具类或那些不依赖于上下文状态的对象。例如,服务层和数据访问层的组件通常被设计为单例,因为它们不应该保留客户特定的数据。

  2. Prototype(原型)

    • 定义:原型作用域的Bean每次从容器中获取时都会创建一个新的实例。与单例不同,原型Bean不是共享的;每个请求、每个依赖或每个引用都会产生一个全新的Bean实例。

    • 示例

      <bean id="prototypeBean" class="com.example.PrototypeBean" scope="prototype"/> 
      
    • 应用场景:适用于每次使用都需要新的状态或者配置信息的场景。比如,如果一个Bean需要根据用户会话的不同而有不同的数据,那么它应该被定义为原型作用域。同样,任何需要频繁创建新实例且每个实例间互不干扰的对象也适合使用原型作用域。

​ 3. 引用其他的依赖后scope的值就不止上述俩个,如引入MVC后会多出request和session.

1.3 bean的延迟加载(lazy-init)

lazy-init是Spring容器中的一个特性,允许将Bean的实例化推迟到实际需要的时候才进行。默认情况下,Spring容器在启动时会立即创建所有单例作用域的Bean,这可能会增加应用的启动时间。通过设置lazy-inittrue,可以告诉Spring只在第一次请求该Bean时才创建其实例。

适用场景

  • 减少启动时间:对于一些大型应用,可能有大量Bean需要在启动时创建。如果这些Bean的创建过程耗时较长或者依赖的资源较多,那么将这些Bean设置为lazy-init可以减少应用的启动时间。
  • 节省资源:对于那些可能永远不会被使用的Bean,使用lazy-init可以避免不必要的资源消耗。
  • 条件性加载:有时候,某些功能模块可能只在特定条件下才会被使用。在这种情况下,可以将相关Bean设置为lazy-init,以便仅在需要时才加载这些模块。
1.4 Bean的初始化和销毁方法配置

1. init-method(初始化方法)

  • 定义init-method是一个配置选项,用于指定在Bean的属性填充之后应该调用的方法。这个方法通常用于执行一些初始化操作,比如建立数据库连接、加载资源等。

  • 示例

     	<!--配置userServiceimpl-->
        <bean id="userService" class="com.tuy.service.impl.UserServiceImpl" init-method="init" destroy-method="destroy" >
            <property name="userDao"  ref="userDao"> </property>
        </bean>
    
        <!--配置userDaoimpl-->
        <bean id="userDao" class="com.tuy.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
    
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
        //BeanFactory 调用该方法,从容器中获得userDao设置到此处
    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
        System.out.println("BeanFactory 调用该方法"+userDao);
    }
        public UserServiceImpl(){
            System.out.println("UserServiceImpl建立了...");
        }
        public  void  init(){
            System.out.println("初始化方法...");
        }
        public void  destroy(){
            System.out.println("销毁方法...");
        }
    }
    
    public class ApplicationContextTest {
        public static void main(String[] args) {
            ApplicationContext applicationContext =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) applicationContext.getBean("userService");
            System.out.println(userService);
        }
    }
    

    销毁方法未执行,是因为容器还未执行到销毁方法时就关闭了,需要显性开启销毁

    image-20241002165833626

    **2.实现InitializingBean接口,重写afterPropertiesSet() (初始化方法) **

    public class UserServiceImpl implements UserService, InitializingBean {
        private UserDao userDao;
        //BeanFactory 调用该方法,从容器中获得userDao设置到此处
    public void setUserDao(UserDao userDao){
        this.userDao=userDao;
        System.out.println("BeanFactory 调用该方法"+userDao);
    }
        public UserServiceImpl(){
            System.out.println("UserServiceImpl建立了...");
        }
        public  void  init(){
            System.out.println("UserServiceImpl初始化方法...");
        }
        public void  destroy(){
            System.out.println("UserServiceImpl销毁方法...");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("afterPropertiesSet执行了...");
        }
    }
    

    执行顺序先创建实例,然后注入属性,调用afterPropertiesSet(),自己写的初始化方法,

    image-20241002171259369

3. destroy-method(销毁方法)

  • 定义destroy-method是一个配置选项,用于指定在Bean被销毁前应该调用的方法。这个方法通常用于执行一些清理工作,比如关闭数据库连接、释放资源等。

  • 示例

     	package com.tuy.test;
    
    import com.tuy.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class ApplicationContextTest {
        public static void main(String[] args) {
         // ApplicationContext中没有销毁方法需要在子类的ClassPathXmlApplicationContext
            ClassPathXmlApplicationContext applicationContext =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) applicationContext.getBean("userService");
            System.out.println(userService);
            applicationContext.close();
        }
    }
    

    ApplicationContext中没有销毁方法需要在子类的ClassPathXmlApplicationContext

    image-20241002170610948

  1. 适用场景
    • 资源管理:对于那些需要显式地管理资源(如数据库连接、文件句柄等)的Bean,使用init-methoddestroy-method可以确保资源的正确开启和关闭。
    • 状态设置:有些Bean可能需要在创建后立即设置特定的状态或执行某些操作,这时可以使用init-method来实现。
    • 清理工作:对于需要执行特定清理工作的Bean,如释放缓存、取消注册监听器等,可以使用destroy-method来处理。
  2. 注意事项
    • 作用域的影响destroy-method主要对单例作用域的Bean有效,因为原型作用域的Bean每次请求都会创建新的实例,容器不会负责它们的生命周期结束。
    • 异常处理:在init-methoddestroy-method中抛出的任何异常都需要妥善处理,以避免影响应用的稳定性。

二.Bean的实例化配置

一.构造函数实例化对象
1.1 无参构造

默认就是无参的

image-20241002173547497

1.2 有参构造

添加标签传入参数

  <!--配置userServiceimpl-->
    <bean id="userService" class="com.tuy.service.impl.UserServiceImpl" >
        <constructor-arg name="name" value="tuy"> </constructor-arg>
        <property name="userDao"  ref="userDao"> </property>
    </bean>
    <!--userDaoimpl-->
    <bean id="userDao" class="com.tuy.dao.impl.UserDaoImpl" ></bean>

image-20241002173506173

二. 工厂方式实例化对象
1.1静态工厂实例化Bean
    <!-- 静态工厂   -->
    <bean id="userDao1" class="com.tuy.factory.MyBeanFactory1" factory-method="userDao"></bean>
public class MyBeanFactory1 {
    public static UserDao userDao(){
        //Bean创建之前可以进行一些其他的操作
        return new UserDaoImpl();
    }
}

public class ApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Object userDao1 = applicationContext.getBean("userDao1");
        System.out.println(userDao1);

    }
}

image-20241002175542025

1.2 实例工厂实例化Bean
<!-- 实例工厂-->
<!--配置工厂对象-->
    <bean id="myBeanFactory2" class="com.tuy.factory.MyBeanFactory2"></bean>
<!--配置工厂对象的方法-->
    <bean id="userDao2" factory-bean="myBeanFactory2" factory-method="userDao"></bean>
public class MyBeanFactory2 {
    public  UserDao userDao(){
        //Bean创建之前可以进行一些其他的操作
        return new UserDaoImpl();
    }
}
public class ApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Object userDao1 = applicationContext.getBean("userDao2");
        System.out.println(userDao1);

    }
}

image-20241002180446513

1.3 实现FactoryBean规范延迟实例化Bean
    <!--实现FactoryBean规范延迟实例化Bean-->
    <bean id="userDao3" class="com.tuy.factory.MyBeanFactory3"> </bean>
public class MyBeanFactory3 implements FactoryBean<UserDao> {

    @Override
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}
public class ApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Object userDao3 = applicationContext.getBean("userDao3");
        System.out.println(userDao3);

    }
}

image-20241002210359201

image-20241002210528027

三. Bean依赖注入的方式

image-20241002210828823

image-20241002211203666

3.1 List的注入
<bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
    <property name="stringslist">
        <list>
            <value>aaa</value>
            <value>bbb</value>
            <value>ccc</value>
        </list>
    </property>
    <property name="userDaoList">
<!--        <list>-->
<!--            <bean class="com.tuy.dao.impl.UserDaoImpl"></bean>-->
<!--            <bean class="com.tuy.dao.impl.UserDaoImpl"></bean>-->
<!--            <bean class="com.tuy.dao.impl.UserDaoImpl"></bean>-->
<!--        </list>-->
        <list>
            <ref bean="userDao1"></ref>
            <ref bean="userDao2"></ref>
            <ref bean="userDao3"></ref>
        </list>
    </property>
</bean>

    <bean id="userDao1"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao2"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao3"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>

//注入List
    private List<String> stringslist;

    public void setStringslist(List<String> stringslist) {
        this.stringslist = stringslist;
    }
    private List<UserDao> userDaoList;

    public void setUserDaoList(List<UserDao> userDaoList) {
        this.userDaoList = userDaoList;
    }

    public void show(){
    System.out.println(stringslist);
        System.out.println(userDaoList);
}
    private UserDao userDao;
    //BeanFactory 调用该方法,从容器中获得userDao设置到此处
public void setUserDao(UserDao userDao){
    this.userDao=userDao;
}

image-20241002215756666

3.2 set的注入
<!--    注入set-->
    <bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
    <property name="stringSet">
        <set>
            <value>000</value>
            <value>222</value>
            <value>333</value>
        </set>
    </property>
    <property name="userDaoSet">
        <set>
            <ref bean="userDao1"></ref>
            <ref bean="userDao2"></ref>
            <ref bean="userDao3"></ref>
        </set>
        <!--        <set>-->
        <!--                        <bean class="com.tuy.dao.impl.UserDaoImpl"></bean>-->
        <!--                        <bean class="com.tuy.dao.impl.UserDaoImpl"></bean>-->
        <!--                        <bean class="com.tuy.dao.impl.UserDaoImpl"></bean>-->
        <!--        </set>-->
    </property>
</bean>
    <bean id="userDao1"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao2"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao3"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
//注入set
    private Set<String> stringSet;

    public void setStringSet(Set<String> stringSet) {
        this.stringSet = stringSet;
    }

    private Set<UserDao> userDaoSet;

    public void setUserDaoSet(Set<UserDao> userDaoSet) {
        this.userDaoSet = userDaoSet;
    }
  public void show(){
        System.out.println(stringSet);
        System.out.println(userDaoSet);}

image-20241002221932889

3.3 map的注入
<bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
        <property name="map">
            <map>
                <entry key="d1" value-ref="userDao1"></entry>
                <entry key="d2" value-ref="userDao2"></entry>
                <entry key="d3" value-ref="userDao3"></entry>
            </map>
        </property>
    </bean>
    <bean id="userDao1"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao2"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao3"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
private Map<String,UserDao> map;

    public void setMap(Map<String, UserDao> map) {
        this.map = map;
    }

    public void show(){
        System.out.println(map);
}

image-20241002223942262

3.4 Properties的注入
   <bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
       <property name="properties">
           <props>
               <prop key="a">123</prop>
               <prop key="b">456</prop>
               <prop key="c">789</prop>
           </props>
       </property>
    </bean>
    <bean id="userDao1"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao2"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    <bean id="userDao3"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void show(){
        System.out.println(properties);
}

image-20241002224512649

3.5 自动注入

byName的自动注入与byType的自动注入

byName要与Set后的名字一样 
<bean id="userService" class="com.tuy.service.impl.UserServiceImpl" autowire="byName"></bean>
    <bean id="userDao"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>

byType要只有一个userDao的bean
<bean id="userService" class="com.tuy.service.impl.UserServiceImpl" autowire="byType"></bean>
    <bean id="userDao"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>

   private UserDao userDao;
    //BeanFactory 调用该方法,从容器中获得userDao设置到此处
public void setUserDao(UserDao userDao){
    this.userDao=userDao;
}
    public void show(){
        System.out.println(userDao);
    }

四. Spring的其它配置标签
4.1 默认标签

image-20241003074207551

image-20241003074327779

image-20241003074526981

image-20241003074808485

image-20241003075319254

4.2 自定义标签

image-20241003075750129

三. Spring的get方法

	ApplicationContext applicationContext =
	new ClassPathXmlApplicationContext("applicationContext.xml");
//根据beanName获取容器中的bean实例,需要手动强转
	UserService userService = 
    (UserService)applicationContext.getBean("userService"); 
//根据Bean类型去容器中匹配对应的bean实例,如存在多个匹配的Bean则报错
UserService userService = 
   applicationContext.getBean(UserService.class); 
//根据beanName获取容器中的bean实例,指定Bean的Type类型
UserService userService = 
   applicationContext.getBean("userService",UserService.class); 

四.Spring配置非自定义的Bean

配置非自定义的Bean需要考虑以下两个问题

  • 被配置的Bean的实例化方式是什么? Bean的实例化配置

  • 被配置的Bean是否需要注入必要的属性

4.1 无参构造配置非自定义的Bean
<--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhast:3306/mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
4.2 静态工厂配置非自定义的Bean
<--配置Connection-->
	<bean id="Clazz" class="java.long.class" factory-method="forName">
<constructor-arg name="className" value="jdbc:mysql://localhast:3306/mybatis"></constructor-arg>  
</bean>
    
    <bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype" >
		<constructor-arg name="url" value="jdbc:mysql://localhast:3306/mybatis"></constructor-arg>  
        <constructor-arg name="user" value="root"></constructor-arg>
        <constructor-arg name="password" value="root"></constructor-arg>
</bean>
4.3实例工厂配置非自定义的Bean
<--配置日期对象-->
<bean id="simpleDateFormat" class="java.text.SimpleDateFormat">
    <constructor-arg name="pattern" value="yyyy-mm-dd HH:mm:ss"></constructor-arg>
</bean>    
    
<bean id="date" factory-bean="simpleDateFormat" factory-method="parse" >
    <constructor-arg name="source" value="2024-10-07 12:00:00"></constructor-arg>
</bean>    

五. Bean实例化的基本流程

​ Spring容器在进行初始化时会将xml文件配置的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储在一个名为BeanDefinitionMap的Map集合中,Spring框架对该map进行遍历,使用反射创建Bean的实例对象,创建好的bean存储在singletonObjects的map集合中,当调用getbean()时则从该map集合中取出bean对象.

image-20241003185030912

image-20241003170233037

image-20241003140733734

image-20241003142217911

六. Spring后处理器

6.1 常见Spring后处理器
  • BeanFactoryPostProcessor:这种后置处理器在Bean实例化之前运行,主要用于对BeanDefinition的属性进行修改,如作用范围、是否懒加载等。

  • BeanPostProcessor:它在Bean实例化之后运行,用于对Bean进行进一步的处理。它包含两个主要方法:postProcessBeforeInitialization和postProcessAfterInitialization。前者在Bean初始化之前调用,后者在Bean初始化之后调用,使得开发者可以在这些点上对Bean进行增强处理,例如通过动态代理进行AOP增强。

  • BeanDefinitionRegistryPostProcessor:这是一种特殊的后置处理器,它允许在Spring的标准初始化之后修改应用上下文的内部Bean定义注册表。这通常用于动态注册额外的Bean定义或对现有的Bean定义进行修改。

  • InstantiationAwareBeanPostProcessor:继承自BeanPostProcessor,提供了在Bean实例化过程中进行操作的能力。它包括三个特有的方法和BeanPostProcessor的两个方法,使得开发者可以在Bean的实例化前后进行更细粒度的控制。

  • MergedBeanDefinitionPostProcessor:这种后置处理器用于合并Bean定义,通常用于处理复杂的Bean定义合并场景,确保最终的Bean定义是正确和完整的。

6.2 作用与意义:
  1. 增强Bean的功能:通过实现BeanPostProcessor接口,可以在Bean的初始化前后进行自定义操作,例如添加额外的属性、修改Bean的行为等。这使得开发者能够在不修改原始代码的情况下,对Bean进行扩展和增强。
  2. 动态代理与AOP:通过实现InstantiationAwareBeanPostProcessor接口,可以在Bean实例化过程中创建代理对象,从而实现AOP(面向切面编程)。这允许开发者在运行时动态地将横切关注点(如日志记录、事务管理等)应用到目标对象上,而无需修改原始代码。
  3. 处理复杂的Bean定义合并:MergedBeanDefinitionPostProcessor接口提供了一种机制来处理复杂的Bean定义合并场景,确保最终的Bean定义是正确和完整的。这对于处理多个配置文件或注解中的Bean定义冲突非常有用。
  4. 定制Bean生命周期:通过实现其他类型的后处理器,如BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,可以对Bean的定义和实例化过程进行定制化处理。这为开发者提供了更大的灵活性,以满足特定的需求。
  5. 提供插件机制:Spring后处理器提供了一种插件机制,允许第三方库或框架扩展Spring容器的功能。这种机制使得开发者能够轻松地集成和使用各种功能模块,而无需修改核心框架代码。
6.3 BeanFactoryPostProcessor

例一:修改某一个beanDefinition

**把继承BeanFactoryPostProcessor接口的类交个Spring管理**
    <bean id="userService" class="com.tuy.service.impl.UserServiceImpl" ></bean>
    <bean id="userDao"  class="com.tuy.dao.impl.UserDaoImpl"> </bean>

    <bean  class="com.tuy.prrocessor.MyBeanFactoryPostProcessor"></bean>
**继承BeanFactoryPostProcessor接口**
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("beanDefinitionMap填充完毕后回调该方法");
        //篡改beanDefinition
        BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("userService");
        beanDefinition.setBeanClassName("com.tuy.dao.impl.UserDaoImpl");
    }
}

**测试类(强转成userService报类型转换错误,不强转,可以看见打印的是userService,控制台输出的却是userDao)**
//强转成userService报类型转换错误
public class ApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        System.out.println(userService);
    }
}


//不强转,可以看见打印的是userService,控制台输出的却是userDao
public class ApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Object userService = applicationContext.getBean("userService");
        System.out.println(userService);
    }
}

image-20241003152552102

image-20241003152753685

例二. 添加某一个未被Spring容器管理的bean

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("beanDefinitionMap填充完毕后回调该方法");
        //动态注册beanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.tuy.dao.impl.PersonDaoImpl");
        DefaultListableBeanFactory defaultListableBeanFactory= (DefaultListableBeanFactory) beanFactory;
        defaultListableBeanFactory.registerBeanDefinition("perssonDao",beanDefinition);
    }
}
public class ApplicationContextTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Object bean = applicationContext.getBean(PersonDao.class);
        System.out.println(bean);
    }
}

image-20241003160940474

6.4 BeanDefinitionRegistryPostProcessor
    <bean  class="com.tuy.prrocessor.MyBeanDefinitionRegistryPostProcessor"></bean>
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry执行啦...");
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.tuy.dao.impl.PersonDaoImpl");
        beanDefinitionRegistry.registerBeanDefinition("perssonDao",beanDefinition);

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory执行啦...");
    }
}

image-20241003163042485

6.5 BeanPostProcessor
  <bean  class="com.tuy.prrocessor.MyBeanPostProcessor"></bean>
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+":postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"postProcessAfterInitialization");
        return bean;
    }
}

image-20241003164318179

七. SpringBean的生命周期

7.1 概述

​ Spring Bean的生命周期是从Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。

Spring Bean的生命周期大体上分为三个阶段:

  • Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,

是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;

  • Bean的初始化阶段:Bean创建之后还仅仅是个“半成品”,还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法 等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等.

  • Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个生命周期。

7.2 初始化阶段

Spring Bean的初始化过程涉及如下几个过程:

  • Bean实例的属性填充

  • Aware接口属性注入

  • BeanPostProcessor的before()方法

  • 回调 InitializingBean接口的初始化方法回调

  • 自定义初始化方法init回调

  • BeanPostProcessor的after()方法回调

Bean实例的属性填充过程

image-20241003173746596

image-20241003174006145

image-20241003174147073

image-20241003174209931

Spring在进行属性注入时,会分为如下几种情况:

  • 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;

  • 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;

  • 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。

注入单向对象引用属性

    <bean id="userService" class="com.tuy.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userDao" class="com.tuy.dao.impl.UserDaoImpl">
        <!--        <property name="userService" ref="userService"></property>-->
    </bean>

image-20241003180349579

注入双向对象引用属性

死循环

image-20241003181059136

解决方案:三级缓存

文字描述:

  • UserService实例化对象,但尚未初始化,将UserService存储到三级缓存;

  • UserService属性注入,需要UserDao,从缓存中获取,没有UserDao;

  • UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;

  • UserDao属性注入,需要UserService,从三级缓存获取UserService, UserService从三级缓存移入二级缓存;

  • UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;

  • UserService注入UserDao;

  • UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

图片描述:

image-20241003183245895

image-20241003183338335

image-20241003183533861

image-20241003183618898

img

Aware接口属性注入

image-20241003184702639

SpringIOC整体流程

image-20241003185147285

八. Spring XML方式整合第三方框架

8.1 xml整合第三方框架有两种整合方案:
  • 不需要自定义命名空间
  • 需要引用第三方框架命名空间

四. 注解开发

一. Bean的基本注解开发

1.1 基本bean注解

image-20241004134209811

image-20241004141127795

image-20241004140637075

//<bean id='userDao' class='com.tuy.dao.Impl.userDaoImpl'/>
@Component("userDao")
public class UserDaoImpl implements UserDao{
    
}
<context :component-scan base-package="com.tuy"/>
public static void main(String[] args){
    ClassPathXmlApplication applicationContext=
        new  ClassPathXmlApplicationContext("applicationContext.xml");
    System.out.println( applicationContext.getBean("userDao"));
   
}

image-20241004140205446

二. Bean的依赖注入注解开发

image-20241004141432458

@Component("userDao")
public class UserDaoImpl implements UserDao{
    @value("zhangsan")
    private String username;
    @Override
    public void show(){System.out.println( username);}
    //@value("zhangsan")
    public void setUsername(String username){this.username=username;}
}
public interface UserDao{
    void show();
}
public static void main(String[] args){
    ClassPathXmlApplication applicationContext=
        new  ClassPathXmlApplicationContext("applicationContext.xml");
    applicationContext.getBean("userDao").show();}

image-20241004142156598

三. 非自定义Bean的注解开发

image-20241004143630075

image-20241004144434735

四. 配置类的开发

@Configuration //标注当前类是一个配置类(代替.xml文件)
//<context :component-scan base-package="com.tuy"/>
@ComponentScan("com.tuy")
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource
//<import resource=""/>
@Import(xxx.class)
public class SpringConfig{}
public static void main(String[] args){
   ApplicationContext applicationContext =
        new  AnnotationConfigApplicationContext("SpringConfig");
   System.out.println( applicationContext.getBean("userDao"));
}

五.注解整合第三方框架

五. AOP

一.简介

image-20241004153325679

二. AOP思想实现方案Proxy

image-20241004153445057

三. AOP相关概念

image-20241004155153865

image-20241004165112555

四. 基于xml的方式配置AOP

image-20241004161727401

<!--配置目标类-->
<bean id="userService" class="com.tuy.Service.impl.UserServiceImpl"></bean>
<!--配置通知-->
<bean id="myAdvice" class="com.tuy.advice.MyAdivace"></bean>
<!--aop配置-->
<aop:config>
<!--配置切点表达式-->
    <aop:pointcut id="myPointcut" expression="execution(void com.tuy.impl.*impl)"/>
<!--配置切面-->
    <aop:aspect ref="myAdvice">
    	<aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
        <aop:before method="beforeAdvice" pointcut="execution(void com.tuy.impl.UserServuceImpl.show())"/>
    </aop:aspect>
</aop:config>

image-20241004164610994

image-20241004170312875

五. 基于注解配置AOP

创建切面类:创建一个类,用于定义切面的行为。在这个类上添加@Aspect注解,表示这是一个切面类。然后,使用@Before@After@Around等注解来定义切面的具体行为

@Aspect
public class MyAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointcut() {}

    @Before("pointcut()")
    public void beforeAdvice() {
        System.out.println("执行前置通知");
    }
}

配置AOP代理:在你的Spring配置文件中(例如applicationContext.xml),添加以下内容以启用AOP代理

<aop:aspectj-autoproxy/>
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

六. 基于AOP的声明式事务控制

一. Spring事务编程概述

事务在开发中是必不可少的。当我们使用JDBC进行开发时,通过connection对事务进行控制;而在使用MyBatis时,则通过SqlSession来管理事务。然而,这种控制方式的缺点显而易见:一旦我们切换数据库访问技术,事务控制的方式也会随之改变。

为了解决这个问题,Spring在这些技术的基础上提供了一个统一的控制事务的接口。Spring的事务控制主要分为两种:编程式事务控制和声明式事务控制。

  1. 编程式事务控制

    解释:Spring提供了一些类和方法,允许我们通过编码的方式来对业务代码进行事务控制。这种方式下,事务控制代码与业务操作代码紧密耦合在一起。

    缺点:由于事务控制代码和业务操作代码混合在一起,导致代码不够清晰,维护和扩展也较为困难。因此,在实际开发中不推荐使用。

  2. 声明式事务控制

    解释:Spring将事务控制的代码进行了封装,对外提供了XML和注解配置的方式。通过这些配置方式,我们可以完成事务的控制。

    优点:声明式事务控制可以实现事务控制与业务操作代码的解耦合,使得代码更加清晰、易于维护。因此,在实际开发中推荐使用这种方式。

总结来说,Spring为事务控制提供了编程式和声明式两种方式。其中,声明式事务控制因其解耦合的优点,更适用于实际开发场景。

二. 搭建测试环境

搭建一个转账的测试环境,我们需要在dao层实现一个转出钱的方法和一个转入钱的方法,service层实现一个转账业务方法,该方法内部分别调用dao层的转出钱和转入钱方法。具体准备工作如下:

  1. 数据库准备一个账户表tb_account;
  2. dao层准备一个AccountMapper,包括incrMoney和decrMoney两个方法;
  3. service层准备一个transferMoney方法,分别调用incrMoney和decrMoney方法;
  4. 在applicationContext文件中进行Bean的管理配置;
  5. 测试正常转账与异常转账。
public interface  AccountMapper{
    @Update("update tb_account set money=money+#{money} where account_name=#(accountName)")
    //+money
    public void incrMoney (@Param("accountName") String accountName,@Param("money") Integer money)
     @Update("update tb_account set money=money-#{money} where account_name=#(accountName)")
    //-money
       public void decrMoney (@Param("accountName") String accountName,@Param("money") Integer money)
}
public interface AccountService{
   void transferMoney(String outAccount,String inAccount,Integer money);
}
    
@Service ("accountService")
public class AccountServiceImpl implements AccountService{
   @Resource
    private AccountMapper accountMapper; 
    @Overrride
    public void transferMoney(String outAccount,String inAccount,Integer money){
        accountMapper.decrMoney(outAccount,money);
         accountMapper.incrMoney(outAccount,money);
        
    }
}
<context :component-scan base-package="com.tuy"/>
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 配置数据源信息 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

<!-- 配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.tuy.mapper"></property>
</bean>

三. 基于xml声明式事务控制

根据我们之前学习的AOP技术,我们可以很容易地想到,可以使用AOP对Service的方法进行事务的增强。

目标类:自定义的AccountServiceImpl,其内部方法是切点。

通知类:Spring提供的,通知方法已经定义好,只需要配置即可。

分析:

  • 通知类是Spring提供的,需要导入Spring事务相关的坐标;
  • 配置目标类AccountServiceImpl;
  • 使用advisor标签配置切面。
  • 配置不同方法的事务属性时,可以设置以下参数:
    1. name:方法名称,代表通配符。例如,添加操作可以设置为adduser、addAccount、addorders等,即add
    2. isolation:事务的隔离级别,用于解决事务并发问题。
    3. timeout:超时时间,默认为-1,单位是秒。
    4. read-only:是否只读,查询操作可以设置为只读。
    5. propagation:事务的传播行为,用于解决业务方法调用业务方法(事务嵌套问题)。
<!--配置平台事务管理器:使用DataSourceTransactionManager作为事务管理器,并引用名为dataSource的数据源。-->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置Spring提供好的Advice:创建一个名为txAdvice的事务增强器,并将事务管理器transactionManager注入其中。在<tx: attributes>标签内,设置所有方法(*)都需要进行事务管理。 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <!-- 配置切点表达式 -->
    <aop:pointcut id="txPointcut" expression="execution (com.itheima.service.impl.**(..))"/>
    <!-- 配置织入关系通知,引入Spring提供的Advice -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

3.1 事务传播行为

事务传播行为是指在一个事务中调用另一个事务时,如何处理这两个事务之间的关系。在Spring框架中,可以通过设置@Transactional注解的propagation属性来配置事务的传播行为。以下是各种传播行为的简要说明:

  1. REQUIRED(默认值):如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
  2. REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  3. SUPPORTS:如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式执行。
  4. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  5. NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  6. MANDATORY:必须在一个已有的事务中执行,如果没有事务,就抛出异常。
  7. NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

四. 基于注解的声明式事务控制

通过在方法或类上添加@Transactional注解来声明事务的属性

  • propagation:设置事务的传播行为,解决嵌套事务问题。
  • isolation:设置事务的隔离级别,解决并发问题。
  • timeout:设置事务的超时时间。
  • readOnly:设置事务是否为只读。
  • rollbackFor:指定哪些异常会导致事务回滚。
  • noRollbackFor:指定哪些异常不会导致事务回滚。