130.【Spring注解】

Spring 注解

  • (一)、AOP功能测试
    • 1.AOP 使用步骤
        • (1).导入AOP对应的依赖
        • (2).编写业务逻辑类
        • (3).编写切面类
        • (4).编写配置类
        • (5). 编写测试类
  • (二)、AOP 原理
    • 1.@EnableAspectJAutoProxy
        • (1).@EnableAspectJAutoProxy源码
        • (2).AspectJAutoProxyRegistrar 自定义注册bean源码
        • (3).打断点进行Debug测试
    • 2.AnnotationAwareAspectJAutoProxyCreator 分析
        • (1).继承树
        • (2).AbstractAutoProxyCreator
        • (3).AbstractAdvisorAutoProxyCreator
    • 3.注册 AnnotationAwareAspectJAutoProxyCreator
        • (1).打断点进行Debug调试
        • (2).运行流程 (创建和注册AnnotationAwareAspectJAutoProxyCreator)
    • 4.AnnotationAwareAspectJAutoProxyCreator 执行时机
    • 5.创建AOP代理对象
    • 6.AOP原理的总结 ⭐
  • (三)、声明式事务
    • 1.声明式事务 - 环境搭建
        • (1).导入依赖
        • (2).实体类和业务层
    • 2.声明式事务 - 测试成功
        • (1).遇见问题
        • (2).解决问题 -> 事务型(成功全成功_失败全失败)
    • 3.声明式事务原理
  • (四)、扩展原理
    • 1.BeanFactoryPostProcessor ->来定制和修改beanFactory的内容
    • 2.BeanDefinitionRegistryPostProcessor -> 额外添加组件
    • 3.ApplicationListener -> 应用监听器
        • (1).ApplicationListener 的应用
    • 4.@EventListener 监听注解
        • (1).@EventListener 注解监听方法事件
  • (五)、Spring源码总结
    • 1.Spring源码总结
  • (六)、WEB 注解
    • 1.简介与测试
        • (1).创建一个Maven的web项目
        • (2).创建页面支持
        • (3).编写后端
    • 2.ServletContainerInitalizer
        • (1).创建我们的指定扫描的文件并编辑
        • (2).编写被扫描的文件信息
        • (3).将要被扫描的类全限定名写入文件中
        • (4).运行结果
    • 3.ServletContext 注册三大组件
    • 4.SpringMVC整合分析
    • 5.Servlet 异步
        • (1).AsyncContext 实现异步处理
        • (2).Callable 实现异步处理
        • (3).DeferredResult 实现异步

在这里插入图片描述

(一)、AOP功能测试

AOP是指在程序的运行期间动态地将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。

1.AOP 使用步骤

 * AOP【动态代理】 : 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;*                  1.如何使用?*                      (1).导入AOP的依赖。*                      (2).定义一个业务逻辑类(MathCalculator)。在业务逻辑运行的时候将日志进行打印(方法之前,方法结束,方法异常)*                      (3).定义一个日志切面类(LogAspect)。切面类里面的方法需要动态的感知 业务逻辑类(MathCalculator)的运行情况*                              (3.1).通知方法: 前置通知@Before(在目标方法运行之前运行)*                                              后置通知@After(在目标方法运行之后运行,不在意正常结束还是异常结束)*                                              返回通知@AfterReturning(在目标方法正常返回之后运行)*                                              异常通知@AfterThrowing(在目标方法发生异常之后运行)*                                              环绕通知@Round(动态代理,手动推进目标方法运行 joinPoint.)*                      (4).给切面类(LogAspect)的目标方法标注何时何处地运行 (通知注解)*                      (5).将切面类和业务逻辑类(目标方法所在类) 都加入到容器中*                      (6).必须告诉SpringIOC容器哪个类是切面类(我们需要给切面类添加一个注解 @Aspect)*                      (7).在配置类上开启 注解版的切面功能 ( <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  @EnableAspectJAutoProxy)*                      (8).测试,这个逻辑类一定要使用IOC容器的类,不能使用new

(1).导入AOP对应的依赖

        <!--    Spring切面的包    --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>4.3.12.RELEASE</version></dependency>

(2).编写业务逻辑类

package com.jsxs.aop;/*** @Author Jsxs* @Date 2023/8/19 11:49* @PackageName:com.jsxs.aop* @ClassName: MathCalculator* @Description: TODO* @Version 1.0*/
public class MathCalculator {public int div(int x,int y){return x/y;}
}

(3).编写切面类

  1. 使用非公共切入点 @Pointcut

标注是切面的注解,且使用四个通知方法和公共切点注解

package com.jsxs.aop;import org.aspectj.lang.annotation.*;/*** @Author Jsxs* @Date 2023/8/19 11:52* @PackageName:com.jsxs.aop* @ClassName: LogAspect* @Description: TODO* @Version 1.0*/@Aspect // 告诉IOC容器这是一个切面类 ⭐
public class LogAspect {// 1.抽取公共地切入点表达式  ⭐⭐@Pointcut("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void  pointCut(){}// 2.在目标方法之前运行切入:切入点表达式( public int com.jsxs.aop.MathCalculator.div(int,int))// *号代表这个类地所有方法, 一个.代表一个变量@Before("pointCut()")  ⭐⭐⭐public void logStart(){System.out.println("除法运行..... 参数列表是:{}");}// 3.在目标方法之后进行切入, 使用公共地切入点表达式@After("pointCut()")  ⭐⭐⭐⭐public void endStart(){System.out.println("除法运行结束....");}// 4.在目标方法返回正确地话@AfterReturning("pointCut()")  ⭐⭐⭐⭐⭐public void success(){System.out.println("除法正常运行... 运行结果:{}");}// 5.在目标方法出现异常地话@AfterThrowing("pointCut()")  ⭐⭐⭐⭐⭐⭐public void logError(){System.out.println("除法异常运行...");}
}
  1. 不使用公共切入点

标注是切面的注解,且使用四个通知方法

package com.jsxs.aop;import org.aspectj.lang.annotation.*;/*** @Author Jsxs* @Date 2023/8/19 11:52* @PackageName:com.jsxs.aop* @ClassName: LogAspect* @Description: TODO* @Version 1.0*/@Aspect // 告诉IOC容器这是一个切面类  ⭐
public class LogAspect {// 2.在目标方法之前运行切入:切入点表达式( public int com.jsxs.aop.MathCalculator.div(int,int)) // *号代表这个类地所有方法, 一个.代表一个变量  ⭐⭐@Before("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void logStart(){System.out.println("除法运行..... 参数列表是:{}");}// 3.在目标方法之后进行切入, 使用公共地切入点表达式 ⭐⭐⭐@After("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void endStart(){System.out.println("除法运行结束....");}// 4.在目标方法返回正确地话  ⭐⭐⭐⭐@AfterReturning("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void success(){System.out.println("除法正常运行... 运行结果:{}");}// 5.在目标方法出现异常地话 ⭐⭐⭐⭐⭐@AfterThrowing("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void logError(){System.out.println("除法异常运行...");}
}

(4).编写配置类

  1. 注解版开启aop切面
package com.jsxs.config;import com.jsxs.aop.LogAspect;
import com.jsxs.aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;/*** @Author Jsxs* @Date 2023/8/19 11:41* @PackageName:com.jsxs.config* @ClassName: MainConfigOfAOP* @Description: TODO  AOP【动态代理】 : 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;*                  1.如何使用?*                      (1).导入AOP的依赖。*                      (2).定义一个业务逻辑类(MathCalculator)。在业务逻辑运行的时候将日志进行打印(方法之前,方法结束,方法异常)*                      (3).定义一个日志切面类(LogAspect)。切面类里面的方法需要动态的感知 业务逻辑类(MathCalculator)的运行情况*                              (3.1).通知方法:  前置通知@Before(在目标方法运行之前运行),*                                              后置通知@After(在目标方法运行之后运行,不在意正常结束还是异常结束),*                                              返回通知@AfterReturning(在目标方法正常返回之后运行),*                                              异常通知@AfterThrowing(在目标方法发生异常之后运行),*                                              环绕通知@Round(动态代理,手动推进目标方法运行 joinPoint.)*                      (4).给切面类的目标方法标注何时何处地运行 (通知注解)⭐⭐*                      (5).将切面类和业务逻辑类(目标方法所在类) 都加入到容器中 ⭐⭐⭐*                      (6).必须告诉Spring的IOC容器哪个类是切面类(我们需要给切面类添加一个注解 @Aspect)  ⭐⭐⭐⭐*                      (7).在配置类上开启 注解版的切面功能 ( <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  @EnableAspectJAutoProxy)*                      (8).测试,这个逻辑类一定要使用IOC容器的类,不能使用new* @Version 1.0*/@Configuration  //⭐
@EnableAspectJAutoProxy //⭐⭐ 开启注解版本的依赖
public class MainConfigOfAOP {// 业务逻辑类  ⭐⭐⭐⭐@Beanpublic MathCalculator mathCalculator(){return new MathCalculator();}// 切面类 ⭐⭐⭐⭐⭐@Beanpublic LogAspect logAspect(){return new LogAspect();}}
  1. 非注解版开启aop依赖

在这里插入图片描述

(5). 编写测试类

package com.jsxs.Test;import com.jsxs.aop.MathCalculator;
import com.jsxs.config.MainConfigOfAOP;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @Author Jsxs* @Date 2023/8/19 12:47* @PackageName:com.jsxs.Test* @ClassName: AOP_Test* @Description: TODO* @Version 1.0*/
public class AOP_Test {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}// 1.这个对象我们不能使用new创建,我们需要使用IOC容器的组件进行调用 ⭐MathCalculator calculator = applicationContext.getBean(MathCalculator.class);calculator.div(1,3);}
}
  1. 测试结果 不写类名 和 结果

在这里插入图片描述

  1. 测试结果 写类名 和 结果

1.我们需要在切面类进行更改
JoinPoint 这个类如果要使用一定要写在参数列表的第一位,否则可能不生效

package com.jsxs.aop;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;import java.util.Arrays;/*** @Author Jsxs* @Date 2023/8/19 11:52* @PackageName:com.jsxs.aop* @ClassName: LogAspect* @Description: TODO* @Version 1.0*/@Aspect // 告诉IOC容器这是一个切面类  ⭐
public class LogAspect {// 2.在目标方法之前运行切入:切入点表达式( public int com.jsxs.aop.MathCalculator.div(int,int))// *号代表这个类地所有方法, 一个.代表一个变量@Before("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void logStart(JoinPoint joinPoint){  //⭐⭐ 切入点类可以获取名字和参数列表System.out.println(joinPoint.getSignature().getName()+"除法运行..... 参数列表是:{"+ Arrays.asList(joinPoint.getArgs())+"}");}// 3.在目标方法之后进行切入, 使用公共地切入点表达式@After("execution(public int com.jsxs.aop.MathCalculator.*(..))")public void endStart(){System.out.println("除法运行结束....");}// 4.在目标方法返回正确地话  ⭐⭐⭐ 获取返回结果,注解里面的参数名要和方法里面的参数名一致@AfterReturning(value = "execution(public int com.jsxs.aop.MathCalculator.*(..))",returning = "result")public void success(JoinPoint joinPoint,Object result){System.out.println(joinPoint.getSignature().getName()+"除法正常运行... 运行结果:{"+result+"}");}// 5.在目标方法出现异常地话 ⭐⭐⭐⭐ 获取异常信息@AfterThrowing(value = "execution(public int com.jsxs.aop.MathCalculator.*(..))",throwing = "exception")public void logError(Exception exception){System.out.println("除法异常运行..."+exception);}
}

在这里插入图片描述

(二)、AOP 原理

  1. 看给容器中注册了什么组件?
  2. 这个组件什么时候工作?
  3. 这个组件工作时候的功能?

1.@EnableAspectJAutoProxy

(1).@EnableAspectJAutoProxy源码

 *                 AOP【原理】*                  1.@EnableAspectJAutoProxy ->核心注解*                      (1).@Import(AspectJAutoProxyRegistrar.class) -> 给容器中导入AspectJAutoProxyRegistrar这个组件*                          internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator*                          给容器中注入了一个AnnotationAwareAspectJAutoProxyCreator

@EnableAspectJAutoProxy ->核心注解 给容器中导入AspectJAutoProxyRegistrar这个组件

在这里插入图片描述

(2).AspectJAutoProxyRegistrar 自定义注册bean源码

通过这个类进行创建自定义组件
在这里插入图片描述

(3).打断点进行Debug测试

  1. registerBeanDefinitions 方法处打一个断点

在这里插入图片描述

  1. registerOrEscalateApcAsRequired()

在这里插入图片描述

  1. 给容器中注入AnnotationAwareAspectJAutoProxyCreator

在这里插入图片描述

2.AnnotationAwareAspectJAutoProxyCreator 分析

(1).继承树

CTRL+H 打开目录树

在这里插入图片描述

 * AOP【原理】*                  1.@EnableAspectJAutoProxy ->核心注解*                      (1).@Import(AspectJAutoProxyRegistrar.class) -> 给容器中导入AspectJAutoProxyRegistrar这个组件*                          internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator*                          给容器中注入了一个AnnotationAwareAspectJAutoProxyCreator*                  2. AnnotationAwareAspectJAutoProxyCreator*                      AnnotationAwareAspectJAutoProxyCreator*                          ->(继承) AspectJAwareAdvisorAutoProxyCreator*                              ->(继承) AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware*                                       关注后置处理器(在bean初始化前后) 和 自动装配BeanFactory*                                       (2.1). setBeanFactory*                                  ->(继承) AbstractAdvisorAutoProxyCreator*                                      ->(继承) ProxyConfig*                                          ->(继承) Object       

(2).AbstractAutoProxyCreator

AbstractAutoProxyCreator类 具有后置处理器的逻辑

在这里插入图片描述
具有setBeanFactry()的功能
在这里插入图片描述

(3).AbstractAdvisorAutoProxyCreator

这里初始化了 initBeanFactory()
在这里插入图片描述

3.注册 AnnotationAwareAspectJAutoProxyCreator

(1).打断点进行Debug调试

1.打上第一个断点
在这里插入图片描述
2.配置类上打上两个断点
在这里插入图片描述

(2).运行流程 (创建和注册AnnotationAwareAspectJAutoProxyCreator)

 *  3.流程:*                      (1).传入配置类,创建IOC容器 -> new AnnotationConfigApplicationContext(MainConfigOfAOP.class);*                      (2).注册配置类register(annotatedClasses),重启IOC容器refresh()*                      (3).registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来方便拦截bean的创建*                          (3.1).先获取IOC容器已经定义了的需要创建对象的所有 BeanPostProcessor*                          (3.2).给容器中添加别的BeanPostProcessor*                          (3.3).优先注册了priorityOrderedPostProcessors接口的BeanPostProcessor*                          (3.4).在给容器中注册实现了Ordered接口的BeanPostProcessor*                          (3.5).注册没实现优先级接口的 BeanPostProcessors*                          (3.6).注册BeanPostProcessors,实际上就是创建BeanPostProcessors对象,保存在容器中;*                              创建internalAutoProxyCreator的BeanPostProcessorsAnnotationAwareAspectJAutoProxyCreator*                                  (1).创建Bean的实列*                                  (2).populateBean:给bean的各种属性赋值*                                  (3).initializeBean: 初始化bean*                                      (1).invokeAwareMethods() :处理Aware接口的方法回调*                                      (2).applyBeanPostProcessorsBeforeInitialization() 应用后置处理器*                                      (3).invokeInitMethods()执行自定义的初始方法*                                      (4).applyBeanPostProcessorsAfterInitialization() 执行后置处理器的postProcessor*                                  (4).BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator) 创建成功*3.7.BeanPostProcessor注册到BeanFactory中 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));

1.创建IOC容器
在这里插入图片描述

2.发现refresh()方法中具有 后置处理器的功能 注册配置类register(annotatedClasses),重启IOC容器refresh()
在这里插入图片描述
3.registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来方便拦截bean的创建

3.1 先获取IOC容器已经定义了的需要创建对象的所有 BeanPostProcessor
在这里插入图片描述
3.2 给容器中添加别的BeanPostProcessor
在这里插入图片描述
3.3 优先注册了priorityOrderedPostProcessors接口的BeanPostProcessor
在这里插入图片描述
3.4 在给容器中注册实现了Ordered接口的BeanPostProcessor
在这里插入图片描述
3.5注册没实现优先级接口的 BeanPostProcessors
在这里插入图片描述
3.6 注册BeanPostProcessors,实际上就是创建BeanPostProcessors对象,保存在容器中;
在这里插入图片描述

4.AnnotationAwareAspectJAutoProxyCreator 执行时机

 * 4.执行时机*                          1.遍历容器中所有的Bean,依次创建对象getBean(beanName)*                              getBean->doGetBean()->getSingleton->*                          2.创建bean*                              (1).先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建*                                  只要创建好的Bean都会被缓存起来。**                              (2).CreateBean() 创建bean*                                  (2.1).resolveBeforeForeInstantion(beanName,mbdToUse) --> 希望后置处理器在此能返回一个代理对象,如果能返回代理对象就使用,如果不能就继续*                                  (2.2).doCreateBean() 真正的去创建一个bean实列*                           3.  AnnotationAwareAspectJAutoProxyCreator 在所有的bean创建之前会有一个拦截,InstantionAnwareBeanPostProcessor,会调用PostProcessBeforeInstantiation*

5.创建AOP代理对象

 *  5.AOP原理*          1.每一个bean创建之前,调用postProcessBeforeInstantiation();*                              关心 普通组件 和 切面组件 的创建*                                  (1).判断当前bean是否在advisedBeans中(保存了所需要增强的bean)*                                  (2).判断是否是切面*                                  (3).是否需要跳过*                                      (3.1).获取候选的增强器(切面里面的通知方法) 每一个封装的通知方法都是一个增强器 ,返回true*                                      (3.2). 返回false -> 永远不跳过*          2.每一个bean创建之后,调用postProcessAfterInstantiation();*                              (1).获取当前bean的所有增强器(增强方法)*                                  (1.1).找到的候选的所有增强器(找那些通知方法是需要切入当前bean方法的)*                                  (1.2).获取能在当前bean使用的增强器*                                  (1.3).给增强器进行排序*                              (2).保存当前bean在adviseBeans中*                              (3).如果当前bean需要增强,创建当前bean的代理对象。*                                  (3.1).获取所有增强器(通知方法)*                                  (3.2).保存到proxyFactory*                                  (3.3).创建代理对象 ⭐ (两种)*                                      (3.3.1).JDKDynamicAopProxy(config)  ->jdk创建代理模式(有接口使用jdk)*                                      (3.3.2).ObjenesisCglibAopProxy(config)  ->cglib创建代理模式(无接口使用cglib)*(3.4).IOC容器返回当前组件使用cglib增强了的代理对象*(3.5).以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程

6.AOP原理的总结 ⭐

  1. @EnableAspectJAutoProxy 作用: 开启AOP注解功能
  2. @EnableAspectJAutoProxy 作用: 会给容器注册一个组件(AnnotationAwareAspectJAutoProxyCreator)
  3. AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器
  4. 容器的创建流程
    • registerBeanPostProcessors(注册后置处理器),作用:创建registerBeanPostProcessors
    • finishBeanFactoryInitialization(beanFactory); 作用:初始化剩下的单实列bean
      • 创建业务逻辑组件和切面组件
      • AnnotationAwareAspectJAutoProxyCreator 拦截组件的创建过程
      • 组件创建完之后,判断组件是否需要增强
        • 是: 切面的通知方法,包装成增强器(Adviros); 业务逻辑组件创建一个代理对象
  5. 执行目标方法
    • 代理对象执行目标方法
    • CglibAopProxy.intercept();
      • 得到目标方法的拦截器链 (增强器包装成拦截器MethodInterceptor)
      • 利用拦截器的链式机制,依次进入每一个拦截器进行执行
      • 效果:
        • 正常执行: 前置通知 -> 目标方法 -> 后置通知 -> 返回通知
        • 出现异常: 前置通知 ->目标方法 -> 后置通知 -> 异常通知

(三)、声明式事务

1.声明式事务 - 环境搭建

(1).导入依赖

        <!--    Spring包  ->IOC  --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.12.RELEASE</version></dependency><!--     C3P0数据包 ->数据源  --><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><!--    Spring切面的包   ->aop --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>4.3.12.RELEASE</version></dependency><!--    Spring-JDBC -> jdbcTemplate   --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>4.3.12.RELEASE</version></dependency>

(2).实体类和业务层

1.实体类

package com.jsxs.bean;/*** @Author Jsxs* @Date 2023/8/22 14:27* @PackageName:com.jsxs.bean* @ClassName: Admin* @Description: TODO* @Version 1.0*/
public class Admin {private int id;private String name;private String password;public Admin() {}public Admin(int id, String name, String password) {this.id = id;this.name = name;this.password = password;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "Admin{" +"id=" + id +", name='" + name + '\'' +", password='" + password + '\'' +'}';}
}

2.Dao层:

package com.jsxs.mapper;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;import javax.annotation.Resource;/*** @Author Jsxs* @Date 2023/8/22 14:28* @PackageName:com.jsxs.mapper* @ClassName: AdminMapper* @Description: TODO* @Version 1.0*/
@Repository
public class AdminMapper {@Resourceprivate JdbcTemplate jdbcTemplate;public void add(){String sql="insert into admin(name,password) values(?,?)";int i = jdbcTemplate.update(sql,222,222);  // 后面的是参数也就是占位符,占位符有几个,参数也就有几个if (i>0){System.out.println("添加数据成功!!");}else {System.out.println("添加数据失败!!");}}}

3.业务层

package com.jsxs.service;import com.jsxs.mapper.AdminMapper;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @Author Jsxs* @Date 2023/8/22 14:30* @PackageName:com.jsxs.service* @ClassName: AdminServer* @Description: TODO* @Version 1.0*/@Service
public class AdminServer {@ResourceAdminMapper adminMapper;public void insert(){adminMapper.add();}
}

4.配置类

package com.jsxs.tx;import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;import javax.sql.DataSource;
import java.beans.PropertyVetoException;/*** @Author Jsxs* @Date 2023/8/22 14:06* @PackageName:com.jsxs.tx* @ClassName: TxConfig* @Description: TODO  声明式事务* 1. 导入相关依赖 : 数据源、数据库驱动、Spring-JDBC->(提供了JDBCTemplate)* 2.配置数据源,JDBCTemplate(Spring提供的简化数据库操作的工具)操作数据库* @Version 1.0*/@Configuration
@ComponentScan(value = "com.jsxs")
@PropertySource(value = "classpath:db.properties")
public class TxConfig {// 1.配置我们的数据源@Beanpublic DataSource dataSource() throws PropertyVetoException {// 1.1.c3p0数据源ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/demo1");dataSource.setUser("root");dataSource.setPassword("121788");dataSource.setDriverClass("com.mysql.jdbc.Driver");return dataSource;}// 2.我们设置JDBC的模板,又因为这个模板需要数据源,所以我们要存放我们的数据源。又因为在配置类中的参数是先从IOC容器中拿取的,所以我们直接设置成参数即可。@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);}
}

5.测试

package com.jsxs.Test;import com.jsxs.service.AdminServer;
import com.jsxs.tx.TxConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @Author Jsxs* @Date 2023/8/22 14:45* @PackageName:com.jsxs.Test* @ClassName: AOP_TX* @Description: TODO* @Version 1.0*/
public class AOP_TX {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}AdminServer server = applicationContext.getBean(AdminServer.class);server.insert();}
}

在这里插入图片描述

2.声明式事务 - 测试成功

(1).遇见问题

 *                      1. 导入相关依赖 : 数据源、数据库驱动、Spring-JDBC->(提供了JDBCTemplate)*                      2.配置数据源,JDBCTemplate(Spring提供的简化数据库操作的工具)操作数据库*3.给方法上标注@Transactional 表示当前方法是一个事务方法.*4.@EnableTransactionManagement 需要放在配置类进行开启注解的事务*5.添加PlatformTransactionManager这个组件

我们在插入的方法中故意设置一个异常的操作,然后我们执行这个方法但是我们发现我们并不会发生事务回滚(即 失败都失败,成功都成功!);
在这里插入图片描述
在这里插入图片描述

(2).解决问题 -> 事务型(成功全成功_失败全失败)

package com.jsxs.service;import com.jsxs.mapper.AdminMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;/*** @Author Jsxs* @Date 2023/8/22 14:30* @PackageName:com.jsxs.service* @ClassName: AdminServer* @Description: TODO* @Version 1.0*/@Service
public class AdminServer {@ResourceAdminMapper adminMapper;@Transactional  // 声明标注事务的注解⭐public void insert(){adminMapper.add();// 2.故意制造异常 ⭐⭐System.out.println(1/0);}
}
  1. 基于XML文件的

在这里插入图片描述

  1. 基于注解

1.业务层添加上事务注解

package com.jsxs.service;import com.jsxs.mapper.AdminMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;/*** @Author Jsxs* @Date 2023/8/22 14:30* @PackageName:com.jsxs.service* @ClassName: AdminServer* @Description: TODO* @Version 1.0*/@Service
public class AdminServer {@ResourceAdminMapper adminMapper;@Transactionalpublic void insert(){adminMapper.add();System.out.println(1/0);}
}

2.需要在配置类上开启注解支持

package com.jsxs.tx;import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;
import java.beans.PropertyVetoException;/*** @Author Jsxs* @Date 2023/8/22 14:06* @PackageName:com.jsxs.tx* @ClassName: TxConfig* @Description: TODO  声明式事务*                      1. 导入相关依赖 : 数据源、数据库驱动、Spring-JDBC->(提供了JDBCTemplate)*                      2.配置数据源,JDBCTemplate(Spring提供的简化数据库操作的工具)操作数据库*                      3.给方法上标注@Transactional 表示当前方法是一个事务方法.*                      4.@EnableTransactionManagement 需要放在配置类进行开启注解的事务* @Version 1.0*/@Configuration
@ComponentScan(value = "com.jsxs")
@PropertySource(value = "classpath:db.properties")
@EnableTransactionManagementpublic class TxConfig {// 1.配置我们的数据源@Beanpublic DataSource dataSource() throws PropertyVetoException {// 1.1.c3p0数据源ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/demo1");dataSource.setUser("root");dataSource.setPassword("121788");dataSource.setDriverClass("com.mysql.jdbc.Driver");return dataSource;}// 2.我们设置JDBC的模板,又因为这个模板需要数据源,所以我们要存放我们的数据源。又因为在配置类中的参数是先从IOC容器中拿取的,所以我们直接设置成参数即可。@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);}
}

3.进行测试的操作

package com.jsxs.Test;import com.jsxs.service.AdminServer;
import com.jsxs.tx.TxConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @Author Jsxs* @Date 2023/8/22 14:45* @PackageName:com.jsxs.Test* @ClassName: AOP_TX* @Description: TODO* @Version 1.0*/
public class AOP_TX {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {System.out.println(beanDefinitionName);}AdminServer server = applicationContext.getBean(AdminServer.class);server.insert();}
}

4.结果我们发现缺少一个组件 PlatformTransactionManager
在这里插入图片描述
5. 给IOC容器中添加这个组件

package com.jsxs.tx;import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jca.cci.connection.CciLocalTransactionManager;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.sql.DataSource;
import java.beans.PropertyVetoException;/*** @Author Jsxs* @Date 2023/8/22 14:06* @PackageName:com.jsxs.tx* @ClassName: TxConfig* @Description: TODO  声明式事务*                      1. 导入相关依赖 : 数据源、数据库驱动、Spring-JDBC->(提供了JDBCTemplate)*                      2.配置数据源,JDBCTemplate(Spring提供的简化数据库操作的工具)操作数据库*                      3.给方法上标注@Transactional 表示当前方法是一个事务方法.*                      4.@EnableTransactionManagement 需要放在配置类进行开启注解的事务*                      5.添加PlatformTransactionManager这个组件* @Version 1.0*/@Configuration
@ComponentScan(value = "com.jsxs")
@PropertySource(value = "classpath:db.properties")
@EnableTransactionManagement  // ⭐
public class TxConfig {// 1.配置我们的数据源@Beanpublic DataSource dataSource() throws PropertyVetoException {// 1.1.c3p0数据源ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/demo1");dataSource.setUser("root");dataSource.setPassword("121788");dataSource.setDriverClass("com.mysql.jdbc.Driver");return dataSource;}// 2.我们设置JDBC的模板,又因为这个模板需要数据源,所以我们要存放我们的数据源。又因为在配置类中的参数是先从IOC容器中拿取的,所以我们直接设置成参数即可。@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){return new JdbcTemplate(dataSource);}@Bean  // 在配置类中的@Bean注解的方法参数,参数默认是从IOC容器中赋值的 ⭐⭐public PlatformTransactionManager platformTransactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}

在这里插入图片描述

3.声明式事务原理

 *   2.事务原理*                      (1).@EnableTransactionManagement*                              利用 TransactionManagementConfigurationSelector 给容器中会导入批量组件*                              导入两个组件*                                  (1).AutoProxyRegistrar (2).ProxyTransactionManagementConfiguration*                      (2).细聊 AutoProxyRegistrar*                              给容器中注入一个组件,利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理方法执行方法利用拦截器链进行调用**                      (3).细聊 ProxyTransactionManagementConfiguration*                              1.给事务注册一个事务增强器->事务增强器要用事务注解的信息*                              2.事务拦截器*                                  (1).先获取事务相关的属性*                                  (2).在获取PlatformTransactionManager,如果事务先没有添加指定任何*                                      最终会从容器中按照类型获取一个PlateFormTransactionManager;*                                  (3).执行目标方法*                                      如果异常,获取到事务管理器,利用事务管理回滚操作*                                      如果正常,利用事务管理器,提交事务控制

(四)、扩展原理

1.BeanFactoryPostProcessor ->来定制和修改beanFactory的内容

在BeanFactory标准初始化之后调用,所有的bean定义已经保存加载到beanFactory,但是bean的实列还未创建

1.判断无参构造函数创建实列是否在BeanFactory之前还是之后?

1.BeanFactory的后置处理器MyBeanFactoryPostProcessor

package com.jsxs.etx;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** @Author Jsxs* @Date 2023/8/24 10:44* @PackageName:com.jsxs.etx* @ClassName: MyBeanFactoryPostProcessor* @Description: TODO* @Version 1.0*/
@Component  ⭐注册组件
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { ⭐⭐ 继承接口@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("MyBeanFactoryPostProcessor->执行力postProcessBeanFactory方法");// 组件的数量int count = beanFactory.getBeanDefinitionCount();// 组件的名字String[] names = beanFactory.getBeanDefinitionNames();System.out.println("当前beanFactory中有"+count+"个Bean");System.out.println(Arrays.asList(names));}
}

2.实体类

package com.jsxs.bean;/*** @Author Jsxs* @Date 2023/8/13 21:11* @PackageName:com.jsxs.bean* @ClassName: Yellow* @Description: TODO* @Version 1.0*/
public class Yellow {public Yellow() {  // ⭐构造函数System.out.println("Yellow无参构造创建实列");}
}

3.配置类

package com.jsxs.etx;import com.jsxs.bean.Yellow;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;/*** @Author Jsxs* @Date 2023/8/24 10:38* @PackageName:com.jsxs.etx* @ClassName: EtxConfig* @Description: TODO   BeanFactoryPostProcessor : beanFactory的后置处理器 ⭐*                          在BeanFactory标准初始化之后调用,所有的bean定义已经保存加载到beanFactory,但是bean的实列还未创建* 							在初始化创建其他组件前面执行* @Version 1.0*/@ComponentScan("com.jsxs.etx")  ⭐扫面组件
@Configuration
public class EtxConfig {@Bean  ⭐⭐ 将组件注册进来public Yellow yellow(){return new Yellow();}
}

4.测试

package com.jsxs.Test;import com.jsxs.etx.EtxConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @Author Jsxs* @Date 2023/8/24 10:48* @PackageName:com.jsxs.Test* @ClassName: IOC_ext* @Description: TODO* @Version 1.0*/
public class IOC_ext {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(EtxConfig.class);applicationContext.close();}
}

所有的bean已经保存到beanFactory工厂中,但是实列依然还没有创建。在初始化创建其他组件前面执行
在这里插入图片描述

2.BeanDefinitionRegistryPostProcessor -> 额外添加组件

1.注册后置处理器

package com.jsxs.etx;import com.jsxs.bean.Yellow;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component;/*** @Author Jsxs* @Date 2023/8/24 11:09* @PackageName:com.jsxs.etx* @ClassName: MyBeanDefinitionRegistryPostProcessor* @Description: TODO* @Version 1.0*/
@Componentpublic class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {// BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实列 ⭐⭐@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {System.out.println("MyBeanDefinitionRegistryPostProcessor的数量是 "+registry.getBeanDefinitionCount()+"Registry ");// 创建一个组件 ⭐⭐⭐RootBeanDefinition beanDefinition = new RootBeanDefinition(Yellow.class);// 将刚才创建的组件注册进IOC中,且名字叫做hello ⭐⭐⭐⭐registry.registerBeanDefinition("hello",beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("MyBeanDefinitionRegistryPostProcessor的数量是:"+beanFactory.getBeanDefinitionCount()+"   BeanFactory");}
}

2. 配置类 EtxConfig.java MyBeanFactoryPostProcessor.java Yellow.java IOC_ext.java 和上面的类是一样的

package com.jsxs.etx;import com.jsxs.bean.Yellow;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;/*** @Author Jsxs* @Date 2023/8/24 10:38* @PackageName:com.jsxs.etx* @ClassName: EtxConfig* @Description: TODO  1. BeanFactoryPostProcessor : beanFactory的后置处理器*                          在BeanFactory标准初始化之后调用,所有的bean定义已经保存加载到beanFactory,但是bean的实列还未创建*                          在初始化创建其他组件之前运行*                          (1).原理*                              (1.1).创建IOC容器**                     2.BeanDefinitionRegistryPostProcessor*                          在所有的bean定义信息将要被加载,bean实列还未创建的时候执行。*                          优先于BeanFactoryPostProcessor执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外的添加一些组件*                          (2).原理*                              (2.1).创建IOC容器*                              (2.2).refresh() 刷新容器 ->invokeBeanFactoryPostProcessors(beanFactory);.*                              *** @Version 1.0*/@ComponentScan("com.jsxs.etx")
@Configuration
public class EtxConfig {@Beanpublic Yellow yellow(){return new Yellow();}
}

3.ApplicationListener -> 应用监听器

(1).ApplicationListener 的应用

1.事件-接口

package com.jsxs.etx;import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** @Author Jsxs* @Date 2023/8/24 11:59* @PackageName:com.jsxs.etx* @ClassName: MyApplicationListener* @Description: TODO* @Version 1.0*/
@Componentpublic class MyApplicationListener implements ApplicationListener<ApplicationEvent> {// 当容器中发布此事以后,方法触发  ⭐⭐@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("收到事件"+event);}
}

2.自定义事件

package com.jsxs.Test;import com.jsxs.etx.EtxConfig;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @Author Jsxs* @Date 2023/8/24 10:48* @PackageName:com.jsxs.Test* @ClassName: IOC_ext* @Description: TODO* @Version 1.0*/
public class IOC_ext {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(EtxConfig.class);// 1.自定义发布一个事件 ⭐⭐⭐applicationContext.publishEvent(new ApplicationEvent(new String("我发布了一个事件")) {});applicationContext.close();}
}

3.配置类等其他的都没有变化

package com.jsxs.etx;import com.jsxs.bean.Yellow;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;/*** @Author Jsxs* @Date 2023/8/24 10:38* @PackageName:com.jsxs.etx* @ClassName: EtxConfig* @Description: TODO  1. BeanFactoryPostProcessor : beanFactory的后置处理器*                          在BeanFactory标准初始化之后调用,所有的bean定义已经保存加载到beanFactory,但是bean的实列还未创建*                          在初始化创建其他组件之前运行*                          (1).原理*                              (1.1).创建IOC容器**                     2.BeanDefinitionRegistryPostProcessor*                          在所有的bean定义信息将要被加载,bean实列还未创建的时候执行。*                          优先于BeanFactoryPostProcessor执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外的添加一些组件*                          (2).原理*                              (2.1).创建IOC容器*                              (2.2).refresh() 刷新容器 ->invokeBeanFactoryPostProcessors(beanFactory);.*                     3.ApplicationListener 监听容器中发布的事件。事件驱动模型开发*                          (1).监听ApplicationEvent及其下面的子事件**                          步骤:*                              (1).写一个监听器来监听某个事件(ApplicationEvent及其子类)*                              (2).把监听器加入到容器中*                              (3).只要容器中有相关事件的发布,我们就能监听到这个事件*                                      ContextRefreshedEvent ->刷新事件 (所有的bean都已经创建)*                                      ContextClosedEvent -> 关闭事件*                              (4).自定义事件 ⭐⭐⭐⭐*                                  applicationContext.publishEvent(new ApplicationEvent(new String("我发布了一个事件")) {});***** @Version 1.0*/@ComponentScan("com.jsxs.etx")
@Configuration
public class EtxConfig {@Beanpublic Yellow yellow(){return new Yellow();}
}

在这里插入图片描述

4.@EventListener 监听注解

(1).@EventListener 注解监听方法事件

@EventListener: 这个注解可以标注在方法上,然后包扫描到我们这个包就可以实现我们的注解监听。

1.UserService.java

package com.jsxs.service;import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;/*** @Author Jsxs* @Date 2023/8/27 9:02* @PackageName:com.jsxs.service* @ClassName: UserService* @Description: TODO* @Version 1.0*/
@Service
public class UserService {// 设置监听的注解,里面的类是被监听的类。这里我们监听 ApplicationEvent 这个类@EventListener(classes = {ApplicationEvent.class})public void Listener(ApplicationEvent applicationEvent){ //这里是拿到我们监听的事件,这个是固定的,System.out.println("UserService 监听事件为...."+applicationEvent);}
}

2.配置类什么的都和上面的一样

package com.jsxs.Test;import com.jsxs.etx.EtxConfig;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** @Author Jsxs* @Date 2023/8/24 10:48* @PackageName:com.jsxs.Test* @ClassName: IOC_ext* @Description: TODO* @Version 1.0*/
public class IOC_ext {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(EtxConfig.class);// 1.自定义发布一个事件applicationContext.publishEvent(new ApplicationEvent(new String("我发布了一个事件")) {});applicationContext.close();}
}

在这里插入图片描述

(五)、Spring源码总结

1.Spring源码总结

1. Spring容器在启动的时候,先保存所有注册进来的Bean定义信息(1).xml注册bean <bean>(2).注解: @Service ....
2.Spring容器保存定义信息之后,将会根据保存的信息在合适的时机创建Bean的实列。(1).用到Bean的时候创建组件的实列,利用getBean的方法创建Bean。创建好的Bean实列将会保存在Spring容器中。(2).统一创建剩下所有Bean的时候。
3.后置处理器(1).每一个Bean创建完成,都会使用各种后置处理器进行处理,来增强我们的Bean功能,后置处理器在bean创建的各个环节中。比如 自动注入功能,Aop代理功能	 
4.事件驱动模型(1).ApplicationListener 事件监听(2).ApplicationEventMulticaster 事件派发	  

(六)、WEB 注解

注意事项: servlet3.0的版本要求要使用tomcat7.0+

1.简介与测试

(1).创建一个Maven的web项目

在这里插入图片描述

(2).创建页面支持

index.jsp

<html>
<body>
<a href="Hello">点击我跳转</a>
</body>
</html>

(3).编写后端

  1. 注解实现映射关系

使用@WebServlet注解之后,我们可以省略我们的web.xml的映射关系
HelloServlet .java

package com.jsxs.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @Author Jsxs* @Date 2023/8/27 10:01* @PackageName:com.jsxs.servlet* @ClassName: HelloServlet* @Description: TODO* @Version 1.0*/@WebServlet("/Hello")  // 1.设置拦截的URL,请求过来就会调用这个类的方法 ⭐public class HelloServlet extends HttpServlet {  // 2.继承Servlet 并重写两个方法 ⭐⭐@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("hello..."); ⭐⭐⭐}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doPost(req, resp);}
}
  1. 使用web.xml 映射关系
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name>
<!--  具体处理器: 具体的业务逻辑类--><servlet><servlet-name>hello</servlet-name><servlet-class>com.jsxs.servlet.HelloServlet</servlet-class></servlet>
<!--  处理器映射器: 主要根据处理器名字和路径找具体的处理器--><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping>
</web-app>

在这里插入图片描述

2.ServletContainerInitalizer

容器在启动应用的时候,会扫描当前应用每一个jar包里面的src/main/webapp/META-INF/services/javax.servlet.ServletContainerInitializer指定实现的类,启动并运行这个实现类的方法。

(1).创建我们的指定扫描的文件并编辑

在这里插入图片描述

(2).编写被扫描的文件信息

package com.jsxs.servlet;import com.jsxs.services.HelloService;import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.util.Set;/*** @Author Jsxs* @Date 2023/8/27 10:41* @PackageName:com.jsxs.servlet* @ClassName: MyServletContainerInitalizer* @Description: TODO* @Version 1.0*/
// 容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口,抽象类)传递过来@HandlesTypes(value = {HelloService.class})  // 传入我们感兴趣的类型并赋值给set 🚹
public class MyServletContainerInitializer implements ServletContainerInitializer {  // 1.继承接口并实现方法 ⭐// 2.应用启动的时候会调用这个方法/*** @param set : 负责接受我们感兴趣类型的所有子类型 即HelloService🚹* @param servletContext: 代表当前web应用的ServletContext,一个web应用相当于一个ServletContext* @throws ServletException*/@Overridepublic void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {System.out.println("感兴趣的类型"+set);for (Class<?> aClass : set) {System.out.println(aClass);}}
}

1.抽象类

package com.jsxs.services;/*** @Author Jsxs* @Date 2023/8/27 10:54* @PackageName:com.jsxs.services* @ClassName: HelloServiceAbart* @Description: TODO  父接口的抽象子类* @Version 1.0*/
public abstract class HelloServiceAbart implements HelloService{
}

2.子接口: 接口继承接口

package com.jsxs.services;/*** @Author Jsxs* @Date 2023/8/27 10:53* @PackageName:com.jsxs.services* @ClassName: HelloServiceExt* @Description: TODO  父接口的子接口* @Version 1.0*/
public interface HelloServiceExt extends HelloService{
}

3.实现类

package com.jsxs.services;/*** @Author Jsxs* @Date 2023/8/27 10:56* @PackageName:com.jsxs.services* @ClassName: HelloServiceImpi* @Description: TODO  普通类继承接口* @Version 1.0*/
public class HelloServiceImpi implements HelloService{
}

(3).将要被扫描的类全限定名写入文件中

com.jsxs.servlet.MyServletContainerInitializer

在这里插入图片描述

(4).运行结果

我们发现不管是子类 子接口 还是抽象类 实现类都被打印出全限定名了。

3.ServletContext 注册三大组件

过滤器 + 监听器 + Servlet

1.要注册的过滤器

package com.jsxs.servlet;import javax.servlet.*;
import java.io.IOException;/*** @Author Jsxs* @Date 2023/8/27 12:28* @PackageName:com.jsxs.servlet* @ClassName: UserFilter* @Description: TODO  过滤器: 使用的是Servlet的包* @Version 1.0*/
public class UserFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}// 过滤请求,假如为false那么就拦截,true就放行@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// 过滤请求System.out.println("UserFilter...");// 放行filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {}
}

2.要注册的监听器

package com.jsxs.servlet;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;/*** @Author Jsxs* @Date 2023/8/27 12:34* @PackageName:com.jsxs.servlet* @ClassName: UserListener* @Description: TODO  监听项目的启动和停止* @Version 1.0*/
public class UserListener implements ServletContextListener {// 监听项目的开始和初始化@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("UserListener---项目初始化成功....");}// 监听销毁@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("UserListener---项目关闭成功....");}
}

3. 要注册的Servlet

package com.jsxs.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @Author Jsxs* @Date 2023/8/27 12:39* @PackageName:com.jsxs.servlet* @ClassName: UserServlet* @Description: TODO* @Version 1.0*/
public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("tomcate...");}
}

4.进行注册的操作

package com.jsxs.servlet;import com.jsxs.services.HelloService;import javax.servlet.*;
import javax.servlet.annotation.HandlesTypes;
import java.util.EnumSet;
import java.util.Set;/*** @Author Jsxs* @Date 2023/8/27 10:41* @PackageName:com.jsxs.servlet* @ClassName: MyServletContainerInitalizer* @Description: TODO* @Version 1.0*/
// 容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口)传递过来@HandlesTypes(value = {HelloService.class})  // 传入我们感兴趣的类型并赋值给set 🚹
public class MyServletContainerInitializer implements ServletContainerInitializer {  // 1.继承接口并实现方法 ⭐// 2.应用启动的时候会调用这个方法/*** @param set : 负责接受我们感兴趣类型的所有子类型 即HelloService🚹* @param servletContext: 代表当前web应用的ServletContext,一个web应用相当于一个ServletContext* @throws ServletException*/@Overridepublic void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {System.out.println("感兴趣的类型"+set);for (Class<?> aClass : set) {System.out.println(aClass);}// 这里我们向容器中注册监听组件和过滤组件 ⭐⭐// (1).添加Servlet组件 -> 具体的映射请求 ⭐⭐⭐ServletRegistration.Dynamic userServlet = servletContext.addServlet("UserServlet", new UserServlet());// 配置要映射请求的路径userServlet.addMapping("/user");// (2).过滤器⭐⭐⭐FilterRegistration.Dynamic userFilter = servletContext.addFilter("UserFilter", UserFilter.class);// 配置映射的信息userFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*"); // 请求方式 是否为true 映射路径// (3).添加监听器⭐⭐⭐servletContext.addListener(UserListener.class);}
}

4.SpringMVC整合分析

1.web容器在启动的时候,会扫描每个jar包下的src/main/webapp/META-INF/services/javax.servlet.ServletContainerInitializer的全限定名
2.根据全限定名加载这个文件指定的类
3.Spring的应用一启动会加载感兴趣的WebApplicationInitializer下的所有组件
4.并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)(1).AbstractContextLoaderInitializer 创建跟容器,createRootApplicationContext();(2).AbstractDispatcherServletInitializer 1.创建一个web的ioc容器 createServletApplicationContext()2.创建一个DispatchServlet  createDispatchServlet()3.将创建的DispatchServlet添加到ServletContext(3). AbstractAnnotationConfigDispatcherServletInitializer注解方式配置的DispatcherServlet初始化器 1.创建更容器: createRootApplicationContext()getRootConfigClasses(); 传入一个配置类2.创建web的ioc容器 createServletApplicationContext()
总结:以注解的方式启动SpringMvc,继承AbstractAnnotationConfigDispatcherServletInitializer实现抽象发发指定DipatcherServlet的配置信息

5.Servlet 异步

(1).AsyncContext 实现异步处理

有时候我们在处理一个数据的时候,会有相对应的时间限制。在实际的用户体验中可能会产生很糟糕的效应。所以我们要使用异步的方式来减少时间差。

package com.jsxs.servlet;import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @Author Jsxs* @Date 2023/8/27 16:10* @PackageName:com.jsxs.servlet* @ClassName: HelloAsyncServlet* @Description: TODO* @Version 1.0*/@WebServlet(value = "/async",asyncSupported = true) // 设置映射的的路径和支持异步 ⭐
public class HelloAsyncServlet extends HttpServlet {@Overrideprotected void doGet(final HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {final AsyncContext asyncContext = req.startAsync(); // 2.开启异步模式 ⭐⭐System.out.println("主线程----------------------");// 3.通过使用线程的方式进行异步处理 ⭐⭐⭐asyncContext.start(new Runnable() {@Overridepublic void run() {System.out.println("hello-------------------------------------");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 4. 通知异步处理完毕 ⭐⭐⭐⭐asyncContext.complete();// 5.获取到异步的上下文 ⭐⭐⭐⭐⭐AsyncContext asyncContext1 = req.getAsyncContext();// 6.获取响应 ⭐⭐⭐⭐⭐⭐ServletResponse response = asyncContext1.getResponse();try {response.getWriter().write("hello async...");} catch (IOException e) {e.printStackTrace();}}});}
}

在这里插入图片描述

(2).Callable 实现异步处理

package com.jsxs.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.concurrent.Callable;/*** @Author Jsxs* @Date 2023/8/27 16:37* @PackageName:com.jsxs.controller* @ClassName: AsyncController* @Description: TODO* @Version 1.0*/
@Controller
public class AsyncController {@RequestMapping("/async")@ResponseBodypublic Callable<String> async01(){System.out.println("主线程开始"+System.currentTimeMillis());Callable<String> callable=new Callable<String>(){@Overridepublic String call() throws Exception {System.out.println("副线程开始A"+System.currentTimeMillis());Thread.sleep(2000);System.out.println("副线程结束B"+System.currentTimeMillis());return "Callabled";}};System.out.println("主线程结束"+System.currentTimeMillis());return callable;}
}

(3).DeferredResult 实现异步

在这里插入图片描述
我们以创建订单为例,创建订单的请求一进来,应用1就要启动一个线程,来帮我们处理这个请求。如果假设应用1并不能创建订单,创建订单需要应用2来完成,那么此时应该怎么办呢?应用1可以把创建订单的消息存放在消息中间件中,比如RabbitMQ、Kafka等等,而应用2就来负责监听这些消息中间件里面的消息,一旦它发现有创建订单这个消息,那么它就进行相应处理,然后将处理完成后的结果,比如订单的订单号等等,再次存放在消息中间件中,接着应用1再启动一个线程,例如线程2,来监听消息中间件中的返回结果,只要订单创建完毕,它就会拿到返回的结果(即订单的订单号),最后将其响应给客户端。

1.创建订单

package com.meimeixia.controller;import java.util.concurrent.Callable;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;@Controller
public class AsyncController {@ResponseBody@RequestMapping("/createOrder")public DeferredResult<Object> createOrder() {/** 在创建DeferredResult对象时,可以像下面这样传入一些参数哟!* * 第一个参数(timeout): 超时时间。限定(请求?)必须在该时间内执行完,如果超出时间限制,那么就会返回一段错误的提示信息(timeoutResult)* 第二个参数(timeoutResult):超出时间限制之后,返回的错误提示信息*/DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");return deferredResult;}@ResponseBody@RequestMapping("/async01")public Callable<String> async01() {System.out.println("主线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());Callable<String> callable = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());Thread.sleep(2000); // 我们来睡上2秒 System.out.println("副线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());// 响应给客户端一串字符串,即"Callable<String> async01()"return "Callable<String> async01()";}};System.out.println("主线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());return callable;}}

在这里插入图片描述

很明显这是超出时间限制了,应该是相当于创建订单失败了吧!那接下来,我们应该怎么办呢?真令人头疼😭

OK,我们不妨来模拟一个队列。首先新建一个类,例如DeferredResultQueue,如下所示。
2.模拟队列

package com.meimeixia.service;import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;import org.springframework.web.context.request.async.DeferredResult;public class DeferredResultQueue {// DeferredResult对象临时保存的地方private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedQueue<DeferredResult<Object>>();// 临时保存DeferredResult对象的方法 ⭐⭐public static void save(DeferredResult<Object> deferredResult) {queue.add(deferredResult); }// 获取DeferredResult对象的方法 ⭐⭐⭐public static DeferredResult<Object> get() {/** poll():检索并且移除,移除的是队列头部的元素*/return queue.poll();}}

然后,修改一下AsyncController中的createOrder方法。上面我也已经说过了,该方法并不能真正地来处理创建订单的请求。即使如此,那也没关系,因为我们可以在该方法中先把new出来的DeferredResult对象临时保存起来。

3.将正在创建中的订单临时保存起来

package com.meimeixia.controller;import java.util.concurrent.Callable;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;import com.meimeixia.service.DeferredResultQueue;@Controller
public class AsyncController {@ResponseBody@RequestMapping("/createOrder")public DeferredResult<Object> createOrder() {/** 在创建DeferredResult对象时,可以像下面这样传入一些参数哟!* * 第一个参数(timeout): 超时时间。限定(请求?)必须在该时间内执行完,如果超出时间限制,那么就会返回一段错误的提示信息(timeoutResult)* 第二个参数(timeoutResult):超出时间限制之后,返回的错误提示信息*/DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000, "create fail...");DeferredResultQueue.save(deferredResult);// 保存起来return deferredResult;}@ResponseBody@RequestMapping("/async01")public Callable<String> async01() {System.out.println("主线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());Callable<String> callable = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("副线程开始..." + Thread.currentThread() + "==>" + System.currentTimeMillis());Thread.sleep(2000); // 我们来睡上2秒 System.out.println("副线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());// 响应给客户端一串字符串,即"Callable<String> async01()"return "Callable<String> async01()";}};System.out.println("主线程结束..." + Thread.currentThread() + "==>" + System.currentTimeMillis());return callable;}}

当然了,实际开发中应该有一个别的线程来专门监听这个事,啥事?我猜应该是在其他的地方临时保存DeferredResult对象这件事吧!不过,我们在这儿并不会这样做,而是再在AsyncController中编写一个方法,例如create,该方法才是真正来处理创建订单的请求的。

4.真正创建订单号(监听)

@ResponseBody
@RequestMapping("/create")
public String create() {// 在这模拟创建订单String order = UUID.randomUUID().toString();/** 如果我们想在上一个请求(即createOrder)中使用订单,那么该怎么办呢?从临时保存DeferredResult对象的地方获取* 到刚才保存的DeferredResult对象,然后调用其setResult方法设置结果,例如设置订单的订单号*/DeferredResult<Object> deferredResult = DeferredResultQueue.get();  // ⭐deferredResult.setResult(order); // 设置结果// 这儿给客户端直接响应"success===>订单号"这样的字符串,不要再跳转页面了return "success===>" + order;
}

至此,可以看到,只要客户端发送一个createOrder请求进来创建订单,那么服务端就会先将new出来的DeferredResult对象临时保存起来。等到create方法触发并把订单号设置进去之后,在createOrder方法中就会立即得到返回结果,即订单号。

最后,我们来重启项目进行测试。重启成功之后,先来访问createOrder请求,以便来创建订单,但是订单必须得在3秒内创建完,所以一旦访问了createOrder请求后,你必须立即访问create请求来真正创建订单,而且至少得在3秒内完成。

这时,你会看到什么结果呢?可以看到访问create请求之后,直接给浏览器页面响应了一个success===>4e7e3e4c-27d2-4989-87c1-00d545e05feb这样的字符串,其中4e7e3e4c-27d2-4989-87c1-00d545e05feb就是所创建订单的订单号,如下图所示。

在这里插入图片描述

切到访问createOrder请求的浏览器窗口之后,你也可以在浏览器页面中看到所创建订单的订单号,即4e7e3e4c-27d2-4989-87c1-00d545e05feb,如下图所示。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

n卡驱动要下java吗_N卡驱动要下载哪一个?NVIDIA显卡驱动下载方法

显卡驱动程序更新周期不定&#xff0c;单就使用第三方驱动更新工具&#xff0c;可能并不能及时的看到新的显卡驱动更新动态&#xff0c;不过我们在该显卡的官网却是可以很方便的选择NVIDIA显卡驱动下载&#xff0c;如何下载N卡驱动呢&#xff1f;请看下文。 NVIDIA显卡驱动下载…

Linux 安装Nvidia GPU显卡驱动

文章目录 安装Nvidia GPU驱动什么是nouveau驱动&#xff1f;Centos7.7安装Nvidia GPU驱动Ubuntu 18.04安装Nvidia GPU驱动准备工作开始安装安装cuda 检测NVIDIA驱动是否成功安装集显与独显的切换 调节显卡功耗 安装Nvidia GPU驱动 [推荐]Linux安装NVIDIA显卡驱动的正确姿势 参…

2024年天津市大学软件学院专升本专业课考试大纲

天津市大学软件学院2024年“高职升本科”联合招生专业考试大纲 一、考试性质 天津市大学软件学院“高职升本科”联合招生专业考试是由合格的高职高专毕业生参加的选拔性考试。学校根据考生的成绩&#xff0c;按照已确定的招生计划&#xff0c;德、智、体全面衡量&#xff0c;…

【亲测有效】Linux系统安装NVIDIA显卡驱动

文章目录 一、NVIDIA显卡驱动安装的方法1、3种安装方式&#xff1a;建议方式二2、原显卡驱动卸载 二、方式一&#xff08;官网下载驱动包&#xff09;步骤一&#xff1a;查询显卡支持的驱动步骤二&#xff1a;通过命令进行驱动安装 二、方式二&#xff08;software & updat…

基于单片机的智能数字电子秤proteus仿真设计

一、系统方案 1、当电子称开机时&#xff0c;单片机会进入一系列初始化&#xff0c;进入1602显示模式设定&#xff0c;如开关显示、光标有无设置、光标闪烁设置&#xff0c;定时器初始化&#xff0c;进入定时器模式&#xff0c;如初始值赋值。之后液晶会显示Welcome To Use Ele…

React与Vue:两大前端巨头的深度对决

引言 在当今的前端开发领域&#xff0c;React和Vue无疑是两大巨头。它们各自有着独特的历史和哲学&#xff0c;但都为开发者提供了强大的工具来构建高效、响应式的web应用。这篇文章将深入探讨这两个框架的差异&#xff0c;帮助开发者更好地理解它们的优势和劣势。 React与Vu…

乐高虚拟搭建软件Studio 2.0怎么导入缺少的零件库文件

Studio 2.0是目前非常流行的乐高虚拟拼搭软件&#xff0c;功能也很强大。我们在利用Studio 2.0进行虚拟拼搭的时候&#xff0c;经常会遇到软件自带的零件库不全的问题。像一些电机、线性执行器、传感器等比较稀有的零件&#xff0c;在软件自带的零件库里是没有的。 导入方法&a…

C#中动态生成鼠标图案

CreateIconIndirect()的API函数可以生成Icon图像 Icon和Cursor实际是同一个结构,fIcon字段区别是否为Icon 字段hbmMask和hbmColor指定掩码图和色彩图 当然,更好的是用Bitmap.MakeTransparent()处理一下透明 这样掩码图和色彩图都指向处理后的位图 public double Decode…

c语言 鼠标指针图标,WIN7系统鼠标图案DIY!自己动手,美不胜收!-win7鼠标指针...

我以前的文章已经给大家分享了系统登陆画面、C盘减肥等等系统DIY技巧&#xff0c;今天给大家带来鼠标样式DIY方法。再美的东西也会看腻&#xff0c;再好的美食也要加点新料&#xff0c;是时候让你的鼠标整个容了&#xff01;下面看小编教你这个实用小妙招。Let‘s go&#xff0…

自定义鼠标图案

自定义鼠标图案 .ablumInfo_style1{cursor:url("images/L.cur"),-moz-cell;} <a href"#" class"ablumInfo_style1">自定义鼠标提示图标 </a> 其中url内是你自定义图案的路径&#xff0c;-moz-cell是FF的专有属性。 注意以下几点…

Win11怎么设置鼠标箭头图案?Win11更换鼠标图案的方法

Win11怎么设置鼠标箭头图案&#xff1f;如果你觉得电脑默认的鼠标图案看腻了&#xff0c;想要换个有趣味性的图案&#xff0c;那么应该如何操作呢&#xff1f;本期教程就为大家带来两种更换鼠标图案的方法。 ​还有更多重装系统教程可参考 方法一&#xff1a; 1、桌面找到&…

杨柳目-杨柳科-柳属-柳树-柳絮:柳絮

ylbtech-杨柳目-杨柳科-柳属-柳树-柳絮&#xff1a;柳絮 柳絮&#xff0c;即柳树的种子&#xff0c;上面有白色绒毛&#xff0c;随风飞散如飘絮&#xff0c;所以称柳絮。柳树&#xff1a;乔木&#xff0c;高可达18m&#xff0c;树冠开展疏散。树皮灰黑色&#xff0c;不规则开裂…

力量:力量

ylbtech-力量&#xff1a;力量 力量是一个汉语词汇&#xff0c;读音为l ling。词语解释为力气&#xff0c;有份量。古诗文解释为强力也。后引申具有了“作用”、“能力”的意思&#xff0c;是汉语中常用词语。 1.返回顶部 1、 中文名&#xff1a;力量 外文名&#xff1a;Voima…

Free Lunch for Few-shot learning:Distribution Calibration(ICLR 2021)详解

本文是个人在看完论文后做的PPT汇报。 论文地址:https://arxiv.org/pdf/2101.06395v1.pdf 代码:https://github.com/ShuoYang-1998/ICLR2021-Oral_Distribution_Calibration 全文翻译:https://blog.csdn.net/weixin_42118657/article/details/112802977 PPT:https://wenku…

【C++】详细介绍模版初阶—函数模版、类模板

文章目录 一、泛型编程二、函数模版2.1 函数模版概念2.2 函数模版格式2.3 函数模版的原理2.4 函数模版的实例化2.5 函数模版的匹配原则 三、类模版3.1 类模版定义3.2 类模版实例化 总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;&…

【SQL】【Oracle+JAVA】数据库管理数据库系统设计综合实验

文章目录 零.前言一.实验前准备1.概述2.关系模式3.关系模式属性4.实体间的联系5.完整性条件&#xff1a;6.相关描述7.规范化8.流程 二、SQL数据库编程工作1.创建用户cc并授权2.使用用户RouTineD 登录数据库3.创建表学生STUDENT4.创建表教师TEACHER5.创建表地点PLACE6.创建表课表…

【小样本学习】近两年小样本学习取得重大进展了吗?

点击上方&#xff0c;选择星标&#xff0c;每天给你送干货&#xff01; 作者丨赵俊博 Jake、杨朔、ICOZ 来源丨知乎问答 编辑丨极市平台 导读 2020年小样本学习可谓如火如荼&#xff0c;可是顶会论文却越看越懵&#xff0c;2020年到2021年初&#xff0c;小样本学习有里程碑意义…

近两年小样本学习取得重大进展了吗?

链接&#xff1a;https://www.zhihu.com/question/439865186 编辑&#xff1a;深度学习与计算机视觉 声明&#xff1a;仅做学术分享&#xff0c;侵删 2020年小样本学习可谓如火如荼&#xff0c;我从2020年六月份入坑小样本&#xff0c;顶会论文越看越懵&#xff0c;请问各位&am…

项目——基于Oracle实现一个简易版的教务系统

一、背景知识 本教务系统用户分为&#xff0c;学生&#xff0c;教师&#xff0c;系主任&#xff0c;管理员。学生具有选课功能&#xff0c;管理员具有排课功能。对于排课与选课考虑了简单的冲突检测&#xff0c;如下&#xff1a; 根据老师教学课表&#xff0c;为教学班所有的…

2022谷歌博士奖学金名单公布:共61人获奖!

来源&#xff1a;机器之心 谷歌博士奖学金&#xff08;Google PhD Fellowship&#xff09;旨在奖励在计算机科学等前瞻科研领域表现优异的年轻学者&#xff0c;奖学金用于直接支持攻读博士学位&#xff0c;并提供与谷歌研究导师合作的机会。 自创立以来&#xff0c;该项目已经资…