Spring+SpringMVC+SpringBoot

Spring

bean

bean基础配置

bean别名配置

注意事项:

获取bean无论是通过id还是name获取。如果无法获取到,将抛出异常NoSuchBeanDefinitionException

bean的作用范围配置

适合交给容器进行管理的bean

表现层对象、业务层对象、数据层对象、工具对象

不适合交给容器进行管理的bean

封装实体的域对象

bean实例化的三种方式

bean实例化--构造方法

提供可访问的构造方法

配置

无参构造方法如果不存在,将抛出异常BeanCreationException

bean实例化--静态工厂(了解)

静态工厂

配置

bean实例化--实例工厂与FactoryBean(了解)

实例工厂

配置

FactoryBean

配置

bean的生命周期

生命周期:从创建到消亡的完整过程

bean生命周期:bean从创建到销毁的整体过程

bean生命周期控制:在bean创建后到销毁前做一些事情

提供生命周期控制方法

控制生命周期控制方法

核心容器

依赖注入方式

setter注入

构造器注入

自动装配

集合注入

  

加载properties文件

1.开启context命名空间

2.使用context空间加载properties文件

3.使用${}读取加载的属性值

加载properties文件

容器

容器初始化,创建容器

获取bean

容器类层次结构图

核心容器总结

容器相关

bean相关

依赖注入相关

注解

注解开发bean

纯注解开发模式

1.定义bean

@Component是通用的

@Controller

@Service

@Repository

2.纯注解开发

@Configuration注解用于设定当前类为配置类

@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

//加载配置文件初始化容器

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

变为

//加载配置类初始化容器

ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);

注解开发依赖注入

bean的作用范围

使用@Scope定义作用范围

使用@PostConstruct,@PreDestroy定义bean生命周期

@Scope("singleton")public class BookDaoImpl implements BookDao {public BookDaoImpl(){System.out.println("book dao constructor...");}@PostConstructpublic void init() {System.out.println("init");}@PreDestroypublic void destrop() {System.out.println("destrop");}}

注解开发依赖注入

使用@Autowired注解开启自动装配模式(按类型)
@Servicepublic class BookServiceImpl implements BookService {@Autowired@Qualifier("bookDao")private BookDao bookDao;public void save() {System.out.println("book service save ...");bookDao.save();}}

注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法

注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法

使用@Qualifier注解开启指定名称装配bean
@Service
public class BookServiceImpl implements BookService {@Autowired@Qualifier("bookDao")private BookDao bookDao;
}

注意:使用@Qualifier注解无法单独使用,必须配合@Autowired注解使用

使用@value实现简单注入
@Repository("bookDao")public class BookDaoImpl implements BookDao {@Value("itheima")private String name;public void save() {System.out.println("book dao save..." + name);}}

注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符*

注解开发管理第三方bean

第三方bean管理

1.使用@Bean配置第三方Bean

@Configurationpublic class SpringConfig {//1.定义一个方法获得要管理的对象//2.添加@Bean,表示该方法的返回值是一个bean@Beanpublic DataSource dataSource(){DruidDataSource ds=new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setName("root");ds.setPassword("root");return ds;}}

2.使用独立的配置类管理第三方Bean

将独立的配置类加入核心配置

方式一:导入式

public class jdbcConfig {@Beanpublic DataSource dataSource(){DruidDataSource ds=new DruidDataSource();/*ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setName("root");ds.setPassword("root");*///相关配置return ds;}
}

使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式

@Configuration@Import({jdbcConfig.class})public class SpringConfig {}

方式二:扫描式(不推荐)

@Configuration
public class jdbcConfig {@Beanpublic DataSource dataSource(){DruidDataSource ds=new DruidDataSource();/*ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/spring_db");ds.setName("root");ds.setPassword("root");*///相关配置return ds;}
}

使用@ComponetScan注解扫描配置类所在的包,加载对应的配置类信息

@Configuration@ComponentScan("com.itheima.config")public class SpringConfig {}

注解开发实现为第三方bean注入资源

1.简单类型的注入
public class jdbcConfig {@Value("com.mysql.jdbc.Driver")private String driver;@Value("jdbc:mysql://localhost:3306/spring_db")private String url;@Value("root")private String userName;@Value("root")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds=new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setName(userName);ds.setPassword(password);//相关配置return ds;}
}
2.引用类型的注入
public class jdbcConfig {@Beanpublic DataSource dataSource(BookDao bookDao){System.out.println(bookDao);DruidDataSource ds=new DruidDataSource();//相关配置return ds;}
}

引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象

@Configuration@Import({jdbcConfig.class})@ComponentScan("com.itheima")public class SpringConfig {}
@Repository("bookDao")public class BookDaoImpl implements BookDao { public void save() {System.out.println("book dao save..." );}}

注解开发总结

XML配置对比注解配置

整合

spring整合mybatis

思路分析

MyBatis程序核心对象分析

核心对象为SqlSessionFactoryBuilder

整合MyBatis

整合MyBatis

添加坐标,版本号要匹配

添加配置类替换xml文件

具体替换:

spring整合JUnit

添加依赖坐标

使用Spring整合JUnit专用的类加载器:@RunWith(SpringJUnit4ClassRunner.class)

指定spring的配置:@ContextConfiguration(classes=SpringConfig.class)

要测谁就自动装配谁:@Autowired

AOP

AOP简介

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

作用:在不惊动原始设计的基础上为其功能进行功能增强

Spring理念:无入侵式/无侵入式

AOP核心概念

连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等

在SpringAOP中,理解为方法的执行

切入点:PoingCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用。

匹配连接点的式子

在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法

通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)

在切入点出执行的操作,也就是共性操作

在SpringAOP中,功能最终以方法的形式呈现

切面:Aspect,描述通知与切入点的对应关系

通知类:定义通知的类

目标对象:Target,通知所应用的对象

原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的

代理:Proxy,目标对象无法直接完成工作,需要对其功能进行回填,通过原始对象的代理对象实现

AOP工作流程

1.Spring容器启动

2.读取所有切面配置中的切入点(只读取pt(),不读取ptx()

3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

*匹配失败,创建对象

*匹配成功,创建原始对象(目标对象)的代理对象

4.获取bean执行方法

*获取bean,调用方法并执行,完成操作

*获取bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

AOP切入点表达式

切入点:要增强的方法

切入点表达式:要进行增强的方法的描述方式

描述方式一:执行com.itheima.dao包下的BookDao接口中的无参数update方法

execution(void com.itheima.dao.BookDao.update())

描述方式二:执行com.itheima.dao.impl包下的BookDaoImpl类中的无参数update方法

切入带你表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution(public User com.itheima.service.UserService.findById(int))

动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点

访问修饰符:public,private等,可以省略

返回值

包名

类/接口名

方法名

参数

异常名:方法定义中抛出指定异常,可以省略

可以使用通配符描述切入点,快速描述

*:单个独立的任意符号,可以单独出现,也可以作为前缀或者后缀的匹配符出现

..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

+:专用于匹配子类类型

书写技巧

所有代码按照标准规范开发,否则以下技巧全部失效.

描述切入点通常描述接口,而不描述实现类.

访问控制修饰符针又书妾口开发均采用 public 描述(可省略访问控制修饰符描述).

返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述.

包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配.

接口名/类名书写名称与模块相关的采用*匹配,例如 Userservice 书写成* Service ,绑定业务层接口名.

方法名书写以动词进行精准匹配,名词采用*匹配,例如 getByld 书写成getBy*, selectAll 书写成 selectAll .

参数规则较为复杂,根据业务方法灵活调整.

通常不使用异常作为匹配规则

AOP通知类型

AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

AOP通知共分为5种类型

前置通知

后置通知

环绕通知(重点)

返回后通知(了解)

抛出异常后通知(了解)

AOP通知获取数据

获取切入点方法的参数

JoinPoint:适用于前置、后置、返回后、抛出异常后通知

ProceedJoinPoint::适用于环绕通知

获取切入点方法返回值

返回后通知

环绕通知

获取切入点方法运行异常通知

抛出异常后通知

环绕通知

事务

事务简介

事务作用:在数据层保障一系列的数据库操作同成功同失败

Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

1.在业务层接口上添加Spring事务管理

注意事项

Spring注解式事务通常添加在业务层接口中方而不会添加到业务层实现类中,降低耦合

注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务

2.设置事务管理器

注意事项

事务管理器要根据实现技术进行选择

MyBatis框架使用的是JDBC事务

3.开启注解式事务驱动

事务角色

事务角色

事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法,

事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法

事务属性

事务传播行为:事务协调员对事务管理员所携带事务的处理态度

SpringMVC

SpringMVC简介

SpringMVC概述

SpringMVC技术与Servlet技术功能等同,均属于web层开发技术

SpringMVC是一种基于Java实现MVC模型的轻量级Web框架

优点:
使用简单,开发便捷(相比于Servlet)

灵活性强

SpringMVC是一种表现出框架技术

SpringMVC用于进行表现出功能开发

入门案例

1.使用SpringMVC技术需要先导入SpringMVC坐标与Servlet坐标

2.创建SpringMVC控制器类(等同于Servlet功能)

3.初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean

4.初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求

注解

入门案例工作流程分析

启动服务器初始化过程

1.服务器启动,执行ServletContainersInitConfig类,初始化web容器

2.执行createServletApplicationContext方法,创建了WebApplicationContext对象

3.加载SpringMvcConfig

4.执行@ComponentScan加载对应的bean

5.加载UserController,每个@RequestMapping的名称对应一个具体的方法

6.执行getServletMappings方法,定义所有的请求都通过SpringMVC

单次请求过程

1.发送请求localhost/save

2.web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理

3.解析请求路径/save

4.由/save匹配执行对应的方法save()

5.执行save()

6.检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方

Controller加载控制

因为功能不同,如何避免Spring错误的加载到SpringMVC的bean——加载Spring控制的bean的时候排除掉SpringMVC控制的bean

SpringMVC相关bean加载控制

SpringMVC加载的bean对应的包均在com.itheima.controller包内

Spring相关bean加载控制

方式一:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean

方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等

方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中

请求与响应

请求映射路径

名称:@RequestMapping

类型:方法注解 类注解

位置:SpringMVC控制器方法定义上方

作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀

范例:

属性 value(默认):请求访问路径,或访问路径前缀

SpringBoot

SpringBoot入门

SpringBoot入门程序

1.创建新模块,选择spring初始化,并配置模块相关基础信息

2.选择当前模块需要使用的技术集

3.开发控制器类

4.运行自动生成的Application类

最简SpringBoot程序所包含的基础文件

1.pom.xml

2.Application类

Spring程序与SpringBoot程序对比

基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架结构

SpringBoot官网创建方式

1.进入官网,点击Quickstart Your Project

2.进行配置

3.添加依赖

4.生成zip压缩文件

SpringBoot项目快速启动

1.对SpringBoot项目打包(执行Maven构建指令package)

2.执行启动指令

注意事项:jar执行命令行启动需要依赖Maven插件支持,请确认打包时是否具有SpringBoot对应的Maven插件

SpringBoot简介

起步依赖

SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程

Spring程序缺点

配置繁琐

依赖设置繁琐

SpringBoot程序优点

自动配置

起步依赖(简化依赖配置)

辅助功能(内置服务器,......)

起步依赖

含start的为起步依赖

辅助功能之切换web服务器

辅助功能

SpringBoot程序启动

启动方式

SpringBoot在创建项目时,采用jar的打包方式

SpringBoot的引导类时项目的入口,运行main方法就可以启动项目

使用maven依赖管理变更起步依赖项

Jetty比Tomcat更轻量级,可扩展性更强(相较于Tomcat),谷歌应用引擎(GAE)已经全面切换为Jetty

基础配置

配置文件格式

修改服务器端口

SpringBoot提供了多种属性配置方式

1.application.properties

2.application.yml

3.application.yaml

自动提示功能消失解决方案

三种配置文件优先级

注意事项:

SpringBoot核心配置文件名为application

SpringBoot内置属性过度,且所有属性集中在一起修改,在使用时,通过提示键+关键字修改属性

yaml

yaml格式

yaml语法规则

yaml数组数据

数组数据在数据书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据间空格分隔

yaml数据读取方式

使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名......}

封装全部数据到Environment对象

自定义对象封装指定数据

注意事项

自定义对象封装数据警告解决方案

添加依赖

多环境启动

多环节开发配置

多环境启动

propertie文件多环境启动

多环节命令行启动参数配置

带参数启动SpringBoot

参数加载优先级顺序

多环节开发兼容问题

Maven与SpringBoot多环境兼容

1.Maven中设置多环境属性

2.SpringBoot中引用Maven属性

3.执行Maven打包指令

Maven指令执行完毕后,生成了对应的包,其中类参与编译,但是配置文件并没有编译,而是复制到包中

解决思路:对于源码中非java类的操作要求加载Maven对应的属性,解析${}占位符

4.对资源文件开启对默认占位符的解析

Maven打包加载到属性,打包顺利通过

配置文件分类

配置文件分类

在SpringBoot中提供有多级配置文件

整合第三方技术

SpringBoot整合junit

Spring整合junit(复习)

SpringBoot整合junit

SpringBoot整合mybatis

SpringBoot整合SSM

Spring整合mybatis(复习)

1.SpringConfig

导入JdbcConfig

导入MyBatisConfig

2.JDBCConfig

定义数据源(加载properties配置项:driver、url、username、password)

3.MyBatisConfig

定义SqlSessionFactoryBean

定义映射配置

SpringBoot整合MyBatis

1.创建新模块,选择Spring初始化,并配置模块相关基础信息

2.选择当前模块需要使用的技术集(MyBatis、MySQL)

3.设置数据源参数

注意事项:

SpringBoot版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在url连接串中配置时区

或在MySQL数据库端配置时区解决此问题

4.定义数据层接口与映射配置

5.测试类中注入dao接口,测试功能

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《opencv实用探索·十九》光流法检测运动目标

前言 光流法(Optical Flow)是计算机视觉中的一种技术,用于估计图像中相邻帧之间的像素位移或运动。它是一种用于追踪图像中物体运动的技术,可以在视频中检测并测量物体的运动轨迹。 光流的直观理解: 光流是一个视频中两…

智能物联网(IoT)VS AI物联网(AIoT)

#IoT# #AIoT# 智能物联网(IoT)和AI物联网(AIoT)区别 概念: 物联网(IoT):即“万物相连的互联网”,是在互联网基础上延伸和扩展的网络,将各种信息传感设备与网…

离散型制造企业MES系统行业应用

离散型制造企业具有产品种类多、生产周期长、生产过程复杂等特点,因此,采用先进的生产管理系统对于提高企业的生产效率和管理水平至关重要。其中,制造执行系统(MES)在离散型制造企业中得到了广泛应用, 一、…

TensorBoard使用和问题解决

一、什么是TensorBoard? TensorBoard 是一组用于数据可视化的工具,它包含在流行的开源机器学习库 Tensorflow 中。TensorBoard 的主要功能包括: 可视化模型的网络架构跟踪模型指标,如损失和准确性等检查机器学习工作流程中权重、偏差和其他…

PDI/Kettle-9.2.0.0-R(对应jdk1.8)源码编译问题记录及源码结构简介

目录 📚第一章 前言📗背景📗目的📗总体方向 📚第二章 代码结构初识基本结构📗代码模块详情 ⁉️问题记录❓问题一:代码分支哪些是发布版本❗答:后缀-R的版本 ❓问题二:50…

Milesight VPN server.js 任意文件读取漏洞(CVE-2023-23907)

0x01 产品简介 MilesightVPN 是一款软件,一个 Milesight 产品的 VPN 通道设置过程更加完善,并可通过网络服务器界面连接状态。 0x02 漏洞概述 MilesightVPN server.js接口处存在文件读取漏洞,攻击者可通过该漏洞读取系统重要文件&#xff…

dockerfite创建镜像---INMP+wordpress

目录 搭建dockerfile---lnmp 创建nginx镜像 运行 创建数据库镜像 运行 ​编辑 创建php镜像 运行 搭建dockerfile---lnmp 在192.168.10.201 服务IP地址nginx 172.111.0.10 dockernginxmysql172.111.0.20dockermysqlphp172.111.0.30dockerphp 创建nginx镜像 路径 vim /…

解锁知识的新大门:自建知识付费小程序的技术指南

在数字化时代,知识付费小程序的崛起为创作者和学习者提供了全新的学习和分享方式。本文将以“知识付费小程序源码”为关键词,从技术角度出发,为你展示如何搭建一个独具特色的知识付费平台。 步骤1:选择适用的知识付费小程序源码…

Android蓝牙协议栈fluoride(七) - 设备管理(bt stack)

前面几篇文章介绍了设备管理对上层的接口和bt profile层的实现,其中涉及到蓝牙开关(初始化)、设备扫描、服务发现、安全管理、功耗管理等等模块,本文将挑选几个模块介绍bt stack层的实现,其他模块(如安全管理)将在后续专门讲述。 使能蓝牙 …

【Java】构建表达式二叉树和表达式二叉树求值

问题背景 1. 实现一个简单的计算器。通过键盘输入一个包含圆括号、加减乘除等符号组成的算术表达式字符串,输出该算术表达式的值。要求: (1)系统至少能实现加、减、乘、除等运算; (2)利用二叉…

Windows更改远程桌面端口并添加防火墙入站规则

1.运行 快捷键winR组合键,win就是键盘上的windows系统图标键。 2.打开注册表 Regedit,在对话框中输入regedit命令,然后回车 3.打开注册表,输入命令后,会打开系统的注册表,左边是目录栏,右边是…

windows安装sqlserver2008后连接失败问题

刚安装好的sqlserver在安装服务器上,直接使用Windows身份认证登录就报错 未找到或无法访问服务器。请验证实例名称是否正确并且SQL Server已配置为允许远程连接。(provider:命名管道提供程序,error:40 -无法打开到SQLS…

GroupMixFormer:基于Group-Mix注意力的视觉Transformer

文章目录 摘要1、简介2、相关工作2.1、视觉转换器2.2、全面的自注意力建模 3、组混合注意力和GroupMixFormer3.1. 动机:从个体到群体3.2. GMA: 混合组以获得更好的注意力3.3. 架构配置 4、实验4.1、实现细节4.2. 与最先进模型的比较4.3. 消融实验 5、结论 摘要 htt…

使用opencv的Sobel算子实现图像边缘检测

1 边缘检测介绍 图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题,也是经典的技术难题之一。如何快速、精确地提取图像边缘信息,一直是国内外的研究热点,同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法…

【教程】源代码加密、防泄密软件

​ 什么是代码混淆? 代码混淆 是一种将应用程序二进制文件转换为功能上等价,但人类难于阅读和理解的行为。在编译 Dart 代码时,混淆会隐藏函数和类的名称,并用其他符号替代每个符号,从而使攻击者难以进行逆向工程。 …

VBA_MF系列技术资料1-242

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧,我参考大量的资料,并结合自己的经验总结了这份MF系列VBA技术综合资料,而且开放源码(MF04除外),其中MF01-04属于定…

C#科学绘图之scottPlot绘制多个图像

文章目录 示例移除图像图例信号图 scott系列:绘图初步 示例 从名字就能看出,ScottPlot的绘图函数AddScatter的作用是为图窗添加数据点,换言之,每调用一次AddScatter,就可以在图窗中添加一组图像。下面添加两个按钮&a…

CS5565设计资料|CS5565规格书|typec转HDMI 8k60Hz方案

CS556x是一款高性能的Type-C/DisplayPort1.4到HDMI2.1协议转换器,可通过Type-C/ DisplayPort链路接收视频和音频流,并转换为支持TMDS或FRL输出信令的HDMI。DP接收器在4个通道上支持高达8.1Gbps的链路速率。HDMI输出端口可用作TMDS或FRL发射器。FRL发射器…

天猫数据分析(天猫数据查询平台):11月天猫啤酒市场销售数据分析报告

在酒类市场中,被视作“气氛担当”的啤酒,是派对聚会或者自饮场景中的常客,消费人群广泛,如今,啤酒市场已进入存量时代,市场中啤酒的销售也在稳步增长。 鲸参谋数据显示,今年11月份,天…

技术分享 | app测试中常用的Android模拟器

Emulator Emualor 是 Android Studio 自带的模拟器,是官方提供的工具,Android 开发最常使用的就是这一款。 它功能非常齐全,电话本、通话等功能都可正常使用。用户可以使用键盘输入,鼠标点击模拟器按键输入,甚至还可以…