Showing Posts From

java

使用清华大学开源软件镜像站下载JDK

为什么选择清华大学开源软件镜像站? 清华大学开源软件镜像站(TUNA)提供了一个快速、稳定且免费的软件源镜像服务,支持众多流行的开源项目,包括 Adoptium 提供的 JDK。使用 TUNA 镜像站可以显著提高下载速度,特别是在中国大陆地区。 下载步骤 步骤 1:访问 TUNA Adoptium 镜像站 首先,打开浏览器并访问以下链接: https://mirrors.tuna.tsinghua.edu.cn/Adoptium/ 步骤 2:选择 JDK 版本 在页面上,可以看到不同版本的 JDK 目录。根据需要选择相应的版本,例如 JDK 8、JDK 11 或 JDK 17。 步骤 3:选择操作系统和架构 进入 JDK 版本目录后,选择适合的操作系统和架构目录。例如,对于 64 位 Windows 系统,可以进入: 8/jdk/x64/windows/步骤 4:选择安装包格式 在操作系统目录下,可以选择不同的安装包格式。对于 Windows 系统,通常有以下选择:MSI 安装包:Windows 的标准安装格式 ZIP 压缩包:免安装版本,解压后可直接使用步骤 5:下载 JDK 选择好需要的安装包格式后,点击下载链接即可开始下载。例如,JDK 8 的 Windows 64 位版本: OpenJDK8U-jdk_x64_windows_hotspot_8u422b05.msi或 ZIP 格式: OpenJDK8U-jdk_x64_windows_hotspot_8u422b05.zip不同操作系统的选择方式Windows:选择 windows/ 目录,然后根据需要选择 MSI 或 ZIP 格式 Linux:选择 linux/ 目录,通常提供 .tar.gz 格式的压缩包 macOS:选择 mac/ 目录,提供 DMG 格式的安装包注意事项在下载和安装 JDK 之前,确保系统满足 JDK 的最低系统要求 安装 JDK 后,可能需要配置环境变量,以便在命令行中使用 java 命令 如果遇到下载速度慢或无法下载的问题,可以尝试更换网络环境或稍后重试总结 通过以上步骤,您可以轻松从清华大学开源软件镜像站下载并安装适合的 JDK 版本。如果有任何问题,可以访问镜像站的帮助文档或社区论坛寻求帮助。希望这篇文章能帮助您快速获取所需的 JDK 版本。

OkHttp 发起 HTTP 请求:POST JSON、表单、文件上传与异步调用

Maven 依赖 <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.12.0</version> </dependency>GET 请求 OkHttpClient client = new OkHttpClient();Request request = new Request.Builder() .url("https://api.example.com/users/1") .build();try (Response response = client.newCall(request).execute()) { String body = response.body().string(); System.out.println(body); }POST JSON OkHttpClient client = new OkHttpClient();MediaType JSON = MediaType.get("application/json; charset=utf-8"); String json = "{\"username\": \"admin\", \"password\": \"secret\"}";RequestBody body = RequestBody.create(json, JSON);Request request = new Request.Builder() .url("https://api.example.com/login") .post(body) .build();try (Response response = client.newCall(request).execute()) { System.out.println(response.body().string()); }自定义 Header Request request = new Request.Builder() .url("https://api.example.com/data") .header("Authorization", "Bearer your-token") .header("Content-Type", "application/json") .post(body) .build();表单提交(application/x-www-form-urlencoded) RequestBody formBody = new FormBody.Builder() .add("username", "admin") .add("password", "secret") .build();Request request = new Request.Builder() .url("https://api.example.com/login") .post(formBody) .build();文件上传(multipart/form-data) File file = new File("/path/to/file.jpg");RequestBody fileBody = RequestBody.create(file, MediaType.get("image/jpeg"));RequestBody multipart = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", file.getName(), fileBody) .addFormDataPart("description", "头像上传") .build();Request request = new Request.Builder() .url("https://api.example.com/upload") .post(multipart) .build();超时配置 OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build();异步请求(enqueue) client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody body = response.body()) { System.out.println(body.string()); } } });异步回调在 OkHttp 内部线程池中执行,Android 中更新 UI 需要切回主线程。 封装工具类 public class HttpUtil { private static final OkHttpClient CLIENT = new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .build(); private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); public static String get(String url) throws IOException { Request request = new Request.Builder().url(url).build(); try (Response response = CLIENT.newCall(request).execute()) { return response.body().string(); } } public static String postJson(String url, String json) throws IOException { RequestBody body = RequestBody.create(json, JSON); Request request = new Request.Builder().url(url).post(body).build(); try (Response response = CLIENT.newCall(request).execute()) { return response.body().string(); } } }OkHttpClient 实例应该全局复用,避免每次请求创建新实例浪费连接池资源。

Spring RestTemplate 发 HTTP 请求:常用姿势速查

Spring 里发 HTTP 请求最常见还是 RestTemplate。虽然官方已经推 WebClient,但存量项目里 RestTemplate 到处都是——写起来简单、同步阻塞、跟传统 Servlet 模型贴合。 注册 Bean @Configuration public class RestConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }注入用: @Autowired private RestTemplate restTemplate;或者构造器注入(推荐): private final RestTemplate rt; public UserService(RestTemplate rt) { this.rt = rt; }GET 最简单: String json = restTemplate.getForObject( "https://api.example.com/users/1", String.class);带 URL 参数(占位符按顺序填): String url = "https://api.example.com/users/{id}?type={type}"; String json = restTemplate.getForObject(url, String.class, 1, "vip");返回对象: @Data public class User { private Integer id; private String name; }User u = restTemplate.getForObject( "https://api.example.com/users/1", User.class);Jackson 会自动反序列化——User 里字段名和 JSON 对得上就行。 POST 发 JSON: Map<String, Object> body = new HashMap<>(); body.put("username", "admin"); body.put("password", "12345");String result = restTemplate.postForObject( "https://api.example.com/login", body, String.class);RestTemplate 会把 Map 序列化成 JSON 并设 Content-Type: application/json(前提是有 Jackson)。 表单: MultiValueMap<String, String> form = new LinkedMultiValueMap<>(); form.add("username", "admin"); form.add("password", "12345");HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, String>> req = new HttpEntity<>(form, headers); String result = restTemplate.postForObject(url, req, String.class);自定义 Header 带 Token 的接口: HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("Authorization", "Bearer " + token);Map<String, Object> body = new HashMap<>(); body.put("name", "test");HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);ResponseEntity<String> resp = restTemplate.exchange( "https://api.example.com/create", HttpMethod.POST, entity, String.class);if (resp.getStatusCode().is2xxSuccessful()) { System.out.println(resp.getBody()); }exchange 是最灵活的方法——可以自由选择 HTTP method、传 headers、拿完整的 ResponseEntity。 超时配置(生产必配) 默认没有超时。远端 hang 住就一直等: @Bean public RestTemplate restTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(5_000); // 连接超时 factory.setReadTimeout(10_000); // 读超时 return new RestTemplate(factory); }更强大的选择:Apache HttpClient 5: @Bean public RestTemplate restTemplate() { RequestConfig config = RequestConfig.custom() .setConnectTimeout(Timeout.ofSeconds(5)) .setResponseTimeout(Timeout.ofSeconds(10)) .build(); CloseableHttpClient http = HttpClients.custom() .setDefaultRequestConfig(config) .setMaxConnTotal(100) .setMaxConnPerRoute(20) .build(); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(http); return new RestTemplate(factory); }带连接池、更好的性能、支持重试。 上传文件 FileSystemResource file = new FileSystemResource("/path/to/a.zip");MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); body.add("file", file); body.add("desc", "备份");HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA);HttpEntity<MultiValueMap<String, Object>> req = new HttpEntity<>(body, headers);String result = restTemplate.postForObject( "https://api.example.com/upload", req, String.class);忽略 SSL 自签证书内网服务: SSLContext ssl = SSLContexts.custom() .loadTrustMaterial((chain, authType) -> true) .build();CloseableHttpClient http = HttpClients.custom() .setConnectionManager( PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory( SSLConnectionSocketFactoryBuilder.create() .setSslContext(ssl) .setHostnameVerifier(NoopHostnameVerifier.INSTANCE) .build()) .build()) .build();生产上少这么干,会漏中间人。 Spring Boot 3 之后怎么办 Spring Framework 6 起 RestTemplate 进入维护模式——不再加新功能,但不会立刻删。新项目推荐:RestClient(Spring 6.1+)— 同步、fluent API,最平滑的替代 WebClient — 响应式(reactive),跟 Reactor 生态整合RestClient 示例: RestClient client = RestClient.create();User u = client.get() .uri("https://api.example.com/users/{id}", 1) .retrieve() .body(User.class);比 RestTemplate 好看得多。老项目不用急着换,新代码优先用 RestClient。 一句话总结 RestTemplate:GET 用 getForObject、POST 用 postForObject 或 exchange、复杂场景走 exchange 拿 ResponseEntity。生产必配超时和连接池。新项目改用 RestClient。

Servlet 规范与 Tomcat 版本对照表 + JDK 兼容性

维护老 SSM/SSH 项目常遇到——web.xml 里 version="2.5",跑起来应该配哪个 Tomcat?JDK 8 能跑吗?先给对照表: Servlet 与 Tomcat 版本对照Servlet 版本 Tomcat 版本 官方支持 JDKServlet 2.4 Tomcat 5.0/5.5 Java 1.4 / 5Servlet 2.5 Tomcat 6.x Java 5 / 6Servlet 3.0 Tomcat 7.x Java 6 / 7Servlet 3.1 Tomcat 8.0 Java 7 / 8Servlet 3.1 Tomcat 8.5 Java 7 / 8Servlet 4.0 Tomcat 9.x Java 8+Jakarta Servlet 5 Tomcat 10.x Java 8+Jakarta Servlet 6 Tomcat 11.x Java 17+Tomcat 10 是分水岭——包名从 javax.servlet.* 改到 jakarta.servlet.*。老代码原封不动放 Tomcat 10 直接编译不过。 判断项目该用哪个 打开 web.xml,看头部: <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> </web-app>version="2.5" 就是 Servlet 2.5,对应 Tomcat 6+。 JDK 8 能跑 Servlet 2.5 项目吗 能。稳定组合:项 推荐Servlet 规范 2.5(保持 web.xml 不变)Tomcat 7.0.109(最后一个 7.x)JDK 1.8Spring 3.x / 4.xMyBatis 3.x理由:Tomcat 7 向下兼容 Servlet 2.5(Tomcat 是超集) Tomcat 7 完整支持 JDK 8,无 PermGen 问题 Tomcat 6 对 JDK 8 支持不完整,从 6.0.53 才勉强能跑,还是别用了迁移 Tomcat 6 到 Tomcat 7 要改什么 九成情况什么都不用改。真要改的坑: 1. web.xml schema 版本:可以不动。Tomcat 7 依然接受 web-app version="2.5"。 2. JSP 的 EL 表达式:Tomcat 7 默认 EL 2.2、Tomcat 6 是 EL 2.1。有些字符串比较写法(比如 ${x eq null})在两版本略有差异,一般不影响。 3. 端口 / 编码:server.xml 里 Connector 改: <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" redirectPort="8443" />URIEncoding="UTF-8" 治所有 GET 参数中文乱码。 4. session:Tomcat 7 默认关闭了 sessionId URL 追加(;jsessionid=xxx)。老 JSP 依赖 <c:url> 处理会不同,一般不影响。 Maven 里跑 Tomcat:tomcat7-maven-plugin 不装 Tomcat 也能跑,用 Maven 插件: <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> </configuration> </plugin> </plugins> </build>跑: mvn tomcat7:run这个插件已经没维护 10 多年了,但对 Servlet 2.5 老项目照样能跑。生产别用,本地开发够。 Tomcat 8/9 呢 Servlet 2.5 项目理论上放 Tomcat 8/9 也能跑(javax.servlet 包名不变)。但会遇到:老 Spring 3.x 和 Tomcat 8 的 WebSocket 冲突 一些 EL 2.2 / 3.0 的表达式行为差异 JSP 编译器更严格迁移建议:如果项目还在开发新功能,直接升到 Tomcat 9 + JDK 8 + Servlet 3.x(web.xml 头改一下);只是维护,稳留 Tomcat 7 + JDK 8。 Tomcat 10+ 的 jakarta 大坑 老代码: import javax.servlet.http.HttpServletRequest;Tomcat 10 只认: import jakarta.servlet.http.HttpServletRequest;想迁移,用官方工具批量替换: # Eclipse Transformer java -jar org.eclipse.transformer.cli-*.jar \ --input old.war --output new.war或者手工找替换: find . -name "*.java" -exec sed -i 's/javax\.servlet/jakarta.servlet/g' {} +改完还要处理 Spring 版本——Spring 5.x 只支持 javax.servlet,要 jakarta 得升到 Spring 6.x(同时 JDK 17+)。这是个大工程,不是小改。 一句话总结 老 Servlet 2.5 项目最稳的组合是 Tomcat 7.0.109 + JDK 8。Tomcat 10 起改包名 jakarta,迁移是大工程。开发用 tomcat7-maven-plugin 快。

Maven 跑 Spring Boot 常用命令 + 依赖解析失败排查

Spring Boot 项目用 Maven 跑,其实就三个常用命令。但组合起来 + 遇到依赖冲突时坑不少。 三个常用命令 开发时启动: mvn spring-boot:run看到 Started XxxApplication in x.xxx seconds 就通了。 跳过测试加速启动: mvn spring-boot:run -DskipTests老项目测试跑几分钟是常态,开发时基本都会跳。 指定环境: mvn spring-boot:run -Dspring-boot.run.profiles=dev读 application-dev.yml 里的配置。多环境就用这个切。 打包 + 运行 生产部署更常用两步走: mvn clean package -DskipTests java -jar target/*.jar产物是 fat jar(Spring Boot 把依赖都塞里面),直接 java -jar 起就行。加参数: java -jar target/myapp.jar \ --spring.profiles.active=prod \ --server.port=8081后台跑用 nohup: nohup java -jar target/myapp.jar > app.log 2>&1 &生产建议走 systemd 或 Docker,不是裸 nohup。 常见问题 1. mvn 不是内部或外部命令 PATH 没配好。先确认: mvn -v不通就照 Windows 追加目录到 PATH 里的方法把 MAVEN_HOME\bin 加进去。改完 新开一个终端 或注销重登。 2. No plugin found for prefix 'spring-boot' pom.xml 里没引 Spring Boot 插件。加: <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>spring-boot-starter-parent 已经管好版本,不用写 version。没继承 parent 的话就得写。 3. 端口占用 Web server failed to start. Port 8080 was already in use.先查是谁: netstat -ano | findstr 8080 # Windows lsof -i:8080 # Linux/Mac杀掉: taskkill /PID 12345 /F或者改端口: server: port: 80814. artifact ${revision} not found 碰到这种: Could not find artifact net.maku:maku-boot:pom:${revision} in public问题不在 Maven 仓库、不在网络。是这个项目用了 Maven 的 CI Friendly Versions 特性——pom.xml 里的版本号写成 ${revision}: <version>${revision}</version>需要在根 pom.xml 里定义: <properties> <revision>4.11.0</revision> </properties>或者启动时传: mvn spring-boot:run -Drevision=4.11.0推荐:用 flatten-maven-plugin 生成扁平化的 pom: <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>flatten-maven-plugin</artifactId> <version>1.6.0</version> <configuration> <flattenMode>resolveCiFriendliesOnly</flattenMode> </configuration> <executions> <execution> <id>flatten</id> <phase>process-resources</phase> <goals><goal>flatten</goal></goals> </execution> <execution> <id>flatten.clean</id> <phase>clean</phase> <goals><goal>clean</goal></goals> </execution> </executions> </plugin>之后 mvn install 会先把 ${revision} 替换成真实版本再安装到本地库,其它模块拉的时候就不会崩了。 5. 依赖下载慢 Maven 默认仓库在国外,换阿里云或华为云: ~/.m2/settings.xml: <settings> <mirrors> <mirror> <id>aliyun</id> <mirrorOf>central</mirrorOf> <url>https://maven.aliyun.com/repository/central</url> </mirror> </mirrors> </settings>一次配好,永久生效。 6. 开发热重载 Spring Boot 自带 devtools: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>IDEA 里要开 "Build project automatically" + "Registry: compiler.automake.allow.when.app.running",代码保存自动 recompile + restart。 一句话总结 开发用 mvn spring-boot:run -DskipTests、生产用 mvn clean package + java -jar。${revision} 报错是 CI Friendly Versions,加 flatten 插件解决。