当前位置: 首页 > news >正文

世纪佳缘网站开发语言广告设计专业课程

世纪佳缘网站开发语言,广告设计专业课程,住房建设官方网站,北京住房与建设部网站SpringFramework 本篇笔记是基于尚硅谷学习资料的整理#xff0c;涉及到其笔记的简化#xff0c;补充#xff0c;以及我在学习中遇到的与无法理解的问题及解决#xff0c;如果想看完整及后续的笔记#xff0c;可以去https://www.wolai.com/v5Kuct5ZtPeVBk4NBUGBWF查看官方…SpringFramework 本篇笔记是基于尚硅谷学习资料的整理涉及到其笔记的简化补充以及我在学习中遇到的与无法理解的问题及解决如果想看完整及后续的笔记可以去https://www.wolai.com/v5Kuct5ZtPeVBk4NBUGBWF查看官方笔记。 一SpringFramework介绍 1.1.Spring与SpringFramework 广义的 SpringSpring 技术栈全家桶 广义上的 Spring 泛指以 Spring Framework 为基础的 Spring 技术栈。 经过十多年的发展Spring 已经不再是一个单纯的应用框架而是逐渐发展成为一个由多个不同子项目模块组成的成熟技术例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等其中 Spring Framework 是其他子项目的基础。 狭义的 SpringSpring Framework基础框架 狭义的 Spring 特指 Spring Framework通常我们将它称为 Spring 框架。 Spring FrameworkSpring框架是一个开源的应用程序框架由SpringSource公司开发最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能例如依赖注入Dependency Injection、面向切面编程AOP、声明式事务管理TX等。其主要目标是使企业级应用程序的开发变得更加简单和快速并且Spring框架被广泛应用于Java企业开发领域。 Spring全家桶的其他框架都是以SpringFramework框架为基础 1.2 SpringFramework主要功能模块 SpringFramework框架结构图 功能模块功能介绍Core Container核心容器在 Spring 环境下使用任何功能都必须基于 IOC 容器。AOPAspects面向切面编程TX声明式事务管理。Spring MVC提供了面向Web应用程序的集成功能。 二Spring IoC: 2.1.简要概述 在Spring框架中IoCInversion of Control控制反转是一种设计原则它通过将对象的创建和组装的控制权交给容器来实现。Spring IoC容器负责管理应用程序中的组件beans并在需要时将它们注入到其他组件中从而实现了组件之间的解耦和灵活性。 在Spring中IoC容器负责实例化、配置和组装应用程序中的对象。开发人员只需要定义组件并告诉容器如何创建和组装这些组件而不需要直接管理组件的生命周期。这种方式使得应用程序更加灵活易于维护和扩展。 Spring的IoC容器有两种实现BeanFactory和ApplicationContext。BeanFactory是Spring的基础容器提供了基本的IoC功能ApplicationContext是BeanFactory的扩展提供了更多的企业级功能如国际化、事件传播、AOP等。 组件beans是应用程序中的基本构建单元它们由IoC容器管理。在Spring中组件可以是任何Java对象通常是业务逻辑、数据访问、视图控制等功能的实现类。开发人员需要在配置文件如XML配置文件、Java配置类中定义这些组件并告诉IoC容器如何创建和管理它们。 组件之间的关系由IoC容器来管理和维护。通过依赖注入Dependency InjectionDI机制一个组件可以依赖于另一个组件而不需要直接创建或查找依赖的对象。IoC容器在初始化组件时会自动将依赖的组件注入到目标组件中从而实现了组件之间的解耦和灵活性。 总之Spring的IoC容器负责管理应用程序中的组件通过依赖注入的方式实现组件之间的关系从而提高了应用程序的可维护性、可扩展性和可测试性。 2.2.Spring IoC容器 Spring IoC 容器负责实例化、配置和组装 bean组件。容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。 SpringIoc容器接口 BeanFactory 接口提供了一种高级配置机制能够管理任何类型的对象它是SpringIoC容器标准化超接口 ApplicationContext 是 BeanFactory 的子接口。它扩展了以下功能 更容易与 Spring 的 AOP 功能集成消息资源处理用于国际化特定于应用程序给予此接口实现例如Web 应用程序的 WebApplicationContext 简而言之 BeanFactory 提供了配置框架和基本功能而 ApplicationContext 添加了更多特定于企业的功能。 ApplicationContext 是 BeanFactory 的完整超集 ApplicationContext容器实现类 类型名简介ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象AnnotationConfigApplicationContext通过读取Java配置类创建 IOC 容器对象WebApplicationContext专门为 Web 应用准备基于 Web 环境创建 IOC 容器对象并将对象引入存入 ServletContext 域中。 Spring框架提供了多种配置方式XML配置方式、注解方式和Java配置类方式 XML配置方式是Spring框架最早的配置方式之一通过在XML文件中定义Bean及其依赖关系、Bean的作用域等信息让Spring IoC容器来管理Bean之间的依赖关系。该方式从Spring框架的第一版开始提供支持。注解方式从Spring 2.5版本开始提供支持可以通过在Bean类上使用注解来代替XML配置文件中的配置信息。通过在Bean类上加上相应的注解如Component, Service, Autowired等将Bean注册到Spring IoC容器中这样Spring IoC容器就可以管理这些Bean之间的依赖关系。Java配置类方式从Spring 3.0版本开始提供支持通过Java类来定义Bean、Bean之间的依赖关系和配置信息从而代替XML配置文件的方式。Java配置类是一种使用Java编写配置信息的方式通过Configuration、Bean等注解来实现Bean和依赖关系的配置。 为了迎合当下开发环境我们将以配置类注解方式为主进行讲解 2.3.IoC的功能 IoCInversion of Control控制反转 IoC 主要是针对对象的创建和调用控制而言的也就是说当应用程序需要使用一个对象时不再是应用程序直接创建该对象而是由 IoC 容器来创建和管理即控制权由应用程序转移到 IoC 容器中也就是“反转”了控制权。这种方式基本上是通过依赖查找的方式来实现的即 IoC 容器维护着构成应用程序的对象并负责创建这些对象。 DI (Dependency Injection) 依赖注入 DI 是指在组件之间传递依赖关系的过程中将依赖关系在容器内部进行处理这样就不必在应用程序代码中硬编码对象之间的依赖关系实现了对象之间的解耦合。在 Spring 中DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入构造函数注入、Setter 方法注入和接口注入。 三Spring IoC应用 3.1.Spring IoC / DI 实现步骤 我们总结下组件交给Spring IoC容器管理并且获取和使用的基本步骤 配置元数据配置 配置元数据既是编写交给SpringIoC容器管理组件的信息配置方式有三种。 基于 XML 的配置元数据的基本结构 1. bean id... [1] class... [2] !-- collaborators and configuration for this bean go here -- /bean?xml version1.0 encodingUTF-8? !-- 此处要添加一些约束配置文件的标签并不是随意命名 -- beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdbean id... [1] class... [2] !-- collaborators and configuration for this bean go here --/beanbean id... class...!-- collaborators and configuration for this bean go here --/bean!-- more bean definitions go here -- /beansSpring IoC 容器管理一个或多个组件。这些 组件是使用你提供给容器的配置元数据例如以 XML bean/ 定义的形式创建的。 标签 组件信息声明 id 属性是标识单个 Bean 定义的字符串。class 属性定义 Bean 的类型并使用完全限定的类名。 实例化IoC容器 提供给 ApplicationContext 构造函数的位置路径是资源字符串地址允许容器从各种外部资源如本地文件系统、Java CLASSPATH 等加载配置元数据。 我们应该选择一个合适的容器实现类进行IoC容器的实例化工作 //实例化ioc容器,读取外部配置文件,最终会在容器内进行ioc和di动作 ApplicationContext context new ClassPathXmlApplicationContext(services.xml, daos.xml);获取Bean组件 ApplicationContext 是一个高级工厂的接口能够维护不同 bean 及其依赖项的注册表。通过使用方法 T getBean(String name, ClassT requiredType) 您可以检索 bean 的实例。 允许读取 Bean 定义并访问它们如以下示例所示 //创建ioc容器对象指定配置文件ioc也开始实例组件对象 ApplicationContext context new ClassPathXmlApplicationContext(services.xml, daos.xml); //获取ioc容器的组件对象 PetStoreService service context.getBean(petStore, PetStoreService.class); //使用组件对象 ListString userList service.getUsernameList();3.2.基于XML的配置 3.2.1 实验一 组件Bean信息声明配置IoC 准备项目 创建maven工程spring-ioc-xml-01 导入SpringIoC相关依赖 pom.xml dependencies!--spring context依赖--!--当你引入Spring Context依赖之后表示将Spring的基础依赖引入了--dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion6.0.6/version/dependency!--junit5测试--dependencygroupIdorg.junit.jupiter/groupIdartifactIdjunit-jupiter-api/artifactIdversion5.3.1/version/dependency /dependencies基于无参数构造函数 当通过构造函数方法创建一个 bean组件对象 时所有普通类都可以由 Spring 使用并与之兼容。也就是说正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 Bean 类信息就足够了。但是默认情况下我们需要一个默认空构造函数。 准备组件类 package com.atguigu.ioc;public class HappyComponent {//默认包含无参数构造函数public void doWork() {System.out.println(HappyComponent.doWork);} }!-- 实验一 [重要]创建bean -- bean idhappyComponent classtest.HappyComponent/- bean标签通过配置bean标签告诉IOC容器需要创建对象的组件信息- id属性bean的唯一标识,方便后期获取Bean- class属性组件类的全限定符- 注意要求当前组件类必须包含无参数构造函数我们可以通过这种方法快速配置Spring。 基于静态工厂方法实例化 除了使用构造函数实例化对象还有一类是通过工厂模式实例化对象。接下来我们讲解如何定义使用静态工厂方法创建Bean的配置 准备组件类 public class ClientService {private static ClientService clientService new ClientService();private ClientService() {}public static ClientService createInstance() {return clientService;} }xml配置文件编写 文件resources/spring-bean-01.xml bean idclientService classtest.ClientService factory-methodcreateInstance/- class属性指定工厂类的全限定符- factory-method: 指定静态工厂方法注意该方法必须是static方法。基于实例工厂方法实例化 接下来我们讲解下如何定义使用实例工厂方法创建Bean的配置 准备组建类 public class DefaultServiceLocator {private static ClientServiceImplclientService new ClientServiceImpl();public ClientService createClientServiceInstance() {return clientService;} }xml配置文件编写 文件resources/spring-bean-01.xml !-- 将工厂类进行ioc配置 -- bean idserviceLocator classexamples.DefaultServiceLocator /bean!-- 根据工厂对象的实例工厂方法进行实例化组件对象 -- bean idclientService factory-beanserviceLocator factory-methodcreateClientServiceInstance/- factory-bean属性指定当前容器中工厂Bean 的名称。- factory-method: 指定实例工厂方法名。注意实例方法必须是非static的3.2.2 实验二 组件Bean依赖注入配置DI 基于构造函数的依赖注入单个构造参数 介绍 基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的每个参数表示一个依赖项。 下面的示例演示一个只能通过构造函数注入进行依赖项注入的类 准备组件类 public class UserDao { }public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao userDao;} } 编写配置文件 文件resources/spring-02.xml beans!-- 引用类bean声明 --bean iduserService classx.y.UserService!-- 构造函数引用 --constructor-arg refuserDao//bean!-- 被引用类bean声明 --bean iduserDao classx.y.UserDao/ /beans- constructor-arg标签可以引用构造参数 ref引用其他bean的标识。注意可以用value进行直接属性配置比如一个具体的字符串。 2.基于构造函数的依赖注入多构造参数解析 介绍 基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的每个参数表示一个依赖项。 下面的示例演示通过构造函数注入多个参数参数包含其他bean和基本数据类型 准备组件类 public class UserDao { }public class UserService {private UserDao userDao;private int age;private String name;public UserService(int age , String name ,UserDao userDao) {this.userDao userDao;this.age age;this.name name;} }3. 编写配置文件!-- 场景1: 多参数可以按照相应构造函数的顺序注入数据 -- beansbean iduserService classx.y.UserService!-- value直接注入基本类型值 --constructor-arg value18/constructor-arg value赵伟风/constructor-arg refuserDao//bean!-- 被引用类bean声明 --bean iduserDao classx.y.UserDao/ /beans!-- 场景2: 多参数可以按照相应构造函数的名称注入数据 -- beansbean iduserService classx.y.UserService!-- value直接注入基本类型值 --constructor-arg namename value赵伟风/constructor-arg nameuserDao refuserDao/constructor-arg nameage value18//bean!-- 被引用类bean声明 --bean iduserDao classx.y.UserDao/ /beans!-- 场景3: 多参数可以按照相应构造函数的角标注入数据 index从0开始 构造函数(0,1,2....)-- beansbean iduserService classx.y.UserService!-- value直接注入基本类型值 --constructor-arg index1 value赵伟风/constructor-arg index2 refuserDao/constructor-arg index0 value18//bean!-- 被引用类bean声明 --bean iduserDao classx.y.UserDao/ /beans - constructor-arg标签指定构造参数和对应的值- constructor-arg标签name属性指定参数名、index属性指定参数角标、value属性指定普通属性值3.基于Setter方法依赖注入 介绍 开发中除了构造函数注入DI更多的使用的Setter方法进行注入 下面的示例演示一个只能使用纯 setter 注入进行依赖项注入的类。 准备组件类 public Class MovieFinder{}public class SimpleMovieLister {private MovieFinder movieFinder;private String movieName;public void setMovieFinder(MovieFinder movieFinder) {this.movieFinder movieFinder;}public void setMovieName(String movieName){this.movieName movieName;}// business logic that actually uses the injected MovieFinder is omitted... }3. 编写配置文件bean idsimpleMovieLister classexamples.SimpleMovieLister!-- setter方法注入movieFinder对象的标识idname 属性名 ref 引用bean的id值--property namemovieFinder refmovieFinder /!-- setter方法注入基本数据类型movieNamename 属性名 value 基本类型值--property namemovieName value消失的她/ /beanbean idmovieFinder classexamples.MovieFinder/ - property标签 可以给setter方法对应的属性赋值- property 标签 name属性代表**set方法标识**、ref代表引用bean的标识id、value属性代表基本属性值注意name一般是set要set的属性其实是set方法名去掉set字段只不过一般规范命名。 总结 依赖注入DI包含引用类型和基本数据类型同时注入的方式也有多种主流的注入方式为setter方法注入和构造函数注入两种注入语法都需要掌握 注意在DI中如果传入的对象可以直接赋值例如String,int那么就用value如果是用一个外部的类对象赋值就用refref中填写的是XML中那个类的id值也就是说我们在进行DI配置时除了对某个方法或者构造器所在的类进行配置还要配置传入的参数所在的类。 3.2.3 实验三 IoC容器创建和使用 介绍 上面的实验只是讲解了如何在XML格式的配置文件编写IoC和DI配置 如图 想要配置文件中声明组件类信息真正的进行实例化成Bean对象和形成Bean之间的引用关系我们需要声明IoC容器对象读取配置文件实例化组件和关系维护的过程都是在IoC容器中实现的 容器实例化 //方式1:实例化并且指定配置文件 //参数String...locations 传入一个或者多个配置文件 ApplicationContext context new ClassPathXmlApplicationContext(services.xml, daos.xml);//方式2:先实例化再指定配置文件最后刷新容器触发Bean实例化动作 [springmvc源码和contextLoadListener源码方式] ApplicationContext context new ClassPathXmlApplicationContext(); //设置配置配置文件,方法参数为可变参数,可以设置一个或者多个配置 iocContainer1.setConfigLocations(services.xml, daos.xml); //后配置的文件,需要调用refresh方法,触发刷新配置 iocContainer1.refresh(); 3. Bean对象读取//方式1: 根据id获取 //没有指定类型,返回为Object,需要类型转化! HappyComponent happyComponent (HappyComponent) iocContainer.getBean(bean的id标识);//使用组件对象 happyComponent.doWork();//方式2: 根据类型获取 //根据类型获取,但是要求,同类型(当前类,或者之类,或者接口的实现类)只能有一个对象交给IoC容器管理 //配置两个或者以上出现: org.springframework.beans.factory.NoUniqueBeanDefinitionException 问题 HappyComponent happyComponent iocContainer.getBean(HappyComponent.class); happyComponent.doWork();//方式3: 根据id和类型获取 HappyComponent happyComponent iocContainer.getBean(bean的id标识, HappyComponent.class); happyComponent.doWork();//根据类型来获取bean时在满足bean唯一性的前提下其实只是看『对象 instanceof 指定的类型』的返回结果 //只要返回的是true就可以认定为和类型匹配能够获取到。因为只是看『对象 instanceof 指定的类型』的返回结果而接口也可以进行获取而且满足该条件所有实现类只能有一个进行配置。 3.2.4 实验四 高级特性组件Bean作用域和周期方法配置 组件周期方法配置 周期方法概念 我们可以在组件类中定义方法然后当IoC容器实例化和销毁组件对象的时候进行调用这两个方法我们成为生命周期方法 类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。 周期方法声明 public class BeanOne {//周期方法要求 方法命名随意但是要求方法必须是 public void 无形参列表public void init() {// 初始化逻辑} }public class BeanTwo {public void cleanup() {// 释放资源逻辑} } //注意销毁方法要容器对象主动调用.close方法才会生效不然来不及执行方法就被销毁了。周期方法配置 beansbean idbeanOne classexamples.BeanOne init-methodinit /bean idbeanTwo classexamples.BeanTwo destroy-methodcleanup / /beans组件作用域配置 Bean作用域概念 bean 标签声明Bean只是将Bean的信息配置给SpringIoC容器 在IoC容器中这些bean标签对应的信息转成Spring内部 BeanDefinition 对象BeanDefinition 对象内包含定义的信息id,class,属性等等 这意味着BeanDefinition与类概念一样SpringIoC容器可以可以根据BeanDefinition对象反射创建多个Bean对象实例。 具体创建多少个Bean的实例对象由Bean的作用域Scope属性指定 作用域可选值 取值含义创建对象的时机默认值singleton在 IOC 容器中这个 bean 的对象始终为单实例IOC 容器初始化时是prototype这个 bean 在 IOC 容器中有多个实例获取 bean 时否 ​ 如果是在WebApplicationContext环境下还会有另外两个作用域但不常用 取值含义创建对象的时机默认值request请求范围内有效的实例每次请求否session会话范围内有效的实例每次会话否 作用域配置 配置scope范围 !--bean的作用域 准备两个引用关系的组件类即可 -- !-- scope属性取值singleton默认值bean在IOC容器中只有一个实例IOC容器初始化时创建对象 -- !-- scope属性取值prototypebean在IOC容器中可以有多个实例getBean()时创建对象 -- bean idhappyMachine8 scopeprototype classcom.atguigu.ioc.HappyMachineproperty namemachineName valuehappyMachine/ /beanbean idhappyComponent8 scopesingleton classcom.atguigu.ioc.HappyComponentproperty namecomponentName valuehappyComponent/ /bean4. 作用域测试Test public void testExperiment08() {ApplicationContext iocContainer new ClassPathXmlApplicationContext(配置文件名);HappyMachine bean iocContainer.getBean(HappyMachine.class);HappyMachine bean1 iocContainer.getBean(HappyMachine.class);//多例对比 falseSystem.out.println(bean bean1);HappyComponent bean2 iocContainer.getBean(HappyComponent.class);HappyComponent bean3 iocContainer.getBean(HappyComponent.class);//单例对比 trueSystem.out.println(bean2 bean3); }4.2.5 实验五 高级特性FactoryBean特性和使用 FactoryBean简介 FactoryBean 接口是Spring IoC容器实例化逻辑的可插拔性点。 用于配置复杂的Bean对象可以将创建过程存储在FactoryBean 的getObject方法 FactoryBeanT 接口提供三种方法 T getObject(): 返回此工厂创建的对象的实例。该返回值会被存储到IoC容器 boolean isSingleton(): 如果此 FactoryBean 返回单例则返回 true 否则返回 false 。此方法的默认实现返回 true 注意lombok插件使用可能影响效果。 Class? getObjectType(): 返回 getObject() 方法返回的对象类型如果事先不知道类型则返回 null 。 FactoryBean使用场景 代理类的创建第三方框架整合复杂对象实例化等 Factorybean应用 准备FactoryBean实现类 // 实现FactoryBean接口时需要指定泛型 // 泛型类型就是当前工厂要生产的对象的类型 public class HappyFactoryBean implements FactoryBeanHappyMachine {private String machineName;public String getMachineName() {return machineName;}public void setMachineName(String machineName) {this.machineName machineName;}Overridepublic HappyMachine getObject() throws Exception {// 方法内部模拟创建、设置一个对象的复杂过程HappyMachine happyMachine new HappyMachine();happyMachine.setMachineName(this.machineName);return happyMachine;}Overridepublic Class? getObjectType() {// 返回要生产的对象的类型return HappyMachine.class;} }2. 配置FactoryBean实现类!-- FactoryBean机制 -- !-- 这个bean标签中class属性指定的是HappyFactoryBean但是将来从这里获取的bean是HappyMachine对象 -- bean idhappyMachine7 classcom.atguigu.ioc.HappyFactoryBean!-- property标签仍然可以用来通过setXxx()方法给属性赋值 --property namemachineName valueiceCreamMachine/ /bean3. 测试读取FactoryBean和FactoryBean.getObject对象Test public void testExperiment07() {ApplicationContext iocContainer new ClassPathXmlApplicationContext(spring-bean-07.xml);//注意: 直接根据声明FactoryBean的id,获取的是getObject方法返回的对象HappyMachine happyMachine iocContainer.getBean(happyMachine7,HappyMachine.class);System.out.println(happyMachine happyMachine);//如果想要获取FactoryBean对象, 直接在id前添加符号即可! happyMachine7 这是一种固定的约束Object bean iocContainer.getBean(happyMachine7);System.out.println(bean bean);//也就是说一般情况下默认返回的是工厂要生产的类的类型而加上后返回的是工厂类型。 }FactoryBean和BeanFactory区别 **FactoryBean **是 Spring 中一种特殊的 bean可以在 getObject() 工厂方法自定义的逻辑创建Bean是一种能够生产其他 Bean 的 Bean。FactoryBean 在容器启动时被创建而在实际使用时则是通过调用 getObject() 方法来得到其所生产的 Bean。因此FactoryBean 可以自定义任何所需的初始化逻辑生产出一些定制化的 bean。 一般情况下整合第三方框架都是通过定义FactoryBean实现 BeanFactory 是 Spring 框架的基础其作为一个顶级接口定义了容器的基本行为例如管理 bean 的生命周期、配置文件的加载和解析、bean 的装配和依赖注入等。BeanFactory 接口提供了访问 bean 的方式例如 getBean() 方法获取指定的 bean 实例。它可以从不同的来源例如 Mysql 数据库、XML 文件、Java 配置类等获取 bean 定义并将其转换为 bean 实例。同时BeanFactory 还包含很多子类例如ApplicationContext 接口提供了额外的强大功能。 总的来说FactoryBean 和 BeanFactory 的区别主要在于前者是用于创建 bean 的接口它提供了更加灵活的初始化定制功能而后者是用于管理 bean 的框架基础接口提供了基本的容器功能和 bean 生命周期管理。 注意一般情况下默认返回的是工厂要生产的类的类型而加上后返回的是工厂类型。但是在 property/中配置的是工厂而不是工厂要生产的类的类型。我们可以在工厂中加上字段并且在方法中设置这样就可以间接配置了。 3.3.补充JdbcTemplate 为了在特定领域帮助我们简化代码Spring 封装了很多 『Template』形式的模板类。例如RedisTemplate、RestTemplate 等等包括我们今天要学习的 JdbcTemplate。 其本质就相当于crud的封装 3.3.1.我们从JAVA代码层面来看一看 3.3.2.我们再来从IoC容器方面来看看 先导入依赖 !-- spring-jdbc --dependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion6.0.6/version/dependency1.我们可以直接在XML文件编写 2.或者用传统方法在properties中编写 atguigu.urljdbc:mysql://localhost:3306/studb atguigu.drivercom.mysql.cj.jdbc.Driver atguigu.usernameroot atguigu.passwordroot注意前面的atguigu可以省略或者按照需要修改。 然后在XML中加上 !-- 导入外部属性文件 --context:property-placeholder locationclasspath:jdbc.properties /!-- 配置数据源 --bean iddruidDataSource classcom.alibaba.druid.pool.DruidDataSourceproperty nameurl value${atguigu.url}/property namedriverClassName value${atguigu.driver}/property nameusername value${atguigu.username}/property namepassword value${atguigu.password}//bean注意当导入上方的文件时会自动增加 xmlns:contexthttp://www.springframework.org/schema/context我们要确保后缀是context。 无论哪种方法后面都需要 !-- 配置 JdbcTemplate --bean idjdbcTemplate classorg.springframework.jdbc.core.JdbcTemplate!-- 装配数据源 --property namedataSource refdruidDataSource//bean一些常见方法 ListStudent studentList jdbcTemplate.query(sql, new BeanPropertyRowMapper(Student.class));jdbcTemplate这是Spring框架提供的一个类用于简化JDBC操作。它封装了一些常见的JDBC操作例如执行SQL查询、更新等。 query(sql, rowMapper)这是JdbcTemplate类的一个方法用于执行SQL查询并将结果映射到Java对象。它接受两个参数 sql表示要执行的SQL查询语句。rowMapper表示结果集中的每一行如何映射到Java对象。在这里我们使用了BeanPropertyRowMapper类作为rowMapper它会根据结果集的列名和Java对象的属性名进行自动映射。 new BeanPropertyRowMapper(Student.class)这里我们创建了一个BeanPropertyRowMapper对象并指定了Student.class作为泛型参数。这意味着我们希望将查询结果映射为Student对象。BeanPropertyRowMapper会通过反射机制自动将查询结果的列值赋给Student对象的属性。 List studentList最终查询结果会被封装为一个List对象其中每个元素都是一个Student对象。 综上所述这行代码的作用是执行给定的SQL查询并将结果映射为Student对象的List。这种方式可以简化JDBC操作避免手动处理结果集的列与Java对象属性之间的映射关系。 3.4.基于注解方式管理 Bean 3.4.1 实验一 Bean注解标记和扫描 (IoC) 注解理解 和 XML 配置文件一样注解本身并不能执行注解本身仅仅只是做一个标记具体的功能是框架检测到注解标记的位置然后针对这个位置按照注解标记的功能来执行具体操作。 扫描理解 Spring 为了知道程序员在哪些地方标记了什么注解就需要通过扫描的方式来进行检测。然后根据注解进行后续操作。 准备Spring项目和组件 ​ 普通的组件 /*** projectName: com.atguigu.components** description: 普通的组件*/ public class CommonComponent { } ​ Controller组件 /*** projectName: com.atguigu.components** description: controller类型组件*/ public class XxxController { } ​ Service组件 /*** projectName: com.atguigu.components** description: service类型组件*/ public class XxxService { } ​ Dao组件 /*** projectName: com.atguigu.components** description: dao类型组件*/ public class XxxDao { } 组件添加标记注解 组件标记注解和区别 Spring 提供了以下多个注解这些注解可以直接标注在 Java 类上将它们定义成 Spring Bean。 注解说明Component该注解用于描述 Spring 中的 Bean它是一个泛化的概念仅仅表示容器中的一个组件Bean并且可以作用在应用的任何层次例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。Repository该注解用于将数据访问层Dao 层的类标识为 Spring 中的 Bean其功能与 Component 相同。Service该注解通常作用在业务层Service 层用于将业务层的类标识为 Spring 中的 Bean其功能与 Component 相同。Controller该注解通常作用在控制层如SpringMVC 的 Controller用于将控制层的类标识为 Spring 中的 Bean其功能与 Component 相同。 ​ 通过查看源码我们得知Controller、Service、Repository这三个注解只是在Component注解的基础上起了三个新的名字注意虽然它们本质上一样但是为了代码的可读性、程序结构严谨我们肯定不能随便胡乱标记。 使用注解标记 普通组件 /*** projectName: com.atguigu.components** description: 普通的组件*/ Component public class CommonComponent { } ​ Controller组件 /*** projectName: com.atguigu.components** description: controller类型组件*/ Controller public class XxxController { } ​ Service组件 /*** projectName: com.atguigu.components** description: service类型组件*/ Service public class XxxService { } ​ Dao组件 /*** projectName: com.atguigu.components** description: dao类型组件*/ Repository public class XxxDao { } 配置文件确定扫描范围 情况1基本扫描配置 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://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!-- 配置自动扫描的包 --!-- 1.包要精准,提高性能!2.会扫描指定的包和子包内容3.多个包可以使用,分割 例如: com.atguigu.controller,com.atguigu.service等--context:component-scan base-packagecom.atguigu.components//beans情况2指定排除组件 !-- 情况三指定不扫描的组件 -- context:component-scan base-packagecom.atguigu.components!-- context:exclude-filter标签指定排除规则 --!-- type属性指定根据什么来进行排除annotation取值表示根据注解来排除 --!-- expression属性指定排除规则的表达式对于注解来说指定全类名即可 --context:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan情况3指定扫描组件 !-- 情况四仅扫描指定的组件 -- !-- 仅扫描 关闭默认规则 追加规则 -- !-- use-default-filters属性取值false表示关闭默认扫描规则 -- context:component-scan base-packagecom.atguigu.ioc.components use-default-filtersfalse!-- context:include-filter标签指定在原有扫描规则的基础上追加的规则 --context:include-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan !--别忘记use-default-filtersfalse不然会出错--组件BeanName问题 在我们使用 XML 方式管理 bean 的时候每个 bean 都有一个唯一标识——id 属性的值便于在其他地方引用。现在使用注解后每个组件仍然应该有一个唯一标识。 默认情况 类名首字母小写就是 bean 的 id。例如SoldierController 类对应的 bean 的 id 就是 soldierController。 使用value属性指定 Controller(value tianDog) public class SoldierController { }当注解中只设置一个属性时value属性的属性名可以省略 Service(smallDog) public class SoldierService { }7. **总结**1. 注解方式IoC只是标记哪些类要被Spring管理2. 最终我们还需要XML方式或者后面讲解Java配置类方式指定注解生效的包3. **现阶段配置方式为 注解 标记 XML扫描**3.4.2 实验二 组件Bean作用域和周期方法注解 组件周期方法配置 周期方法概念 我们可以在组件类中定义方法然后当IoC容器实例化和销毁组件对象的时候进行调用这两个方法我们成为生命周期方法 类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。 周期方法声明 public class BeanOne {//周期方法要求 方法命名随意但是要求方法必须是 public void 无形参列表PostConstruct //注解制指定初始化方法public void init() {// 初始化逻辑} }public class BeanTwo {PreDestroy //注解指定销毁方法public void cleanup() {// 释放资源逻辑} }组件作用域配置 Bean作用域概念 bean 标签声明Bean只是将Bean的信息配置给SpringIoC容器 在IoC容器中这些bean标签对应的信息转成Spring内部 BeanDefinition 对象BeanDefinition 对象内包含定义的信息id,class,属性等等 这意味着BeanDefinition与类概念一样SpringIoC容器可以可以根据BeanDefinition对象反射创建多个Bean对象实例。 具体创建多少个Bean的实例对象由Bean的作用域Scope属性指定 作用域可选值 取值含义创建对象的时机默认值singleton在 IOC 容器中这个 bean 的对象始终为单实例IOC 容器初始化时是prototype这个 bean 在 IOC 容器中有多个实例获取 bean 时否 ​ 如果是在WebApplicationContext环境下还会有另外两个作用域但不常用 取值含义创建对象的时机默认值request请求范围内有效的实例每次请求否session会话范围内有效的实例每次会话否 3. 作用域配置Scope(scopeName ConfigurableBeanFactory.SCOPE_SINGLETON) //单例,默认值 Scope(scopeName ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多例 二选一 public class BeanOne {//周期方法要求 方法命名随意但是要求方法必须是 public void 无形参列表PostConstruct //注解制指定初始化方法public void init() {// 初始化逻辑} }3.4.3 实验三 Bean属性赋值引用类型自动装配 (DI) 设定场景 SoldierController 需要 SoldierService SoldierService 需要 SoldierDao 同时在各个组件中声明要调用的方法。 SoldierController中声明方法 import org.springframework.stereotype.Controller;Controller(value tianDog) public class SoldierController {private SoldierService soldierService;public void getMessage() {soldierService.getMessage();}}SoldierService中声明方法 Service(smallDog) public class SoldierService {private SoldierDao soldierDao;public void getMessage() {soldierDao.getMessage();} }SoldierDao中声明方法 Repository public class SoldierDao {public void getMessage() {System.out.print(I am a soldier);}}自动装配实现 前提 参与自动装配的组件需要装配、被装配全部都必须在IoC容器中。 注意不区分IoC的方式XML和注解都可以 Autowired注解 在成员变量上直接标记Autowired注解即可不需要提供setXxx()方法。以后我们在项目中的正式用法就是这样。 给Controller装配Service Controller(value tianDog) public class SoldierController {Autowiredprivate SoldierService soldierService;public void getMessage() {soldierService.getMessage();}}4. 给Service装配DaoService(smallDog) public class SoldierService {Autowiredprivate SoldierDao soldierDao;public void getMessage() {soldierDao.getMessage();} }Autowired注解细节 标记位置 成员变量 这是最主要的使用方式 与xml进行bean ref引用不同他不需要有set方法 Service(smallDog) public class SoldierService {Autowiredprivate SoldierDao soldierDao;public void getMessage() {soldierDao.getMessage();} }​ 2. 构造器 Controller(value tianDog) public class SoldierController {private SoldierService soldierService;Autowiredpublic SoldierController(SoldierService soldierService) {this.soldierService soldierService;}……​ 3. setXxx()方法 Controller(value tianDog) public class SoldierController {private SoldierService soldierService;Autowiredpublic void setSoldierService(SoldierService soldierService) {this.soldierService soldierService;}……工作流程 首先根据所需要的组件类型到 IOC 容器中查找也就是说必须要有注解egController(value “tianDog”) 能够找到唯一的 bean直接执行装配如果完全找不到匹配这个类型的 bean装配失败和所需类型匹配的 bean 不止一个 没有 Qualifier 注解根据 Autowired 标记位置成员变量的变量名作为 bean 的 id 进行匹配 能够找到执行装配找不到装配失败 使用 Qualifier 注解根据 Qualifier 注解中指定的名称作为 bean 的id进行匹配 能够找到执行装配找不到装配失败 Controller(value tianDog) public class SoldierController {AutowiredQualifier(value maomiService222)// 根据面向接口编程思想使用接口类型引入Service组件private ISoldierService soldierService;佛系装配 给 Autowired 注解设置 required false 属性表示能装就装装不上就不装。但是实际开发时基本上所有需要装配组件的地方都是必须装配的用不上这个属性 Controller(value tianDog) public class SoldierController {// 给Autowired注解设置required false属性表示能装就装装不上就不装Autowired(required false)private ISoldierService soldierService;扩展JSR-250注解Resource 理解JSR系列注解 JSRJava Specification Requests是Java平台标准化进程中的一种技术规范而JSR注解是其中一部分重要的内容。按照JSR的分类以及注解语义的不同可以将JSR注解分为不同的系列主要有以下几个系列 JSR-175: 这个JSR是Java SE 5引入的是Java注解最早的规范化版本Java SE 5后的版本中都包含该JSR中定义的注解。主要包括以下几种标准注解 Deprecated: 标识一个程序元素如类、方法或字段已过时并且在将来的版本中可能会被删除。Override: 标识一个方法重写了父类中的方法。SuppressWarnings: 抑制编译时产生的警告消息。SafeVarargs: 标识一个有安全性警告的可变参数方法。FunctionalInterface: 标识一个接口只有一个抽象方法可以作为lambda表达式的目标。 JSR-250: 这个JSR主要用于在Java EE 5中定义一些支持注解。该JSR主要定义了一些用于进行对象管理的注解包括 Resource: 标识一个需要注入的资源是实现Java EE组件之间依赖关系的一种方式。PostConstruct: 标识一个方法作为初始化方法。PreDestroy: 标识一个方法作为销毁方法。Resource.AuthenticationType: 标识注入的资源的身份验证类型。Resource.AuthenticationType: 标识注入的资源的默认名称。 JSR-269: 这个JSR主要是Java SE 6中引入的一种支持编译时元数据处理的框架即使用注解来处理Java源文件。该JSR定义了一些可以用注解标记的注解处理器用于生成一些元数据常用的注解有 SupportedAnnotationTypes: 标识注解处理器所处理的注解类型。SupportedSourceVersion: 标识注解处理器支持的Java源码版本。 JSR-330: 该JSR主要为Java应用程序定义了一个依赖注入的标准即Java依赖注入标准javax.inject。在此规范中定义了多种注解包括 Named: 标识一个被依赖注入的组件的名称。Inject: 标识一个需要被注入的依赖组件。Singleton: 标识一个组件的生命周期只有一个唯一的实例。 JSR-250: 这个JSR主要是Java EE 5中定义一些支持注解。该JSR包含了一些支持注解可以用于对Java EE组件进行管理包括 RolesAllowed: 标识授权角色PermitAll: 标识一个活动无需进行身份验证。DenyAll: 标识不提供针对该方法的访问控制。DeclareRoles: 声明安全角色。 但是你要理解JSR是Java提供的技术规范也就是说他只是规定了注解和注解的含义JSR并不是直接提供特定的实现而是提供标准和指导方针由第三方框架Spring和库来实现和提供对应的功能。 JSR-250 Resource注解 Resource注解也可以完成属性注入。那它和Autowired注解有什么区别 Resource注解是JDK扩展包中的也就是说属于JDK的一部分。所以该注解是标准注解更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)Autowired注解是Spring框架自己的。Resource注解默认根据Bean名称装配未指定name时使用属性名作为name。通过name找不到的话会自动启动通过类型装配。Autowired注解默认根据类型装配如果想根据名称装配需要配合Qualifier注解一起用。Resource注解用在属性上、setter方法上。Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。 Resource注解属于JDK扩展包所以不在JDK当中需要额外引入以下依赖【高于JDK11或低于JDK8需要引入以下依赖】 dependencygroupIdjakarta.annotation/groupIdartifactIdjakarta.annotation-api/artifactIdversion2.1.1/version /dependencyResource使用 Controller public class XxxController {/*** 1. 如果没有指定name,先根据属性名查找IoC中组件xxxService* 2. 如果没有指定name,并且属性名没有对应的组件,会根据属性类型查找* 3. 可以指定name名称查找! Resource(nametest) Autowired Qualifier(valuetest)*/Resourceprivate XxxService xxxService;//Resource(name 指定beanName)//private XxxService xxxService;public void show(){System.out.println(XxxController.show);xxxService.show();} }此时相当于 AutowiredQualifier。 3.3.4 实验四 Bean属性赋值基本类型属性赋值 (DI) Value 通常用于注入外部化属性 声明外部配置 application.properties catalog.nameMovieCatalogxml引入外部配置 !-- 引入外部配置文件-- context:property-placeholder locationapplication.properties /Value注解读取配置 package com.atguigu.components;import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;/*** projectName: com.atguigu.components** description: 普通的组件*/ Component public class CommonComponent {/*** 情况1: ${key} 取外部配置key对应的值!* 情况2: ${key:defaultValue} 没有key,可以给与默认值*/Value(${catalog:hahaha})private String name;public String getName() {return name;}public void setName(String name) {this.name name;} } 3.5.基于配置类的方式管理 Bean 3.5.1 实验一配置类和扫描注解 xml注解方式 配置文件application.xml ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://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!-- 配置自动扫描的包 --!-- 1.包要精准,提高性能!2.会扫描指定的包和子包内容3.多个包可以使用,分割 例如: com.atguigu.controller,com.atguigu.service等--context:component-scan base-packagecom.atguigu.components/!-- 引入外部配置文件--context:property-placeholder locationapplication.properties / /beans测试创建IoC容器 // xml方式配置文件使用ClassPathXmlApplicationContext容器读取ApplicationContext applicationContext new ClassPathXmlApplicationContext(application.xml);配置类注解方式完全注解方式 配置类 使用 Configuration 注解将一个普通的类标记为 Spring 的配置类。 import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource;//标注当前类是配置类替代application.xml Configuration //使用注解读取外部配置替代 context:property-placeholder标签 PropertySource(classpath:application.properties) //使用ComponentScan注解,可以配置扫描包,替代context:component-scan标签 ComponentScan(basePackages {com.atguigu.components}) public class MyConfiguration {}测试创建IoC容器 // AnnotationConfigApplicationContext 根据配置类创建 IOC 容器对象 ApplicationContext iocContainerAnnotation new AnnotationConfigApplicationContext(MyConfiguration.class);可以使用 no-arg 构造函数实例化 AnnotationConfigApplicationContext 然后使用 register() 方法对其进行配置。此方法在以编程方式生成 AnnotationConfigApplicationContext 时特别有用。以下示例演示如何执行此操作 // AnnotationConfigApplicationContext-IOC容器对象 ApplicationContext iocContainerAnnotation new AnnotationConfigApplicationContext(); //外部设置配置类 iocContainerAnnotation.register(MyConfiguration.class); //刷新后方可生效 iocContainerAnnotation.refresh();总结 Configuration指定一个类为配置类可以添加配置注解替代配置xml文件 ComponentScan(basePackages {“包”,“包”}) 替代context:component-scan标签实现注解扫描 PropertySource(“classpath:配置文件地址”) 替代 context:property-placeholder标签 配合IoC/DI注解可以进行完整注解开发 3.5.2.实验二Bean定义组件 场景需求将Druid连接池对象存储到IoC容器 需求分析第三方jar包的类添加到ioc容器无法使用Component等相关注解因为源码jar包内容为只读模式 xml方式实现 ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://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!-- 引入外部属性文件 --context:property-placeholder locationclasspath:jdbc.properties/!-- 实验六 [重要]给bean的属性赋值引入外部属性文件 --bean iddruidDataSource classcom.alibaba.druid.pool.DruidDataSourceproperty nameurl value${jdbc.url}/property namedriverClassName value${jdbc.driver}/property nameusername value${jdbc.user}/property namepassword value${jdbc.password}//bean/beans配置类方式实现 Bean 注释用于指示方法实例化、配置和初始化要由 Spring IoC 容器管理的新对象。对于那些熟悉 Spring 的 beans/ XML 配置的人来说 Bean 注释与 bean/ 元素起着相同的作用。 //标注当前类是配置类替代application.xml Configuration //引入jdbc.properties文件 PropertySource({classpath:application.properties,classpath:jdbc.properties}) ComponentScan(basePackages {com.atguigu.components}) public class MyConfiguration {//如果第三方类进行IoC管理,无法直接使用Component相关注解//解决方案: xml方式可以使用bean标签//解决方案: 配置类方式,可以使用方法返回值Bean注解Beanpublic DataSource createDataSource(Value(${jdbc.user}) String username,Value(${jdbc.password})String password,Value(${jdbc.url})String url,Value(${jdbc.driver})String driverClassName){//使用Java代码实例化DruidDataSource dataSource new DruidDataSource();dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setUrl(url);dataSource.setDriverClassName(driverClassName);//返回结果即可return dataSource;} }注意方法的返回值类型为bean组件的类型或者它接口和父类的类型。而方法的名字与bean的id相同。方法体可以自定义。 3.5.4 实验三高级特性Bean注解细节 Bean生成BeanName问题 Bean注解源码 public interface Bean {//前两个注解可以指定Bean的标识AliasFor(name)String[] value() default {};AliasFor(value)String[] name() default {};//autowireCandidate 属性来指示该 Bean 是否候选用于自动装配。//autowireCandidate 属性默认值为 true表示该 Bean 是一个默认的装配目标//可被候选用于自动装配。如果将 autowireCandidate 属性设置为 false则说明该 Bean 不是默认的装配目标不会被候选用于自动装配。boolean autowireCandidate() default true;//指定初始化方法String initMethod() default ;//指定销毁方法String destroyMethod() default (inferred); } 指定Bean的名称 Configuration public class AppConfig {Bean(myThing) //指定名称public Thing thing() {return new Thing();} }Bean 注释注释方法。使用此方法在指定为方法返回值的类型的 ApplicationContext 中注册 Bean 定义。缺省情况下Bean 名称与方法名称相同。下面的示例演示 Bean 方法声明 Configuration public class AppConfig {Beanpublic TransferServiceImpl transferService() {return new TransferServiceImpl();} }前面的配置完全等同于下面的Spring XML beansbean idtransferService classcom.acme.TransferServiceImpl/ /beansBean 初始化和销毁方法指定 Bean 注解支持指定任意初始化和销毁回调方法非常类似于 Spring XML 在 bean 元素上的 init-method 和 destroy-method 属性如以下示例所示 public class BeanOne {public void init() {// initialization logic} }public class BeanTwo {public void cleanup() {// destruction logic} }Configuration public class AppConfig {Bean(initMethod init)public BeanOne beanOne() {return new BeanOne();}Bean(destroyMethod cleanup)public BeanTwo beanTwo() {return new BeanTwo();} }Bean Scope作用域 可以指定使用 Bean 注释定义的 bean 应具有特定范围。您可以使用在 Bean 作用域部分中指定的任何标准作用域。 默认作用域为 singleton 但您可以使用 Scope 注释覆盖此范围如以下示例所示 Configuration public class MyConfiguration {BeanScope(prototype)public Encryptor encryptor() {// ...} }Bean方法之间依赖 准备组件 public class HappyMachine {private String machineName;public String getMachineName() {return machineName;}public void setMachineName(String machineName) {this.machineName machineName;} }public class HappyComponent {//引用新组件private HappyMachine happyMachine;public HappyMachine getHappyMachine() {return happyMachine;}public void setHappyMachine(HappyMachine happyMachine) {this.happyMachine happyMachine;}public void doWork() {System.out.println(HappyComponent.doWork);}}Java配置类实现 方案1 直接调用方法返回 Bean 实例在一个 Bean 方法中直接调用其他 Bean 方法来获取 Bean 实例虽然是方法调用也是通过IoC容器获取对应的Bean例如 Configuration public class JavaConfig {Beanpublic HappyMachine happyMachine(){return new HappyMachine();}Beanpublic HappyComponent happyComponent(){HappyComponent happyComponent new HappyComponent();//直接调用方法即可! happyComponent.setHappyMachine(happyMachine());return happyComponent;}}方案2 参数引用法通过方法参数传递 Bean 实例的引用来解决 Bean 实例之间的依赖关系例如 package com.atguigu.config;import com.atguigu.ioc.HappyComponent; import com.atguigu.ioc.HappyMachine; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;/*** projectName: com.atguigu.config* description: 配置HappyComponent和HappyMachine关系*/Configuration public class JavaConfig {Beanpublic HappyMachine happyMachine(){return new HappyMachine();}/*** 可以直接在形参列表接收IoC容器中的Bean!* 情况1: 直接指定类型即可* 情况2: 如果有多个bean,(HappyMachine 名称 ) 形参名称等于要指定的bean名称!* 例如:* Bean* public Foo foo1(){* return new Foo();* }* Bean* public Foo foo2(){* return new Foo()* }* Bean* public Component component(Foo foo1 / foo2 通过此处指定引入的bean)*/Beanpublic HappyComponent happyComponent(HappyMachine happyMachine){HappyComponent happyComponent new HappyComponent();//赋值happyComponent.setHappyMachine(happyMachine);return happyComponent;}}无论哪种方法都有两个要求 1.被bean注解。 2.不能存在多个满足条件的类型或者不存在该类型。 3.5.5 实验四高级特性Import扩展 Import 注释允许从另一个配置类加载 Bean 定义如以下示例所示 Configuration public class ConfigA {Beanpublic A a() {return new A();} }Configuration Import(ConfigA.class) public class ConfigB {Beanpublic B b() {return new B();} }现在在实例化上下文时不需要同时指定 ConfigA.class 和 ConfigB.class 只需显式提供 ConfigB 如以下示例所示 public static void main(String[] args) {ApplicationContext ctx new AnnotationConfigApplicationContext(ConfigB.class);// now both beans A and B will be available...A a ctx.getBean(A.class);B b ctx.getBean(B.class); }此方法简化了容器实例化因为只需要处理一个类而不是要求您在构造期间记住可能大量的 Configuration 类。 3.6 三种配置方式总结 3.6.1 XML方式配置总结 所有内容写到xml格式配置文件中声明bean通过bean标签bean标签包含基本信息id,class和属性信息 property name value / ref引入外部的properties文件可以通过context:property-placeholderIoC具体容器实现选择ClassPathXmlApplicationContext对象 3.6.2 XML注解方式配置总结 注解负责标记IoC的类和进行属性装配xml文件依然需要需要通过context:component-scan标签指定注解范围标记IoC注解Component,Service,Controller,Repository标记DI注解Autowired Qualifier Resource ValueIoC具体容器实现选择ClassPathXmlApplicationContext对象 3.6.3 完全注解方式配置总结 完全注解方式指的是去掉xml文件使用配置类 注解实现xml文件替换成使用Configuration注解标记的类标记IoC注解Component,Service,Controller,Repository标记DI注解Autowired Qualifier Resource Valuecontext:component-scan标签指定注解范围使用ComponentScan(basePackages {“com.atguigu.components”})替代context:property-placeholder引入外部配置文件使用PropertySource({“classpath:application.properties”,“classpath:jdbc.properties”})替代bean 标签使用Bean注解和方法实现IoC具体容器实现选择AnnotationConfigApplicationContext对象 3.7 整合Spring5-Test5搭建测试环境 整合测试环境作用 好处1不需要自己创建IOC容器对象了 好处2任何需要的bean都可以在测试类中直接享受自动装配 导入相关依赖 !--junit5测试-- dependencygroupIdorg.junit.jupiter/groupIdartifactIdjunit-jupiter-api/artifactIdversion5.3.1/version /dependency dependencygroupIdorg.springframework/groupIdartifactIdspring-test/artifactIdversion6.0.6/versionscopetest/scope /dependency3. 整合测试注解使用//SpringJUnitConfig(locations {classpath:spring-context.xml}) //指定配置文件xml SpringJUnitConfig(value {BeanConfig.class}) //指定配置类 public class Junit5IntegrationTest {Autowiredprivate User user;Testpublic void testJunit5() {System.out.println(user);} }专门服务单元测试的一种方法。 四Spring AOP面向切面编程 4.1 场景设定和问题复现 当我们写需要大量相同操作且是一些不重要的代码时 代码缺陷 对核心业务功能有干扰导致程序员在开发核心业务功能时分散了精力附加功能代码重复分散在各个业务功能方法中冗余且不方便统一维护 解决思路 核心就是解耦。我们需要把附加功能从业务功能代码中抽取出来。 将重复的代码统一提取并且[[动态插入]]到每个业务方法 技术困难 解决问题的困难提取重复附加功能代码到一个类中可以实现 但是如何将代码插入到各个方法中我们不会我们需要引用新技术 4.2 解决技术代理模式 代理模式 二十三种设计模式中的一种属于结构型模式。它的作用就是通过提供一个代理类让我们在调用目标方法的时候不再是直接对目标方法进行调用而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法减少对目标方法的调用和打扰同时让附加功能能够集中在一起也有利于统一维护。 无代理场景 有代理场景 ​ 相关术语 - 代理将非核心逻辑剥离出来以后封装这些非核心逻辑的类、对象、方法。(中介)- 动词指做代理这个动作或这项工作- 名词扮演代理这个角色的类、对象、方法- 目标**被代理**“套用”了核心逻辑代码的类、对象、方法。(房东)​ 代理在开发中实现的方式具体有两种静态代理[动态代理技术] 静态代理 创建接口 /*** - * / 运算的标准接口!*/ public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);}接口实现 package com.atguigu.proxy;/*** 实现计算接口,单纯添加 - * / 实现! 掺杂其他功能!*/ public class CalculatorPureImpl implements Calculator {Overridepublic int add(int i, int j) {int result i j;return result;}Overridepublic int sub(int i, int j) {int result i - j;return result;}Overridepublic int mul(int i, int j) {int result i * j;return result;}Overridepublic int div(int i, int j) {int result i / j;return result;} }主动创建代理类 public class CalculatorStaticProxy implements Calculator {// 将被代理的目标对象声明为成员变量private Calculator target;public CalculatorStaticProxy(Calculator target) {this.target target;}Overridepublic int add(int i, int j) {// 附加功能由代理类中的代理方法来实现System.out.println(参数是 i , j);// 通过目标对象来实现核心业务逻辑int addResult target.add(i, j);System.out.println(方法内部 result addResult);return addResult;}……​ 测试类 public class TestCalculator { public static void main(String[] args) { // 创建一个目标对象被代理的对象 Calculator target new CalculatorPureImpl(); // 创建一个代理对象将目标对象传递给它 Calculator proxy new CalculatorStaticProxy(target); // 通过代理对象调用add方法这将触发代理类中的附加功能并最终调用目标对象的add方法 int result proxy.add(5, 3); System.out.println(最终结果是 result); // 输出最终结果预期为853 } }静态代理确实实现了解耦但是由于代码都写死了完全不具备任何的灵活性。就拿日志功能来说将来其他地方也需要附加日志那还得再声明更多个静态代理类那就产生了大量重复的代码日志功能还是分散的没有统一管理。 ​ 提出进一步的需求将日志功能集中到一个代理类中将来有任何日志需求都通过这一个代理类来实现。这就需要使用动态代理技术了。 动态代理 动态代理技术分类 JDK动态代理JDK原生的实现方式需要被代理的目标类必须实现接口他会根据目标类的接口动态生成一个代理对象代理对象和目标对象有相同的接口拜把子cglib通过继承被代理的目标类实现代理所以不需要目标类实现接口认干爹 JDK动态代理技术实现了解 代理工程基于jdk代理技术生成代理对象 public class ProxyFactory {private Object target;public ProxyFactory(Object target) {this.target target;}public Object getProxy(){/*** newProxyInstance()创建一个代理实例* 其中有三个参数* 1、classLoader加载动态生成的代理类的类加载器* 2、interfaces目标对象实现的所有接口的class对象所组成的数组* 3、invocationHandler设置代理对象实现目标对象方法的过程即代理类中如何重写接口中的抽象方法*/ClassLoader classLoader target.getClass().getClassLoader();Class?[] interfaces target.getClass().getInterfaces();InvocationHandler invocationHandler new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** proxy代理对象* method代理对象需要实现的方法即其中需要重写的方法* argsmethod所对应方法的参数*/Object result null;try {System.out.println([动态代理][日志] method.getName()参数 Arrays.toString(args));result method.invoke(target, args);System.out.println([动态代理][日志] method.getName()结果 result);} catch (Exception e) {e.printStackTrace();System.out.println([动态代理][日志] method.getName()异常e.getMessage());} finally {System.out.println([动态代理][日志] method.getName()方法执行完毕);}return result;}};return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);} }​ 测试代码 Test public void testDynamicProxy(){ProxyFactory factory new ProxyFactory(new CalculatorLogImpl());Calculator proxy (Calculator) factory.getProxy();proxy.div(1,0);//proxy.div(1,1); }代理总结 代理方式可以解决附加功能代码干扰核心代码和不方便统一维护的问题 他主要是将附加功能代码提取到代理中执行不干扰目标核心代码 但是我们也发现无论使用静态代理和动态代理(jdk,cglib)程序员的工作都比较繁琐 需要自己编写代理工厂等 但是提前剧透我们在实际开发中不需要编写代理代码我们可以使用[Spring AOP]框架 他会简化动态代理的实现 4.3 面向切面编程思维AOP 面向切面编程思想AOP AOPAspect Oriented Programming面向切面编程 AOP可以说是OOPObject Oriented Programming面向对象编程的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系但并不适合定义横向的关系例如日志功能。日志代码往往横向地散布在所有对象层次中而与它对应的对象的核心功能毫无关系对于其他类型的代码如安全性、异常处理和透明的持续性也都是如此这种散布在各处的无关的代码被称为横切cross cutting在OOP设计中它导致了大量代码的重复而不利于各个模块的重用。 AOP技术恰恰相反它利用一种称为横切的技术剖解开封装的对象内部并将那些影响了多个类的公共行为封装到一个可重用模块并将其命名为Aspect即切面。所谓切面简单说就是那些与业务无关却为业务模块所共同调用的逻辑或责任封装起来便于减少系统的重复代码降低模块之间的耦合度并有利于未来的可操作性和可维护性。 使用AOP可以在不修改原来代码的基础上添加新功能。 AOP思想主要的应用场景 AOP面向切面编程是一种编程范式它通过将通用的横切关注点如日志、事务、权限控制等与业务逻辑分离使得代码更加清晰、简洁、易于维护。AOP可以应用于各种场景以下是一些常见的AOP应用场景 日志记录在系统中记录日志是非常重要的可以使用AOP来实现日志记录的功能可以在方法执行前、执行后或异常抛出时记录日志。事务处理在数据库操作中使用事务可以保证数据的一致性可以使用AOP来实现事务处理的功能可以在方法开始前开启事务在方法执行完毕后提交或回滚事务。安全控制在系统中包含某些需要安全控制的操作如登录、修改密码、授权等可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断如果用户没有权限则抛出异常或转向到错误页面以防止未经授权的访问。性能监控在系统运行过程中有时需要对某些方法的性能进行监控以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能可以在方法执行前记录时间戳在方法执行完毕后计算方法执行时间并输出到日志中。异常处理系统中可能出现各种异常情况如空指针异常、数据库连接异常等可以使用AOP来实现异常处理的功能在方法执行过程中如果出现异常则进行异常处理如记录日志、发送邮件等。缓存控制在系统中有些数据可以缓存起来以提高访问速度可以使用AOP来实现缓存控制的功能可以在方法执行前查询缓存中是否有数据如果有则返回否则执行方法并将方法返回值存入缓存中。动态代理AOP的实现方式之一是通过动态代理可以代理某个类的所有方法用于实现各种功能。 综上所述AOP可以应用于各种场景它的作用是将通用的横切关注点与业务逻辑分离使得代码更加清晰、简洁、易于维护。 AOP术语名词介绍 1-横切关注点 从每个方法中抽取出来的同一类非核心业务。在同一个项目中我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。 这个概念不是语法层面天然存在的而是根据附加功能的逻辑上的需要有十个附加功能就有十个横切关注点。 AOP把软件系统分为两个部分核心关注点和横切关注点。业务处理的主要流程是核心关注点与之关系不大的部分是横切关注点。横切关注点的一个特点是他们经常发生在核心关注点的多处而各处基本相似比如权限认证、日志、事务、异常等。AOP的作用在于分离系统中的各种关注点将核心关注点和横切关注点分离开来。 2-通知(增强) 每一个横切关注点上要做的事情都需要写一个方法来实现这样的方法就叫通知方法。 前置通知在被代理的目标方法前执行返回通知在被代理的目标方法成功结束后执行寿终正寝异常通知在被代理的目标方法异常结束后执行死于非命后置通知在被代理的目标方法最终结束后执行盖棺定论环绕通知使用try…catch…finally结构围绕整个被代理的目标方法包括上面四种通知对应的所有位置 ​ 3-连接点 joinpoint ​ 这也是一个纯逻辑概念不是语法定义的。 ​ 指那些被拦截到的点。在 Spring 中可以被动态代理拦截目标类的方法 ​ ​ 4-切入点 pointcut ​ 定位连接点的方式或者可以理解成被选中的连接点 ​ 是一个表达式比如execution(* com.spring.service.impl..(…))。符合条件的每个方法都是一个具体的连接点。 ​ 5-切面 aspect ​ 切入点和通知的结合。是一个类。 ​ ​ 6-目标 target ​ 被代理的目标对象。 ​ 7-代理 proxy ​ 向目标对象应用通知之后创建的代理对象。 ​ 8-织入 weave ​ 指把通知应用到目标上生成代理对象的过程。可以在编译期织入也可以在运行期织入Spring采用后者。 4.4 Spring AOP框架介绍和关系梳理 AOP一种区别于OOP的编程思维用来完善和解决OOP的非核心代码冗余和不方便统一维护问题代理技术动态代理|静态代理是实现AOP思维编程的具体技术但是自己使用动态代理实现代码比较繁琐Spring AOP框架基于AOP编程思维封装动态代理技术简化动态代理技术实现的框架SpringAOP内部帮助我们实现动态代理我们只需写少量的配置指定生效范围即可,即可完成面向切面思维编程的实现 4.5 Spring AOP基于注解方式实现和细节 4.5.1 Spring AOP底层技术组成 动态代理InvocationHandlerJDK原生的实现方式需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口兄弟两个拜把子模式。cglib通过继承被代理的目标类认干爹模式实现代理所以不需要目标类实现接口。AspectJ早期的AOP实现的框架SpringAOP借用了AspectJ中的AOP注解。 4.5.2 初步实现 1. 加入依赖!-- spring-aspects会帮我们传递过来aspectjweaver -- dependencygroupIdorg.springframework/groupIdartifactIdspring-aop/artifactIdversion6.0.6/version /dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion6.0.6/version /dependency2. 准备接口public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);}3. 纯净实现类package com.atguigu.proxy; /*** 实现计算接口,单纯添加 - * / 实现! 不掺杂其他功能!*/ Component public class CalculatorPureImpl implements Calculator {Overridepublic int add(int i, int j) {int result i j;return result;}Overridepublic int sub(int i, int j) {int result i - j;return result;}Overridepublic int mul(int i, int j) {int result i * j;return result;}Overridepublic int div(int i, int j) {int result i / j;return result;} }声明切面类 最后还要配置支持别忘了扫描切面类。 package com.atguigu.advice;import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;// Aspect表示这个类是一个切面类 Aspect // Component注解保证这个切面类能够放入IOC容器 Component public class LogAspect {// Before注解声明当前方法是前置通知方法// value属性指定切入点表达式由切入点表达式控制当前通知方法要作用在哪一个目标方法上Before(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int)))public void printLogBeforeCore() {System.out.println([AOP前置通知] 方法开始了);}AfterReturning(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int)))public void printLogAfterSuccess() {System.out.println([AOP返回通知] 方法成功返回了);}AfterThrowing(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int)))public void printLogAfterException() {System.out.println([AOP异常通知] 方法抛异常了);}After(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int)))public void printLogFinallyEnd() {System.out.println([AOP后置通知] 方法最终结束了);}}5. 开启aspectj注解支持1. xml方式?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://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!-- 进行包扫描--context:component-scan base-packagecom.atguigu /!-- 开启aspectj框架注解支持--aop:aspectj-autoproxy / /beans​ 2. 配置类方式 Configuration ComponentScan(basePackages com.atguigu) //作用等于 aop:aspectj-autoproxy / 配置类上开启 Aspectj注解支持! EnableAspectJAutoProxy public class MyConfig { }6. 测试效果//SpringJUnitConfig(locations classpath:spring-aop.xml) SpringJUnitConfig(value {MyConfig.class}) public class AopTest {Autowiredprivate Calculator calculator;Testpublic void testCalculator(){calculator.add(1,1);} }​ 输出结果 com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 com.atguigu.test.AopTest,testCalculator [AOP前置通知] 方法开始了 [AOP返回通知] 方法成功返回了 [AOP后置通知] 方法最终结束了注意在aop的JDK代理中要接口接值而不要实现类接值。 基于接口的代理Spring AOP默认使用基于接口的代理。这意味着它只能为接口创建代理而不能为类创建代理。因此当你试图使用实现类作为类型时Spring AOP无法为它创建代理从而导致报错。 JDK动态代理与CGLIBSpring AOP支持两种代理机制JDK动态代理和CGLIB。JDK动态代理只能为接口创建代理而CGLIB可以为类创建代理。但是Spring AOP默认使用JDK动态代理除非你显式配置它使用CGLIB。 4.5.3 获取通知细节信息 JointPoint接口 需要获取方法签名、传入的实参等信息时可以在通知方法声明JoinPoint类型的形参。 要点1JoinPoint 接口通过 getSignature() 方法获取目标方法的签名方法声明时的完整信息要点2通过目标方法签名对象获取方法名要点3通过 JoinPoint 对象获取外界调用目标方法时传入的实参列表组成的数组 // Before注解标记前置通知方法 // value属性切入点表达式告诉Spring当前通知方法要套用到哪个目标方法上 // 在前置通知方法形参位置声明一个JoinPoint类型的参数Spring就会将这个对象传入 // 根据JoinPoint对象就可以获取目标方法名称、实际参数列表 Before(value execution(public int com.atguigu.aop.api.Calculator.add(int,int))) public void printLogBeforeCore(JoinPoint joinPoint) {// 1.通过JoinPoint对象获取目标方法签名对象// 方法的签名一个方法的全部声明信息Signature signature joinPoint.getSignature();// 2.通过方法的签名对象获取目标方法的详细信息String methodName signature.getName();System.out.println(methodName methodName);int modifiers signature.getModifiers();System.out.println(modifiers modifiers);String declaringTypeName signature.getDeclaringTypeName();System.out.println(declaringTypeName declaringTypeName);// 3.通过JoinPoint对象获取外界调用目标方法时传入的实参列表Object[] args joinPoint.getArgs();// 4.由于数组直接打印看不到具体数据所以转换为List集合ListObject argList Arrays.asList(args);System.out.println([AOP前置通知] methodName 方法开始了参数列表 argList); }方法返回值 在返回通知中通过**AfterReturning**注解的returning属性获取目标方法的返回值 // AfterReturning注解标记返回通知方法 // 在返回通知中获取目标方法返回值分两步 // 第一步在AfterReturning注解中通过returning属性设置一个名称 // 第二步使用returning属性设置的名称在通知方法中声明一个对应的形参 AfterReturning(value execution(public int com.atguigu.aop.api.Calculator.add(int,int)),returning targetMethodReturnValue ) public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {String methodName joinPoint.getSignature().getName();System.out.println([AOP返回通知] methodName方法成功结束了返回值是 targetMethodReturnValue); }异常对象捕捉 在异常通知中通过AfterThrowing注解的throwing属性获取目标方法抛出的异常对象 // AfterThrowing注解标记异常通知方法 // 在异常通知中获取目标方法抛出的异常分两步 // 第一步在AfterThrowing注解中声明一个throwing属性设定形参名称 // 第二步使用throwing属性指定的名称在通知方法声明形参Spring会将目标方法抛出的异常对象从这里传给我们 AfterThrowing(value execution(public int com.atguigu.aop.api.Calculator.add(int,int)),throwing targetMethodException ) public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {String methodName joinPoint.getSignature().getName();System.out.println([AOP异常通知] methodName方法抛异常了异常类型是 targetMethodException.getClass().getName()); }4.5.4 切点表达式语法 切点表达式作用 AOP切点表达式Pointcut Expression是一种用于指定切点的语言它可以通过定义匹配规则来选择需要被切入的目标对象。 ​ 切点表达式语法 切点表达式总结 语法细节 第一位execution( ) 固定开头第二位方法访问修饰符 public private 直接描述对应修饰符即可​ - 第三位方法返回值 int String void 直接描述返回值类型注意 特殊情况 不考虑 访问修饰符和返回值 execution(* * ) 这是错误语法 execution(*) 你只要考虑返回值 或者 不考虑访问修饰符 相当于全部不考虑了 ​ - 第四位指定包的地址 固定的包: com.atguigu.api | service | dao 单层的任意命名: com.atguigu.* com.atguigu.api com.atguigu.dao * 任意一层的任意命名 任意层任意命名: com… com.atguigu.api.erdaye com.a.a.a.a.a.a.a …任意层,任意命名 用在包上! 注意: …不能用作包开头 public int … 错误语法 com… 找到任何包下: *… ​ - 第五位指定类名称 固定名称: UserService 任意类名: * 部分任意: com…service.impl.*Impl 任意包任意类: … ​ - 第六位指定方法名称 语法和类名一致 任意访问修饰符,任意类的任意方法: * ….* ​ - 第七位方法参数 第七位: 方法的参数描述 具体值: (String,int) ! (int,String) 没有参数 () 模糊值: 任意参数 有 或者 没有 (…) …任意参数的意识 部分具体和模糊: 第一个参数是字符串的方法 (String…) 最后一个参数是字符串 (…String) 字符串开头,int结尾 (String…int) 包含int类型(…int…) 切点表达式案例 1.查询某包某类下访问修饰符是公有返回值是int的全部方法 2.查询某包下类中第一个参数是String的方法 3.查询全部包下无参数的方法 4.查询com包下以int参数类型结尾的方法 5.查询指定包下Service开头类的私有返回值int的无参数方法 4.5.5 重用提取切点表达式 1. 重用切点表达式优点// Before注解声明当前方法是前置通知方法 // value属性指定切入点表达式由切入点表达式控制当前通知方法要作用在哪一个目标方法上 Before(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))) public void printLogBeforeCore() {System.out.println([AOP前置通知] 方法开始了); }AfterReturning(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))) public void printLogAfterSuccess() {System.out.println([AOP返回通知] 方法成功返回了); }AfterThrowing(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))) public void printLogAfterException() {System.out.println([AOP异常通知] 方法抛异常了); }After(value execution(public int com.atguigu.proxy.CalculatorPureImpl.add(int,int))) public void printLogFinallyEnd() {System.out.println([AOP后置通知] 方法最终结束了); }​ 上面案例是我们之前编写切点表达式的方式发现 所有增强方法的切点表达式相同 ​ 出现了冗余如果需要切换也不方便统一维护 ​ 我们可以将切点提取在增强上进行引用即可 同一类内部引用 提取 // 切入点表达式重用 Pointcut(execution(public int com.atguigu.aop.api.Calculator.add(int,int)))) public void declarPointCut() {}​ 注意提取切点注解使用Pointcut(切点表达式) 需要添加到一个无参数无返回值方法上即可 引用value属性为刚才无参数无返回值方法的方法名 Before(value declarPointCut()) public void printLogBeforeCoreOperation(JoinPoint joinPoint) {不同类中引用 不同类在引用切点只需要添加类的全限定符方法名即可 Before(value com.atguigu.spring.aop.aspect.LogAspect.declarPointCut()) public Object roundAdvice(ProceedingJoinPoint joinPoint) {切点统一管理 建议将切点表达式统一存储到一个类中进行集中管理和维护 Component public class AtguiguPointCut {Pointcut(value execution(public int *..Calculator.sub(int,int)))public void atguiguGlobalPointCut(){}Pointcut(value execution(public int *..Calculator.add(int,int)))public void atguiguSecondPointCut(){}Pointcut(value execution(* *..*Service.*(..)))public void transactionPointCut(){} }4.5.6 环绕通知 环绕通知对应整个 try…catch…finally 结构包括前面四种通知的所有功能。 // 使用Around注解标明环绕通知方法 Around(value com.atguigu.aop.aspect.AtguiguPointCut.transactionPointCut()) public Object manageTransaction(// 通过在通知方法形参位置声明ProceedingJoinPoint类型的形参// Spring会将这个类型的对象传给我们ProceedingJoinPoint joinPoint) {// 通过ProceedingJoinPoint对象获取外界调用目标方法时传入的实参数组Object[] args joinPoint.getArgs();// 通过ProceedingJoinPoint对象获取目标方法的签名对象Signature signature joinPoint.getSignature();// 通过签名对象获取目标方法的方法名String methodName signature.getName();// 声明变量用来存储目标方法的返回值Object targetMethodReturnValue null;try {// 在目标方法执行前开启事务模拟log.debug([AOP 环绕通知] 开启事务方法名 methodName 参数列表 Arrays.asList(args));// 过ProceedingJoinPoint对象调用目标方法// 目标方法的返回值一定要返回给外界调用者targetMethodReturnValue joinPoint.proceed(args);// 在目标方法成功返回后提交事务模拟log.debug([AOP 环绕通知] 提交事务方法名 methodName 方法返回值 targetMethodReturnValue);}catch (Throwable e){// 在目标方法抛异常后回滚事务模拟log.debug([AOP 环绕通知] 回滚事务方法名 methodName 异常 e.getClass().getName());}finally {// 在目标方法最终结束后释放数据库连接log.debug([AOP 环绕通知] 释放数据库连接方法名 methodName);}return targetMethodReturnValue; }4.5.7 切面优先级设置 相同目标方法上同时存在多个切面时切面的优先级控制切面的内外嵌套顺序。 优先级高的切面外面优先级低的切面里面 使用 Order 注解可以控制切面的优先级 Order(较小的数)优先级高Order(较大的数)优先级低 实际意义 实际开发时如果有多个切面嵌套的情况要慎重考虑。例如如果事务切面优先级高那么在缓存中命中数据的情况下事务切面的操作都浪费了。 ​ 此时应该将缓存切面的优先级提高在事务操作之前先检查缓存中是否存在目标数据。 4.5.8 CGLib动态代理生效 在目标类没有实现任何接口的情况下Spring会自动使用cglib技术实现代理。为了证明这一点我们做下面的测试 Service public class EmployeeService {public void getEmpList() {System.out.print(方法内部 com.atguigu.aop.imp.EmployeeService.getEmpList);} }测试 Autowiredprivate EmployeeService employeeService;Testpublic void testNoInterfaceProxy() {employeeService.getEmpList();}注意这里是实现类接值。 没有接口 有接口 使用总结 ​ a. 如果目标类有接口,选择使用jdk动态代理 ​ b. 如果目标类没有接口,选择cglib动态代理 ​ c. 如果有接口,接口接值 ​ d. 如果没有接口,类进行接值 4.5.9 注解实现小结 4.6 Spring AOP基于XML方式实现(了解) 准备工作 加入依赖 和基于注解的 AOP 时一样。 准备代码 把测试基于注解功能时的Java类复制到新module中去除所有注解。 配置Spring配置文件 !-- 配置目标类的bean -- bean idcalculatorPure classcom.atguigu.aop.imp.CalculatorPureImpl/!-- 配置切面类的bean -- bean idlogAspect classcom.atguigu.aop.aspect.LogAspect/!-- 配置AOP -- aop:config!-- 配置切入点表达式 --aop:pointcut idlogPointCut expressionexecution(* *..*.*(..))/!-- aop:aspect标签配置切面 --!-- ref属性关联切面类的bean --aop:aspect reflogAspect!-- aop:before标签配置前置通知 --!-- method属性指定前置通知的方法名 --!-- pointcut-ref属性引用切入点表达式 --aop:before methodprintLogBeforeCore pointcut-reflogPointCut/!-- aop:after-returning标签配置返回通知 --!-- returning属性指定通知方法中用来接收目标方法返回值的参数名 --aop:after-returningmethodprintLogAfterCoreSuccesspointcut-reflogPointCutreturningtargetMethodReturnValue/!-- aop:after-throwing标签配置异常通知 --!-- throwing属性指定通知方法中用来接收目标方法抛出异常的异常对象的参数名 --aop:after-throwingmethodprintLogAfterCoreExceptionpointcut-reflogPointCutthrowingtargetMethodException/!-- aop:after标签配置后置通知 --aop:after methodprintLogCoreFinallyEnd pointcut-reflogPointCut/!-- aop:around标签配置环绕通知 --!--aop:around method…… pointcut-reflogPointCut/--/aop:aspect/aop:config 测试 SpringJUnitConfig(locations classpath:spring-aop.xml) public class AopTest {Autowiredprivate Calculator calculator;Testpublic void testCalculator(){System.out.println(calculator);calculator.add(1,1);} }4.7 Spring AOP对获取Bean的影响理解 4.7.1 根据类型装配 bean 情景一 bean 对应的类没有实现任何接口 根据 bean 本身的类型获取 bean 测试IOC容器中同类型的 bean 只有一个 正常获取到 IOC 容器中的那个 bean 对象 测试IOC 容器中同类型的 bean 有多个 会抛出 NoUniqueBeanDefinitionException 异常表示 IOC 容器中这个类型的 bean 有多个 情景二 bean 对应的类实现了接口这个接口也只有这一个实现类 测试根据接口类型获取 bean测试根据类获取 bean结论上面两种情况其实都能够正常获取到 bean而且是同一个对象 情景三 声明一个接口 接口有多个实现类 接口所有实现类都放入 IOC 容器 测试根据接口类型获取 bean 会抛出 NoUniqueBeanDefinitionException 异常表示 IOC 容器中这个类型的 bean 有多个 测试根据类获取bean 正常 情景四 声明一个接口 接口有一个实现类 创建一个切面类对上面接口的实现类应用通知 测试根据接口类型获取bean 正常 测试根据类获取bean 无法获取 原因分析 应用了切面后真正放在IOC容器中的是代理类的对象 目标类并没有被放到IOC容器中所以根据目标类的类型从IOC容器中是找不到的 情景五 声明一个类 创建一个切面类对上面的类应用通知 测试根据类获取 bean能获取到 debug查看实际类型 五Spring 声明式事务 5.1 声明式事务概念 5.1.1 编程式事务 编程式事务是指手动编写程序来管理事务即通过编写代码的方式直接控制事务的提交和回滚。在 Java 中通常使用事务管理器(如 Spring 中的 PlatformTransactionManager)来实现编程式事务。 编程式事务的主要优点是灵活性高可以按照自己的需求来控制事务的粒度、模式等等。但是编写大量的事务控制代码容易出现问题对代码的可读性和可维护性有一定影响。 Connection conn ...;try {// 开启事务关闭事务的自动提交conn.setAutoCommit(false);// 核心操作// 业务代码// 提交事务conn.commit();}catch(Exception e){// 回滚事务conn.rollBack();}finally{// 释放数据库连接conn.close();}编程式的实现方式存在缺陷 细节没有被屏蔽具体操作过程中所有细节都需要程序员自己来完成比较繁琐。代码复用性不高如果没有有效抽取出来每次实现功能都需要自己编写代码代码就没有得到复用。 5.1.2 声明式事务 声明式事务是指使用注解或 XML 配置的方式来控制事务的提交和回滚。 开发者只需要添加配置即可 具体事务的实现由第三方框架实现避免我们直接进行事务操作 使用声明式事务可以将事务的控制和业务逻辑分离开来提高代码的可读性和可维护性。 区别 编程式事务需要手动编写代码来管理事务而声明式事务可以通过配置文件或注解来控制事务。 5.1.3 Spring事务管理器 Spring声明式事务对应依赖 spring-tx: 包含声明式事务实现的基本规范事务管理器规范接口和事务增强等等spring-jdbc: 包含DataSource方式事务管理器实现类DataSourceTransactionManagerspring-orm: 包含其他持久层框架的事务管理器实现类例如Hibernate/Jpa等 Spring声明式事务对应事务管理器接口 我们现在要使用的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager将来整合 JDBC方式、JdbcTemplate方式、Mybatis方式的事务实现 DataSourceTransactionManager类中的主要方法 doBegin()开启事务doSuspend()挂起事务doResume()恢复挂起的事务doCommit()提交事务doRollback()回滚事务 5.2 基于注解的声明式事务 5.2.1 准备工作 1. 准备项目dependencies!--spring context依赖--!--当你引入Spring Context依赖之后表示将Spring的基础依赖引入了--dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion6.0.6/version/dependency!--junit5测试--dependencygroupIdorg.junit.jupiter/groupIdartifactIdjunit-jupiter-api/artifactIdversion5.3.1/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-test/artifactIdversion6.0.6/versionscopetest/scope/dependencydependencygroupIdjakarta.annotation/groupIdartifactIdjakarta.annotation-api/artifactIdversion2.1.1/version/dependency!-- 数据库驱动 和 连接池--dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.25/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.2.8/version/dependency!-- spring-jdbc --dependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion6.0.6/version/dependency!-- 声明式事务依赖--dependencygroupIdorg.springframework/groupIdartifactIdspring-tx/artifactIdversion6.0.6/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-aop/artifactIdversion6.0.6/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion6.0.6/version/dependency /dependencies外部配置文件 jdbc.properties atguigu.urljdbc:mysql://localhost:3306/studb atguigu.drivercom.mysql.cj.jdbc.Driver atguigu.usernameroot atguigu.passwordrootspring配置文件 Configuration ComponentScan(com.atguigu) PropertySource(classpath:jdbc.properties) public class JavaConfig {Value(${atguigu.driver})private String driver;Value(${atguigu.url})private String url;Value(${atguigu.username})private String username;Value(${atguigu.password})private String password;//druid连接池Beanpublic DataSource dataSource(){DruidDataSource dataSource new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}Bean//jdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}} 准备dao/service层 dao Repository public class StudentDao {Autowiredprivate JdbcTemplate jdbcTemplate;public void updateNameById(String name,Integer id){String sql update students set name ? where id ? ;;int rows jdbcTemplate.update(sql, name, id);}public void updateAgeById(Integer age,Integer id){String sql update students set age ? where id ? ;;jdbcTemplate.update(sql,age,id);} } ​ service Service public class StudentService {Autowiredprivate StudentDao studentDao;public void changeInfo(){studentDao.updateAgeById(100,1);System.out.println(-----------);studentDao.updateNameById(test1,1);} } 测试环境搭建 /*** projectName: com.atguigu.test** description:*/ SpringJUnitConfig(JavaConfig.class) public class TxTest {Autowiredprivate StudentService studentService;Testpublic void testTx(){studentService.changeInfo();} } 5.2.2 基本事务控制 配置事务管理器 数据库相关的配置 /*** projectName: com.atguigu.config** description: 数据库和连接池配置类*/Configuration ComponenScan(com.atguigu) PropertySource(value classpath:jdbc.properties) EnableTransactionManagement//别忘了加上这一步注解 public class DataSourceConfig {/*** 实例化dataSource加入到ioc容器* param url* param driver* param username* param password* return*/Beanpublic DataSource dataSource(Value(${atguigu.url})String url,Value(${atguigu.driver})String driver,Value(${atguigu.username})String username,Value(${atguigu.password})String password){DruidDataSource dataSource new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}/*** 实例化JdbcTemplate对象,需要使用ioc中的DataSource* param dataSource* return*/Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}/*** 装配事务管理实现对象* param dataSource* return*/Beanpublic TransactionManager transactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}} 使用声明事务注解Transactional /*** projectName: com.atguigu.service**/ Service public class StudentService {Autowiredprivate StudentDao studentDao;Transactionalpublic void changeInfo(){studentDao.updateAgeById(100,1);System.out.println(-----------);int i 1/0;studentDao.updateNameById(test1,1);} }测试事务效果 /*** projectName: com.atguigu.test** description:*/ //SpringJUnitConfig(locations classpath:application.xml) SpringJUnitConfig(classes DataSourceConfig.class) public class TxTest {Autowiredprivate StudentService studentService;Testpublic void testTx(){studentService.changeInfo();} } 5.2.3 事务属性只读 只读介绍 对一个查询操作来说如果我们把它设置成只读就能够明确告诉数据库这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。 设置方式 // readOnly true把当前事务设置为只读 默认是false! Transactional(readOnly true)针对DML动作设置只读模式 会抛出下面异常 Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed Transactional注解放在类上 生效原则 如果一个类中每一个方法上都使用了 Transactional 注解那么就可以将 Transactional 注解提取到类上。反过来说Transactional 注解在类级别标记会影响到类中的每一个方法。同时类级别标记的 Transactional 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 Transactional 注解。 对一个方法来说离它最近的 Transactional 注解中的事务属性设置生效。 用法举例 在类级别Transactional注解中设置只读这样类中所有的查询方法都不需要设置Transactional注解了。因为对查询操作来说其他属性通常不需要设置所以使用公共设置即可。 然后在这个基础上对增删改方法设置Transactional注解 readOnly 属性为 false。 Service Transactional(readOnly true) public class EmpService {// 为了便于核对数据库操作结果不要修改同一条记录Transactional(readOnly false)public void updateTwice(……) {……}// readOnly true把当前事务设置为只读// Transactional(readOnly true)public String getEmpName(Integer empId) {……}}5.2.4 事务属性超时时间 需求 事务在执行过程中有可能因为遇到某些问题导致程序卡住从而长时间占用数据库资源。而长时间占用资源大概率是因为程序运行出现了问题可能是Java程序或MySQL数据库或网络连接等等。 此时这个很可能出问题的程序应该被回滚撤销它已做的操作事务结束把资源让出来让其他正常程序可以执行。 概括来说就是一句话超时回滚释放资源。 设置超时时间 Service public class StudentService {Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!*/Transactional(readOnly false,timeout 3)public void changeInfo(){studentDao.updateAgeById(100,1);//休眠4秒,等待方法超时!try {Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}studentDao.updateNameById(test1,1);} } 测试超时效果 执行抛出事务超时异常 org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Wed May 24 09:10:43 IRKT 2023at org.springframework.transaction.support.ResourceHolderSupport.checkTransactionTimeout(ResourceHolderSupport.java:155)at org.springframework.transaction.support.ResourceHolderSupport.getTimeToLiveInMillis(ResourceHolderSupport.java:144)at org.springframework.transaction.support.ResourceHolderSupport.getTimeToLiveInSeconds(ResourceHolderSupport.java:128)at org.springframework.jdbc.datasource.DataSourceUtils.applyTimeout(DataSourceUtils.java:341)at org.springframework.jdbc.core.JdbcTemplate.applyStatementSettings(JdbcTemplate.java:1467)5.2.5 事务属性事务异常 默认情况 默认只针对运行时异常回滚编译时异常不回滚。情景模拟代码如下 Service public class StudentService {Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/Transactional(readOnly false,timeout 3)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! new FileInputStream(xxxx);studentDao.updateNameById(test1,1);} }设置回滚异常 rollbackFor属性指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚! /*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/ Transactional(readOnly false,timeout 3,rollbackFor Exception.class) public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! new FileInputStream(xxxx);studentDao.updateNameById(test1,1); }设置不回滚的异常 在默认设置和已有设置的基础上再指定一个异常类型碰到它不回滚。 noRollbackFor属性指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内! Service public class StudentService {Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/Transactional(readOnly false,timeout 3,rollbackFor Exception.class,noRollbackFor FileNotFoundException.class)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!new FileInputStream(xxxx);studentDao.updateNameById(test1,1);} } 5.2.6 事务属性事务隔离级别 事务隔离级别 数据库事务的隔离级别是指在多个事务并发执行时数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括 读未提交Read Uncommitted事务可以读取未被提交的数据容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全一般不用。读已提交Read Committed事务只能读取已经提交的数据可以避免脏读问题但可能引发不可重复读和幻读。可重复读Repeatable Read在一个事务中相同的查询将返回相同的结果集不管其他事务对数据做了什么修改。可以避免脏读和不可重复读但仍有幻读的问题。串行化Serializable最高的隔离级别完全禁止了并发只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题但效率较低不适用于高并发场景。 不同的隔离级别适用于不同的场景需要根据实际业务需求进行选择和调整。 事务隔离级别设置 package com.atguigu.service;import com.atguigu.dao.StudentDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional;import java.io.FileInputStream; import java.io.FileNotFoundException;/*** projectName: com.atguigu.service*/ Service public class StudentService {Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!* isolation 设置事务的隔离级别,mysql默认是repeatable read!*/Transactional(readOnly false,timeout 3,rollbackFor Exception.class,noRollbackFor FileNotFoundException.class,isolation Isolation.REPEATABLE_READ)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!new FileInputStream(xxxx);studentDao.updateNameById(test1,1);} } 5.2.7 事务属性事务传播行为 事务传播行为要研究的问题 举例代码 Transactional public void MethodA(){// ...MethodB();// ... }//在被调用的子方法中设置传播行为代表如何处理调用的事务 是加入还是新事务等 Transactional(propagation Propagation.REQUIRES_NEW) public void MethodB(){// ... } propagation属性 Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是 Propagation propagation() default Propagation.REQUIRED;​ propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供 名称含义REQUIRED 默认值如果父方法有事务就加入如果没有就新建自己独立REQUIRES_NEW不管父方法是否有事务我都新建事务都是独立的 3. 测试1. 声明两个业务方法Service public class StudentService {Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!* isolation 设置事务的隔离级别,mysql默认是repeatable read!*/Transactional(readOnly false,timeout 3,rollbackFor Exception.class,noRollbackFor FileNotFoundException.class,isolation Isolation.REPEATABLE_READ)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!new FileInputStream(xxxx);studentDao.updateNameById(test1,1);}/*** 声明两个独立修改数据库的事务业务方法*/Transactional(propagation Propagation.REQUIRED)public void changeAge(){studentDao.updateAgeById(99,1);}Transactional(propagation Propagation.REQUIRED)public void changeName(){studentDao.updateNameById(test2,1);int i 1/0;}}​ 2. 声明一个整合业务方法 Service public class TopService {Autowiredprivate StudentService studentService;Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();} } ​ 3. 添加传播行为测试 SpringJUnitConfig(classes AppConfig.class) public class TxTest {Autowiredprivate StudentService studentService;Autowiredprivate TopService topService;Testpublic void testTx() throws FileNotFoundException {topService.topService();} } ​ 注意 ​ 在同一个类中对于Transactional注解的方法调用事务传播行为不会生效。这是因为Spring框架中使用代理模式实现了事务机制在同一个类中的方法调用并不经过代理而是通过对象的方法调用因此Transactional注解的设置不会被代理捕获也就不会产生任何事务传播行为的效果。 4. 其他传播行为值了解1. Propagation.REQUIRED如果当前存在事务则加入当前事务否则创建一个新事务。2. Propagation.REQUIRES_NEW创建一个新事务并在新事务中执行。如果当前存在事务则挂起当前事务即使新事务抛出异常也不会影响当前事务。3. Propagation.NESTED如果当前存在事务则在该事务中嵌套一个新事务如果没有事务则与Propagation.REQUIRED一样。4. Propagation.SUPPORTS如果当前存在事务则加入该事务否则以非事务方式执行。5. Propagation.NOT_SUPPORTED以非事务方式执行如果当前存在事务挂起该事务。6. Propagation.MANDATORY必须在一个已有的事务中执行否则抛出异常。7. Propagation.NEVER必须在没有事务的情况下执行否则抛出异常。六Spring核心掌握总结 核心点掌握目标spring框架理解spring家族和spring framework框架spring核心功能ioc/di , aop , txspring ioc / di组件管理、ioc容器、ioc/di , 三种配置方式spring aopaop和aop框架和代理技术、基于注解的aop配置spring tx声明式和编程式事务、动态事务管理器、事务注解、属性
http://www.dnsts.com.cn/news/206992.html

相关文章:

  • 站长工具电商运营主要工作内容
  • 网站建设托管公司大连模版网站
  • 制作视频网站教程自媒体账号下载注册
  • 网站建设的书 推荐直播app定制开发
  • 深圳建立企业网站衣服定制app
  • 韩国封号事件网站建设淘宝网店模板
  • 查网站跳出率薇诺娜经常在那个网站做特价
  • 濮阳网站开发wordpress 下划线
  • 肥城市建设局网站建标库官网
  • 营销型企业网站建设体会网站建设优化公司招聘
  • 莱阳做网站微网站开发报价
  • 网站县区分站点建设网页qq邮箱
  • 摄影网站需求分析asp.net购物网站模板
  • 网站 维护 页面二级域名网站免费申请
  • 网站建设工作自查报告收费搭建网站
  • 动态ip建网站做视频网站的备案要求吗
  • 哪个网站可以查当地建设项目wordpress做表格插件
  • 中英繁网站源码如何在建设部网站补录项目
  • 个人网站可以收费吗网络营销模式的有形收益包括
  • 架构图在什么网站可以做网址大全下载到桌面上
  • 长沙网站建设企业网站设计可以用性原则
  • 苏州公司建设网站制作广州手机软件开发制作
  • 景区官方网站建设网页编程入门
  • 网站是怎么做排名的域名网站
  • python 做网站开发吗应用搭建平台
  • 一流的做pc端网站中企动力邮箱客户端
  • 如果做网站接口做的网站搜不到
  • 网站备案多少岁学生作业做网站需要什么
  • 虚拟机做网站网页设计基础课程设计报告
  • 个人站长网站应该如何定位江苏建设工程信息网网址