SSM之Spring(一)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

一Spring简介

1.1 Spring概述

 1.2 SpringFrameWork

1.2.1 SpringFrameWork特性

 1.2.2 Spring Framework五大功能模块

二IOC

2.1 IOC容器

2.1.1 IOC思想

 2.1.2 IOC容器在Spring中的实现

2.2 基于XML管理bean

2.2.1 入门案例

 2.2.2 获取bean的方式

 2.2.3 依赖注入之setter注入

2.2.4 依赖注入之构造器注入

 2.2.5 特殊值处理

2.2.6 为类类型属性赋值

2.2.7 为数组类型属性赋值

2.2.8 为集合类型属性赋值

2.2.9 p命名空间

 2.2.10 引入外部属性文件

2.2.11 bean的作用域

2.2.12 bean的生命周期  

2.2.13 FactoryBean

2.2.14 基于xml的自动装配


一Spring简介

1.1 Spring概述

  • Spring 是最受欢迎的企业级 Java 应用程序开发框架数以百万的来自世界各地的开发人员使用
  • Spring 框架来创建性能好、易于测试、可重用的代码。
  • Spring 框架是一个开源的 Java 平台它最初是由 Rod Johnson 编写的并且于 2003 6 月首次在 Apache 2.0 许可下发布。
  • Spring 是轻量级的框架其基础版本只有 2 MB 左右的大小。
  • Spring 框架的核心特性是可以用于开发任何 Java 应用程序但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用通过启用基于 POJO编程模型来促进良好的编程实践。

 1.2 SpringFrameWork

Spring 基础框架可以视为 Spring 基础设施基本上任何其他 Spring 项目都是以 Spring Framework为基础的。

1.2.1 SpringFrameWork特性

  • 非侵入式使用 Spring Framework 开发应用程序时Spring 对应用程序本身的结构影响非常小。对领域模型可以做到零污染对功能性组件也只需要使用几个简单的注解进行标记完全不会破坏原有结构反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
  • 控制反转IOC——Inversion of Control翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好我们享受资源注入。
  • 面向切面编程AOP——Aspect Oriented Programming在不修改源代码的基础上增强代码功能。
  • 容器Spring IOC 是一个容器因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理替程序员屏蔽了组件创建过程中的大量细节极大的降低了使用门槛大幅度提高了开发效率。
  • 组件化Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。
  • 声明式很多以前需要编写代码才能实现的功能现在只需要声明需求即可由框架代为实现。
  • 一站式在 IOC AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且Spring 旗下的项目已经覆盖了广泛领域很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。

 1.2.2 Spring Framework五大功能模块

二IOC

2.1 IOC容器

2.1.1 IOC思想

IOCInversion of Control翻译过来是反转控制 

①获取资源的传统方式
自己做饭买菜、洗菜、择菜、改刀、炒菜全过程参与费时费力必须清楚了解资源创建整个过程
中的全部细节且熟练掌握。
在应用程序中的组件需要获取资源时传统的方式是组件 主动 的从容器中获取所需要的资源在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式增加了学习成本同时降低了开发效率。
②反转控制方式获取资源
点外卖下单、等、吃省时省力不必关心资源创建过程的所有细节。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式反转了资源的获取方向 —— 改由容器主动的将资源推送给需要的组件开发人员不需要知道容器是如何创建资源对象的只需要提供接收资源的方式即可极大的降低了学习成本提高了开发的效率。这种行为也称为查找的被动 形式。
DI
DI Dependency Injection 翻译过来是 依赖注入
DI IOC 的另一种表述方式即组件以一些预先定义好的方式例如 setter 方法接受来自于容器的资源注入。相对于IOC 而言这种表述更直接。
所以结论是 IOC 就是一种反转控制的思想 而 DI 是对 IOC 的一种具体实现。

 2.1.2 IOC容器在Spring中的实现

Spring IOC 容器就是 IOC 思想的一个落地的产品实现。 IOC 容器中管理的组件也叫做 bean 。在创建bean 之前首先需要创建 IOC 容器。 Spring 提供了 IOC 容器的两种实现方式
BeanFactory
这是 IOC 容器的基本实现是 Spring 内部使用的接口。面向 Spring 本身不提供给开发人员使用。
ApplicationContext
BeanFactory 的子接口提供了更多高级特性。面向 Spring 的使用者几乎所有场合都使用ApplicationContext 而不是底层的 BeanFactory
ApplicationContext 的主要实现类

2.2 基于XML管理bean

2.2.1 入门案例

一创建模块

二导入依赖

<dependencies>
<!-- 基于Maven依赖传递性导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

 三创建类HelloWorld

public class HelloWorld {
public void sayHello(){
System.out.println("helloworld");
}
}

四创建Spring的配置文件

五在配置文件中配置bean

<!--
配置HelloWorld所对应的bean即将HelloWorld的对象交给Spring的IOC容器管理
通过bean标签配置IOC容器所管理的bean
属性
id设置bean的唯一标识
class设置bean所对应类型的全类名
-->
<bean id="helloworld" class="com.atguigu.spring.bean.HelloWorld"></bean>

六创建测试类

@Test
public void testHelloWorld(){
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld");
helloworld.sayHello();
}

七整体思路

注意Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象这一点需要注意。如果在需要无参构造器时没有无参构造器则会抛出异常。

 2.2.2 获取bean的方式

①方式一根据 id 获取
由于 id 属性指定了 bean 的唯一标识所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。上个实验中我们使用的就是这种方式。
②方式二根据类型获取
③方式三根据 id 和类型
④注意
当根据类型获取 bean 时要求 IOC 容器中指定类型的 bean 有且只能有一个
⑤扩展
如果组件类实现了接口根据接口类型可以获取 bean
可以前提是 bean 唯一
如果一个接口有多个实现类这些实现类都配置了 bean 根据接口类型可以获取 bean
不行因为 bean 不唯一
⑥结论
根据类型来获取 bean 时在满足 bean 唯一性的前提下其实只是看『对象 instanceof 指定的类型』的返回结果只要返回的是true 就可以认定为和类型匹配能够获取到。

 2.2.3 依赖注入之setter注入

①创建学生类 Student
②配置 bean 时为属性赋值
<bean id="studentOne" class="com.atguigu.spring.bean.Student">
<!-- property标签通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性指定属性名这个属性名是getXxx()、setXxx()方法定义的和成员变量无关
-->
<!-- value属性指定属性值 -->
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>

2.2.4 依赖注入之构造器注入

①在 Student 类中添加有参构造
②配置 bean
<bean id="studentTwo" class="com.atguigu.spring.bean.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="33"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>
注意
constructor-arg 标签还有两个属性可以进一步描述构造器参数
index 属性指定参数所在位置的索引从 0 开始
name 属性指定参数名

 2.2.5 特殊值处理

①字面量赋值
什么是字面量
int a = 10;
声明一个变量 a 初始化为 10 此时 a 就不代表字母 a 了而是作为一个变量的名字。当我们引用 a的时候我们实际上拿到的值是10
而如果 a 是带引号的 'a' 那么它现在不是一个变量它就是代表 a 这个字母本身这就是字面量。所以字面量没有引申含义就是我们看到的这个数据本身。
<!-- 使用value属性给bean的属性赋值时Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>
null
<property name="name">
<null />
</property>
xml 实体
<!-- 小于号在XML文档中用来定义标签的开始不能随便使用 -->
<!-- 解决方案一使用XML实体来代替 -->
<property name="expression" value="a &lt; b"/>
CDATA
<property name="expression">
<!-- 解决方案二使用CDATA节 -->
<!-- CDATA中的C代表Character是文本、字符的含义CDATA就表示纯文本数据 -->
<!-- XML解析器看到CDATA节就知道这里是纯文本就不会当作XML标签或属性来解析 -->
<!-- 所以CDATA节中写什么符号都随意 -->
<value><![CDATA[a < b]]></value>
</property>

2.2.6 为类类型属性赋值

①创建班级类 Clazz
②修改 Student
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
③方式一引用外部已声明的 bean
<bean id="clazzOne" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="1111"></property>
<property name="clazzName" value="财源滚滚班"></property>
</bean>
Student 中的 clazz 属性赋值
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性引用IOC容器中某个bean的id将所对应的bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
</bean>
④方式二内部 bean
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<property name="clazz">
<!-- 在一个bean中再声明一个bean就是内部bean -->
<!-- 内部bean只能用于给属性赋值不能在外部通过IOC容器获取因此可以省略id属性 -->
<bean id="clazzInner" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="2222"></property>
<property name="clazzName" value="远大前程班"></property>
</bean>
</property>
</bean>
③方式三级联属性赋值
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- 一定先引用某个bean为属性赋值才可以使用级联方式更新属性 -->
<property name="clazz" ref="clazzOne"></property>
<property name="clazz.clazzId" value="3333"></property>
<property name="clazz.clazzName" value="最强王者班"></property>
</bean>

2.2.7 为数组类型属性赋值

①修改 Student
Student 类中添加以下代码
private String[] hobbies;
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
②配置 bean
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性引用IOC容器中某个bean的id将所对应的bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</bean>

2.2.8 为集合类型属性赋值

①为 List 集合类型属性赋值
Clazz 类中添加以下代码
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
配置 bean
<bean id="clazzTwo" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="4444"></property>
<property name="clazzName" value="Javaee0222"></property>
<property name="students">
<list>
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</list>
</property>
</bean>
若为 Set 集合类型属性赋值只需要将其中的 list 标签改为 set 标签即可

②为Map集合类型属性赋值

创建教师类Teacher

Student类中添加以下代码

private Map<String, Teacher> teacherMap;
public Map<String, Teacher> getTeacherMap() {
return teacherMap;
}
public void setTeacherMap(Map<String, Teacher> teacherMap) {
this.teacherMap = teacherMap;
}

 配置bean

<bean id="teacherOne" class="com.atguigu.spring.bean.Teacher">
<property name="teacherId" value="10010"></property>
<property name="teacherName" value="大宝"></property>
</bean>
<bean id="teacherTwo" class="com.atguigu.spring.bean.Teacher">
<property name="teacherId" value="10086"></property>
<property name="teacherName" value="二宝"></property>
</bean>
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性引用IOC容器中某个bean的id将所对应的bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<property name="teacherMap">
<map>
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teacherTwo"></ref>
</entry>
</map>
</property>
</bean>
③引用集合类型的 bean
<!--list集合类型的bean-->
<util:list id="students">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>
<!--map集合类型的bean-->
<util:map id="teacherMap">
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teacherTwo"></ref>
</entry>
</util:map>
<bean id="clazzTwo" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="4444"></property>
<property name="clazzName" value="Javaee0222"></property>
<property name="students" ref="students"></property>
</bean>
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性引用IOC容器中某个bean的id将所对应的bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<property name="teacherMap" ref="teacherMap"></property>
</bean>
使用 util:list util:map 标签必须引入相应的命名空间可以通过 idea 的提示功能选择

2.2.9 p命名空间

引入p命名空间后可以通过以下方式为bean的各个属性赋值

<bean id="studentSix" class="com.atguigu.spring.bean.Student"
p:id="1006" p:name="小明" p:clazz-ref="clazzOne" p:teacherMapref="teacherMap"></bean>

 2.2.10 引入外部属性文件

①加入依赖
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
②创建外部属性文件jdbc.properties
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
③引入属性文件
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
④配置 bean
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
⑤测试
@Test
public void testDataSource() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("springdatasource.xml");
DataSource dataSource = ac.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
}

2.2.11 bean的作用域

①概念
Spring 中可以通过配置 bean 标签的 scope 属性来指定 bean 的作用域范围各取值含义参加下表

如果是在WebApplicationContext环境下还会有另外两个作用域但不常用

②创建类 User
③配置 bean
<!-- scope属性取值singleton默认值bean在IOC容器中只有一个实例IOC容器初始化时创建
对象 -->
<!-- scope属性取值prototypebean在IOC容器中可以有多个实例getBean()时创建对象 -->
<bean class="com.atguigu.bean.User" scope="prototype"></bean>
④测试
@Test
public void testBeanScope(){
ApplicationContext ac = new ClassPathXmlApplicationContext("springscope.xml");
User user1 = ac.getBean(User.class);
User user2 = ac.getBean(User.class);
System.out.println(user1==user2);
}

2.2.12 bean的生命周期  

①具体的生命周期过程
  • bean对象创建调用无参构造器
  • bean对象设置属性
  • bean对象初始化之前操作由bean的后置处理器负责
  • bean对象初始化需在配置bean时指定初始化方法
  • bean对象初始化之后操作由bean的后置处理器负责
  • bean对象就绪可以使用
  • bean对象销毁需在配置bean时指定销毁方法
  • IOC容器关闭

②修改类User

注意其中的 initMethod() destroyMethod() 可以通过配置 bean 指定为初始化和销毁的方法

 ③配置bean

<!-- 使用init-method属性指定初始化方法 -->
<!-- 使用destroy-method属性指定销毁方法 -->
<bean class="com.atguigu.bean.User" scope="prototype" init-method="initMethod"
destroy-method="destroyMethod">
<property name="id" value="1001"></property>
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
<property name="age" value="23"></property>
</bean>
④测试
@Test
public void testLife(){
ClassPathXmlApplicationContext ac = new
ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ac.getBean(User.class);
System.out.println("生命周期4、通过IOC容器获取bean并使用");
ac.close();
}
bean 的后置处理器
bean 后置处理器会在生命周期的初始化前后添加额外的操作需要实现 BeanPostProcessor 接口且配置到IOC 容器中需要注意的是 bean后置处理器不是单独针对某一个bean生效而是针对IOC容器中所有bean都会执行创建bean 的后置处理器
package com.atguigu.spring.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("☆☆☆" + beanName + " = " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("★★★" + beanName + " = " + bean);
return bean;
}
}
IOC 容器中配置后置处理器
<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.atguigu.spring.process.MyBeanProcessor"/>

2.2.13 FactoryBean

①简介
FactoryBean Spring 提供的一种整合第三方框架的常用机制。和普通的 bean 不同配置一个FactoryBean类型的 bean 在获取 bean 的时候得到的并不是 class 属性中配置的这个类的对象而是getObject()方法的返回值。通过这种机制 Spring 可以帮我们把复杂组件创建的详细过程和繁琐细节都
屏蔽起来只把最简洁的使用界面展示给我们。
将来我们整合 Mybatis Spring 就是通过 FactoryBean 机制来帮我们创建 SqlSessionFactory 对象的。
②创建类 UserFactoryBean
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
③配置 bean
<bean id="user" class="com.atguigu.bean.UserFactoryBean"></bean>
④测试
@Test
public void testUserFactoryBean(){
//获取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("springfactorybean.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}

2.2.14 基于xml的自动装配

自动装配
根据指定的策略在 IOC 容器中匹配某一个 bean 自动为指定的 bean 中所依赖的类类型或接口类型属性赋值

①场景模拟

创建类UserController

public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}

 创建接口UserService

public interface UserService {
void saveUser();
}
创建类 UserServiceImpl 实现接口 UserService
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
创建接口 UserDao
public interface UserDao {
void saveUser();
}
创建类 UserDaoImpl 实现接口 UserDao
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
②配置 bean
使用 bean 标签的 autowire 属性设置自动装配效果
自动装配方式 byType
byType 根据类型匹配 IOC 容器中的某个兼容类型的 bean 为属性自动赋值
若在 IOC 中没有任何一个兼容类型的 bean 能够为属性赋值则该属性不装配即值为默认值null
若在 IOC 中有多个兼容类型的 bean 能够为属性赋值则抛出异常NoUniqueBeanDefinitionException
<bean id="userController"
class="com.atguigu.autowire.xml.controller.UserController" autowire="byType">
</bean>
<bean id="userService"
class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byType">
</bean>
<bean id="userDao" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl"></bean>
自动装配方式 byName
byName 将自动装配的属性的属性名作为 bean id IOC 容器中匹配相对应的 bean 进行赋值
<bean id="userController"
class="com.atguigu.autowire.xml.controller.UserController" autowire="byName">
</bean>
<bean id="userService"
class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byName">
</bean>
<bean id="userServiceImpl"
class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byName">
</bean>
<bean id="userDao" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoImpl" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl">
</bean>

 ③测试

@Test
public void testAutoWireByXML(){
ApplicationContext ac = new ClassPathXmlApplicationContext("autowirexml.xml");
UserController userController = ac.getBean(UserController.class);
userController.saveUser();
}

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring