Java17 升级到 Java21,修改了一些兼容性问题

This commit is contained in:
Tian jianyong 2025-08-08 17:45:43 +08:00
parent abd60d22b4
commit 270ec9cf1f
27 changed files with 218 additions and 96 deletions

74
doc/JDK21-升级指南.md Normal file
View File

@ -0,0 +1,74 @@
# JDK21 升级指南
本文档提供了将项目从JDK17升级到JDK21的详细步骤和注意事项。
## 已完成的升级工作
1. **更新Java版本配置**
- 在根`pom.xml`中将`java.version`从17更新到21
- 更新Maven编译器插件配置以支持JDK21
2. **启用虚拟线程**
- 在`QuapApplication.java`中添加虚拟线程支持
- 创建`Java21Config.java`配置类,为异步任务和定时任务启用虚拟线程
- 更新`application.yml`中的Tomcat配置优化虚拟线程环境下的性能参数
3. **更新测试类**
- 在`TrafficLightSignalParserEnhancedTest.java`中添加使用JDK21新特性的测试方法
- 使用虚拟线程进行并发测试
- 使用参数化测试简化测试代码
## 升级后的新特性
JDK21提供了多项重要的新特性本项目已经利用了以下几项
1. **虚拟线程Project Loom**
- 轻量级线程实现,可以创建数百万个线程而不会耗尽系统资源
- 特别适合IO密集型应用如Web服务器、数据库访问等
- 已在Spring MVC请求处理、异步任务和定时任务中启用
2. **结构化并发**
- 简化并发编程模型,使并发代码更易于理解和维护
- 提供更好的错误处理和取消传播机制
3. **记录模式匹配**
- 简化数据处理代码
- 使代码更简洁、更不易出错
## 后续工作
虽然基本的升级工作已经完成,但还有一些优化工作可以进一步提升系统性能:
1. **进一步优化数据库访问**
- 使用虚拟线程优化数据库连接池配置
- 考虑使用响应式编程模型进一步提高性能
2. **利用更多JDK21特性**
- 使用字符串模板简化日志和消息格式化
- 使用外部函数和内存API优化本地代码集成
3. **性能测试和监控**
- 进行全面的性能测试比较JDK17和JDK21的性能差异
- 监控虚拟线程的使用情况和系统资源消耗
## 注意事项
1. **兼容性问题**
- 如果遇到第三方库兼容性问题,可能需要更新这些库到最新版本
- 某些使用了线程本地变量(ThreadLocal)的代码可能需要调整
2. **调试和监控**
- 虚拟线程的调试和监控与传统线程有所不同
- 使用JDK21提供的新工具进行性能分析和问题排查
3. **部署要求**
- 确保生产环境已安装JDK21
- 更新CI/CD流程以使用JDK21进行构建和测试
## 参考资料
- [JDK21官方文档](https://docs.oracle.com/en/java/javase/21/)
- [Spring Boot与虚拟线程](https://spring.io/blog/2022/10/11/embracing-virtual-threads)
- [JEP 444: Virtual Threads](https://openjdk.org/jeps/444)
- [JEP 440: Record Patterns](https://openjdk.org/jeps/440)
- [JEP 430: String Templates](https://openjdk.org/jeps/430)

View File

@ -12,7 +12,7 @@
<qaup.version>1.0.1</qaup.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<java.version>21</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<mybatis-spring-boot.version>3.0.4</mybatis-spring-boot.version>
<druid.version>1.2.25</druid.version>
@ -297,7 +297,7 @@
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.5.3</version>
<configuration>
<jvmArguments>--enable-native-access=ALL-UNNAMED</jvmArguments>
<jvmArguments>--enable-native-access=ALL-UNNAMED --enable-preview</jvmArguments>
</configuration>
</plugin>
</plugins>

View File

@ -100,6 +100,9 @@
</goals>
</execution>
</executions>
<configuration>
<jvmArguments>--enable-native-access=ALL-UNNAMED --enable-preview</jvmArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@ -5,8 +5,15 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.concurrent.Executors;
/**
* 启动程序
*
@ -14,7 +21,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@EnableScheduling
public class QuapApplication
{
private static final Logger logger = LoggerFactory.getLogger(QuapApplication.class);
@ -29,6 +35,27 @@ public class QuapApplication
logger.info("✅ 后台管理系统启动成功!");
logger.info("🌐 访问地址: http://localhost:8080");
logger.info("🔗 WebSocket端点: ws://localhost:8080/collision");
logger.info("🧵 使用JDK21虚拟线程处理请求");
System.out.println("(♥) 后台管理系统启动成功\n");
}
/**
* 配置Spring MVC使用虚拟线程处理请求
* JDK21虚拟线程可以显著提高并发性能
*/
@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor asyncTaskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
/**
* 配置Tomcat使用虚拟线程处理请求
*/
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
logger.info("配置Tomcat使用虚拟线程池");
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}

View File

@ -13,12 +13,10 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.LocalDateTime;
@ -48,9 +46,6 @@ public class FlywayMigrationHandler implements ApplicationRunner, Callback {
@Autowired
private DataSource dataSource;
@Autowired
private ApplicationContext applicationContext;
@Value("${spring.profiles.active:dev}")
private String activeProfile;

View File

@ -0,0 +1,72 @@
package com.qaup.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* JDK21特性配置类
*
* 配置系统使用JDK21的虚拟线程和其他新特性
*
* @author qaup
*/
@Configuration
@EnableAsync
public class Java21Config implements AsyncConfigurer {
private static final Logger logger = LoggerFactory.getLogger(Java21Config.class);
/**
* 配置异步任务执行器使用虚拟线程
* 虚拟线程适合IO密集型任务可以创建数百万个线程而不会耗尽系统资源
*/
@Override
public Executor getAsyncExecutor() {
logger.info("配置异步任务执行器使用虚拟线程");
return Executors.newVirtualThreadPerTaskExecutor();
}
/**
* 配置定时任务执行器使用虚拟线程
* 适用于@Scheduled注解的任务
*/
@Bean(name = "scheduledTaskExecutor")
public Executor scheduledTaskExecutor() {
logger.info("配置定时任务执行器使用虚拟线程");
return Executors.newVirtualThreadPerTaskExecutor();
}
/**
* 配置通用线程池使用虚拟线程
* 用于需要手动提交任务的场景
*/
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
logger.info("配置通用线程池使用虚拟线程");
return Executors.newVirtualThreadPerTaskExecutor();
}
/**
* 配置兼容性线程池
* 用于不适合虚拟线程的场景如CPU密集型任务
*/
@Bean(name = "compatibilityExecutor")
public Executor compatibilityExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("compat-");
executor.initialize();
logger.info("配置兼容性线程池,核心线程数: {}", Runtime.getRuntime().availableProcessors());
return executor;
}
}

View File

@ -59,13 +59,15 @@ public class CacheController
result.put("dbSize", dbSize);
List<Map<String, String>> pieList = new ArrayList<>();
commandStats.stringPropertyNames().forEach(key -> {
Map<String, String> data = new HashMap<>(2);
String property = commandStats.getProperty(key);
data.put("name", StringUtils.removeStart(key, "cmdstat_"));
data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
pieList.add(data);
});
if (commandStats != null) {
commandStats.stringPropertyNames().forEach(key -> {
Map<String, String> data = new HashMap<>(2);
String property = commandStats.getProperty(key);
data.put("name", StringUtils.removeStart(key, "cmdstat_"));
data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
pieList.add(data);
});
}
result.put("commandStats", pieList);
return AjaxResult.success(result);
}

View File

@ -24,12 +24,13 @@ server:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
accept-count: 2000
# 使用JDK21虚拟线程不需要限制线程数
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 虚拟线程模式下可以处理更多请求
max: 2000
# Tomcat启动初始化的线程数
min-spare: 50
# 日志配置
logging:

View File

@ -2,7 +2,6 @@ package com.qaup.collision.common.adapter;
import com.qaup.collision.common.model.spatial.VehicleLocation;
import com.qaup.system.domain.SysVehicleInfo;
import com.qaup.system.domain.SysVehicleType;
import com.qaup.system.service.ISysVehicleInfoService;
import com.qaup.system.service.ISysDriverInfoService;
import com.qaup.system.service.ISysVehicleTypeService;
@ -104,29 +103,6 @@ public class QuapDataAdapter {
}
}
/**
* 查询指定类型的所有车辆向后兼容方法
*
* @param typeId 车辆类型ID已废弃用于向后兼容
* @return 车辆信息列表
* @deprecated 使用 findVehiclesByTypeCode(String typeCode) 代替
*/
@Deprecated
public List<SysVehicleInfo> findVehiclesByType(Long typeId) {
try {
// 通过ID查找类型编码然后使用新方法
SysVehicleType vehicleType = vehicleTypeService.selectSysVehicleTypeById(typeId);
if (vehicleType != null && vehicleType.getTypeCode() != null) {
return findVehiclesByTypeCode(vehicleType.getTypeCode());
}
logger.warn("未找到车辆类型typeId: {}", typeId);
return List.of();
} catch (Exception e) {
logger.error("根据类型ID查询车辆信息失败typeId: {}", typeId, e);
return List.of();
}
}
/**
* 查询所有车辆信息
*

View File

@ -2,7 +2,6 @@ package com.qaup.collision.common.service;
import com.qaup.collision.common.model.spatial.VehicleLocation;
import com.qaup.collision.common.model.repository.VehicleLocationRepository;
import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.common.adapter.QuapDataAdapter;
import com.qaup.collision.rule.event.RuleViolationEvent;

View File

@ -281,7 +281,7 @@ public class DataCollectorDao {
return null;
}
String url = UriComponentsBuilder.fromHttpUrl(vehicleBaseUrl)
String url = UriComponentsBuilder.fromUriString(vehicleBaseUrl)
.path("/runwayPathPlanningController/findArrTaxiwayByRunwayAndContactCrossAndSeat")
.queryParam("inRunway", inRunway)
.queryParam("outRunway", outRunway)
@ -334,7 +334,7 @@ public class DataCollectorDao {
return null;
}
String url = UriComponentsBuilder.fromHttpUrl(vehicleBaseUrl)
String url = UriComponentsBuilder.fromUriString(vehicleBaseUrl)
.path("/runwayPathPlanningController/findDepTaxiwayByRunwayAndContactCrossAndSeat")
.queryParam("inRunway", inRunway)
.queryParam("outRunway", outRunway)

View File

@ -4,7 +4,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;

View File

@ -4,7 +4,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 航空器状态API响应DTO

View File

@ -7,9 +7,7 @@ import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

View File

@ -22,7 +22,6 @@ import com.qaup.collision.websocket.event.GeofenceAlertWebSocketEvent;
import com.qaup.collision.websocket.message.GeofenceAlertPayload;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

View File

@ -1,6 +1,5 @@
package com.qaup.collision.rule.service.impl;
import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.common.model.spatial.VehicleLocation;
import com.qaup.collision.common.model.spatial.AirportArea;

View File

@ -1,6 +1,5 @@
package com.qaup.collision.rule.service.impl;
import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.rule.model.entity.SpatialRule;
import com.qaup.collision.rule.model.enums.RuleCategory;

View File

@ -1,6 +1,5 @@
package com.qaup.collision.rule.service.impl;
import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.common.model.spatial.AirportArea;
import com.qaup.collision.common.service.AirportAreaService;

View File

@ -1,6 +1,7 @@
package com.qaup.collision.websocket.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@ -20,26 +21,26 @@ import org.slf4j.LoggerFactory;
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketConfig.class);
private static final Logger logger = LoggerFactory.getLogger(WebSocketConfig.class);
private final CollisionWebSocketHandler collisionWebSocketHandler;
public WebSocketConfig(CollisionWebSocketHandler collisionWebSocketHandler) {
this.collisionWebSocketHandler = collisionWebSocketHandler;
LOGGER.info("🚀 WebSocket配置类初始化...");
logger.info("🚀 WebSocket配置类初始化...");
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
LOGGER.info("📝 注册WebSocket处理器...");
public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
logger.info("📝 注册WebSocket处理器...");
// 注册冲突检测WebSocket端点
registry.addHandler(collisionWebSocketHandler, "/collision")
.setAllowedOrigins("*"); // 允许所有来源生产环境应该限制具体域名
LOGGER.info("✅ WebSocket端点注册完成");
LOGGER.info("🎯 端点路径: /collision");
LOGGER.info("🌐 允许的来源: *");
LOGGER.info("📡 WebSocket服务可用: ws://localhost:8080/collision");
logger.info("✅ WebSocket端点注册完成");
logger.info("🎯 端点路径: /collision");
logger.info("🌐 允许的来源: *");
logger.info("📡 WebSocket服务可用: ws://localhost:8080/collision");
}
}

View File

@ -59,8 +59,11 @@ public class AircraftRouteUpdateEventListener {
* WebSocket消息包装类
*/
private static class WebSocketMessage {
@com.fasterxml.jackson.annotation.JsonProperty
public final String type;
@com.fasterxml.jackson.annotation.JsonProperty
public final Object data;
@com.fasterxml.jackson.annotation.JsonProperty
public final long timestamp;
public WebSocketMessage(String type, Object data, long timestamp) {

View File

@ -6,8 +6,6 @@ import com.qaup.system.service.ISysDriverInfoService;
import com.qaup.system.service.ISysVehicleInfoService;
import com.qaup.system.service.ISysVehicleTypeService;
import com.qaup.collision.common.model.spatial.VehicleLocation;
import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -65,21 +63,18 @@ class QuapDataAdapterTest {
// 模拟车辆类型服务支持新的类型编码系统
SysVehicleType normalVehicleType = new SysVehicleType();
normalVehicleType.setTypeId(1L);
normalVehicleType.setTypeCode("NM");
normalVehicleType.setTypeName("普通车辆");
when(vehicleTypeService.selectSysVehicleTypeByCode("NM")).thenReturn(normalVehicleType);
when(vehicleTypeService.selectSysVehicleTypeById(1L)).thenReturn(normalVehicleType);
SysVehicleType unmannedVehicleType = new SysVehicleType();
unmannedVehicleType.setTypeId(2L);
unmannedVehicleType.setTypeCode("UV");
unmannedVehicleType.setTypeName("无人车");
when(vehicleTypeService.selectSysVehicleTypeByCode("UV")).thenReturn(unmannedVehicleType);
when(vehicleTypeService.selectSysVehicleTypeById(2L)).thenReturn(unmannedVehicleType);
SysVehicleType specialVehicleType = new SysVehicleType();
specialVehicleType.setTypeId(3L);
specialVehicleType.setTypeCode("SP");
specialVehicleType.setTypeName("特勤车辆");
when(vehicleTypeService.selectSysVehicleTypeByCode("SP")).thenReturn(specialVehicleType);
@ -180,27 +175,6 @@ class QuapDataAdapterTest {
verify(vehicleInfoService).selectSysVehicleInfoList(any(SysVehicleInfo.class));
}
@Test
void testFindVehiclesByType_Deprecated() {
// 准备测试数据
Long typeId = 1L;
List<SysVehicleInfo> expectedVehicles = Arrays.asList(testVehicleInfo);
when(vehicleInfoService.selectSysVehicleInfoList(any(SysVehicleInfo.class)))
.thenReturn(expectedVehicles);
// 执行测试
List<SysVehicleInfo> result = quapDataAdapter.findVehiclesByType(typeId);
// 验证结果
assertEquals(1, result.size());
assertEquals(testVehicleInfo.getVehicleId(), result.get(0).getVehicleId());
// 验证方法调用
verify(vehicleTypeService).selectSysVehicleTypeById(typeId);
verify(vehicleInfoService).selectSysVehicleInfoList(any(SysVehicleInfo.class));
}
@Test
void testFindAllVehicles() {
// 准备测试数据

View File

@ -9,7 +9,6 @@ import org.mockito.MockitoAnnotations;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

View File

@ -20,7 +20,9 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{
/**
* 私有构造函数防止实例化
* @deprecated 此构造函数已被弃用
*/
@Deprecated
private DateUtils()
{
}

View File

@ -20,6 +20,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
/**
* 私有构造函数防止实例化
*/
@Deprecated
private StringUtils()
{
}

View File

@ -3,6 +3,7 @@ package com.qaup.common.utils.file;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
@ -69,7 +70,7 @@ public class ImageUtils
if (url.startsWith("http"))
{
// 网络地址
URL urlObj = new URL(url);
URL urlObj = URI.create(url).toURL();
URLConnection urlConnection = urlObj.openConnection();
urlConnection.setConnectTimeout(30 * 1000);
urlConnection.setReadTimeout(60 * 1000);

View File

@ -7,6 +7,7 @@ import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
@ -71,7 +72,7 @@ public class HttpUtils
{
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URL realUrl = URI.create(urlNameString).toURL();
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
@ -146,7 +147,7 @@ public class HttpUtils
try
{
log.info("sendPost - {}", url);
URL realUrl = new URL(url);
URL realUrl = URI.create(url).toURL();
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
@ -217,7 +218,7 @@ public class HttpUtils
log.info("sendSSLPost - {}", urlNameString);
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
URL console = new URL(urlNameString);
URL console = URI.create(urlNameString).toURL();
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");

View File

@ -72,8 +72,7 @@ public class SecurityConfig
@Bean
public AuthenticationManager authenticationManager()
{
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}