Spring框架

1. Spring框架

Spring框架是JavaEE开源的企业级框架:解决企业级别复杂的业务逻辑和应用

  • IOC:控制反转,把创建对象的过程交给Spring来管理

  • AOP:面向切面,在不修改源代码的基础上进行功能增强

特点

  • 方便解耦,简化开发

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

  • AOP编程的支持

  • 方便和其他框架进行整合

    • SSM = Spring+SpringMVC+MyBatis
    • SSH= Spring+Struts+Hibernate
  • 方便程序的测试

  • 方便进行事务开发

  • 降低API开发难度

    • API:Applicantion Program Interface 应用程序接口

IOC

控制反转: 把对象的创建和对象之间的调用过程,交给Spring来管理。
使用目的: 为了降低耦合度

Bean管理

两个意思:Spring创建对象Spring属性注入

两种实现方式:xml 和 注解

xml方式

注解方式

属性注入

在 Spring 框架中,属性注入是一种常见的依赖注入方式,用于将属性值注入到 Bean 实例中。Spring 提供了多种属性注入的方式,主要包括以下几种:

  1. 构造器注入

    • 通过构造器参数传递依赖项,Spring 容器在实例化 Bean 时会调用相应的构造器,并将参数传递进去。
  2. Setter 方法注入

    • 通过调用 Bean 的 setter 方法来设置属性的值,Spring 容器在实例化 Bean 后会调用相应的 setter 方法进行属性注入。
  3. 字段注入

    • 直接在类的字段上添加 @Autowired@Value 注解,Spring 容器会自动通过反射机制将属性值注入到字段中。
  4. 接口注入

    • 使用 @Autowired 注解或者 Spring 的 @Resource 注解来注入接口类型的属性,Spring 会根据接口类型自动选择合适的实现类进行注入。
  5. 注解注入

    • 使用 @Autowired@Value@Resource 等注解来标记需要注入的属性,Spring 容器会根据注解进行自动注入。
  6. XML 配置注入

    • 在 XML 配置文件中通过 <property> 标签或 <constructor-arg> 标签指定属性值,Spring 容器会根据配置文件进行属性注入。
  7. Java Config 注入

    • 使用 Java Config 类(如 @Configuration 标记的配置类)来配置 Bean,通过方法返回值或方法参数进行属性注入。

以上是 Spring 中常见的属性注入方式,开发人员可以根据具体需求和习惯选择合适的方式来进行属性注入。不同的注入方式各有优缺点,开发人员可以根据实际情况选择最适合的方式。

Spring中的单例和多例

Spring容器创建对象默认是单例模式 ,用scope属性来设置

  • scope = “prototype”
  • scope = “singleton”
<bean id = "book" class = "com.gk.entity.Book" scope = ""></bean>

区别:

  • singleton单例,prototype多实例

  • 当scope取值singleton时,在加载aplicationContext.xml配置文件时创建对象,只创建一个

  • 当scope取值prototype的时候,加载Spring配置文件时不会创建对象,在调用getBean方法创建实例对象,每调用一次创建一次

Bean的生命周期

生命周期:从对象的创建到对象的销毁的过程

  1. 实例化:当容器接收到Bean的定义信息时,容器会根据定义信息创建出Bean实例。
  2. 属性赋值:创建Bean实例后,容器会将配置文件中的属性值或注解信息注入到Bean实例中。
  3. 自定义初始化方法调用:当Bean实例的属性都设置完成后,容器会调用Bean实现的InitializingBean接口的afterPropertiesSet方法或者使用元素的init-method属性指定的初始化方法来进行自定义初始化。
  4. BeanPostProcessor前置处理器beforeInitialization方法调用:在自定义初始化方法之前,容器会调用BeanPostProcessor前置处理器的beforeInitialization方法对Bean进行处理。
  5. 自定义初始化方法执行:执行自定义初始化方法。
  6. BeanPostProcessor后置处理器afterInitialization方法调用:在自定义初始化方法之后,容器会调用BeanPostProcessor后置处理器的afterInitialization方法对Bean进行处理。
  7. Bean销毁:当容器关闭时,或者通过配置文件指定Bean的destroy-method属性指定的方法来销毁Bean实例。

不添加后置处理器

生命周期为:

  • 第一步: 执行无参数的构造方法
  • 第二步 : 执行sette方法设置属性值
  • 第三步: 执行初始化方法
  • 第四步:获取bean的实例对象
  • 第五步: 执行销毁方法

添加后置处理器

在 Spring 框架中,**Bean 后置处理器(BeanPostProcessor)**是一种特殊的 Bean,它可以在 Spring 容器实例化 Bean 并完成依赖注入后对 Bean 进行加工处理。

Bean 后置处理器为程序员提供了一个修改和定制 Bean 的机会,常用于以下场景:

  1. 初始化前后进行某些逻辑操作,如检查 Bean 属性是否正确设置、增强 Bean 的功能等。

  2. 为一些特殊的 Bean 提供通用的逻辑处理功能,如声明式事务管理、声明式安全检查、AOP 的切面支持等。

Bean 后置处理器通过实现 BeanPostProcessor 接口来定义自己的逻辑。在 Spring 容器初始化 Bean 时,会调用所有注册的 Bean 后置处理器的 postProcessBeforeInitialization() 方法,在 Bean 的初始化方法被调用前加以处理;在 Bean 的初始化方法被调用后,会调用所有注册的 Bean 后置处理器的 postProcessAfterInitialization() 方法,以完成 Bean 的加工处理工作。

请注意,Bean 后置处理器在 Spring 容器中的执行顺序是固定的,即先注册先执行。

生命周期变为:

  • 第一步: 执行无参数的构造方法
  • 第二步 : 执行set方法设置属性值
  • 在初始化之前执行的方法 -----
  • 第三步: 执行初始化方法
  • 在初始化之后执行的方法-----
  • 第四步:获取bean的实例对象
  • 第五步: 执行销毁方法

自动装配

xml配置

注解配置

在Spring框架中,有几个常用的注解用于标识不同类型的Bean,它们都实现<bean id = "" class = ""></bean>的功能。这些注解包括:

  1. @Component:通用的组件注解,用于标识一个普通的Spring管理的Bean。
@Component
public class MyComponent {
    // ...
}
  1. @Service:用于标识一个服务层的Bean,表示该Bean用于业务逻辑的处理。
@Service
public class MyService {
    // ...
}
  1. @Controller:用于标识一个控制器层的Bean,表示该Bean用于处理请求和返回视图。
@Controller
public class MyController {
    // ...
}
  1. @Repository:用于标识一个数据访问层的Bean,表示该Bean用于数据库操作或其他持久化操作。
@Repository
public class MyRepository {
    // ...
}

这些注解都是基于@Component注解,并提供了更具体的语义。

  • 在使用这些注解时,需要确保它们能够被扫描到并交由Spring容器进行管理。一般可以通过在Spring配置文件中添加<context:component-scan/>来启用组件扫描。

需要注意的是,这些注解只是用于标识不同类型的Bean,并不能代替实际的业务逻辑或功能实现。它们的作用是让Spring容器能够正确地创建和管理各个组件,并能够进行依赖注入等操作。

eg:下面的事务中的BankDaoImpl.java和xml

全注解配置

在注解配置的基础上

AOP

  • **连接点:**类里面哪些方法可以被增强,这些方法称为连接点

  • **切入点:**实际上真正被增强的方法被称为切入点

  • **切入点表达式:**作用–知道哪个类被增强

    execution([权限修饰符][返回值类型][类全路径][方法名][参数列表])

    • 对 cn.liushao.dao.BookDao 类里面的add方法进行增强
      execution(* cn.liushao.dao.BookDao.add(…))
    • 对 cn.liushao.dao.BookDao 类里面的所有方法进行增强
      execution(* cn.liushao.dao.BookDao.*(…))
    • 对 cn.liushao.dao 包里面的所有类中所有方法进行增强
      execution(* cn.liushao.dao..(…))
  • **通知(增强):**实际上被增强的逻辑部分被称为通知

    • 通知类型
      • 前置通知
      • 后置通知
      • 环绕通知
      • 异常通知
      • 最终通知
  • **切面:**是动作,把通知应用到切入点的过程称为切面

Spring中事务管理

spring-transaction

事务

事务(Transaction)是数据库管理系统中的一个基本概念,它代表了一组数据库操作的逻辑单位。事务可以确保这组操作要么全部执行成功,要么全部回滚(即撤销),从而保证数据库的一致性和完整性

  • 声明式事务
  • 编程式事务

事务的隔离级别

事务隔离级别脏读不可重复读幻读
读未提交Read Uncommited可能可能可能
读已提交Read Commited不可能可能可能
可重复读Repeatable Read不可能不可能可能
串行化 Serializable不可能不可能不可能

不使用事务

不使用事务,没有异常,正常运行

在加钱和减钱中间写入异常,前面执行,后面不执行

  1. BankDao.java
package com.xwk.dao;

/**
 * @Author GaoKun
 * @Date 2023/10/18 0018 11:09:59
 * @Description: TODO
 */
public interface BankDao {
    public void addMoney();
    public void delMoney();
}

  1. BankDaoImpl.java
package com.xwk.dao.impl;

import com.xwk.dao.BankDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

/**
 * @Author GaoKun
 * @Date 2023/10/18 0018 11:14:02
 * @Description: TODO
 */
@Component
public class BankDaoImpl implements BankDao {
    @Autowired
    public JdbcTemplate jdbcTemplate;
    @Override
    public void addMoney() {
        String sql = "update bank set money=money+? where id = ?";
        jdbcTemplate.update(sql,100,1);
    }
    @Override
    public void delMoney() {
        String sql = "update bank set money=money-? where id = ?";
        jdbcTemplate.update(sql,100,2);
    }
}
  1. BankService.java
package com.xwk.service;

import com.xwk.dao.BankDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Author GaoKun
 * @Date 2023/10/18 0018 11:19:31
 * @Description: TODO
 */

/**
 * 业务层,实现具体功能  事务
 */
@Service
public class BankService {
    @Autowired
    public BankDao bankDao;
    public void accountMoney(){
        bankDao.delMoney();//减少钱
        System.out.println("----------------------");
        //System.out.println(5/0);//模拟 出现了问题!!导致减钱成功,加钱失败
        bankDao.addMoney(); //加钱
        System.out.println("转账成功了!");
    }
}

  1. BankTest.java
package com.xwk.test;

import com.xwk.dao.impl.BankDaoImpl;
import com.xwk.service.BankService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author GaoKun
 * @Date 2023/10/18 0018 11:20:43
 * @Description: TODO
 */
public class BankTest {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        BankService bankService = ac.getBean("bankService", BankService.class);
        bankService.accountMoney();
    }
}

5.applicationContext.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p" 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
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="password" value="123456"></property>
        <property name="username" value="root"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"></property>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

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

</beans>

开启事务

使用注解方式

  1. 配置事务管理器

    在application.xml中

    <!--开启事务-配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
  2. 开启注解事务,以使用注解

    <!--开启注解事务-->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    

1

  1. 注解写在方法上(写在类上代表这个类的所有方法都开启事务,查询是不需要开启事务的,所以注解要写在需要开启的方法上)

    @Transactional注解

    此注解可以设置多个属性:

    • propagation 事务的传播行为
      • REQUIRED 如果有事务在运行,当前方法就在这个事务内运行,否则,就会启动一个新的事务,并在自己的事务内运行
      • REQUIRES_NEW 当前的方法必须启动新的事务,并在自己的事务内运行,如果有事务在运行,会将它挂起
      • SUPPORTS 如果有事务在运行,当前方法就在这个事务内运行,否则它可以不在事务内运行
      • NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
      • MANDATORY : 当前的方法必须运行在事务内部,如果没有运行的事务,就会抛出异常
      • NEVER : 当前方法不应该运行在事务中,如果有运行的事务,就会抛出异常
      • NESTED : 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。否则,就启动一个新的事务,并在它自己的事务内运行
    • isolation 事务的隔离级别
      • 读已提交
      • 读未提交
      • 可重复读
      • 串行化
    • timeout 是以秒为单位的!!!
    • readOnly 默认值是false 如果设置为 true只能查询!!
    • rollbackFor 回滚 后面指定异常类型 , norollbackFor 不回滚

    修改后的BankService.java,出现异常,操作回滚,数据不变

    package com.xwk.service;
    
    import com.xwk.dao.BankDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.beans.Transient;
    @Service
    public class BankService {
        @Autowired
        public BankDao bankDao;
        @Transactional(
                propagation = Propagation.REQUIRED,
                isolation = Isolation.REPEATABLE_READ,
                timeout = 10,
                readOnly = false,
                rollbackFor = Exception.class
        )
        public void accountMoney(){
            bankDao.delMoney();//减少钱
            System.out.println("----------------------");
            System.out.println(5/0);//模拟 出现了问题!!
            bankDao.addMoney(); //加钱
            System.out.println("转账成功了!");
        }
    }
    
    

使用xml方式

先删掉

applicationContext1.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p" 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
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="password" value="123456"></property>
            <property name="username" value="root"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"></property>
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    </bean>

    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="bankDao" class="com.gk.dao.impl.BankDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <bean id="bankService" class="com.gk.service.BankService">
        <property name="bankDao" ref="bankDao"></property>
    </bean>

    <!--开启事务-配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置通知-->
    <tx:advice id="transactionInterceptor">
        <!--配置事务参数-->
        <tx:attributes>
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--<tx:method name="del*" propagation="REQUIRED"/> 为其他方法配置
            <tx:method name="add*" propagation="REQUIRED"/>-->
        </tx:attributes>
    </tx:advice>
    <!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="p" expression="execution(* com.gk.service.BankService.accountMoney(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="p"></aop:advisor>
    </aop:config>

</beans>

这里使用AOP对accountMoney方法进行增强(在不改变原来方法的基础上加入事务)操作

三级缓存解决循环依赖

Spring 中的三级缓存是解决循环依赖问题的一种机制。当 Bean 之间存在循环依赖时,Spring 容器会通过三级缓存机制来避免依赖死锁或依赖注入失败的情况。

三级缓存中包括了三个 Map 容器,分别为 singletonObjects、earlySingletonObjects 和 singletonFactories。具体作用如下:

  1. singletonObjects 存放已经完全实例化好的单例 Bean 对象。
  2. earlySingletonObjects 存放尚未完全实例化好的单例 Bean 对象,即正在创建过程中的 Bean 对象。
  3. singletonFactories 存放用于创建单例 Bean 的 ObjectFactory 对象。

在解决循环依赖时,Spring 会先创建一个 earlySingletonObjects 对象,并将该对象放入到第二级缓存 earlySingletonObjects 中。接着,Spring 会创建一个空对象并放入第一级缓存 singletonObjects 中,并将该对象的实例化过程记录到 singletonFactories 中。然后,Spring 就会根据该对象所依赖的其他 Bean 创建出一个完整的 Bean 实例,并将该实例放入到第一级缓存 singletonObjects 中。

如果此时有其他 Bean 依赖该对象,Spring 会从 earlySingletonObjects 中取出该对象并完成实例化,并将其放入到第一级缓存 singletonObjects 中。这样就避免了循环依赖的问题。

当 Bean 的实例化完成后,Spring 会从三级缓存中清除相应的对象,并将该对象放入到第一级缓存 singletonObjects 中。这样在后续的 Bean 创建过程中,就能直接从第一级缓存 singletonObjects 中获取已经实例化好的 Bean 对象,避免重复创建。

需要注意的是,三级缓存只适用于单例 Bean,对于原型 Bean 或其他作用域的 Bean 无效。此外,三级缓存也会增加 Spring 容器的内存消耗,因此开发人员应该避免出现过多的循环依赖情况,以提高系统性能和稳定性。

Spring中的设计模式

Spring 框架是一个基于 Java 的开源框架,它采用了多种设计模式来实现其功能。

以下是 Spring 框架中常用的设计模式:

  1. 控制反转(Inversion of Control,IoC)/ 依赖注入(Dependency Injection,DI):IoC/DI 是 Spring 框架的核心设计模式之一。通过 IoC 容器管理对象的创建和生命周期,将对象的依赖关系交由 IoC 容器负责装配,从而实现松耦合和可维护性。

  2. 工厂模式(Factory Pattern):Spring 使用工厂模式来创建和管理对象。通过配置文件或注解等方式定义 Bean 的属性和依赖关系,并由容器负责创建和管理这些 Bean 实例。

  3. 单例模式(Singleton Pattern):Spring 中的 Bean 默认是单例的,即每个 Bean 在容器中只有一个实例。这种设计可以提高应用程序的性能和响应速度,减少资源的浪费。

  4. 代理模式(Proxy Pattern):Spring 的 AOP(Aspect-Oriented Programming,面向切面编程)功能是基于代理模式实现的。AOP 可以在不修改原有代码的情况下,通过动态代理的方式对方法进行增强,如事务管理、安全控制等。

  5. 观察者模式(Observer Pattern):Spring 框架中的事件机制就是基于观察者模式实现的。通过定义事件、注册监听器、触发事件等方式,实现应用程序中各个模块之间的松耦合。

  6. 模板方法模式(Template Method Pattern):Spring 框架中的 JdbcTemplate 就是典型的模板方法模式实现。JdbcTemplate 封装了 JDBC 的操作流程,将常见的 SQL 操作流程抽象为模板方法,从而简化开发者的代码实现。

Spring 框架采用了多种设计模式来实现其功能,这些模式的应用使得 Spring 具有良好的可扩展性、灵活性和易用性,成为了 Java 开发中广泛使用的框架之一。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值