- Published on
java经典框架-springboot (续)
- Authors

- Name
- MissTree
Redis
说明:在 SpringBoot2.x之后,原来使用的jedis 被替换为了 lettuce?
- jedis:采用的査连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool 连接池! 更像 B0 模式
- lettuce:采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像 Nio 横式
基本使用
// 1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
// 2. 配置 Redis 连接信息
spring.redis.host=127.0.0.1
spring.redis.port=6379
// 3. 使用 RedisTemplate 操作 Redis
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@GetMapping("/redis")
public String redis() {
redisTemplate.opsForValue().set("key", "value");
Object value = redisTemplate.opsForValue().get("key");
return value.toString();
}
}
RedisTemplate 是 Spring Boot 提供的一个用于操作 Redis 的模板类,它封装了 Redis 的各种操作,可以方便地进行数据的读写操作。但是在实际开发中往往不太适合我们企业级自定义的开发,所以一般我们都是自定义 RedisTemplate 的配置类的序列化,以便在本地redis-cli可以直接查看而不是查看转译的。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 设置键的序列化方式(String)
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
// 设置值的序列化方式(JSON)
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
// 设置默认序列化方式(可选)
template.setDefaultSerializer(jsonSerializer);
// 初始化 RedisTemplate 的其他配置
template.afterPropertiesSet();
return template;
}
}
redis工具类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// ==================== String 相关操作 ====================
/**
* 设置键值对(默认永不过期)
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 设置键值对,并设置过期时间
*/
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
/**
* 获取键对应的值
*/
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
// ==================== Hash 相关操作 ====================
/**
* 设置 Hash 字段
*/
public void hSet(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
/**
* 获取 Hash 字段值
*/
public Object hGet(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}
/**
* 获取整个 Hash
*/
public Object hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
// ==================== List 相关操作 ====================
/**
* 左端插入 List
*/
public void lPush(String key, Object value) {
redisTemplate.opsForList().leftPush(key, value);
}
/**
* 右端插入 List
*/
public void rPush(String key, Object value) {
redisTemplate.opsForList().rightPush(key, value);
}
/**
* 获取 List 范围内的元素
*/
public Object lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
// ==================== Set 相关操作 ====================
/**
* 添加 Set 元素
*/
public void sAdd(String key, Object value) {
redisTemplate.opsForSet().add(key, value);
}
/**
* 获取 Set 所有元素
*/
public Object sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
// ==================== ZSet 相关操作 ====================
/**
* 添加 ZSet 元素(带分数)
*/
public void zAdd(String key, double score, Object value) {
redisTemplate.opsForZSet().add(key, value, score);
}
/**
* 获取 ZSet 范围内的元素(按分数升序)
*/
public Object zRange(String key, long start, long end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
// ==================== 通用操作 ====================
/**
* 删除键
*/
public boolean delete(String key) {
return Boolean.TRUE.equals(redisTemplate.delete(key));
}
/**
* 判断键是否存在
*/
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
/**
* 设置过期时间
*/
public boolean expire(String key, long timeout, TimeUnit unit) {
return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
}
/**
* 获取剩余过期时间
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 清空所有 Redis 数据(慎用!)
*/
public void flushDB() {
redisTemplate.execute((connection) -> {
connection.flushDb();
return null;
});
}
}
任务
异步任务
在执行一些不需要立即返回结果的任务时,可以使用异步任务来提高系统的并发性能。Spring Boot 提供了异步任务的支持,可以通过以下步骤实现异步任务:
// 1. 在启动类上添加 @EnableAsync 注解开启异步任务支持
@SpringBootApplication
@EnableAsync
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
// 2. 在需要异步执行的方法上添加 @Async 注解
@Service
public class AsyncService {
@Async
public void asyncMethod() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test");
}
}
// 3. 调用异步方法
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async")
public String async() {
asyncService.asyncMethod();
return "success";
}
}
定时任务
// 1. 在启动类上添加 @EnableScheduling 注解开启定时任务支持
@SpringBootApplication
@EnableScheduling
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
// 2. 在需要定时执行的方法上添加 @Scheduled 注解
@Service
public class ScheduledService {
@Scheduled(cron = "0/5 * * * * ?") // 每5秒执行一次 0 */5 * * * ? 每5分钟执行一次
// @Scheduled(cron = "0 * 19 * * ?") 每分钟执行一次 0 15 10 ? *1-6 每个月的周一到周六 10.15分钟执行一次
public void scheduledMethod() {
System.out.println("定时任务执行");
}
}
邮件任务
// 1. 在 application.properties 中配置邮件服务器信息
spring.mail.host=smtp.example.com // SMTP 服务器地址 smtp.qq.com
spring.mail.username=your-email@example.com
spring.mail.password=your-email-password // QQ邮箱授权码
spring.mail.properties.mail.smtp.ssl.enable=true
// 2. 创建邮件发送器
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
@Service
public class EmailService {
@Autowired
private JavaMailSender mailSender;
public void sendEmail() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom("515072052@qq.com"); // 确保发件人地址与配置文件
mailMessage.setSubject("测试邮件");
mailMessage.setText("测试邮件内容");
mailMessage.setTo("515072052@qq.com");
mailSender.send(mailMessage);
}
// 复杂邮件发送
public void sendComplexEmail() {
MimeMessage message = mailSender.createMimeMessage();
// 组装
MimeMessageHelper helper = new MimeMessageHelper(message, true);
// 正文
helper.setText("<html><head></head><body><h1 style='color:red'>你好,这是一个HTML文本</h1></body></html>", true);
helper.setSubject("测试邮件");
// 附件
helper.addAttachment("test.txt", new java.io.File("C:\\Users\\admin\\Desktop\\服务器常用命令.txt"));
helper.addAttachment("test.png", new java.io.File("C:\\Users\\admin\\Pictures\\22222222222.jpg"));
helper.setFrom("515072052@qq.com");
helper.setTo("8xxxx48@qq.com");
}
}
// 3. 调用邮件发送器发送邮件
@RestController
public class EmailController {
@Autowired
private EmailService emailService;
@GetMapping("/sendEmail")
public String sendEmail() {
emailService.sendComplexEmail();
return "success";
}
}
注意 :QQ邮箱需要开启 SMTP 服务,并获取授权码作为密码,而不是邮箱密码。application中的username看情况定,可能有几个邮箱别名,要使用当前授权码的邮箱别名。

分布式
分布式系统由多个独立计算机(节点)组成,通过网络协同工作,对外表现为一个整体系统。每个节点可以独立部署、扩展和维护。
单一系统、分布式、云服务 | 特性 | 单一系统 | 分布式系统 | 云开发 | | --- | --- | --- | --- | | 架构 | 单体 | 微服务/分布式 | 基于云服务(IaaS/PaaS/Serverless) | | 扩展性 | 差(垂直扩展) | 高(水平扩展) | 极高(自动扩缩容) | | 运维复杂度 | 低 | 高 | 低(云平台托管) | | 成本 | 固定(硬件投入) | 较高(需管理多节点) | 按需付费(灵活控制) | | 适用场景 | 小型应用 | 大型企业级系统 | 快速开发、初创公司 | | | | | |
未来趋势
- 云原生(Cloud Native):微服务 + 容器化(Docker/K8s) + Serverless。
- 边缘计算(Edge Computing):计算靠近数据源(如 IoT 设备)。
- Serverless 进化:更多 BaaS 服务,降低开发门槛。
RPC
RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范,它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的的数,本质上编写的调用代码基本相同。 也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。为什么要用RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如不同的系统间的通讯,甚至不同的组织间的通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用,RPC就是要像调用本地的函数一样去调远程函数;
逻辑流程:
- 将服务器B上的函数注册到服务器A上,作为一个provider。
- 客户端H在服务器A上调用函数,就像调用本地函数一样,实际上是通过网络调用服务器B上的函数,服务器A上有一个proxy,负责接收客户端的请求,并将请求转发给服务器B上的provider给客户端H消费。
常见框架:
- gRPC(微服务、跨语言推荐):基于 HTTP/2 和 Protocol Buffers,支持多种语言。
- Thrift:由 Facebook 开发,支持多种语言,使用 IDL 定义接口。
- Dubbo(推荐):阿里巴巴开源的 RPC 框架,支持多种协议和负载均衡策略的Java微服务。
- Spring Feign(spring cloud推荐):基于 HTTP 的 RPC 框架,支持 Spring Cloud。
Dubbo
Dubbo 是阿里巴巴开源的 RPC 框架,支持多种协议和负载均衡策略的Java微服务。Dubbo 提供了服务治理、服务发现、负载均衡等功能,可以帮助开发者构建高性能、高可用的分布式系统。提供了多种多种语言,如 Java、Go、Node、Python 等支持。
插件 zkclient-spring-boot-starter 和 dubbo-spring-boot-starter 在 spring boot 项目使用时会有坑点,比如日志会有冲突:解决办法,在依赖中使用 exclusions 标签排除 slf4j-log4j12 依赖。
使用方式:
- zookeeper 作为注册中心
- dubbo-admin 作为监控中心,查看哪些服务被注册,哪些服务被调用
- dubbo jar包作为服务提供者
步骤:
- 服务提供者:引入依赖,配置文件,实现接口,启动类添加注解
- 引入依赖
<!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.2.0</version> </dependency> <!-- Zookeeper 客户端 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.3.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>5.3.0</version> </dependency>- 2.配置注册中心的地址,以及服务发现名,和要扫描的包
# application.yml spring: application: name: dubbo-provider-demo dubbo: application: name: dubbo-provider-demo protocol: name: dubbo port: 20880 registry: address: zookeeper://127.0.0.1:2181 scan: base-packages: com.example.provider.service- 3.在想要被注册的服务上面~增加一个注解 @Service (使用的是dubbo的而不是spring的)
// UserService.java public interface UserService { String getUserName(Long id); List<String> getUserRoles(Long id); } // UserServiceImpl.java import org.apache.dubbo.config.annotation.DubboService; @DubboService public class UserServiceImpl implements UserService { @Override public String getUserName(Long id) { return "User-" + id; } @Override public List<String> getUserRoles(Long id) { return Arrays.asList("admin", "user"); } } - 消费者使用
- 引入依赖
- 配置注册中心的地址,配置自己的股务名~
# application.yml spring: application: name: dubbo-consumer-demo dubbo: application: name: dubbo-consumer-demo registry: address: zookeeper://127.0.0.1:2181 consumer: check: false- 在需要使用的地方使用 @Reference 注解,指定要使用的服务名,就可以直接使用这个服务了
// UserServiceConsumer.java import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.stereotype.Component; @Component public class UserServiceConsumer { @DubboReference private UserService userService; public String getUserName(Long id) { return userService.getUserName(id); } public List<String> getUserRoles(Long id) { return userService.getUserRoles(id); } } // UserController.java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { private final UserServiceConsumer userServiceConsumer; public UserController(UserServiceConsumer userServiceConsumer) { this.userServiceConsumer = userServiceConsumer; } @GetMapping("/user/{id}/name") public String getUserName(@PathVariable Long id) { return userServiceConsumer.getUserName(id); } @GetMapping("/user/{id}/roles") public List<String> getUserRoles(@PathVariable Long id) { return userServiceConsumer.getUserRoles(id); } }
这样,消费者就可以通过 Dubbo 框架调用服务提供者的服务了。若是服务提供者有多个实例,Dubbo 会自动进行负载均衡,选择一个最优的实例进行调用。 如果想看可视化监控,可以使用 dubbo-admin。 dubbo-admin支持如下功能:
- 服务治理:查看服务提供者和消费者的信息,查看服务调用的实时数据。
- 服务监控:查看服务调用的实时数据,包括调用次数、调用时间等。
- 服务测试:可以测试服务是否可用,以及服务的性能。
- 服务配置:可以配置服务的参数,修改服务负载均衡的权重。
Nacos
Nacos 是阿里巴巴开源的微服务注册中心,支持服务发现、配置管理、动态 DNS 解析等功能。Nacos 可以帮助开发者构建高性能、高可用的分布式系统。 在上面的例子中,我们使用了 Zookeeper 作为注册中心,现在我们使用 Nacos 作为注册中心。
<!-- pom.xml -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.0</version>
</dependency>
配置
# application.yml provider
spring:
application:
name: dubbo-provider-demo
dubbo:
application:
name: dubbo-provider-demo
protocol:
name: dubbo
port: 20880
registry:
address: nacos://127.0.0.1:8848
parameters:
namespace: public
scan:
base-packages: com.example.provider.service
# application.yml consumer
spring:
application:
name: dubbo-consumer-demo
dubbo:
application:
name: dubbo-consumer-demo
registry:
address: nacos://127.0.0.1:8848
parameters:
namespace: public
consumer:
check: false
接口和实现类不变,只需要修改注册中心的地址即可。
Swagger
Swagger 是一个用于生成API文档的工具,它可以帮助开发人员快速地创建和查看API文档,并且可以与Spring Boot无缝集成。
安装依赖
<!-- spring 2.x 版本swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!-- spring 3.x 版本 swagger -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version> <!-- 使用最新版本 -->
</dependency>
配置
spring:
autoconfigure:
// 若是配置了 security 要关闭,否则要权限
exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
springdoc:
swagger-ui:
path: /swagger-ui.html # Swagger UI 访问路径
tags-sorter: alpha # 按字母排序标签
operations-sorter: alpha
api-docs:
path: /v3/api-docs # OpenAPI JSON 路径
default-produces-media-type: application/json
info:
title: Spring Boot 3 API
version: 1.0.0
description: API Documentation
项目目录下的config文件夹配置
// spring 2.x 版本swagger
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
// 通过 grounpName 配置多个模块分组 要求有多个Docket
.grounpName("default")
//为当前包路径 指定扫描的controller
.apis(RequestHandlerSelectors.basePackage("com.misstree.demo.controller"))
// 过滤规则
.paths(PathSelectors.any())
.build();
}
// 配置swagger 信息apiInfo()用来创建该API的基本信息(这些信息会展示在文档页面中)
public ApiInfo apiInfo() {
// 作者信息
Contact contact = new Contact("misstree", "https://github.com/misstree", "misstree@163.com");
return new ApiInfo(
title: "Spring Boot 2 API",
description: "API Documentation",
version: "1.0.0",
contact: contact,
license: new License("Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0.html"),
licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0.html",
new ArrayList()
)
}
}
}
// spring 3.x 版本 swagger
import org.springframework.context.annotation.Profile;
@Configuration
@Profile({"dev", "test"}) // 仅在 dev 和 test 环境下启用,生产环境不启用保证安全和性能
public class SwaggerConfig {
@Bean
public OpenApi openApi() {
return new OpenApi()
.info(new Info()
.title("Spring Boot 3 API")
.version("1.0.0")
.description("API Documentation")
);
}
}
使用
// spring 2.x 版本swagger
package com.example.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Operation(summary = "获取用户信息", description = "根据用户 ID 获取用户信息")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取用户信息"),
@ApiResponse(responseCode = "404", description = "用户未找到")
})
@GetMapping("/users/{id}")
public String getUser(@PathVariable @Parameter(description = "用户 ID") Long id) {
return "User with id: " + id;
}
}
// spring 3.x 版本swagger
package com.misstree.demo.controller;
import com.misstree.demo.commentsMaper.Comments;
import com.misstree.demo.mapper.CommentsMaper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@RestController
// 功能模块的名称 描述
@Tag(name = "评论模块", description = "用于博客文章的评论管理")
@RequestMapping("/comments") // 接口请求路径前缀
public class CommentsController {
@Autowired
private CommentsMaper commentsMaper;
@GetMapping("/getAllComments")
// 描述接口的功能
@Operation(summary = "获取评论列表")
// 设置 dto 方式请求数据
@Parameters({
@Parameter(name = "name", description = "评论人姓名"),
@Parameter(name = "content", description = "评论内容"),
@Parameter(name = "time", description = "评论时间"),
})
// 设置响应的结果
@ApiResponses({
@ApiResponse(responseCode = "200", description = "获取评论列表成功"),
@ApiResponse(responseCode = "400", description = "参数错误"),
@ApiResponse(responseCode = "401", description = "没有权限"),
@ApiResponse(responseCode = "403", description = "禁止访问"),
})
public List<Comments> getComments(){
return commentsMaper.gets();
}
}
访问
http://localhost:8080/swagger-ui.html 
打包与部署
在项目开发完成后,需要使用 Maven 或 Gradle 构建工具将项目打包成可执行的 jar 文件,并部署到服务器上运行。
Maven
Maven下载对应的压缩文件,windows 下载 apache-maven-3.x.x-bin.zip 文件。将下载好的文件解压到指定目录D:\apache-maven-3.9.9,配置环境变量D:\apache-maven-3.9.9\bin,在命令行输入 mvn -v 查看是否配置成功。
在项目文件夹中执行命令
mvn clean package -Dmaven.test.skip=true
-Dmaven.test.skip=true 跳过测试
这样在target 目录下会生成一个可执行的 jar 文件,使用命令 java -jar xxx.jar 启动项目。
修改服务配置
- java -jar xxx.jar --server.port=8880
- java -jar xxx.jar --spring.profiles.active=dev
- java -jar xxx.jar --server.port=8880 --spring.profiles.active=dev --spring.datasource.url=jdbc:mysql://localhost:3306/test
项目配置优先级:
命令行参数 > 环境变量 > 配置文件 > 默认配置
- 命令行参数:java -jar xxx.jar --server.port=8880
- 环境变量:在启动项目时,通过设置环境变量来传递配置参数,例如:export JAVA_OPTS="-Dserver.port=8880"
- 配置文件:在项目打包好文件
target层级下设置,例如:application.properties 或 application.yml,本质的等同于之前说的 配置文件优先级的设置 - 默认配置:在 Spring Boot 的默认配置resources/application.properties 中设置参数