Spring3(代理模式 Spring1案例补充 Aop 面试题)

Spring3

  • 代理模式概述
    • 介绍
    • 什么是代理模式?
    • 为什么要使用代理模式?
    • 有哪几种代理模式?
    • 静态代理
    • 基于接口实现的动态代理(JDK自带)
    • 基于子类的动态代理
  • Spring_AOP_01案例补充(添加事务管理)
    • 实现完整代码:
    • 常规实现:
    • 代理实现
  • AOP
    • xml实现日志记录
    • 注解实现日志记录
  • 面试题:
    • ⭐spring 框架如何解决循环依赖?
    • Spring 框架中有哪些注解?
    • ⭐Spring 框架中用到的设计模式
    • Spring 框架中AOP的基本理解
    • ⭐Spring AOP和 AspectJ AOP有什么区别?
    • Spring AOP有哪些通知类型?
    • ⭐Spring 管理事务的方式有几种?
    • ⭐Spring事务中有哪几种事务传播行为?
    • ⭐Spring 事务中有哪几种事务隔离级别?
    • ⭐Spring 事务在什么情况下会失效?
  • 图片版

代理模式概述

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。

代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍

意图

  • 为其他对象提供一种代理以控制对这个对象的访问。

主要解决的问题

  • 代理模式解决的是在直接访问某些对象时可能遇到的问题,例如对象创建成本高、需要安全控制或远程访问等。

使用场景

  • 当需要在访问一个对象时进行一些控制或额外处理时。

实现方式

  • 增加中间层:创建一个代理类,作为真实对象的中间层。
  • 代理与真实对象组合:代理类持有真实对象的引用,并在访问时进行控制。

关键代码

  • 代理类:实现与真实对象相同的接口,并添加额外的控制逻辑。
  • 真实对象:实际执行任务的对象。

应用实例

  • 快捷方式:Windows系统中的快捷方式作为文件或程序的代理。
  • 角色扮演:孙悟空作为高翠兰的代理,猪八戒无法区分。
  • 代售点:购买火车票时,代售点作为火车站的代理。
  • 支票:作为银行账户资金的代理,控制资金的访问。
  • Spring AOP:使用代理模式来实现面向切面编程。

优点

  • 职责分离:代理模式将访问控制与业务逻辑分离。
  • 扩展性:可以灵活地添加额外的功能或控制。
  • 智能化:可以智能地处理访问请求,如延迟加载、缓存等。

缺点

  • 性能开销:增加了代理层可能会影响请求的处理速度。
  • 实现复杂性:某些类型的代理模式实现起来可能较为复杂。

使用建议

  • 根据具体需求选择合适的代理类型,如远程代理、虚拟代理、保护代理等。
  • 确保代理类与真实对象接口一致,以便客户端透明地使用代理。

注意事项

  • 与适配器模式的区别:适配器模式改变接口,而代理模式不改变接口。
  • 与装饰器模式的区别:装饰器模式用于增强功能,代理模式用于控制访问。

结构

主要涉及到以下几个核心角色:

  • 抽象主题(Subject):
    • 定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
  • 真实主题(Real Subject):
    • 实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
  • 代理(Proxy):
    • 实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
  • 客户端(Client):
    • 使用抽象主题接口来操作真实主题或代理主题,不需要知道具体是哪一个实现类。

什么是代理模式?

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

通俗的来讲代理模式就是我们生活中常见的中介

举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了

为什么要使用代理模式?

**中介隔离作用:**在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

**开闭原则,增加功能:**代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。

有哪几种代理模式?

我们有多种方式来实现代理,如果按照代理创建的时期来进行分类的话,可以分为两种:

静态代理:

静态代理是由程序员创建或特定工具自动生成源代码,再对其进行编译。在程序员运行之前,代理类class文件就已经被创建了。

动态代理:

动态代理分为:

  • 基于接口的动态代理(JDK自带)
  • 基于子类的动他代理(第三方,例如cglib)

项目结构:

静态代理

实现:

ISinger接口:

ISinger.java

package com.stringzhua.staticproxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:27
 * description:
 */
public interface ISinger {
    public void sing();
}

被代理对象歌手XuSinger

XuSinger.java

package com.stringzhua.staticproxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:27
 * description:
 */
public class XuSinger implements ISinger {

    public void sing() {
        System.out.println("断桥残雪");
    }
}

经济人实现了ISinger接口,实现代理,在经纪人方法上进行歌手对象的增强

JinJiRen.java

package com.stringzhua.staticproxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:29
 * description:
 */
public class JinJiRen implements ISinger {
    //被代理对象
    //1.中介隔离作用
    ISinger singer;

    public JinJiRen(ISinger singer) {
        this.singer = singer;
    }

    public void sing() {
        //2.方法增强
        System.out.println("自我介绍");
        singer.sing();
    }
}

请求:

package com.stringzhua.staticproxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:31
 * description:代理模式:
 *     功能:
 *         1.中介隔离作用
 *         2.方法增强
 *     方式:
 *         1.静态代理
 *         2.动态动态
 *             jdk自带
 *             第三方cglib
 */
public class Test {
    public static void main(String[] args) {
        //1.被代理对象
        ISinger xu = new XuSinger();
        //2.代理对象
        ISinger jinjiren = new JinJiRen(xu);
        jinjiren.sing();
    }
}

基于接口实现的动态代理(JDK自带)

基于接口的动态代理:

  • 特点:字节码随用随创建,随用随加载
  • 作用:不修改源码的基础上对方法增强

涉及的类:Proxy

提供者:JDK官方

**如何创建代理对象:**使用Proxy类中的newProxyInstance方法

创建代理对象的要求:被代理类最少实现一个接口,如果没有则不能使用

newProxyInstance方法的参数

  • ClassLoader:类加载器
    • 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
  • Class[]:字节码数组
    • 它是用于让代理对象和被代理对象有相同方法。固定写法。
  • InvocationHandler:用于提供增强的代码

实现:

ISinger接口:

ISinger.java

package com.stringzhua.proxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:27
 * description:
 */
public interface ISinger {
    public void sing();
}

XuSinger实现类:

XuSinger.java

package com.stringzhua.proxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:27
 * description:
 */
public class XuSinger implements ISinger {

    public void sing() {
        System.out.println("断桥残雪");
    }
}

测试类:

Test.java

package com.stringzhua.proxy;

import com.stringzhua.staticproxy.JinJiRen;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:31
 * description:代理模式:
 * 功能:
 * 1.中介隔离作用
 * 2.方法增强
 * 方式:
 * 1.静态代理
 * 2.动态动态
 * jdk自带
 * 第三方cglib
 */
public class Test {
    public static void main(String[] args) {
        //1.被代理对象
        final ISinger xu = new XuSinger();
        /**
         * 基于接口的动态代理:
         *  特点:字节码随用随创建,随用随加载
         *  作用:不修改源码的基础上对方法增强
         *  涉及的类:Proxy
         *  提供者:JDK官方
         *  如何创建代理对象:
         *      使用Proxy类中的newProxyInstance方法
         *  创建代理对象的要求:
         *      被代理类最少实现一个接口,如果没有则不能使用
         *  newProxyInstance方法的参数:
         *      ClassLoader:类加载器
         *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
         *      Class[]:字节码数组
         *          它是用于让代理对象和被代理对象有相同方法。固定写法。
         *      InvocationHandler:用于提供增强的代码
         *
        //2.代理对象-->内部类
        //JDK自带
        ISinger jinjiren = (ISinger) Proxy.newProxyInstance(
                xu.getClass().getClassLoader(), xu.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("自我介绍");
                Object invoke = method.invoke(xu, args);
                return invoke;
            }
        });
        jinjiren.sing();
    }
}

基于子类的动态代理

基于子类的动态代理

  • 涉及的类:Enhancer
  • 提供者:第三方cglib库
  • 开发环境:添加cglib依赖坐标
<dependencies>
  <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.1_3</version>
  </dependency>
</dependencies>

如何创建代理对象:使用Enhancer类中的create方法

创建代理对象的要求:被代理类不能是最终类

create方法的参数:

  • Class:字节码
    • 它是用于指定被代理对象的字节码。
  • Callback:用于提供增强的代码
    • 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。

此接口的实现类都是谁用谁写。

我们一般写的都是该接口的子接口实现类:MethodInterceptor


实现:

ISinger接口:

package com.stringzhua.cglibproxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:27
 * description:
 */
public interface ISinger {
    public void sing();
}

XuSinger接口实现类:

package com.stringzhua.cglibproxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:27
 * description:
 */
public class XuSinger implements ISinger {

    public void sing() {
        System.out.println("断桥残雪");
    }
}

测试,Test.java

package com.stringzhua.cglibproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 16:31
 * description:代理模式:
 * 功能:
 * 1.中介隔离作用
 * 2.方法增强
 * 方式:
 * 1.静态代理
 * 2.动态动态
 * jdk自带
 * 第三方cglib
 */
public class Test {
    public static void main(String[] args) {
        //1.被代理对象
        final ISinger xu = new XuSinger();
        //2.代理对象-->内部类
        /**
         * 2.创建代理对象
         * 参数1:被代理对象的字节码
         * 参数2:InvocationHandler
         * */
        /**
         * 执行被代理对象的任何方法都会经过该方法
         * @param proxy
         * @param method
         * @param args
         *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
         * @param methodProxy :当前执行方法的代理对象
         * @return
         * @throws Throwable
         */
        //第三方cglib
        ISinger jinjiren = (ISinger) Enhancer.create(xu.getClass(), new InvocationHandler() {
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                System.out.println("自我介绍");
                Object invoke = method.invoke(xu, objects);
                return invoke;
            }
        });
        jinjiren.sing();
    }
}

Spring_AOP_01案例补充(添加事务管理)

在Spring_AOP_01中的基础上 实现 转钱功能

xml实现

项目状态:成功提交,失败不能做到一个业务方法一起回滚

分析原因:项目存在事务自动管理,且自动管理在dao层实现

解决方案:事务管理未来一定是在service层实现

方案思考:

1.dao层不在进行事务管理,自动事务提交关闭

2.业务类的每个业务方法中的多个dao操作,公用同一个连接connection对象

3.ThreadLocal

实现完整代码:

Controller层:

IAccountController.java

package com.stringzhua.controller;

import com.stringzhua.pojo.Account;

import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 17:05
 * description:
 */
public interface IAccountController {
    public void add(Account account);
    public void update(Account account);
    public Account findByName(String name);
    public List<Account> findAll();
    public void transformMoney(String fromName, String targetName, int money);
}

AccountControllerImpl.java

package com.stringzhua.controller;

import com.stringzhua.pojo.Account;
import com.stringzhua.service.IAccountService;

import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 17:06
 * description:
 */
public class AccountControllerImpl implements IAccountController {
    IAccountService service;

    public void setService(IAccountService service) {
        this.service = service;
    }

    public void add(Account account) {
        service.add(account);
    }

    public void update(Account account) {
        service.update(account);
    }

    public Account findByName(String name) {
        return service.findByName(name);
    }

    public List<Account> findAll() {
        return service.findAll();
    }

    public void transformMoney(String fromName, String targetName, int money) {
        service.transformMoney(fromName, targetName, money);
    }
}

Dao层:

IAccountDao.java

package com.stringzhua.dao;

import com.stringzhua.pojo.Account;

import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 16:53
 * description:
 */
public interface IAccountDao {
    public void add(Account account);
    public void update(Account account);
    public Account findByName(String name);
    public List<Account> findAll();
}

AccountDaoImpl.java

package com.stringzhua.dao;

import com.stringzhua.pojo.Account;
import com.stringzhua.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 16:54
 * description:
 */
public class AccountDaoImpl implements IAccountDao {
    //装配
    QueryRunner queryRunner;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    //装配工具类的Connection,使用同一个Connection
    ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    public void add(Account account) {
        try {
            queryRunner.update(connectionUtils.createConn(), "insert into account(aname,amoney) values(?,?)", account.getAname(), account.getAmoney());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public void update(Account account) {
        try {
            queryRunner.update(connectionUtils.createConn(), "update account set aname=?,amoney=? where aid=?", account.getAname(), account.getAmoney(), account.getAid());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public Account findByName(String name) {
        try {
            return queryRunner.query(connectionUtils.createConn(), "select * from account where aname=?", new BeanHandler<Account>(Account.class), name);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    public List<Account> findAll() {
        try {
            return queryRunner.query(connectionUtils.createConn(), "select * from account", new BeanListHandler<Account>(Account.class));
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}

factory层:

ServiceFactory.java

package com.stringzhua.factory;

import com.stringzhua.controller.IAccountController;
import com.stringzhua.service.IAccountService;
import com.stringzhua.utils.TransactionUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 18:39
 * description:
 */
public class ServiceFactory {
    //被代理的是service
    IAccountService proxyservice;

    public void setProxyservice(IAccountService proxyservice) {
        this.proxyservice = proxyservice;
    }

    //代理的是transaction事务
    TransactionUtils transactionUtils;

    public void setTransactionUtils(TransactionUtils transactionUtils) {
        this.transactionUtils = transactionUtils;
    }

    //创建代理
    public IAccountService proxyService() {
        return (IAccountService) Proxy.newProxyInstance(
            proxyservice.getClass().getClassLoader(), proxyservice.getClass().getInterfaces(), new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object object = null;
                    try {
                        //开启事务
                        transactionUtils.beginTransaction();
                        object = method.invoke(proxyservice, args);
                        //自动提交
                        transactionUtils.commitTransaction();
                    } catch (Exception e) {
                        e.printStackTrace();
                        transactionUtils.rollbackTransaction();
                    } finally {
                        transactionUtils.closeTransaction();
                    }
                    return object;

                }
            });
        }
}

POJO层:

Account.java

package com.stringzhua.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 16:41
 * description:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private int aid;
    private String aname;
    private int amoney;

    public Account(String aname, int amoney) {
        this.aname = aname;
        this.amoney = amoney;
    }
}

Service层:

IAccountService.java

package com.stringzhua.service;

import com.stringzhua.pojo.Account;

import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 16:53
 * description:
 */
public interface IAccountService {
    public void add(Account account);

    public void update(Account account);

    public Account findByName(String name);

    public List<Account> findAll();

    public void transformMoney(String fromName, String targetName, int money);
}

AccountServiceImpl.java

package com.stringzhua.service;

import com.stringzhua.dao.IAccountDao;
import com.stringzhua.pojo.Account;
import com.stringzhua.utils.TransactionUtils;

import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 17:04
 * description:
 */
public class AccountServiceImpl implements IAccountService {
    IAccountDao dao;

    public void setDao(IAccountDao dao) {
        this.dao = dao;
    }

    public void add(Account account) {
        dao.add(account);
    }

    public void update(Account account) {
        dao.update(account);
    }

    public Account findByName(String name) {
        return dao.findByName(name);
    }

    public List<Account> findAll() {
        return dao.findAll();
    }

    public void transformMoney(String fromName, String targetName, int money) {

        Account fromAccount = dao.findByName(fromName);
        Account targetAccount = dao.findByName(targetName);

        fromAccount.setAmoney(fromAccount.getAmoney() - money);
        targetAccount.setAmoney(targetAccount.getAmoney() + money);

        dao.update(fromAccount);
        //假设出现异常
        int a = 10 / 0;
        dao.update(targetAccount);
    }
}

Util层:

ConnectionUtils.java

package com.stringzhua.utils;


import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 12:01
 * description:
 */
public class ConnectionUtils {
    DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    //这里创建了线程区域对象
    ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

    //获取链接
    public Connection createConn() {
        try {
            Connection connection = threadLocal.get();
            //判断是不是第一次获取
            if (connection == null) {
                connection = dataSource.getConnection();
                //将connection存入threadlocal中
                threadLocal.set(connection);
            }
            return connection;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    //从ThreadLocal中移除链接
    public void removeConn() {
        threadLocal.remove();
    }
}

TransactionUtils.java

package com.stringzhua.utils;

import java.sql.SQLException;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 12:10
 * description:
 */
public class TransactionUtils {
    ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    //开启手动管理事务
    public void beginTransaction() {
        try {
            connectionUtils.createConn().setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //提交事务
    public void commitTransaction() {
        try {
            connectionUtils.createConn().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //回滚事务
    public void rollbackTransaction() {
        try {
            connectionUtils.createConn().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //关闭事务
    public void closeTransaction() {
        try {
            connectionUtils.createConn().close();
            connectionUtils.removeConn();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

}

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--    注入dataSource-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mavendb?serverTimezone=Asia/Shanghai"></property>
        <property name="user" value="root"></property>
        <property name="password" value="12345678"></property>
    </bean>

    <!--    queryRunner的注入-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!--    connectionUtils工具类的注入-->
    <bean id="connectionUtils" class="com.stringzhua.utils.ConnectionUtils">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--    transactionUtils工具类的注入-->
    <bean id="transactionUtils" class="com.stringzhua.utils.TransactionUtils">
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>


    <!--    dao层的注入-->
    <bean id="daoImpl" class="com.stringzhua.dao.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"></property>
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

    <!--    service层注入(被代理) -->
    <bean id="serviceImpl" class="com.stringzhua.service.AccountServiceImpl">
        <property name="dao" ref="daoImpl"></property>
    </bean>

    <!--    service注入(代理)-->
    <bean id="proxyServiceImpl" class="com.stringzhua.service.AccountServiceImpl" factory-bean="factory"
          factory-method="proxyService"></bean>
    <bean id="factory" class="com.stringzhua.factory.ServiceFactory">
        <property name="transactionUtils" ref="transactionUtils"></property>
        <property name="proxyservice" ref="serviceImpl"></property>
    </bean>

    <!--    controller层的注入(消费者)-->
    <bean id="controllerImpl" class="com.stringzhua.controller.AccountControllerImpl">
        <property name="service" ref="proxyServiceImpl"></property>
    </bean>
</beans>

进行单元测试:

package com.stringzhua.test;

import com.stringzhua.controller.IAccountController;
import com.stringzhua.pojo.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * @Author Stringzhua
 * @Date 2024/9/11 17:07
 * description:
 */
//让测试运行于Spring测试环境,以便在测试开始的时候自动创建Spring的应用上下文
@RunWith(SpringJUnit4ClassRunner.class)
//获取applicationContext容器
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test01 {
    @Autowired
    IAccountController controller;

    @Test
    public void test01() {
        controller.add(new Account("田晞冉", 8000));
        controller.add(new Account("爪爪", 6000));
    }

    @Test
    public void test02() {
        controller.update(new Account(1, "田晞冉", 6000));
    }

    @Test
    public void test03() {
        List<Account> all = controller.findAll();
        for (int i = 0; i < all.size(); i++) {
            Account account = all.get(i);
            System.out.println(account);
        }
    }

    @Test
    public void test04() {
        Account account = controller.findByName("田晞冉");
        System.out.println(account);
    }

    @Test
    public void test05(){
        controller.transformMoney("田晞冉","爪爪",1000);
    }
}

常规实现:

Service新增接口

public void transformMoney(String fromName, String targetName, int money);

实现类重写该方法

public void transformMoney(String fromName, String targetName, int money) {

        Account fromAccount = dao.findByName(fromName);
        Account targetAccount = dao.findByName(targetName);

        fromAccount.setAmoney(fromAccount.getAmoney() - money);
        targetAccount.setAmoney(targetAccount.getAmoney() + money);

        dao.update(fromAccount);
        //假设出现异常
        int a = 10 / 0;
        dao.update(targetAccount);
    }

controller同理:

同时需要在service 实现类装配事务工具类

//装配事务工具类
TransactionUtil transactionUtil;

public void setTransactionUtil(TransactionUtil transactionUtil) {
    this.transactionUtil = transactionUtil;
}

dao层装配连接工具类

 //装配连接工具类
    ConnectionUtil connectionUtil;
 
    public void setConnectionUtil(ConnectionUtil connectionUtil) {
        this.connectionUtil = connectionUtil;
    }

在xml文件中注入

<!-- 注入连接工具类 -->
<bean id="connectionUtil" class="com.zkt.util.ConnectionUtil">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- 注入事务工具类 -->
<bean id="transactionUtil" class="com.zkt.util.TransactionUtil">
    <property name="connectionUtil" ref="connectionUtil"/>
</bean>
<!-- 注入dao -->
<bean id="dao" class="com.zkt.dao.AccountDaoImp">
    <property name="queryRunner" ref="queryRunner"/>
    <property name="connectionUtil" ref="connectionUtil"/>
</bean>
<!-- 注入service -->
<bean id="service" class="com.zkt.service.AccountServiceImp">
    <property name="dao" ref="dao"/>
    <property name="transactionUtil" ref="transactionUtil"/>
</bean>

代理实现

在factory包下创建代理:

package com.stringzhua.factory;

import com.stringzhua.controller.IAccountController;
import com.stringzhua.service.IAccountService;
import com.stringzhua.utils.TransactionUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author Stringzhua
 * @Date 2024/9/12 18:39
 * description:
 */
public class ServiceFactory {
    //被代理的是service
    IAccountService proxyservice;

    public void setProxyservice(IAccountService proxyservice) {
        this.proxyservice = proxyservice;
    }

    //代理的是transaction事务
    TransactionUtils transactionUtils;

    public void setTransactionUtils(TransactionUtils transactionUtils) {
        this.transactionUtils = transactionUtils;
    }

    //创建代理
    public IAccountService proxyService() {
        return (IAccountService) Proxy.newProxyInstance(
            proxyservice.getClass().getClassLoader(), proxyservice.getClass().getInterfaces(), new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object object = null;
                    try {
                        //开启事务
                        transactionUtils.beginTransaction();
                        object = method.invoke(proxyservice, args);
                        //自动提交
                        transactionUtils.commitTransaction();
                    } catch (Exception e) {
                        e.printStackTrace();
                        transactionUtils.rollbackTransaction();
                    } finally {
                        transactionUtils.closeTransaction();
                    }
                    return object;

                }
            });
        }
}

事务工具类不变

连接工具类

public class ConnectionUtil {
    //线程区域对象
    ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>();
 
    //数据源
    DataSource dataSource;
 
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
 
    //获取连接
    public Connection createConn() {
        try {
            //在口袋里面找连接
            Connection connection = connectionThreadLocal.get();
            //判断
            if (connection==null){
                connection=dataSource.getConnection();
                connectionThreadLocal.set(connection);
            }
            return  connection;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return  null;
    }
    //关闭连接
    public void closeConn(){
        connectionThreadLocal.remove();//解绑
    }
}

故service的transfer 正常写就行

 @Override
    public void transfer(String sourceName, String targetName, int money) {
 
 
        Account sourceAccount = dao.findByName(sourceName);
        Account targetAccount = dao.findByName(targetName);
 
        sourceAccount.setAmoney(sourceAccount.getAmoney() - money);
        targetAccount.setAmoney(targetAccount.getAmoney() + money);
 
        dao.updateById(sourceAccount);
//        int a = 10 / 0;//模拟异常
        dao.updateById(targetAccount);
 
    }

在xml文件中注入:

<!-- 注入service(被代理对象) -->
<bean id="service" class="com.zkt.service.AccountServiceImp">
    <property name="dao" ref="dao"/>
</bean>
<!-- 注入service(代理对象) -->
<bean id="proxyService" class="com.zkt.service.AccountServiceImp" factory-bean="factory" factory-method="createProxy"/>
<bean id="factory" class="com.zkt.util.ProxyFactory">
    <property name="transactionUtil" ref="transactionUtil"/>
    <property name="toServiceProxy" ref="service"/>
</bean>
<!-- 注入controller(消费者) -->
<bean id="controller" class="com.zkt.controller.AccountControllerImp">
    <property name="service" ref="proxyService"/>
</bean>

AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

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

AOP 的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

AOP 的应用

  • 1.日志
  • 2.事务
  • 3.权限

AOP术语:

AOP通知类型

AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分

  • 前置通知(Before)
  • 后置通知(After)
  • 返回通知(After-returning)
  • 异常通知(After-throwing)
  • 环绕通知(Around)

AOP连接点(Join point)

  • AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点

AOP切点(Pointcut)

  • AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集

AOP目标对象(Target):

  • 就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的

AOP织入(Weaving):

  • 就是将挖掉的功能回填的动态过程

AOP切面:

  • 切点+通知


实现步骤:

  • 1.添加依赖,aop与aspectj表达式的依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.25</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.25</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>
  • 2.创建spring的主配置文件,bean内的命名空间要添加aop的
<!--    前置通知,开启aop-->
<aop:config>
    <!--        配置切面-->
    <aop:aspect id="aopAspect" ref="logger">
        <!--            切点-->
        <aop:pointcut id="point" expression="execution( * com.stringzhua.service.*.*(..))"/>
        <!--            通知-->
        <aop:before method="printLogger" pointcut-ref="point"></aop:before>
    </aop:aspect>
</aop:config>
  • 3.创建业务代码并编写日志记录代码(事务管理代码)
  • 4.将业务层与日志记录层注入spring容器
  • 5.aop:config–aop配置
  • aop:aspect–aop切面
  • aop:before–通知内容与通知类型

切点表达式语法:

execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意
    execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
    execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
    execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数
    execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意
    execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
    execution(* *...*.*(..))
    如果有参数
        int======>int
        String===>java.lang.String

xml实现日志记录

pom.xml

 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.25</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.25</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

Service层:

IAccountService.java

package com.stringzhua.service;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 10:14
 * description:
 */
public interface IAccountService {
    //新增
    public void add(int i);

    //修改
    public void update();

    //删除
    public int delete();
}

AccountService.java

package com.stringzhua.service;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 10:16
 * description:
 */
public class AccountServiceImpl implements IAccountService {
    public void add(int i) {
        System.out.println("service的add方法" + i);
//        int a = 10 / 0;//模拟异常
    }

    public void update() {
        System.out.println("service的update方法");
    }

    public int delete() {
        System.out.println("service的delete方法");
        return 0;
    }
}

Logger层:

Logger.java

package com.stringzhua.utils;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 10:17
 * description:
 */
public class Logger {
    //前置通知
    public void beforeMethod() {
        System.out.println("前置通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //返回通知
    public void returnMethod() {
        System.out.println("返回通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //异常通知
    public void throwMethod() {
        System.out.println("异常通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //后置通知
    public void afterMethod() {
        System.out.println("后置通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //环绕通知
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object object = null;//保存主业务方法的返回值
        try {
            //1.前置通知
            System.out.println("环绕通知===>前置通知");

            Object[] objects = pjp.getArgs();//主业务方法的参数
            object = pjp.proceed(objects);//调用主业务方法

            //2.返回通知
            System.out.println("环绕通知===>返回通知");
        } catch (Throwable e) {
            e.printStackTrace();
            //3.异常通知
            System.out.println("环绕通知===>异常通知");
        } finally {
            //4.后置通知
            System.out.println("环绕通知===>后置通知");
        }
        return object;
    }
}

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: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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="service" class="com.stringzhua.service.AccountServiceImpl"></bean>

    <bean id="logger" class="com.stringzhua.utils.Logger"></bean>

    <!--    前置通知,开启aop-->
    <aop:config>
        <!--        配置切面-->
        <aop:aspect id="aopAspect" ref="logger">
            <!--            切点-->
            <aop:pointcut id="point" expression="execution( * com.stringzhua.service.*.*(..))"/>
            <!--            通知-->
            <aop:before method="beforeMethod" pointcut-ref="point"></aop:before>
            <aop:after-returning method="returnMethod" pointcut-ref="point"></aop:after-returning>
            <aop:after-throwing method="throwMethod" pointcut-ref="point"></aop:after-throwing>
            <aop:after method="afterMethod" pointcut-ref="point"></aop:after>

            <aop:around method="aroundMethod" pointcut-ref="point"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

单元测试:

package com.stringzhua.test;

import com.stringzhua.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 10:23
 * description:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test01 {
    @Autowired
    IAccountService service;

    @Test
    public void test1() {
        service.add(1);
//        System.out.println("=======");
//        service.delete();
//        System.out.println("=======");
//        service.update();
    }

}

正常情况:

异常情况:

注解实现日志记录

项目结构:

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.25</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.25</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.7</version>
    </dependency>
</dependencies>

Service层:

IAccountService.java

package com.stringzhua.service;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 11:36
 * description:
 */
public interface IAccountService {
    //新增
    public void add(int i);

    //修改
    public void update();

    //删除
    public int delete();
}

AccountServiceImpl.java

package com.stringzhua.service;

import org.springframework.stereotype.Service;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 11:36
 * description:
 */
@Service
public class AccountServiceImpl implements IAccountService{
    public void add(int i) {
        System.out.println("service的add方法" + i);
//        int a = 10 / 0;//模拟异常
    }

    public void update() {
        System.out.println("service的update方法");
    }

    public int delete() {
        System.out.println("service的delete方法");
        return 0;
    }
}

Utils包下的Logger.java

package com.stringzhua.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 11:38
 * description:
 */
@Component
@Aspect//切面
public class Logger {
    @Pointcut("execution( * com.stringzhua.service.*.*(..)))")
    public void point(){}

    //前置通知
//    @Before("point()")
    public void beforeMethod() {
        System.out.println("前置通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //返回通知
//    @AfterReturning("point()")
    public void returnMethod() {
        System.out.println("返回通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //异常通知
//    @AfterThrowing("point()")
    public void throwMethod() {
        System.out.println("异常通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //后置通知
//    @After("point()")
    public void afterMethod() {
        System.out.println("后置通知===>日志类logger中调用printLogger方法进行日志记录");
    }

    //环绕通知
    @Around("point()")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object object = null;//保存主业务方法的返回值
        try {
            //1.前置通知
            System.out.println("环绕通知===>前置通知");

            Object[] objects = pjp.getArgs();//主业务方法的参数,返回被通知方法参数列表
            object = pjp.proceed(objects);//调用主业务方法

            //2.返回通知
            System.out.println("环绕通知===>返回通知");
        } catch (Throwable e) {
            e.printStackTrace();
            //3.异常通知
            System.out.println("环绕通知===>异常通知");
        } finally {
            //4.后置通知
            System.out.println("环绕通知===>后置通知");
        }
        return object;
    }
}

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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--    扫描注解-->
    <context:component-scan base-package="com.stringzhua"></context:component-scan>
    <!--    开启spring注解的aop动态代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

单元测试:

Test.java

package com.stringzhua.test;

import com.stringzhua.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Author Stringzhua
 * @Date 2024/9/13 11:39
 * description:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test01 {
    @Autowired
    IAccountService service;

    @Test
    public void test01() {
        service.add(1);
    }
}

正常情况:

异常情况:

面试题:

⭐spring 框架如何解决循环依赖?

循环依赖问题是指∶ 类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。

Spring使用三级缓存来解决循环依赖

Spring 框架中有哪些注解?

用于声明 bean 的注解:

  • @Component:定义通用bean的注解,可标注任意类为 bean。如果一个 bean 不知道属于 哪个层,可以使用 @component 注解标注
  • @Repository:定义数据访问层bean的注解
  • @Service:定义业务层bean的注解
  • @Controller:定义控制器bean的注解

用于注入的注解:

  • @Autowired:按类型自动注入
  • @Qualifier:按名称自动注入

声明配置、扫描、启用特性的注解:

  • @Configuration:声明配置类
  • @ComponentScan:组件扫描
  • @EnablesScheduling:启用任务调度
  • @EnableAspectJAutoProxy:启用自动代理工厂

⭐Spring 框架中用到的设计模式

工厂模式:spring 使用工厂模式,通过 Beanfactory 或 ApplicationContext 来创建对象

单例模式::bean 默认作用域为单例,按照单例设计模式进行设计实现

策略模式:resource 的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

代理模式:spring 的 aop 的实现依靠动态代理(jdk的反射和cglib),通过为对象提供代理,来控制对象的访问,作用:中介隔离,方法增强,分为动态代理和静态代理

模板方法:Spring 提供了 JdbcTemplate,RedisTemplate等模板对象,将相同的操作步骤进行封装

适配器模式:Spring AOP的增强或通知( Advice )使用到了适配器模式,Spring MVC 中也用到了适配器模式适配Controller

Spring 框架中AOP的基本理解

关键字: AOP名词解释,AOP实现原理(动态代理)

  • AOP(aspect-oriented programming :面向切面编程):将那些与业务无关,却为业务模块所同时调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为“切面”(aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性
  • Spring AOP 基于动态代理实现∶
    • 如果被代理的对象,已经实现某个接口,则spring aop 会使用jdk proxy(反射),基于接口的方式,创建代理对象(jdk 动态代理的核心是 invocationhandler 接口和 proxy类)
    • 如果被代理的对象,没有实现某个接口,这时候 spring aop 会使用 cglib,基于继承的方式,生成一个被代理对象的子类来作为代理(cglib 动态代理的核心是 methodinterceptor 接口和 enhancer 类)

⭐Spring AOP和 AspectJ AOP有什么区别?

关键字:增强方式的差异(运行时、编译时),实现方式的差异(动态代理、字节码操作)

  • Spring AOP 已经集成了 AspectJ,AspectJ是一个 Java 技术生态系统中实现 aop编程思想的独立框架; AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单更容易;
  • Spring AOP属于运行时增强,而AspectJ是编译时增强
  • Spring AOP 基于动态代理( Proxying ),而 AspectJ基于字节码操作( Bytecode Manipulation )
  • 如果项目中的切面代码比较少,两者性能差异不大。如果切面代码太多的话,最好选择 AspectJ,因为基于字节码操作的缘故,AspectJ比 Spring AOP 从性能上要快很多;

Spring AOP有哪些通知类型?

关键字: 分别介绍每种通知的实现接口,执行方式

  • 前置通知∶ 实现 MethodBeforeAdvice 接口,在目标方法调用前,执行通知;
  • 环绕通知∶ 实现 MethodInterceptor 接口,是一个包围目标方法的通知。环绕通知可以在方法用前后完成自定义的行为;
  • 后置通知∶ 实现 AfterReturningAdvice 接口,在目标方法调用后,执行通知(如果方法抛常,则不执行通知);
  • 最终通知:实现了AfterReturingAdvice接口,在目标方法调用后,执行通知;
  • 异常通知:实现 ThrowsAdvice 接口,在方法抛出异常时,执行通知;

AOP通知类型**
**** AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分**

  • 前置通知(Before)
  • 后置通知(After)
  • 返回通知(After-returning)
  • 异常通知(After-throwing)
  • 环绕通知(Around)

⭐Spring 管理事务的方式有几种?

  • 编程式事务:在代码中硬编码(不推荐使用):通过 TransactionTemplate 或者 TransactionManager 手动管理事务,实际应用中很少使用,用于理解 spring 事务管理
  • 声明式事务:在 xml 配置文件或者基于注解 @Transactional (推荐使用),通过 AOP 实现。

⭐Spring事务中有哪几种事务传播行为?

  • 事务传播行为是为了解决业务层方法之间互相调用时,产生事务问题
  • 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
  • 比如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
  • 事务传播行为有如下分类
  1. TransactionDefinition.propagation_Required

如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

@transaction注解默认使用的事务传播

  1. TransactionDefinition.propagation_Requires_NEW:

创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,propagation.requires_new 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰

  1. TransactionDefinition.propagation_Nested:

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,等价于 transactiondefinition.propagatton_required。

  1. Transactiondefinition.propagation_Mandatory:

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory: 代强制性)

  1. TransactionDefinition.propagation_Supports:

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  1. TransactionDefinition.propagation_Not_Supported:

以非事务方式运行,如果当前存在事务,则把当前事务挂起

  1. Transactiondefinition.propagation_Never:

以非事务方式运行,如果当前存在事务,则抛出异常

⭐Spring 事务中有哪几种事务隔离级别?

  1. TransactionDefition.isolation_default 默认隔离级别

使用当前数据库的默认隔离级别,MySQL 默认采用的是可重复读repeatable_read 隔离级别。oracle 默认采用的是读已提交 read_coommitted 隔离级别

  1. TransactionDefinition.isolation_read_uncommited 读未提交

最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

  1. TransactionDefinition.isolation_read_commited 读已提交

允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。

  1. TransactionDefinition.isolation_repeatable_read 可重复读

对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

  1. transactiondefinition.isolation_serialzable 串行化

最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

⭐Spring 事务在什么情况下会失效?

  • 1.数据库不支持事务

Spring 事务生效的前提是所连接的数据库要支持事务,如果底层的数据库都不支持事务,则spring的事务肯定会失效。

例如:如果使用的数据库为mysql,并且选用了myisan 存储引擎,则 spring 的事务就会失效

  • 2.事务方法未被Spring管理

如果事务方法所在的类没有加载到 Spring IOC 容器中,也就是说,事务方法所在的类没有被Spring管理,则Spring事务会失效。

例如:ProductService类上没有标注@Service注解,Product的实例没有加载到Spring IOC容器中,就会造成updateProductStockCountById()方法的事务在Spring中失效。

public class ProductServie{
  @AutoWired
  private ProductDao productdao;

  @Transactional(propagation = Propagetion.REQUIRES_NEW)
  public void updateProductStockCountById(Integer stockCount,Long id){
    productDao.updateProductStockCountById(stockCount,id);
  }
}
  • 3.方法没有被public修饰

如果事务所在的方法没有被 public 修饰,此时 spring 的事务会失效。

例如:虽然在ProductService上标注了@Service注解,同时updateProductStockCountById()方法上标注了@Transactional(propagation = Propagation.REQUIRES_NEW)注解。

但是,由于updateProductStockCountById()方法为内部的私有方法(使用private修饰),那么此时的updateProductStockCountById()方法的事务会在Spring中失效。

@Service
public class ProductService {
    @Autowired
    private ProductDao productDao;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private void updateProductStockCountById(Integer stockCount, Long id) {
        productDao.updateProductStockCountById(stockCount, id);
    }
}
  • 4.同一类中方法相互调用

如果同一个类中的两个方法分别为a和b,方法a上没有添加事务注解,方法b上添加了 @transactional 事务注解,方法a调用方法b,则方法b的事务会失效。

  • 5.未配置开启事务管理器

如果项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效。

  • 6.方法的事务传播类型不支持事务

如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务在 Spring中会失效。

例如:b方法支持事务,b调用了a方法,由于 a方法的事务传播类型为 not supported,不支持事务,则 a方法的事务会在 spring 中失效。

  • 7.不正确的捕获异常

不正确的捕获异常也会导致 spring 的事务失效。

例如: a方法中使用try-catch 代码块捕获了异常,即使 a方法内部会抛出异常,但也会被 catch 代码块捕获到,此时 a方法的事务会提交而不会回滚,并且 submitorder()方法的事务会提交而不会回滚,这就造成了 spring 事务的回滚失效问题。

  • 8.错误的标注异常类型

如果在 @transactional注解中标注了错误的异常类型,则 Spring 事务的回滚会失效。

图片版

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/908447.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开源模型应用落地-Qwen2.5-7B-Instruct与TGI实现推理加速

一、前言 目前&#xff0c;大语言模型已升级至Qwen2.5版本。无论是语言模型还是多模态模型&#xff0c;均在大规模多语言和多模态数据上进行预训练&#xff0c;并通过高质量数据进行后期微调以贴近人类偏好。在本篇学习中&#xff0c;将集成 Hugging Face的TGI框架实现模型推理…

Android 使用ninja加速编译的方法

ninja的简介 随着Android版本的更迭&#xff0c;makefile体系逐渐增多&#xff0c;导致make单编模块的时间越来越长&#xff0c;每次都需要半个小时甚至更长时间&#xff0c;其原因为每次make都会重新加载所有mk文件&#xff0c;再生成ninja编译&#xff0c;此完整过程十分耗时…

javaNIO核心知识.中

Channel&#xff08;通道&#xff09; Channel 是一个通道&#xff0c;它建立了与数据源&#xff08;如文件、网络套接字等&#xff09;之间的连接。我们可以利用它来读取和写入数据&#xff0c;就像打开了一条自来水管&#xff0c;让数据在 Channel 中自由流动。 BIO 中的流…

缓存、注解、分页

一.缓存 作用&#xff1a;应用查询上&#xff0c;内存中的块区域。 缓存查询结果&#xff0c;减少与数据库的交互&#xff0c;从而提高运行效率。 1.SqlSession 缓存 1. 又称为一级缓存&#xff0c;mybatis自动开启。 2. 作用范围&#xff1a;同一…

流畅!HTMLCSS打造网格方块加载动画

效果演示 这个动画的效果是五个方块在网格中上下移动&#xff0c;模拟了一个连续的加载过程。每个方块的动画都是独立的&#xff0c;但是它们的时间间隔和路径被设计为相互协调&#xff0c;以创建出流畅的动画效果。 HTML <div class"loadingspinner"><…

【skywalking 】More than 15,000 ‘grammar‘ tokens have been presented. 【未解决请求答案】

问题 skywalking相关版本信息 jdk&#xff1a;17skywalking&#xff1a;10.1.0apache-skywalking-java-agent&#xff1a;9.3.0ElasticSearch : 8.8.2 问题描述 More than 15,000 grammar tokens have been presented. To prevent Denial Of Service attacks, parsing has b…

docker desktop使用ubuntu18.04带图形化+运行qemu

记录一下docker desktop使用ubuntu18.04带图形化命令和使用步骤 1. 下载镜像 参考&#xff1a;【Docker教程】Docker部署Ubuntu18.04(带图形化界面) 命令&#xff1a; docker pull kasmweb/ubuntu-bionic-desktop:1.10.02. 启动镜像 命令&#xff1a; docker run -d -it …

jmeter压测工具环境搭建(Linux、Mac)

目录 java环境安装 1、anaconda安装java环境&#xff08;推荐&#xff09; 2、直接在本地环境安装java环境 yum方式安装jdk 二进制方式安装jdk jmeter环境安装 1、jmeter单机安装 启动jmeter 配置环境变量 jmeter配置中文 2、jmeter集群搭建 多台机器部署jmeter集群…

ai翻唱部分步骤

模型部署 我是用的RVC进行的训练&#xff0c;也可以使用so-vits-svc。 通过百度网盘分享的文件&#xff1a;RVC-beta 链接&#xff1a;https://pan.baidu.com/s/1c99jR2fLChoqUFqf9gLUzg 提取码&#xff1a;4090 以Nvida显卡为例&#xff0c;分别下载“RVC1006Nvidia”和…

算法笔记-Day09(字符篇)

151. 反转字符串中的单词 class Solution {public String reverseWords(String s) {int lens.length(),count0;StringBuffer tempnew StringBuffer();StringBuffer ansnew StringBuffer();for(int i0;i<len;i){if(s.charAt(i)! &&(i0 || s.charAt(i-1) )){while(i&l…

安科瑞电能质量治理产品在光伏电站的应用有效解决了光伏电站面临的功率因数过低和谐波问题-安科瑞黄安南

1. 概述 随着全球对可再生能源需求的增加&#xff0c;分布式光伏电站的建设和发展迅速。然而&#xff0c;分布式光伏电站的运行过程中面临着一系列问题&#xff0c;比如导致企业关口计量点功率因数过低、谐波污染等。这些问题不仅影响光伏电站自身的运行效率&#xff0c;还会对…

Leetcode137只出现一次的数字|| 及其拓展

简述&#xff1a; 虽然标题是这么描述的&#xff0c;但是我们不是一上来就解这道题&#xff0c;先看一下他的子题和扩展 子题&#xff1a;136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 扩展题&#xff1a; 所以我们由易到难&#xff0c;先来看第一道&#x…

leetcode 382.链表随机结点

1.题目要求: 2.题目代码: /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x)…

GaussDB Ustore存储引擎解读

目录 一、数据库存储引擎 二、GaussDB Ustore存储引擎 总结 本文将介绍GaussDB中的Ustore存储引擎&#xff0c;包括Ustore的设计背景、特点介绍和适用业务场景等。 一、数据库存储引擎 数据库的存储引擎负责在内存和磁盘上存储、检索和管理数据&#xff0c;确保每个节点的…

使用 Sortable.js 库 实现 Vue3 elementPlus 的 el-table 拖拽排序

文章目录 实现效果Sortable.js介绍下载依赖添加类名导入sortablejs初始化拖拽实例拖拽完成后的处理总结 在开发过程中&#xff0c;我们经常需要处理表格数据&#xff0c;并为用户提供便捷的排序方式。特别是在需要管理长列表、分类数据或动态内容时&#xff0c;拖拽排序功能显得…

机器学习是什么?AIGC又是什么?机器学习与AIGC未来科技的双引擎

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

.net core 接口,动态接收各类型请求的参数

[HttpPost] public async Task<IActionResult> testpost([FromForm] object info) { //Postman工具测试结果&#xff1a; //FromBody,Postman的body只有rawjson时才进的来 //参数为空时&#xff0c;Body(form-data、x-www-form-urlencoded)解析到的数据也有所…

探索Unity:从游戏引擎到元宇宙体验,聚焦内容创作

unity是实时3D互动内容创作和运营平台&#xff0c;包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者&#xff0c;借助Unity将创意变成现实。提供一整套完善的软件解决方案&#xff0c;可用于创作、运营和变现任何实时互动的2D和3D内容&#xff0c;支持平台包括手机、…

构造有向(无向)加权图

邻接表的一般构造 #include<bits/stdc.h> #define N 1e4 using namespace std; typedef struct BP{ int P;//边所指的顶点位置 struct BP *nextB;//指向下一条边的指针 int Q;//储存边的信息 }BP; typedef struct DP{ int date;//顶点信息 BP *FirstB;//指向第一条连接…

RabbitMQ交换机类型

RabbitMQ交换机类型 1、RabbitMQ工作模型2、RabbitMQ交换机类型2.1、Fanout Exchange&#xff08;扇形&#xff09;2.1.1、介绍2.1.2、示例2.1.2.1、生产者2.1.2.2、消费者2.1.2.3、测试 2.2、Direct Exchange&#xff08;直连&#xff09;2.2.1、介绍2.2.2、示例2.2.2.1、生产…