首页 > 其他分享 >Spring-day03

Spring-day03

时间:2023-03-11 16:33:06浏览次数:35  
标签:事务 day03 Spring 切入点 void AOP 方法 public

Spring-day03

01_Spring的AOP概述

目标

  • 了解AOP存在的意义及方式

路径

  1. AOP介绍
  2. AOP作用
  3. AOP优势
  4. Spring中AOP的实现方式

在前面我们提到:Spring技术的两大核心就是Ioc(控制反转)和AOP

AOP介绍

AOP(Aspect Oriented Programing) 面向切面编程,一种编程范式,指导开发者如何组织程序结构

  • OOP(Object Oriented Programing) 面向对象编程

AOP作用

在程序运行期间,不修改源码的基础上对已有方法进行增强(无侵入性: 解耦)

AOP优势

  1. 减少重复代码
  2. 提高开发效率
  3. 维护方便

Spring中AOP的实现方式

spring的AOP有两种实现方式:

  1. 基于JDK官方的动态代理(优先使用)

    • 当bean实现接口时,spring就会用JDK的动态代理
  2. 基于第三方的cglib的动态代理

    • 当bean没有实现接口时,spring使用CGLib来实现(JDK8后, JDK动态代理效率高于CGlib)
    备注: 开发者可以在spring中强制使用CGLib (了解)
    在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
    

02_Spring前置知识点

目标

  • 清楚动态代理的代码书写方式 (仅做了解)

路径

  1. 动态代理:Proxy
  2. 动态代理:CGLIb

需求:统计service层AccountServiceImpl类(有父接口)中所有方法的运行时间。

  • 要求:不修改源码的基础上, 对每个方法进行增强

动态代理:Proxy

JDK的Proxy动态代理是针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强。

代码示例:

  • 业务层
@Service("accountService")
public class AccountServiceImpl implements IAccountService { //类有实现接口
    @Override
    public void saveAccount(Account account) {
        System.out.println("AccountServiceImpl => saveAccount方法");
    }

    @Override
    public void updateAccount(Account account) {
        System.out.println("AccountServiceImpl => updateAccount方法");
    }

    @Override
    public void deleteAccount(Integer id) {
        System.out.println("AccountServiceImpl => deleteAccount方法");
    }

    @Override
    public Account queryAccountById(Integer id) {
        System.out.println("AccountServiceImpl => queryAccountById方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Account> queryAllAccount() {
        System.out.println("AccountServiceImpl => queryAllAccount方法");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }
}
  • 测试类
public class JdkProxyTest {
    @Test
    public void testProxy() {
        //创建对象
        AccountServiceImpl accountService = new AccountServiceImpl();
        
        //类加载器
        ClassLoader classLoader = JdkProxyTest.class.getClassLoader();
        //父接口
        Class[] interfaces = AccountServiceImpl.class.getInterfaces();
        //处理器
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;//方法执行完后的返回值

                long beginTime = System.currentTimeMillis();//开时时间

                result = method.invoke(accountService, args);//执行方法

                long endTime = System.currentTimeMillis();//结束时间

                System.out.println("执行时间:" + (endTime - beginTime) + "毫秒");

                return result;
            }
        };

        //使用jdk的Proxy获取代理对象
        IAccountService accountServiceProxy = (IAccountService) Proxy.newProxyInstance(classLoader, interfaces, handler);

        //使用代理对象调用方法
        List<Account> accountList = accountServiceProxy.queryAllAccount();
    }
}
//输出结果:
AccountServiceImpl => queryAllAccount方法
执行时间:3007毫秒

动态代理:CGLIB

CGLIB(Code Generation Library),Code生成类库 (第三方的库,不是JDK)

  • CGLIB动态代理不限定被代理类是否具有接口,可以对任意操作进行增强
  • 底层实现原理:继承
    • CGLIB动态代理继承于被代理类,动态创建出新的代理对象

代码示例:

使用cglib需要导入cglib的jar包
在Spring中已经整合了cglib,所以导入Spring-context包即可
  • 业务类
@Service("accountService2")
public class AccountServiceClass {  //类没有实现接口
    public Account queryAccountById(Integer id) {
        System.out.println("AccountServiceClass  =>  queryAccountById方法");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<Account> queryAllAccount() {
        System.out.println("AccountServiceClass  =>  queryAllAccount方法");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }
}
  • 测试类
import com.itheima.pojo.Account;
import com.itheima.service.AccountServiceClass;
import org.junit.Test;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.List;

public class CglibTest {
    @Test
    public void testCglib() {
        //1. 创建被代理类的对象
        AccountServiceClass accountService = new AccountServiceClass();

        //2. 创建代理类对象
        Callback callback = new MethodInterceptor() {
            /*
                intercept方法: 代理类对象执行任意方法,都会调用此方法
                1. proxy :表示代理类对象本身 (没什么用)
                2. method : 表示代理类对象当前调用的方法
                3. args : 表示代理类对象当前调用方法传入的参数
                4. methodProxy : 方法代理 (没什么用)
                返回值Object : 表示代理类对象当前调用方法产生的返回值
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object result = null;//方法执行完后的返回值
                long beginTime = System.currentTimeMillis();//开时时间

                result = method.invoke(accountService, args);//执行方法

                long endTime = System.currentTimeMillis();//结束时间
                System.out.println("执行时间:" + (endTime - beginTime) + "毫秒");

                return result;
            }
        };
        Class type = accountService.getClass();
        AccountServiceClass proxy = (AccountServiceClass) Enhancer.create(type,callback);

        //3. 使用代理类对象调用方法
        List<Account> accountList = proxy.queryAllAccount();
        Account account = proxy.queryAccountById(1);
    }
}
//输出结果:
AccountServiceClass  =>  queryAllAccount方法
执行时间:3008毫秒
AccountServiceClass  =>  queryAccountById方法
执行时间:2010毫秒

03_Spring的AOP基础

目标

  • 了解Spring中AOP开发基础概念

路径

  1. AOP编程用到的概念
  2. AOP的开发过程
  3. AOP的开发方式

AOP编程用到的概念

面向切面编程中的相关概念:

image-20220429105931541

  1. Target(目标对象)
    • 要被增强的对象(被代理类的对象)
  2. Proxy(代理对象)
    • 对目标对象的增强对象(生成的代理类对象)
  3. Joinpoint(连接点)
    • 目标对象中的可被增强的方法(被代理类中的方法)
      • 不可被增强的方法:
        • Proxy中被代理类不可被增强方法(父接口中没有的方法)
        • CGlib中被代理类不可被增强方法(final修饰的方法)
  4. Pointcut(切入点)
    • 要被增强的方法(被代理类中要增强的方法)
      • 切入点一定是连接点;但连接点不一定是切入点
        • 切入点是要增强的方法,而该方法必须是连接点
        • 连接点,不一定要被增强
  5. Advice(通知)
    • 通知是增强的那段代码形成的方法
    • 通知的分类:
      1. 前置通知 在方法之前进行增强
      2. 后置通知 在方法之后进行增强
      3. 异常通知 在方法异常进行增强
      4. 最终通知 最终执行的方法进行增强
      5. 环绕通知 单独使用(以上所有通知)
  6. Aspect(切面)
    • 切面 = 切入点+通知
      • 目标方法和增强代码合到一起叫做切面
  7. Weaving(织入)
    • 在运行过程中spring底层将通知和切入点进行整合的过程称为织入

AOP的开发过程

开发阶段(开发者完成)

  • 正常的制作程序
  • 将非共性功能开发到对应的目标对象类中,并制作成切入点方法
  • 将共性功能独立开发出来,制作成通知
  • 在配置类中,声明切入点
  • 在配置类中,声明切入点通知间的关系(含通知类型),即切面

运行阶段(AOP完成)

  • Spring容器加载配置文件时, 使用代理机制,动态创建目标对象代理对象,根据通知类别,在代理对象的对应位置将通知对应的功能织入,形成完整的代码逻辑
  • 切入点方法被运行,将会调用代理对象的方法,达到增强目标对象的效果

AOP的开发方式

AOP有两种开发方式:

  1. XML方式
  2. 注解方式(课程中学习这个)

04_Spring的AOP入门

目标

  • 能够使用Spring中的AOP配置增强类中方法

路径

  1. Spring中AOP开发步骤
  2. Spring的AOP注解
  3. 使用Spring的AOP方式统计业务类中方法执行时间

Spring中AOP开发步骤

在spring中使用aop开发的步骤:

  1. 导入相关坐标(Spring、切入点表达式)

  2. 开启aop注解支持

  3. 编写切面类

    • 配置切入点

    • 配置通知类型

  4. 运行程序(测试)

Spring的AOP注解

开启aop注解支持:

@EnableAspectJAutoProxy
public class SpringConfig{
}

切面类:

@Aspect  //配置当前类为切面类(切入点+通知)
public class ClassName{
    //配置切入点 : @Pointcut("切入点表达式")
    //定义切入点表达式:com.itheima包及子包下的AccountServiceImpl类中所有方法 
    /*
       举例1:针对AccountServiceImpl类中的queryAllAccount()方法进行增强
       切入点表达式: com.itheima.service.impl.AccountServiceImpl.queryAllAccount()
       
       举例2:针对AccountServiceImpl类中的queryAccountById(Integer id)方法进行增强
       切入点表达式: com.itheima.service.impl.AccountServiceImpl.queryAccountById(Integer)
       
        举例3:针对AccountServiceImpl类中的所有方法进行增强
        切入点表达式:com.itheima.service.impl.AccountServiceImpl.*(..)
       
    */
    @Pointcut("execution(* com.itheima..AccountServiceImpl.*(..))")
    public void pt(){
        //切入点,必须配置在三无方法上(无参、无返回值、空方法体)
    }
    
    //配置通知类型
    /* @Before: 前置通知
       @AfterReturning:后置通知
       @AfterThrowing :异常通知
       @After :最终通知
       @Around:环绕通知
    */
    @Before("pt()") //指定切点入(原因:明确要对哪个方法进行前置增强)
    public void 方法名(){
        //增强功能的代码
    }
    @AfterReturning("pt()")
    public void 方法名(){
        //增强功能的代码
    }
}

使用Spring的AOP方式统计业务类中方法执行时间

代码示例:

  • 导入坐标
<!-- spring核心jar包,已存在依赖的AOP的jar -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

<!-- 切入点表达式 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
  • 配置类(开启aop注解支持)
@Configurable
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy  //开启aop注解支持
public class SpringConfig {
}
  • 业务层
//接口
public interface IAccountService {
    public void saveAccount(Account account);

    public void updateAccount(Account account);

    public void deleteAccount(Integer id);
    
    public Account queryAccountById(Integer id);

    public List<Account> queryAllAccount();
}


//实现类
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
    @Override
    public void saveAccount(Account account) {
        System.out.println("AccountServiceImpl => saveAccount方法");
    }

    @Override
    public void updateAccount(Account account) {
        System.out.println("AccountServiceImpl => updateAccount方法");
    }

    @Override
    public void deleteAccount(Integer id) {
        System.out.println("AccountServiceImpl => deleteAccount方法");
    }

    @Override
    public Account queryAccountById(Integer id) {
        System.out.println("AccountServiceImpl => queryAccountById方法");
        try {
            Thread.sleep(2000);//睡眠2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Account> queryAllAccount() {
        System.out.println("AccountServiceImpl => queryAllAccount方法");

        try {
            Thread.sleep(3000);//睡眠3秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }
}

  • 切面类
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component //当前bean需要加载到ioc容器中
@Aspect //切面类(切入点+通知)
public class AccountServiceAdvice {
    //配置切入点     切入点表达式=com.itheima.service.impl.AccountServiceImpl类中所有方法
    @Pointcut("execution(* com.itheima.service.impl.AccountServiceImpl.*(..))")
    public void pt() {
        //切入点配置在无参无返回值的空方法上
    }

    long beginTime; //起始时间
    long endTime;//终止时间

    //配置通知
    @Before("pt()") //前置通知,在目标方法执行前运行
    public void beginTime() {
        System.out.println("开始计时...");
        beginTime = System.currentTimeMillis();
    }

    @AfterReturning("pt()") //后置通知,在目标方法执行后运行
    public void endTime() {
        endTime = System.currentTimeMillis();
        System.out.println("执行时间:" + (endTime - beginTime) + "毫秒");
    }
}
  • 测试类
@RunWith(SpringJUnit4ClassRunner.class) //spring整合junit
@ContextConfiguration(classes = SpringConfig.class) //加载注解配置类
public class AspectTest {
    @Autowired
    IAccountService accountService;

    @Test
    public void testAspect(){
        List<Account> accountList = accountService.queryAllAccount();

        Account account = accountService.queryAccountById(1);
    }
}
//输出结果:
开始计时...
AccountServiceImpl => queryAllAccount方法
执行时间:3002毫秒
开始计时...
AccountServiceImpl => queryAccountById方法
执行时间:2001毫秒

05_Spring的AOP详解-切入点

目标

  • 能够书写SpringAOP的切入点表达式

路径

  1. 案例分析
  2. 切入点表达式语法
  3. 切入点表达式示例

案例分析

spring的aop入门案例:

  1. 导入依赖
  2. 编写service层(接口、实现类[目标对象])
  3. 编写切面类(切入点 、通知)

aop入门案例执行机制:

  1. 创建目标对象:AccoucntServiceImpl (使用IoC配置)

  2. 创建代理对象

    2.1、基于目标对象 => 代理类

    2.2、基于AccountServiceAdvice => 代理类方法体

    • 切面 = 切入点 + 通知

    2.3、Weaving(织入)

    • 在运行过程中spring底层将通知和切入点进行整合
  3. 使用代理对象调用方法

    @Autowired
    IAccountService accountService; //代理对象
    
    //调用方法
    Account account = accountService.queryAccountById(1);
    

切入点表达式

通过切入点表达式可以让spring找到要增强的目标方法

要使用切入点表达式,需要添加依赖:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

切入点表达式的语法:

  • 格式:execution(方法的修饰符 方法的返回值 类的全限定名.方法名(参数))

    示例:
    execution(public void com.itheima.service.impl.AccountServiceImpl.deleteAccount(Integer))
    
  • 通配符

    • 任意字符串:*
    • 任意重复次数:..
    示例:
    execution(public * com.itheima..AccountServiceImpl.*(..))
    
  • 规则

    1. 方法的修饰符可以省略
    2. 返回值可以使用*号代替(任意返回值类型)
    3. 包名可以使用*号代替,代表任意包(一层包使用一个*
    4. 使用..配置包名,标识此包以及此包下的所有子包
    5. 类名可以使用*号代替,标识任意类
    6. 方法名可以使用*号代替,表示任意方法
    7. 可以使用..配置参数,任意参数

切入点表达式示例

  • 省略方法的修饰符号

    execution(void com.itheima.service.impl.AccountServiceImpl.deleteAccount(Integer))
    
  • 使用*代替返回值类型

    execution(* com.itheima.service.impl.AccountServiceImpl.deleteAccount(Integer))
    
  • 使用*代替包名(一层包使用一个*

    execution(* com.itheima.*.*.AccountServiceImpl.deleteAccount(Integer))
    
  • 使用..省略包名

    execution(* com..AccountServiceImpl.deleteAccount(Integer))
    
  • 使用*代替类名

    execution(* com..*.deleteAccount(Integer))
    
  • 使用*代替方法名

    execution(* com..*.*(Integer))
    
  • 使用..省略参数

    execution(* com..*.*(..))
    

说明:在需求范围越具体越好(效率高)

//service包下的所有方法,均为切入点
execution(* com.itheima.service..*.*(..))

//service包下以Service结尾的业务类(如:IUserService、IAccountService、...)中所有方法
execution(* com.itheima.service.*Service.*(..))    

06_Spring的AOP详解-通知

目标

  • 能够配置Spring中AOP的通知

路径

  1. AOP中通知介绍
  2. AOP中环绕通知的使用

AOP中通知介绍

在AOP中有5种类型通知:

  1. 前置通知:@Before

    • 原始方法(切入点)执行前执行,如果通知中抛出异常,阻止原始方法运行
    • 应用场景:数据校验
    @Before("配置了切入点的方法名()")
    public void beforeMethod(){
        //前增强功能(在目标方法执行前运行)
    }
    
  2. 后置通知:@AfterReturning

    • 原始方法执行后执行,原始方法中出现异常,不再执行
    • 应用场景:返回值相关数据处理
    @AfterReturning("配置了切入点的方法名()")
    public void afterMethod(){
        //后增强功能(在目标方法执行后运行)
    }
    
  3. 抛出异常后通知:@AfterThrowing

    • 原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
    • 应用场景:对原始方法中出现的异常信息进行处理
    @AfterThrowing("配置了切入点的方法名()")
    public void throwMethod(){
        //在目标方法执行有异常时运行
    }
    
  4. 最终通知:@After

    • 无论如何最终都执行(不论是否有异常发生,都会执行)
    • 应用场景:清理资源
    @After("配置了切入点的方法名()")
    public void finallyMethod(){
        //不论是否有异常,在目标方法执行完后都会运行
    }
    
  5. 环绕通知:@Around

    • 在原始方法执行前后均有对应执行,还可以阻止原始方法的执行
    • 应用场景:可以做以上四种类型通知的所有事情(十分强大)

AOP中环绕通知的使用

环绕通知的开发方式:

  1. 环绕通知是在原始方法的前后添加功能,在环绕通知中,存在对原始方法的显式调用

    @Around("配置了切入点的方法名()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = pjp.proceed();//调用原始方法
        return ret;
    }
    
  2. 环绕通知方法相关说明:

    • 方法须设定Object类型的返回值,否则会拦截原始方法的返回。如果原始方法返回值类型为void,通知方也可以设定返回值类型为void,最终返回null
    • 方法需在第一个参数位置设定ProceedingJoinPoint对象(代表切入点),通过该对象调用proceed()方法,实现对原始方法的调用。如省略该参数,原始方法将无法执行。
      • ProceedingJoinPoint相当于Method,但是比Method的封装度更高
    • 使用proceed()方法调用原始方法时,因无法预知原始方法运行过程中是否会出现异常,强制抛出Throwable对象,封装原始方法中可能出现的异常信息

代码示例:

  • 切面类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AccountServiceAdvice2 {
    //配置切入点
    @Pointcut("execution(* com.itheima.service.impl.AccountServiceImpl.*(..))")
    public void pt() {
        //切入点配置在无参无返回值的空方法上
    }

    @Around("pt()")         // ProceedingJoinPoint 相当于:Method
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object result =null ;//返回结果

        try {
            //前置增强
            System.out.println("开始计时...");
            long beginTime = System.currentTimeMillis();

            //执行原始方法(被增强的原方法)
            result = pjp.proceed();

            //模拟异常:
            //int num = 10/0;

            //后置增强
            long endTime = System.currentTimeMillis();
            Signature signature = pjp.getSignature();//获取切入点方法
            System.out.println(signature.getName()+" 执行时间:" + (endTime - beginTime) + "毫秒");

        } catch (Throwable throwable) {
            System.out.println("异常通知");
        } finally {
            System.out.println("最终通知");
        }

        return result;//返回执行结果
    }
}
  • 测试类
@RunWith(SpringJUnit4ClassRunner.class) //spring整合junit
@ContextConfiguration(classes = SpringConfig.class) //加载注解配置类
public class AspectTest {
    @Autowired
    IAccountService accountService;

    @Test
    public void testAspect(){
        List<Account> accountList = accountService.queryAllAccount();

        Account account = accountService.queryAccountById(1);

        accountService.deleteAccount(1);

        accountService.saveAccount(null);
    }
}
//输出结果:
开始计时...
AccountServiceImpl => queryAllAccount方法
queryAllAccount 执行时间:3001毫秒
最终通知
开始计时...
AccountServiceImpl => queryAccountById方法
queryAccountById 执行时间:2002毫秒
最终通知
开始计时...
AccountServiceImpl => deleteAccount方法
deleteAccount 执行时间:0毫秒
最终通知
开始计时...
AccountServiceImpl => saveAccount方法
saveAccount 执行时间:0毫秒
最终通知

07_事务案例-转账

目标

  • 为学习后续的Spring事务管理做铺垫

路径

  1. 事务介绍
  2. 案例:转账业务

事务介绍

事务:transaction

  • 一组SQL操作,要么全部成功,要么全部失败

事务操作步骤:

  1. 开启事务
  2. 业务操作(多条sql语句)
  3. 提交事务/回滚事务

事务的特性:(ACID)

  • 原子性(Atomicity)
    • 指事务是一个不可分割的整体,其中的操作要么全执行或全不执行
  • 一致性(Consistency)
    • 事务前后数据的完整性必须保持一致
  • 隔离性(Isolation)
    • 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
  • 持久性(Durability)
    • 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

事务的隔离级别:

隔离级别 说明 脏读 不可重复读 幻读
READ_UNCOMMITTED 读未提交
READ_COMMITTED 读已提交 ×
REPEATABLE_READ 可重复读 × ×
SERIALIZABLE 串行化操作 × × ×
  • 隔离级别由低到高:
    • READ_UNCOMMITTED => READ_COMMITTED => REPEATABLE_READ => SERIALIZABLE
  • 数据库默认隔离级别:
    • MySQL采用:REPEATABLE_READ(可重复读)
    • Oracle采用:READ__COMMITTED(读已提交)

案例:转账业务

需求:jack给rose转账500元

数据准备:

create database spring_db;
use spring_db;
create table account(
	id int primary key auto_increment,
	name varchar(20),
	money double	
);
insert into account values(null,'jack',1000),(null,'rose',1000);

导入素材中的代码:day03-transfer

代码示例:

  • 数据库配置文件:jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=itheima
  • Pojo类
public class Account {
    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getMoney() {
        return money;
    }
    public void setMoney(Double money) {
        this.money = money;
    }
}
  • Mybatis配置类
public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource ds){
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        //设置pojo的包扫描
        factoryBean.setTypeAliasesPackage("com.itheima.pojo");
        //设置连接池
        factoryBean.setDataSource(ds);

        return factoryBean;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        //设置dao层的接口扫描
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
  • Spring配置类
@Configuration
@ComponentScan("com.itheima")//把bean加载到IOC容器
@PropertySource("classpath:jdbc.properties")
@Import(MybatisConfig.class)
@EnableAspectJAutoProxy   //开启aop注解支持
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}
  • Dao层
public interface AccountDao {
    //转出
    @Update("update account set money = money - #{money} where id = #{outId}")
    public abstract int outMoney(@Param("outId") int outId, 
                                 @Param("money")double money);
    
    //转入
    @Update("update account set money = money + #{money} where id = #{inId}")
    public abstract int inMoney(@Param("inId") int inId, 
                                @Param("money")double money);
}
  • 业务层
//接口
public interface AccountService {
    //转账业务
    public abstract void transfer(int outId,int inId,double money);
}


//实现类
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao dao;

    @Override
    public void transfer(int outId, int inId, double money) {
        try {
            //转出
            dao.outMoney(outId, money);
            
            //可能在转账过程中发生意外: 转出执行,转入还未执行
            //int i = 1/0;
            
            //转入
            dao.inMoney(inId, money);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class WebApp {
    @Autowired
    private AccountService service;

    @Test
    public void test01(){
        service.transfer(1, 2, 500);
    }
}

08_Spring事务管理对象

目标

  • 能够写出Spring中事务管理对象API的基础代码

路径

  1. Spring事务核心对象介绍
  2. 事务管理器对象
  3. 事务定义对象
  4. 事务状态对象

Spring事务核心对象介绍

  • JavaEE开发使用分层设计的思想进行

    一般dao层只做数据库增删改查实现,当业务中包含多个dao层的调用时,需要在service层开启事务,对数据层中多个操作进行组合并归属于同一个事务进行处理

  • Spring为业务层提供了整套的事务解决方案

    • PlatformTransactionManager //平台事务管理器
    • TransactionDefinition //事务定义
    • TransactionStatus //事务状态

事务管理器对象

PlatformTransactionManager接口(平台事务管理器)

  • DataSourceTransactionManager类
    • 适用于SpringJDBC或MyBatis
  • HibernateTransactionManager类
    • 适用于Hibernate3.0及以上版本
  • JpaTransactionManager类
    • 适用于JPA (Java EE 标准之一,为POJO提供持久化标准规范,并规范了持久化开发的统一API,符合JPA规范的开发可以在不同的JPA框架下运行)

PlatformTransactionManager接口定义了事务的基本操作:

  • 获取事务

    TransactionStatus getTransaction(TransactionDefinition definition)
    
  • 提交事务

    void commit(TransactionStatus status)
    
  • 回滚事务

    void rollback(TransactionStatus status)
    

代码示例:

//1. 创建事务管理器
 DataSourceTransactionManager dstm = new DataSourceTransactionManager();
//2. 为事务管理器设置与数据层相同的数据源
 dstm.setDataSource(dataSource);

事务定义对象

TransactionDefinition接口(事务定义)

  • DefaultTransactionDefinition类

    DefaultTransactionDefinition td = new DefaultTransactionDefinition();
    

TransactionDefinition接口定义了事务的基本信息:

  • 事务隔离级别

    • spring默认使用和数据库一样的隔离级别
    //设置事务的隔离级别
    td.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
    //mysql默认隔离级别是REPEATABLE_READ
    //oracle默认隔离级别是READ_COMMITTED
    
  • 事务是否只读

    //设置是否为只读事务
    td.setReadOnly(false);
    //false, 表示可读可写(适合增删改操作)【默认设置】
    //true, 表示只读(适合查,效率高)
    
  • 事务超时时间(单位:秒)

    //设置超时时间
    td.setTimeout(10);//超时时间为10秒
    //默认值是-1, 表示永不超时
    
  • 事务传播行为

    //设置事务传播行为 (这个比较复杂,后续详解)
    td.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    

示例代码:

//创建事务定义对象
DefaultTransactionDefinition td = new DefaultTransactionDefinition();
//设置事务隔离级别
td.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
//设置是否为只读事务
td.setReadOnly(false);
//设置超时时间
td.setTimeout(10);
//设置事务传播行为 
td.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

事务状态对象

TransactionStatus接口(事务状态)

  • 定义了事务在执行过程中某个时间点上事务对象的状态信息
获取事务是否处于新开启事务状态
boolean isNewTransaction();

获取事务是否处于完成状态
boolean isCompleted();

//获取事务是否处于回滚状态
boolean isRollbackOnly();

//获取事务是否具备回滚存储点
boolean hasSavepoint();

//设置事务处于回滚状态
void setRollbackOnly()

//刷新事务状态
void flush();

完整代码示例:

/* Spring提供的事务管理对象:
     平台事务管理器对象 :PlatformTransactionManager接口
     事务定义对象:TransactionDefinition接口
     事务状态对象:TransactionStatus接口
*/
//1、创建事务管理器对象(Mybatis专用)
DataSourceTransactionManager dstm = new DataSourceTransactionManager();
//为事务管理器设置数据源
dstm.setDataSource(dataSource);//数据库连接池对象和事务管理器绑定了

//2、创建事务定义对象
DefaultTransactionDefinition td = new DefaultTransactionDefinition();
//设置事务隔离级别
td.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
//设置是否为只读事务
td.setReadOnly(false);
//设置超时时间
td.setTimeout(10);
//设置事务传播行为 
td.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

//3、创建事务状态对象   (用于控制事务执行[相当于开启事务]) 
TransactionStatus ts = dstm.getTransaction(td);

//省略:业务层调用dao层功能代码

// 提交事务
dstm.commit(ts); 
// 回滚事务
dstm.rollback(ts);

简化:

//1、创建事务管理器对象
DataSourceTransactionManager dstm = new DataSourceTransactionManager();
//2、把事务器对象和数据库连接池进行绑定
dstm.setDataSource(dataSource);

//3、开启事务
TransactionStatus ts = dstm.getTransaction(new DefaultTransactionDefinition());

//4、事务操作(业务)
业务层中的代码.....


//5、提交事务|回滚事务
dstm.commit(ts); 

09_Spring事务-编程式事务

目标

  • 了解Spring中编程式事务的使用

路径

  1. 使用编程式事务改造转账业务

spring事务管理有两种方式:

  1. 编程式(了解)
  2. 声明式(重点)

使用编程式事务改造转账业务

使用spring提供的api进行事务管理,对转账业务代码进行修改:

/*
    TODO : Spring编程式事务
    1. 解释: 用Spring中的事务相关API用编码的方式来实现事务
    2. 但是这样实现不好: 耦合严重
        1). 事务管理代码跟业务代码耦合在一起
        2). 如果后续还有其他业务方法需要事务, 还得一个个编写事务代码,很冗余
    3. 解决方案:
        AOP
        1). 特点: 在不惊动原始设计的前提下,增强方法
        2). 概述: 无侵入性, 解耦
 */
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private DataSource dataSource;

    /**
     * 功能: 转账
     *
     * @param outId 出款人id
     * @param inId  收款人id
     * @param money 转账金额
     */
    @Override
    public void transfer(int outId, int inId, double money) {
        //1. 创建事务管理器
        DataSourceTransactionManager dstm = new DataSourceTransactionManager();
        //为事务管理器设置与数据层相同的数据源!!!
        dstm.setDataSource(dataSource);
        //2. 创建事务定义对象 : 隔离级别/传播特性/超时时间...
        DefaultTransactionDefinition td = new DefaultTransactionDefinition();
        /*
           设置事务隔离级别
           0). spring默认隔离级别是跟数据库软件一致 (ISOLATION_DEFAULT)
           1). mysql默认是REPEATABLE_READ
           2). oracle默认是READ_COMMITTED
        */
        td.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        /**
         * 设置是否为只读事务
         * 1). false,表示读写均可(默认设置,适合增删改操作)
         * 2). true,表示只读(适合查,效率高)
         */
        td.setReadOnly(false);
        /**
         * 设置超时时间
         * 1). 默认值是-1, 表示永不超时
         * 2). 单位是秒
         */
        td.setTimeout(10);
        /**
         * 设置事务传播行为 (这个比较复杂,后续详解)
         * 1. 一般增删改:REQUIRED (默认值)
         * 2. 一般查询  SUPPORTS
         */
        td.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //3.创建事务状态对象,用于控制事务执行(了解)  -> 相当于开启事务
        TransactionStatus ts = dstm.getTransaction(td);

        try {
            //转出
            accountDao.outMoney(outId, money);
            //可能在转账过程中发生意外: 转出执行,转入还未执行
            int i = 1 / 0;
            //转入
            accountDao.inMoney(inId, money);
            //成功:事务提交
            dstm.commit(ts);
        } catch (Exception e) {
            //e.printStackTrace();
            //失败:事务回滚
            dstm.rollback(ts);
        }
    }
}

以上代码中存在的问题:

  1. 事务管理代码跟业务代码耦合在一起(耦合严重)
  2. 如果后续还有其他业务方法需要事务, 还得一个个编写事务代码(过于冗余)

解决方案:AOP

  • 无侵入性, 解耦(在不惊动原始代码设计的前提下添加事务管理)

10_Spring事务-AOP改造编程式事务

目标

  • 了解使用Spring的AOP改造编程式事务的方式

路径

  1. 使用Spring的AOP改造编程式事务

使用Spring的AOP改造编程式事务

使用spring的aop改造案例中的事务管理代码:

  • Spring配置类
@Configurable
@ComponentScan("com.itheima")
@PropertySource("classpath:db.properties")
@Import(MybatisConfig.class)

//开启AOP支持
@EnableAspectJAutoProxy  
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}
  • 修改业务类
    • 注意:不要在转账业务代码中添加异常处理代码
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    /**
     * 功能: 转账
     *
     * @param outId 出款人id
     * @param inId  收款人id
     * @param money 转账金额
     */
    @Override
    public void transfer(int outId, int inId, double money) {
        /**
         * 注意: 在aop使用中,切入点方法千万不能自己catch异常
         *      原因: 如果切入点自己catch了异常,那么通知中是调用切入点的地方是不会感知到异常,就不会执行catch了
         *           (相当于异常通知失效)
         *      解决方案:
         *          方案1: 有异常直接抛出,不要catch
         *          方案2: 可以catch,但是再new一个异常抛出
         */
        //转出
        accountDao.outMoney(outId, money);

        //可能在转账过程中发生意外: 转出执行,转入还未执行
        int i = 1 / 0;

        //转入
        accountDao.inMoney(inId, money);
    }
}
  • 切面类:事务管理
@Component
@Aspect
public class TransactionAdvice { //事务通知类
    @Autowired
    private DataSource dataSource;

    //切入点
    @Pointcut("execution(* com.itheima.service.impl.AccountServiceImpl.transfer(..))")
    public void pt(){
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp){
        Object result = null;
        //1. 创建事务管理器
        DataSourceTransactionManager dstm = new DataSourceTransactionManager();
        //为事务管理器设置与数据层相同的数据源!!!
        dstm.setDataSource(dataSource);
        //2. 创建事务定义对象 : 隔离级别/传播特性/超时时间...
        DefaultTransactionDefinition td = new DefaultTransactionDefinition();
        /*
           设置事务隔离级别
           0). spring默认隔离级别是跟数据库软件一致 (ISOLATION_DEFAULT)
           1). mysql默认是REPEATABLE_READ
           2). oracle默认是READ_COMMITTED
        */
        td.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        /**
         * 设置是否为只读事务
         * 1). false,表示读写均可(默认设置,适合增删改操作)
         * 2). true,表示只读(适合查,效率高)
         * /
         td.setReadOnly(false);
         /**
         * 设置超时时间
         * 1). 默认值是-1, 表示永不超时
         * 2). 单位是秒
         */
        td.setTimeout(10);
        /**
         * 设置事务传播行为 (这个比较复杂,后续详解)
         * 1. 一般增删改:REQUIRED (默认值)
         * 2. 一般查询  SUPPORTS
         */
        td.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //3.创建事务状态对象,用于控制事务执行 -> 相当于开启事务
        TransactionStatus ts = dstm.getTransaction(td);

        try {
            //执行原始方式
            result = pjp.proceed();
            //成功:事务提交
            dstm.commit(ts);
        } catch (Throwable throwable) {
            //throwable.printStackTrace();
            System.out.println("转账业务异常!");
            //失败:事务回滚
            dstm.rollback(ts);
        }
        //返回值
        return result;
    }
}

11_Spring事务-声明式事务

目标

  • 使用声明式事务解决转账业务

路径

  1. 声明式事务前言
  2. 声明式事务开发步骤
  3. 使用声明式事务实现转账业务

声明式事务前言

AOP配置事务不具备特例性(通俗的说:任何业务添加事务都是一样的操作)

Spring底层封装了事务通知类,直接配置即可用。称为:声明式事务

  • 也就说, 之前案例中的TransactionAdvice类+相应的AOP配置可以不写了

声明式事务的开发步骤

声明式事务的开发步骤:

  • 第1步:开启事务管理支持

    @EnableTransactionManagement
    
  • 第2步:配置事务管理器

    @Bean
    public DataSourceTransactionManager getTxManager(DataSource dataSource){
        //创建事务管理器
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);//给事务管理器设置数据源
        return manager;
    }
    
  • 第3步:配置需要事务支持的切入点

    @Transactional(
        isolation = Isolation.DEFAULT,  //使用数据库默认的隔离级别
        readOnly = false,  //不是只读事务(增删改查都可以)
        timeout = 10,  //超时时间 
        propagation = Propagation.REQUIRED //设置事务传播行为
    )
    void transfer(int outId,int inId,double money);
    

使用声明式事务实现转账业务

代码示例:

  • Spring配置类
@Configurable
@ComponentScan("com.itheima")
@PropertySource("classpath:db.properties")
@Import(MybatisConfig.class)
//@EnableAspectJAutoProxy  //开启AOP支持
//声明式事务第1步:开启事务管理支持
@EnableTransactionManagement
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }

    //声明式事务第2步:配置事务管理器
    @Bean
    public DataSourceTransactionManager getTxManager(DataSource dataSource){
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        return manager;
    }
}
  • 业务层
//接口
public interface AccountService {
    //声明式事务第3步:配置需要事务支持的切入点
    @Transactional(
            isolation = Isolation.DEFAULT,
            readOnly = false,
            timeout = 10,
            propagation = Propagation.REQUIRED
    )
    //转账业务
    public abstract void transfer(int outId, int inId, double money);
}
  • @Transactional注解书写位置
    1. 可以直接放在接口上,表示该接口的所有方法都是切入点 (推荐)
    2. 可以放在接口方法上,表示该方法的所有重写方法都是切入点 (推荐)
    3. 放在实现类上,表示该实现类所有方法都是切入点
    4. 放在实现类的方法上,表示该方法是切入点

12_Spring事务传播行为

目标

  • 能够讲出Spring中常用的事务传播行为

路径

  1. 什么是事务传播行为
  2. 事务传播行为的设置
  3. 事务传播行为的应用场景
  4. 事务传播行为示例

什么是事务传播行为

事务传播行为:

  • 在一个业务流程(有事务的业务流)中,通常会调用多个方法执行,而每个方法对于事务的处理态度

image-20220513115431498

事务传播行为的设置

@Transactional(
    propagation = Propagation.REQUIRED //事务传播行为
)
public abstract void transfer(int outId, int inId, money money);

事务传播行为Propagation的取值:

  • REQUIRED (默认的传播行为)
    • 支持当前事务,如果不存在,就新建一个
  • SUPPORTS
    • 支持当前事务,如果不存在,就不使用事务
  • REQUIRES_NEW
    • 不论当前事务是否存在,都创建一个新的事务
  • NOT_SUPPORTED
    • 以非事务方式运行,如果有事务存在,挂起当前事务
  • MANDATORY
    • 支持当前事务,如果不存在,抛出异常
  • NEVER
    • 以非事务方式运行,如果有事务存在,抛出异常
  • NESTED
    • 如果当前事务存在,则嵌套事务执行(一个事务, 在A事务调用B过程中,B失败了,回滚事务到之前SavePoint ,用户可以选择提交事务或者回滚事务)

常用事务传播行为:

一般增删改:加事务 REQUIRED  (默认取值)
一般查询:不加事务 SUPPORTS
非主要业务不对主业务造成影响:REQUIRES_NEW

事务传播行为的应用场景

应用场景举例:转账操作

转账业务:transfer()
    - 转出转入 inAndOut() :X用户执行转出操作,修改表 , Y用户执行转入操作,修改表 
    - 记录日志 saveLog() :银行记录转账日志到数据库日志表中 
业务逻辑:
	1). 如果"转账"操作失败了, "转出转入"需要回滚
		如果"转账"有事务,"转出转入"跟随即可, 如果"转账"没有事务,"转出转入"需要自己创建事务
		所以"转出转入"适合设置传播行为属性为:REQUIRED
		
	2). 如果"转账"操作失败了, "记录日志"不需要回滚,必须要执行
    	如果"转账"有事务, "记录日志"不跟随(创建新事务)
    	如果"转账"没有事务, "记录日志"创建事务
    	所以"记录日志"适合设置传播行为属性为:REQUIRES_NEW

事务传播行为示例

事务传播行为是发生在两个Bean之间的

代码示例:

  • 数据表:
#日志表
create table db_log(
    id int primary key auto_increment,
    message varchar(20)
);
insert into db_log values(null,"测试");
  • Mybatis配置类
public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource ds){
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        //设置pojo的包扫描
        factoryBean.setTypeAliasesPackage("com.itheima.pojo");
        //设置连接池
        factoryBean.setDataSource(ds);

        return factoryBean;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        //设置dao层的接口扫描
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
  • Spring配置类
@Configurable
@ComponentScan("com.itheima")
@PropertySource("classpath:db.properties")
@Import(MybatisConfig.class)
//声明式事务第1步:开启事务管理支持
@EnableTransactionManagement
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }

    //声明式事务第2步:配置事务管理器
    @Bean
    public DataSourceTransactionManager getTxManager(DataSource dataSource){
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        return manager;
    }
}
  • 业务层
//接口
public interface AccountService {
    //转账业务
    public abstract void transfer(int outId, int inId, double money);
}

//实现类
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountServiceClass accountService;

    /**
     * 功能: 转账
     *
     * @param outId 出款人id
     * @param inId  收款人id
     * @param money 转账金额
     */
    @Override
    //声明式事务第3步:配置需要事务支持的切入点
    @Transactional
    public void transfer(int outId, int inId, double money) {
        try{
            //转出转入
            accountService.inAndOut(outId,inId,money);
        }finally {
            //记录日志
            accountService.saveLog();
        }
    }
}



//业务类
@Service
public class AccountServiceClass {
    @Autowired
    private AccountDao accountDao;

    //转出转入
    @Transactional(
            propagation = Propagation.REQUIRED //默认值
    )
    public void inAndOut(int outId, int inId, float money){
        //转出
        accountDao.outMoney(outId, money);
        //可能在转账过程中发生意外: 转出执行,转入还未执行
        int i = 1 / 0;
        //转入
        accountDao.inMoney(inId, money);
    }

    //日志记录
    @Transactional(
            propagation = Propagation.REQUIRES_NEW //开启新事务
    )
    public void saveLog() {
        accountDao.insertLog("转账日志");
    }
}
  • 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class WebApp {
    @Autowired
    private AccountService accountService;

    @Test
    public void test01(){
        accountService.transfer(1, 2, 500);
    }
}

标签:事务,day03,Spring,切入点,void,AOP,方法,public
From: https://www.cnblogs.com/-turing/p/17206358.html

相关文章

  • Kuboard安装并部署springcloud微服务项目
    Kuboard安装并部署springcloud微服务项目Kuboard是一款k8s的管理界面,我认为它有页面美观(颜值即正义)、操作简洁、系统配置要求不高、对新手友好的特点。开发者认为他们......
  • Spring-初始Spring
    Spring1、简介Spring:春天:----给软件行业带来了春天2002年,首次推出了Spring框架的雏形:interface21框架2004年3月24号诞生RodJohnson,SpringFramework创始人,......
  • Spring-IOC理论浅析
    IOC理论最开始时业务实现:UserDao接口UserDaoImpl实现类UserService业务接口UserServiceImpl业务实现类在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们......
  • Spring-第一个Spring程序
    1、HelloSpring1、第一个Spring项目创建Spring配置文件XXX.xml(标准名称:ApplicationContext.xml)<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.spri......
  • Spring-DI(依赖注入)及Bean的作用域
    DI(依赖注入)1、构造器注入前面已经说过了2、set方式注入【重点】依赖注入:Set注入依赖:bean对象的创建依赖于容器注入:bean对象中的所有属性,由容器来注入【环境......
  • Spring-Bean的自动装配
    Bean的自动装配自动装配是Spring满足bean依赖的一种方式Spring会在上下文中自动寻找,并自动给bean装配属性在Spring中有三种装配的方式在xml中显式的配置在java中显......
  • springboot打包时遇到的问题
    第一个问题是Failedtoexecutegoalorg.apache.maven.plugins:maven-resources-plugin:3.3.0:resources(default-resources)onproject   我的springboot版本......
  • 2.HelloSpring
    2.HelloSpring思考问题?Hello对象是谁创建的?Hello对象是由Spring设置的Hello对象的属性是怎么设置的?Hello对象的属性是Spring容器设置的这个过程就叫控制反转......
  • Spring:简述一下bean的生命周期吧?
    bean的生命周期是指它在ioc容器中从创建到销毁的整个过程。 <hr> 一、生命周期1、实例化,为对象分配内存。2、构造方法。3、属性注入,set注入。4......
  • SpringBoot 文件上传+拦截器
    SpringBoot文件上传+拦截器文件上传原理表单的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。当表单的enctype="application/x-www-form-urlencoded......