第一章 Spring概述
Spring框架是什么
Spring是一个框架,核心是IOC(控制反转)和AOP(面向切面编程)。
主要作用是降低代码之间的耦合度。
Spring的优点
- 轻量
- 针对接口编程,解耦合
- AOP编程的支持
-
怎样使用Spring
Spring是一个容器,把项目中用的对象放入容器中。
- 让容器完成对象的创建,对象之间关系的管理(属性赋值)。
-
应该放入容器中的对象
dao类,service类,controller类,工具类。
Spring中的对象默认都是单例的,在容器中叫这个名称的对象只有一个。
不应该放入容器中的对象
实体类对象,实体类的数据来源于数据库。
- Servlet,listener,filter等。
第二章 IOC 控制反转⭐
什么是控制反转
假如有 a 和 b 两个对象,在注入 IOC 之前,a 依赖于 b 那么对象 a 在初始化或者运行到某一点的时候,自己必须主动去创建对象 b 或者使用已经创建的对象 b ,无论是创建还是使用对象 b ,控制权都在自己手上 。
而注入 IOC 之后就变了,对象 a 与对象 b 之间失去了直接联系,当对象 a 运行到需要对象 b 的时候,IOC 容器会主动创建一个对象 b 注入到对象 a 需要的地方。其实通过上边这个举例可以很明显的就看出来,对象 a 获得依赖对象 b 的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。依赖注入(DI)是什么
开发人员在项目中只需要提供对象的名称。对象的创建,查找,赋值都由容器内部自己来实现。
Spring框架使用di实现了ioc。
Spring底层使用的是反射机制。
⭐第一个Spring项目-使用步骤
- 引入maven依赖

- 定义接口与实体类
- 创建Spring配置文件
在src/main/resources/目录下创建一个xml文件
推荐命名:applicationContext.xml
- Spring配置文件的编写

- 定义测试类

① 从类路径开始算。 类路径-target-classes
② ClassPathXmlApplicationContext-表示从类路径下加载Spring的配置文件
在创建Spring容器时,会创建配置文件中的所有的对象。
③ getBean()-获取容器中的对象
getBeanDefinitionCount()-获取容器中定义的对象的数量
getBeanDefinitionNames()-获取容器中每个定义的对象的名称
④ 无
基于XML的DI
注入分类
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化
是由容器自动完成的,称为注入。
分为:① set注入 ② 构造注入
set注入(常用)
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。
- 简单类型(基本数据类型+String)
①
②使用
注:只要有set方法,就可以赋值。 就算没有定义该属性。
③测试类
- 引用类型
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。
ref的值必须为某 bean 的 id 值 。
①
② 声明School对象
③ 使用property标签的ref属性,对其他bean对象进行引用。
④
构造注入(理解)
构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系 。
①在实体类中添加有参构造方法。
②声明school对象
③使用
name:与实体类中有参构造的形参名一致。
index: 根据实体类中有参构造的形参顺序编号(从0开始)
value:与前面用法一致
ref:与前面用法一致
④测试类
引用类型属性自动注入
byName方式自动注入
当配置文件中的bean的id值与bean类的属性名相同时,可以使用byName的方式。
①实体类
②声明School对象
③通过为
④测试类
byType的方式自动注入
要求:
- 配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。
- 这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。
什么叫同源?
- java类中引用类型的数据类型和bean的calss值一样。
- java类中引用类型的数据类型和bean的calss值是父子类关系。
- java类中引用类型的数据类型和bean的calss值是接口和实现类的关系。
①实体类
②声明school对象
③通过为

④测试类
为应用指定多个Spring配置文件
优势:
- 每个文件的大小比一个文件小很多,效率高。
- 避免多人开发带来的冲突。
多文件的分配方式:
- 按功能模块,一个模块一个配置文件。
- 按类的功能,数据相关的一个配置文件,事务功能的一个配置文件,Service功能的一个配置文件。
①根据功能编写多个配置文件



②在总配置文件下通过

classpath:表示类路径下(target/classes)
引入时也可以使用通配符
*要求:
- 父配置文件名不能满足*所能匹配的格式,否则将出现循环递归包含。
就本例而言,父配置文件不能匹配 spring-*.xml 的格式,即不能起名为spring-total.xml
- 如果要使用通配符*,必须在resources目录下建一个文件夹统一管理。
基于注解的DI⭐
7个注解
- @Component
- @Repository
- @Service
- @Controller
- @Value
- @Autowired
-
步骤
加入maven依赖spring-context,间接加入了spring-aop依赖(使用注解必备)。
- 在类中加入spring的注解。
在spring的配置文件中,加入一个组件扫描器(component-scan)的标签,说明注解在你项目中的位置。
配置组件扫描器
在 Spring 配置文件中配置组件扫描器,用于在指定在基本包中扫描注解。
快捷键:com
指定多个包的3种方式
使用多个 context:component-scan 指定不同的包路径

- 在指定 base-package 的值时使用分隔符写多个值
分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。
- base-package 指定到父包名
base-package 的值表是基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到
子包下级的子包。所以 base-package 可以指定一个父包就可以 。
也可以指定最顶级的包
不建议:使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。
建议:指定到目标包和合适的。也就是注解所在包全路径。
@Component-创建对象的注解
需要在类上使用注解@Component,该注解的 value 属性(可省略)用于指定该 bean 的 id 值。
什么时候用@Component呢?
不是dao层的,不是Service层的,不是Controller层的,但需要创建对象时。
@Component 不指定 value 属性, bean 的 id 是类名的首字母小写。
另外3个创建对象的注解
语法与@Component相同。
这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,可以给项目的对象分层。
@Value-简单类型的属性注入
需要在属性上使用注解@Value,该注解的 value 属性(String类型的,都要用” “)用于指定要注入的值。
位置:
- 使用该注解完成属性注入时,类中无需 setter。
- 若属性有 setter,则也可将其加到 setter 上。
引用类型属性自动注入
@Autowired-byType自动注入
在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。
@Autowired与@Qualifier-byName自动注入
在引用属性上联合使用注解@Autowired 与@Qualifier。 (无先后顺序)
@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。 
属性required
@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。
若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null 。
@Resources-JDK注解自动注入⭐
- Spring提供了对 jdk中@Resource注解的支持。
- @Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。
- 使用该注解,要求 JDK 必须是 6 及以上版本。
- @Resource 可在属性上,也可在 set 方法上。
(1)byType 注入引用类型属性
@Resource 注解若不带任何参数, 采用默认按名称(byName)的方式注入。
按名称不能注入 bean,则会按照类型(byType)进行 Bean 的匹配注入。
(2)byName注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。
注解与xml的对比
第三章 AOP 面向切面编程⭐
概述
AOP就是动态代理的规范化,把动态代理的实现步骤、方式都定义好了。
让开发人员用一种统一的方式使用动态代理。
AOP简介
AOP(Aspect Orient Programming),面向切面编程。
面向切面编程:就是将交叉业务逻辑封装成切面,利用AOP容器的功能将切面织入到主业务逻辑中。
交叉业务逻辑:是指通用的,与主业务逻辑无关的代码。例如事务,日志等。
AOP的底层就是采用动态代理模式实现的:JDK的动态代理和CGLIB的动态代理。
AOP三要素:
- 切面的功能代码,切面要干什么?(Aspect表示)- 切面的执行位置,哪个类?哪个方法?(Pointcut表示)- 切面的执行时间,在目标方法前执行还是目标方法后执行?(Advice表示)
AOP的作用
- 在目标类源代码不改变的情况下,增加功能。
- 减少代码的重复
- 专注业务逻辑代码
- 解耦合,让你的业务功能和日志,事务非业务功能分离。
什么时候使用AOP⭐
通知表示切面的执行时间,Advice也叫增强。定义切入的时间。
- 连接点(JointPoint)
通常业务接口中的方法均为连接点。
- 切入点(Pointcut)
让切点来筛选连接点,选中那几个你想要的方法。定义切入的位置。
- 切面(Aspect)
切面是通知和切入点的结合。
Advice→切面的执行时间,在目标方法前执行还是目标方法后执行?
Pointcut→切面的执行位置,哪个类?哪个方法?
- 目标对象(Target)
目标对象指将要被增强的对象。AspectJ对AOP的实现⭐
AspectJ 是一个优秀面向切面的框架, 它扩展了 Java 语言,提供了强大的切面实现。AspectJ的通知(Advice)类型
切面的执行时间
- @Before-前置通知
- @AfterReturning-后置通知
- @Around-环绕通知
- @AfterThrowing-异常通知
- @After-最终通知
⭐AspectJ的切入点(Pointcut)表达式
切面的执行位置
AspectJ 定义了专门的表达式用于指定切入点。
表达式的原型:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
?:表示可有可无。
modifiers-pattern:访问权限类型 [public之类的]
ret-type-pattern:返回值类型 [void之类的] 必有!
declaring-type-pattern:包名类名 [com.mu.player]
name-pattern(param-pattern):方法名(参数类型参数个数) [ sellUsb(int amount) ] 必有!
throws-pattern: 抛出异常类型
以上表达式共4个部分:
execution(访问权限 方法返回值 方法名(参数) 异常类型)
注意:表达式中的黑色文字表示可省略部分,各部分使用空格隔开。
在其中也可以使用以下符号:
| 符号 | 意义 |
|---|---|
| * | 0至多个任意字符 |
| .. | 在方法参数中→表示任意多个参数 在包名后,表示当前包及其子包路径 |
| + | 在类名后,表示当前类及其子类 在接口后,表示当前接口及其实现类 |
eg:
- execution(public (..))
指定切入点为:任意的公共方法。
- execution( set(..))
指定切入点为:任意的以“set”开头的方法。
- execution( com.mu.service..*(..))
指定切入点为:service包下的任意类的任意方法。
- execution( com.mu.service...*(..))
指定切入点为:service和其子包下的任意类的任意方法。
注:”..”出现在类名中时,后面必须跟”*”,表示包和其子包下的所有类。
- execution( ..service..(..))
指定切入点为:所有包下的service包下的任意类的任意方法。
⭐AspectJ基于注解的AOP实现
AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
使用AOP的目的:在不改变原来的类的代码的前提下,给已经存在的一些类和方法,增加额外的功能。
基本实现步骤-@Before
后面的通知步骤只有第三步的区别!
- 添加maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
- 定义业务接口与目标类
- 创建切面类
@AspectJ
- 是aspectJ框架的注解
- 作用是表示当前类是切面类
- 切面类:用来给业务方法增加功能的类。
- 位置:在类定义的上方
在切面类(@Before)中定义方法的要求:
- 必须是public的
- 必须是void的
- 方法名称自定义。
- 方法可以无参,可以有参
但如果有参,参数不可以自定义,有几个参数类型可以选择。
@Before
- 属性:value,是切入点表达式,表示切面的功能的执行位置(execution)
- 位置:用于方法之上
- 作用:表示切面功能的执行时间

- 在spring的配置文件中声明对象,把对象交给容器统一管理。
- 声明对象可以选择使用注解或者xml配置文件方式。
此处由第二章知识书写。
- 在spring的配置文件中声明自动代理生成器。
用来完成代理对象的自动创建功能。
- 测试类
@Before-方法有JoinPoint参数
作用:可以在通知方法中获取方法执行时的信息,例如方法的定义,方法的参数等信息。
何时用:当你的切面功能中需要用到方法的信息时,就加入JoinPoint参数。
要求:如果用,必须作为第一个参数出现。
输出结果:
@AfterReturning-注解有returning属性
在@AfterReturning中定义方法的要求:
- 必须是public的
- 必须是void的
- 方法名称自定义。
- 方法是有参数的,推荐使用Object类型,参数名自定义。
@AfterReturning
属性:value,是切入点表达式,表示切面的功能的执行位置(execution)
returning:自定义的变量,表示目标方法的返回值的。
自定义变量名必须和通知方法的形参名一样。位置:用于方法之上
- 作用:表示切面功能的执行时间
特点:
必须是public的
- 必须有返回值,推荐使用Object
- 方法名称自定义。
- 方法是有参数的,固定的参数→ProceedingJoinPoint
因为ProceedingJoinPoint继承了JoinPoint,所以JoinPoint有的功能它都有,比如获取参数等。
@Around
- 属性:value,是切入点表达式,表示切面的功能的执行位置(execution)
- 位置:用于方法之上
- 作用:表示切面功能的执行时间
- 特点:
- 它是功能最强的通知。
- 在目标方法的前和后都能增强功能。
- 可以控制目标方法的执行。
- 可以修改原来的目标方法的执行结果,影响最后的调用结果。
- 环绕通知等同于jdk动态代理的InvocationHandler接口,
pjp.proceed() = method.invoke(target,args)
返回值: 就是目标方法的执行结果,可以被修改。
经常用于事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务
了解-@AfterThrowing-注解中有throwing属性
在@AfterThrowing中定义方法的要求:
必须是public的
- void
- 方法名称自定义。
- 方法有参数→Throwable
@AfterThrowing
属性:value,是切入点表达式,表示切面的功能的执行位置(execution)
throwinng 自定义的变量,表示目标方法抛出的异常对象。
变量名必须和方法的参数名一样位置:用于方法之上
- 作用:表示切面功能的执行时间
- 特点:
- 在目标方法抛出异常时执行的
- 可以做异常的监控程序, 监控目标方法执行时是不是有异常。
了解-@After
无论目标方法是否抛出异常,该增强均会被执行。
在@After中定义方法的要求:
- 必须是public的
- void
- 方法名称自定义。
- 方法没有参数
@After
- 属性:value,是切入点表达式,表示切面的功能的执行位置(execution)
- 位置:用于方法之上
- 作用:表示切面功能的执行时间
- 特点:
- 总是会执行
- 在目标方法之后执行的
- 一般做资源清除工作的。
@Pointcut定义切入点
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
用法:
- 将@Pointcut 注解在一个方法之上,以后所有的execution的 value 属性值均可使用该方法名作为切入点.
- 这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
cglib代理
在没有接口时,AspectJ会自动使用cglib代理,步骤没啥区别。
在有接口时,默认使用jdk代理,若要强制使用cglib代理(效率高一些):
第四章 Spring集成MyBatis
概述
原理:使用ioc实现
Spring帮mybatis创建的3个对象:
- 阿里的druid连接池
- SqlSessionFactory对象
- dao对象
步骤
1.添加Maven依赖(8个)
- 测试依赖
- Spring依赖
- Spring事务相关依赖-2个
- mybatis依赖
- mybatis-spring集成依赖
用来在spring项目中创建mybatis的SqlsessionFactory,dao对象的。
- mysql驱动
- 阿里数据库连接池
2.创建数据库相关实体类

3.定义相关dao接口和mapper映射文件
4.创建mybatis主配置文件
5.定义Service接口和实现类
6.创建spring的配置文件:声明mybatis的对象交给spring创建
7.创建测试类,获取Service对象,通过service调用dao完成数据库的访问
第五章 Spring事务
概述
- 什么是事务?
事务是指一组sql语句的集合, 集合中有多条sql语句 可能是insert , update ,select ,delete,这些操作作为一个整体一起向系统提交,要么都执行,要么都不执行。
如果不使用事务,当报异常时,数据扔会被添加到表中。
- 什么时候使用事务?
当操作涉及到多个表或者多个sql语句的insert,delete,update。需要这些语句都成功或者都失败才能完成某项功能时,需要使用事务。 例如:转账
- 之前事务的处理方式,有什么不足?
多种数据库的访问技术,有不同的事务处理的机制,对象,方法。学习成本太高。
如何解决之前的不足?
spring提供一种处理事务的统一模型,能使用统一步骤的方式,完成多种不同数据库访问技术的事务处理。
Spring的事务管理
事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。
这样做是为了能够使用事务的特性来管理具体的业务。
Spring中实现对事务管理的两种方式:DataSourceTransactionManager:使用JDBC或MyBatis进行数据库操作时使用。
HibernateTransactionManager:使用Hibernate进行持久化操作。
Spring的回滚方式
Spring 事务的默认rollback方式是:
- 发生运行时异常和error时,rollback。
- 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕时,commit。
- 发生编译时异常时,commit。 对于编译时异常,程序员也可以手工设置其回滚方式。
回顾异常与错误
运行时异常:RuntimeException和他的子类都是运行时异常,例如NullPointException,NumberFormatException.
编译时异常:在你写代码中,必须处理的异常。例如IOException, SQLException.
自定义异常:继承RuntimeException-运行时异常;继承Exception-编译时异常。事务定义接口-TransactionDefinition
TransactionDefinition:事务定义接口,定义了描述事务的三类常量:
①事务隔离级别 ②事务传播行为 ③ 事务默认超时时限事务隔离级别常量(5个)
这些常量均以-ISOLATION_ 开头,格式为→ISOLATION_XXX
DEFAULT:采用 DB 默认的事务隔离级别。
MySql 的默认为 REPEATABLE_READ;Oracle默认为 READ_COMMITTED。
READ_UNCOMMITTED: 读未提交。未解决任何并发问题。
- READ_COMMITTED: 读已提交。解决脏读,存在不可重复读与幻读。
- REPEATABLE_READ: 可重复读。解决脏读、不可重复读,存在幻读。
-
事务传播行为常量(7个)
事务传播行为:处于不同事务中的方法在相互调用时,执行期间事务的维护情况。
这些常量均以-PROPAGATION_ 开头,格式为→PROPAGATION_XXX PROPAGATION_REQUIRED(默认)
- 若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。
- Spring默认的事务传播行为。

- PROPAGATION_REQUIRES_NEW
- 若当前存在事务,就加入到当前事务中;若当前没有事务,可以以非事务方式执行。

- PROPAGATION_SUPPORTS
- 总是新建一个事务。
- 若当前存在事务,就将当前事务挂起,直到新事务执行完毕。

- PROPAGATION_MANDATORY
- PROPAGATION_NESTED
- PROPAGATION_NEVER
-
事务默认超时时限
常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限, sql 语句的执行时长。
注:事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可.程序举例环境搭建
实例:购买商品trans_sale项目
本例要实现购买商品,模拟用户下订单,向订单表添加销售记录,从商品表减少库存。 创建数据库表-sale,goods
- 添加maven依赖
- 编写dao接口和其mapper文件
- 编写mybatis主配置文件
- 编写自定义异常类
- 定义service接口和实现类
- 编写Spring配置文件
- 测试
Spring的事务注解管理事务-@Transactional
概述
- 适用于中小型项目
- 通过@Transactional 注解方式, 可将事务织入到相应 public 方法中,实现事务管理。
- @Transactional 若用在方法上,只能用于 public 方法上。
- 若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
-
@Transactional的可选属性
propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,
默认值为Propagation.REQUIRED。
isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
timeout:用于设置事务的默认超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。
当然,若只有一个异常类时,可以不使用数组。
运行时异常不需要指定。一般是想要给编译时异常进行rollback,才写此类属性。
rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。
当然,若只有一个异常类时,可以不使用数组。
noRollbackFor:指定不需要回滚的异常类。
noRollbackForClassName:指定不需要回滚的异常类类名。
步骤
声明事务管理器

- 开启注解驱动

- 在业务层的public方法上加入事务属性

- 声明事务管理器

- 配置事务通知
如果使用默认的话,propagation之类的属性不用写。
添加:add 修改:modify 删除:remove 查询:query
- 配置aop,通知应用的切入点

-
第六章 Spring与Web
问题:Spring容器对象不唯一。
需求:web项目中容器对象只需要创建一次, 把容器对象放入到全局作用域ServletContext中。
实现:使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext。基本步骤
创建maven项目-web模板,添加java文件和resources文件。
- 配置Tomcat
- 在之前的依赖基础上,添加Servlet依赖和jsp依赖。
- 编写JSP页面
- 解决web.xml版本过低问题
- 先删除自带的web.xml

- 新建web.xml
- 先删除自带的web.xml

- 添加maven依赖-Spring-web依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
- 在web.xml中注册监听器

- 指定Spring配置文件的位置
ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。其默认的 Spring 配置文件位置与名称为: WEB-INF/applicationContext.xml。但是,一般会将该配置文件放置于项目的 classpath 下,即 src 下,所以需要在 web.xml 中对 Spring 配置文件的位置及名称进行指定。 
- 编写Servlet代码-获取Spring容器对象
- 直接从ServletContext中获取Spring容器对象

- 通过WebApplicationContextUtils获取Spring容器对象

- 直接从ServletContext中获取Spring容器对象
- 完整的Servlet代码 ```java package com.mu.controller;
import com.mu.pojo.Student; import com.mu.service.StudentService; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
public class RegisterServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求参数 String id = request.getParameter(“id”); String name = request.getParameter(“name”); String email = request.getParameter(“email”); String age = request.getParameter(“age”);
/ //获取Spring容器对象-直接从ServletContext中获取 1️⃣ String attr = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; WebApplicationContext ac = (WebApplicationContext) this.getServletContext().getAttribute(attr);/
//通过WebApplicationContextUtils获取Spring容器对象 2️⃣
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
//获取Service
StudentService service = (StudentService) ctx.getBean("studentService");
Student student = new Student();
student.setId(Integer.parseInt(id));
student.setName(name);
student.setEmail(email);
student.setAge(Integer.valueOf(age));
service.addStudent(student);
//跳转注册成功页面
request.getRequestDispatcher("/result.jsp").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
```







