• [技术干货] h2数据库在单元测试中的作用和如何使用
    H2数据库在单元测试中的作用及使用方法单元测试是软件开发过程中的一种重要测试方法,用于验证代码的各个模块是否能够正常工作。在Java开发中,常用的单元测试框架有JUnit、TestNG等。本文将介绍H2数据库在单元测试中的作用以及如何使用。一、H2数据库简介H2是一个开源的嵌入式数据库,采用纯Java编写,不需要安装和配置,体积小、速度快、支持事务处理等特点。在Java项目中,经常使用H2数据库作为内存数据库进行开发和测试。二、H2数据库在单元测试中的作用模拟真实数据库环境:在进行单元测试时,需要模拟实际生产环境中的数据存储和访问方式。H2数据库可以作为一个轻量级的内存数据库,方便地创建和管理数据表,为测试提供真实的数据环境。隔离测试数据:使用H2数据库可以避免测试数据与生产数据相互干扰,保证测试的准确性和可靠性。同时,H2数据库支持多线程访问,可以在多线程环境下进行单元测试。便于调试和维护:H2数据库提供了丰富的日志和性能分析工具,可以帮助开发人员快速定位和解决单元测试中出现的问题,提高开发效率。三、如何使用H2数据库进行单元测试添加H2依赖在Java项目的pom.xml文件中添加H2数据库的依赖:<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.200</version> </dependency>初始化H2数据库在使用H2数据库之前,需要先创建一个数据源并初始化:import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import javax.sql.DataSource; import org.h2.tools.Server; import org.h2.tools.ServerSetup; public class H2Database { private static final String DB_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"; private static final String USER = "sa"; private static final String PASSWORD = ""; public static DataSource getDataSource() throws SQLException { ServerSetup setup = new ServerSetup().setPort(8082).setHttpEnabled(false).setAllowCreate(true); org.h2.tools.Console console = Server.createWebServer("", 8082, setup); console.start(); System.out.println("Opening database"); Server server = Server.createWebServer("-webAllowOthers").start(); return new DataSource() { @Override public Connection getConnection() throws SQLException { return DriverManager.getConnection(DB_URL, USER, PASSWORD); } }; } }使用H2数据库进行单元测试在单元测试中,可以通过JUnit或TestNG等框架获取到数据源,然后使用H2数据库进行数据的增删改查操作。例如:```java import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.test.context.junit4.SpringRunner; import org.junit.runner.RunWith; import javax.sql.DataSource; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.; import static org.mockito.Mockito.; @RunWith(SpringRunner.class) @SpringBootTest(classes = {YourApplicationClass}) // 替换为你的应用类名 public class YourTestClass { // 替换为你的测试类名 @Autowired private JdbcTemplate jdbcTemplate; @Autowired private DataSource dataSource; // 这里注入上面创建的数据源对象即可获取到H2数据库实例 private List users = new ArrayList<>(); // 假设有一个User实体类和对应的repository接口用于操作数据库,这里只是示例数据结构而已。在实际项目中可能会有所不同。注意:此处仅为示例代码,具体实现需要根据实际情况进行调整。)
  • [技术干货] java中使用h2数据库
    Java中使用H2数据库的详解在Java开发中,我们经常需要使用数据库来存储和管理数据。H2是一个轻量级的嵌入式数据库,非常适合用于开发和测试环境。本文将介绍如何在Java项目中使用H2数据库。添加H2依赖首先,我们需要在项目的pom.xml文件中添加H2数据库的依赖:<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.200</version> </dependency>配置H2数据库在项目的src/main/resources目录下创建一个名为application.properties的文件,然后添加以下内容来配置H2数据库:# 数据库文件路径 spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= # 控制台输出SQL日志 logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE这里我们配置了一个内存型H2数据库,数据库文件路径为testdb,用户名为sa,密码为空。同时,我们还开启了SQL日志输出,以便于调试。创建实体类和Repository接下来,我们创建一个实体类User,并为其添加JPA注解:import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String email; // 省略getter和setter方法 }然后,我们创建一个UserRepository接口,继承自JpaRepository:import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, Long> { }使用H2数据库进行操作现在我们已经配置好了H2数据库和相关代码,可以开始进行数据的增删改查操作了。例如,我们可以在服务类中注入UserRepository,然后使用其提供的方法进行数据操作:import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; @Service public class UserService { @Autowired private UserRepository userRepository; public List<User> findAll() { return userRepository.findAll(); } public Optional<User> findById(Long id) { return userRepository.findById(id); } public User save(User user) { return userRepository.save(user); } public void deleteById(Long id) { userRepository.deleteById(id); } }
  • [技术干货] GET和POST的区别,java模拟postman发post请求
     一、先说一下get和post GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二。  最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。  1、看一下人畜无害的w3schools怎么说: GET在浏览器回退时是无害的,而POST会再次提交请求; GET产生的URL地址可以被Bookmark,而POST不可以; GET请求会被浏览器主动cache,而POST不会,除非手动设置; GET请求只能进行url编码,而POST支持多种编码方式; GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留; GET请求在URL中传送的参数是有长度限制的,而POST么有; 对参数的数据类型,GET只接受ASCII字符,而POST没有限制; GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息; GET参数通过URL传递,POST放在Request body中。 2、问一下文心你言哥,轻轻松松给你一个标准答案:  3、卧槽,懂了,好像又没懂 主要是这么官方的话术,没进入你的心里。  二、让我们扒下GET和POST的外衣,坦诚相见吧! GET和POST是什么?HTTP协议中的两种发送请求的方法。  HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。  HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。  在我大万维网世界中,TCP就像汽车,我们用TCP来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。  为了避免这种情况发生,交通规则HTTP诞生了。  HTTP给汽车运输设定了好几个服务类别,有GET, POST, PUT, DELETE等等,HTTP规定,当执行GET请求的时候,要给汽车贴上GET的标签(设置method为GET),而且要求把传送的数据放在车顶上(url中)以方便记录。如果是POST请求,就要在车上贴上POST的标签,并把货物放在车厢里。  当然,你也可以在GET的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在POST的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。  但是,我们只看到HTTP对GET和POST参数的传送渠道(url还是requrest body)提出了要求。“标准答案”里关于参数大小的限制又是从哪来的呢?  在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起http请求)和服务器(接受http请求)就是不同的运输公司。 虽然理论上,你可以在车顶上无限的堆货物(url中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。  业界不成文的规定是,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。如果你用GET服务,在request body偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然GET可以带request body,也不能保证一定能被接收到哦。  好了,现在你知道,GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。  你以为本文就这么结束了?  三、我们的大BOSS还等着出场呢 这位BOSS有多神秘?当你试图在网上找“GET和POST的区别”的时候,那些你会看到的搜索结果里,从没有提到他。他究竟是什么呢。。。  GET和POST还有一个重大区别,简单的说:  GET产生一个TCP数据包;POST产生两个TCP数据包。  长的说:  对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);  而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。  也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。  因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?  GET与POST都有自己的语义,不能随便混用; 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点; 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。 现在,当面试官再问你“GET与POST的区别”的时候,你的内心是不是这样的?  你以为本文就这么结束了?  四、java 模拟post请求 言归正传,书接上回,本文的标题是《java 模拟post请求》,你这跑题了啊,吒哥。  1、弯了?那就给它掰回来。 实际的开发中,有很多这样的需求,“调用第三方接口”。  说人话,第三方接口,就是别人的接口,通过POST调用一下,返回点东西给你。  Java提供了多种方式来模拟POST请求,包括使用HttpURLConnection、HttpClient和OkHttp等库。  不扯了,直接上代码。毕竟吒哥这是以干货著称的,虽然一直在想办法转型,写一些有趣的文章,洞穿心灵的文章,脱离低级趣味的文章,但,技术本身就是乏味的~  2、HttpURLConnection HttpURLConnection是Java自带的用于发送HTTP请求的类。我们可以使用它来模拟POST请求。  (1)发起POST请求:  @Controller @RequestMapping("/client") public class TestController {      private static final String urlStream = "http://127.0.0.1:8080/MyProject/nezha/getUserStream";      @RequestMapping(value = "/getUserStream", method = RequestMethod.POST)     @ResponseBody     public User getUserStream(@RequestBody User user) {         System.out.println("请求参数:"+user);         String userRet = HttpUtil.sendPostByURLConnection(urlStream, user);         User ret = JSON.toJavaObject(JSONObject.parseObject(userRet), User.class);         System.out.println("URLConnection方式发送POST请求:"+ret);         return ret;     } }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (2)模拟服务端  @Controller @RequestMapping("/server") public class PostServerController {      @RequestMapping(value = "/getUserStream", method = RequestMethod.POST)     @ResponseBody     public User getUserStream(HttpServletRequest request) throws IOException {         String user = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);         User user1 = JSON.toJavaObject(JSONObject.parseObject(user), User.class);         user1.setInfo("getUserStream,我 OK 啦");         System.out.println(user1);         return user1;     } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (3)通过postman调用  3、HttpClient Apache HttpClient是一个流行的Java库,用于发送HTTP请求。我们可以使用该库来模拟POST请求。  (1)发起POST请求:  @Controller @RequestMapping("/client") public class TestController {      private static final String url = "http://127.0.0.1:8080/MyProject/nezha/getUser";      @RequestMapping(value = "/getUser", method = RequestMethod.POST)     @ResponseBody     public User getUser(@RequestBody User user) {         System.out.println("请求参数:"+user);         String json = JSON.toJSONString(user);         String userRet = HttpUtil.sendHttpPost(url, json);         User ret = JSON.toJavaObject(JSONObject.parseObject(userRet), User.class);         System.out.println("HttpPost方式发送POST请求:"+ret);         return ret;     } }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 (2)模拟服务端  @Controller @RequestMapping("/server") public class PostServerController {      @RequestMapping(value = "/getUser", method = RequestMethod.POST)     @ResponseBody     public User getUser(@RequestBody User user) {         user.setInfo("getUser,我 OK 啦");         System.out.println(user);         return user;     } } 1 2 3 4 5 6 7 8 9 10 11 12 (3)通过postman调用   4、CRUD从业人员最爱的“工具类” import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils;  import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection;  public class HttpUtil {     /**      * URLConnection方式      * 发送POST请求      */     public static <T> String sendPostByURLConnection(String url, T entity) {         PrintWriter out = null;         BufferedReader in = null;         String result = "";         try {             URL realUrl = new URL(url);             // 打开和URL之间的连接             URLConnection conn = realUrl.openConnection();             // 设置通用的请求属性             conn.setRequestProperty("accept", "*/*");             conn.setRequestProperty("connection", "Keep-Alive");             // 设置请求类型             //conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");             conn.setRequestProperty("Content-Type", "application/json");             // 发送POST请求必须设置如下两行             conn.setDoOutput(true);             conn.setDoInput(true);             // 获取URLConnection对象对应的输出流             out = new PrintWriter(conn.getOutputStream());             // 发送请求参数             out.print(entity);             // flush输出流的缓冲             out.flush();             // 定义BufferedReader输入流来读取URL的响应             in = new BufferedReader(new InputStreamReader(conn.getInputStream()));             String line;             while ((line = in.readLine()) != null) {                 result += line;             }         } catch (Exception e) {             System.out.println(e);             return null;         }         //使用finally块来关闭输出流、输入流         finally {             try {                 if (out != null) {                     out.close();                 }                 if (in != null) {                     in.close();                 }             } catch (IOException ex) {             }         }         return result;     }      /**      * HttpPost方式      * 发送POST请求      */     public static String sendHttpPost(String url, String data) {         String response = null;         try {             CloseableHttpClient httpclient = null;             CloseableHttpResponse httpresponse = null;             try {                 httpclient = HttpClients.createDefault();                 HttpPost httppost = new HttpPost(url);                 RequestConfig requestConfig = RequestConfig.custom()                         .setConnectTimeout(5000)  //连接超时时间                         .setSocketTimeout(5000)   //读取数据超时时间                         .build();                 httppost.setConfig(requestConfig);                 StringEntity stringentity = new StringEntity(data, ContentType.create("application/json", "UTF-8"));                 httppost.setEntity(stringentity);                 httpresponse = httpclient.execute(httppost);                 response = EntityUtils.toString(httpresponse.getEntity());             } finally {                 if (httpclient != null) {                     httpclient.close();                 }                 if (httpresponse != null) {                     httpresponse.close();                 }             }         } catch (Exception e) {             return null;         }         return response;     } }  ———————————————— 原文链接:https://blog.csdn.net/guorui_java/article/details/132122466 
  • [技术干货] 下单时如何保证数据一致性?
     在一个在线商城系统中,面临一个重要的问题:如何在订单支付过程中保证数据的一致性,并且如何优化支付操作的性能。  1、订单支付需求 在用户下单后,需要执行订单支付操作,确保支付和订单状态的一致性。  2、数据一致性要求 支付成功后,必须将订单状态更新为已支付,以保持数据的一致性。  3、高并发支付 在高并发的情况下,需要确保订单支付的性能和数据一致性。  为了解决以上问题,我们可以使用Redis提供的事务和管道机制。  一、Redis事务 1、什么是Redis事务  在Redis中,事务是一组命令的集合,可以在一个单独的流程中执行,以保证这些命令的原子性、一致性、隔离性和持久性。  (1)事务概述 Redis事务由以下四个关键命令进行管理:  命令    描述 MULTI    开启事务,标记事务块的开始。 EXEC    执行事务中的所有命令。 DISCARD    取消事务,放弃所有已经入队的命令。 WATCH    监视一个或多个键,用于乐观锁。 (2)Redis的事务特性 Redis事务具有以下关键特性:  事务特性    描述 原子性    事务中的所有命令要么全部执行,要么全部不执行。这确保了在事务执行期间,不会发生部分命令执行成功而部分命令执行失败的情况。 一致性    事务中的命令会按照被添加的顺序执行,不会被其他客户端的命令打断。这保证了事务中的操作按照期望的顺序执行,不会受到并发操作的影响。 隔离性    在事务执行期间,事务会被隔离,不会受到其他事务的影响。即使有其他并发事务在执行,事务中的操作也不会被其他事务看到,直到事务被执行提交。 持久性    事务执行结束后对数据库的修改将被持久化到磁盘上。这确保了事务中的操作不会因为系统故障而丢失,从而保证了数据的持久性。 以上是Redis事务的基本概念和特性,它们保证了在Redis中执行的事务是可靠的、具备一致性的操作集合。   上图形表示了Redis事务的关键特性之间的相互关系。这些特性相互支持,共同确保了Redis事务的可靠性和一致性。  原子性保证了事务中的操作要么全部成功,要么全部失败。 一致性保证了事务中的操作按照特定的顺序执行,不会受到其他操作的干扰。 隔离性确保了事务在执行期间与其他事务相互隔离,互不干扰。 持久性确保了事务执行后的修改会被持久保存,不会因系统故障而丢失。这些特性一起构成了Redis事务的可靠性和稳定性的基础。 2、使用Redis事务 (1)开始和提交事务 在Redis中,使用事务需要遵循以下步骤:  使用MULTI命令开启事务。 执行需要在事务中执行的命令。 使用EXEC命令提交事务,执行事务中的所有命令。 下面是一个使用Java代码示例的详细步骤:  // 创建与Redis服务器的连接 Jedis jedis = new Jedis("localhost", 6379);  // 开启事务 Transaction transaction = jedis.multi();  // 执行事务中的命令 transaction.set("key1", "value1"); transaction.set("key2", "value2");  // 提交事务并获取执行结果 List<Object> results = transaction.exec(); 1 2 3 4 5 6 7 8 9 10 11 12 在上面的示例中,transaction.set("key1", "value1") 和 transaction.set("key2", "value2") 这两个命令会被添加到事务队列中,当transaction.exec()被调用时,事务中的所有命令会被一起执行。如果在MULTI和EXEC之间有错误发生,事务会被取消,命令不会执行。  (2)事务命令 在事务中,您可以使用常规的Redis命令,例如SET、GET、HSET、ZADD等等。这些命令会被添加到事务队列中,直到执行EXEC命令。  (3)事务示例 以下是使用Java代码示例来演示在事务中执行常见的Redis命令:  import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction;  public class RedisTransactionCommandsExample {     public static void main(String[] args) {         Jedis jedis = new Jedis("localhost", 6379);          // 开启事务         Transaction transaction = jedis.multi();          // 执行事务中的命令         transaction.set("name", "Alice");         transaction.hset("user:1", "name", "Bob");         transaction.zadd("scores", 100, "Alice");         transaction.zadd("scores", 200, "Bob");          // 提交事务并获取执行结果         List<Object> results = transaction.exec();          // 打印执行结果         for (Object result : results) {             System.out.println("Result: " + result);         }          // 关闭连接         jedis.close();     } }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 在上述示例中,使用了SET、HSET和ZADD命令,这些命令被添加到了事务队列中。当执行transaction.exec()时,事务中的所有命令会被一起执行。这里的示例是简单的演示,您可以根据需要添加更多的命令来构建更复杂的事务。   二、Redis管道 1、什么是Redis管道 Redis管道(Pipeline)是一种优化Redis操作的技术,它允许在单次通信中发送多个命令到Redis服务器,从而显著减少了通信开销,提高了性能。  管道可以将多个命令一次性发送给服务器,而不需要等待每个命令的响应,这使得Redis能够更高效地处理批量操作和大规模数据的读写。  下图展示了Redis管道的工作原理:   在上图中,客户端(Client)向Redis服务器(Server)发送多个命令,每个命令用Command 1、Command 2等表示。这些命令被一次性发送到服务器,而不需要等待每个命令的响应。服务器在执行所有命令后,一次性将结果响应给客户端。同时说明了Redis管道的工作方式:通过将多个命令打包成一次通信,减少了每个命令的通信开销,提高了系统的性能。  使用Redis管道时,客户端通过创建一个管道对象,将多个命令添加到管道中,然后一次性执行管道中的命令。最后,客户端可以收集所有命令的执行结果。  (1)管道概述 在Redis中,管道是通过以下命令进行管理:  命令    描述 PIPELINE    开启管道模式,用于一次性发送多个命令。 MULTI    开启事务模式,用于在管道中执行一系列命令。 EXEC    提交管道中的事务,执行并返回结果。 使用管道,您可以将多个命令一次性发送给服务器,然后通过一次通信获得所有命令的执行结果,从而减少了每个命令的通信开销,提高了系统的性能。  (2)Redis的管道特性 使用Redis管道可以获得以下优势:  减少通信开销: 在普通的命令传输中,每个命令都需要来回的网络通信,而管道可以将多个命令打包一次性发送给服务器,从而大大减少了通信开销。这对于网络延迟较高的场景尤为重要,有效提高了性能。 提高吞吐量: 管道允许在一次通信中执行多个命令,从而在单位时间内处理更多的命令。这对于需要处理大量命令的场景,如批量数据处理、并发请求处理等,能够有效提高Redis的吞吐量和响应能力。 2、使用Redis管道 (1)管道命令 以下是一个实际案例,展示如何使用Redis管道来执行多个命令并提高性能:  import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import java.util.List;  public class RedisPipelineExample {     public static void main(String[] args) {         Jedis jedis = new Jedis("localhost", 6379);          // 创建管道         Pipeline pipeline = jedis.pipelined();          // 向管道中添加命令         for (int i = 0; i < 10000; i++) {             pipeline.set("key" + i, "value" + i);         }          // 执行管道中的命令         List<Object> results = pipeline.syncAndReturnAll();          // 关闭连接         jedis.close();     } }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 在上述案例中,使用了一个循环来向管道中添加10000个SET命令。通过使用管道,可以在一次通信中将所有命令发送到服务器,而不是逐个发送,从而减少了通信开销,提高了性能。  (2)管道优化性能 使用Redis管道可以提高性能,特别是在需要批量处理多个命令的情况下。管道的原理是一次性将多个命令发送给服务器,然后一次性获取结果,这减少了通信的往返次数,从而显著提高了吞吐量。  然而,需要注意以下几点:  管道不支持事务,不能保证多个命令的原子性执行。 使用管道时,命令的执行顺序可能与添加顺序不一致,这需要根据业务需求进行考虑。 管道并非在所有场景下都能带来性能提升,需要根据实际情况进行评估。 通过合理使用管道,可以最大限度地发挥Redis在高性能数据处理中的优势。   三、事务 vs 管道:何时使用何种 1、事务的适用场景 事务在某些场景下可以保证原子性和一致性的操作,特别适用于强一致性要求的业务操作,例如支付操作。  (1)强一致性操作 事务是一种适用于需要强一致性操作的机制。当多个命令需要在一个操作序列中原子性地执行时,事务可以确保这些命令要么全部执行,要么全部不执行,以保持数据的一致性。  在以下示例中,模拟一个银行转账操作,其中需要同时扣减一个账户的余额并增加另一个账户的余额:  Jedis jedis = new Jedis("localhost", 6379);  // 开启事务 Transaction transaction = jedis.multi();  // 扣减账户1余额 transaction.decrBy("account1", 100);  // 增加账户2余额 transaction.incrBy("account2", 100);  // 提交事务并获取执行结果 List<Object> results = transaction.exec();  // 关闭连接 jedis.close();  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (2)原子性要求高 当业务要求多个操作要么全部成功,要么全部失败时,事务是更好的选择。事务确保了事务中的一系列命令以原子操作方式执行,从而维护了数据的一致性。  2、管道的适用场景 管道适用于需要批量操作和吞吐量要求较高的场景。通过一次性发送多个命令到服务器,可以减少通信开销,提高性能。  (1)批量操作 使用管道可以有效地执行批量操作。例如,当您需要向数据库中添加大量数据时,使用管道可以减少每个命令的通信成本,从而大大提高操作的效率。  以下示例演示了如何使用管道进行批量设置操作:  import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import java.util.List;  public class RedisPipelineBatchExample {     public static void main(String[] args) {         Jedis jedis = new Jedis("localhost", 6379);         Pipeline pipeline = jedis.pipelined();          // 向管道中添加一批设置操作         for (int i = 0; i < 1000; i++) {             pipeline.set("key" + i, "value" + i);         }          // 执行管道中的命令         List<Object> results = pipeline.syncAndReturnAll();          // 关闭连接         jedis.close();     } }  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 (2)吞吐量要求高 在需要高吞吐量的场景下,管道可以显著提升性能。当多个命令需要在短时间内执行时,使用管道可以将这些命令打包发送,减少了通信的往返次数。  使用管道来进行大规模数据处理时,尤其可以在高负载的情况下提高系统的处理能力。   四、案例研究:保证订单支付的数据一致性与性能优化 1、场景描述 在一个在线商城系统中,面临一个重要的问题:如何在订单支付过程中保证数据的一致性,并且如何优化支付操作的性能。  (1)订单支付需求 在用户下单后,需要执行订单支付操作,确保支付和订单状态的一致性。  (2)数据一致性要求 支付成功后,必须将订单状态更新为已支付,以保持数据的一致性。  (3)高并发支付 在高并发的情况下,需要确保订单支付的性能和数据一致性。  2、使用Redis事务解决数据一致性问题 (1)事务实现订单支付 Jedis jedis = new Jedis("localhost", 6379); Transaction transaction = jedis.multi();  // 扣除用户余额 transaction.decrBy("user:balance:1", orderAmount);  // 更新订单状态为已支付 transaction.hset("order:1", "status", "paid");  List<Object> results = transaction.exec(); 1 2 3 4 5 6 7 8 9 10 以上示例中,使用了Redis事务来确保在一个操作序列中,用户余额的扣除和订单状态的更新同时发生。如果事务中的任何一步操作失败,整个事务都会被回滚,保证了数据的一致性。  (2)事务的一致性保证 使用事务可以保证用户余额和订单状态的一致性,要么同时成功,要么同时失败。这样,可以确保支付和订单状态的正确性,避免了潜在的数据不一致问题。  3、使用Redis管道优化支付性能 (1)管道批量支付 Jedis jedis = new Jedis("localhost", 6379); Pipeline pipeline = jedis.pipelined();  for (Order order : orders) {     pipeline.decrBy("user:balance:" + order.getUserId(), order.getAmount());     pipeline.hset("order:" + order.getId(), "status", "paid"); }  List<Object> results = pipeline.syncAndReturnAll(); 1 2 3 4 5 6 7 8 9 在这个示例中,使用了Redis管道来批量处理多个订单的支付。通过将多个命令一次性发送给服务器,可以减少通信开销,从而显著提高支付操作的性能。  (2)管道的性能提升 通过使用管道,可以将多个支付操作打包在一次通信中进行,减少了通信往返次数,从而提高了支付的性能。  尤其在高并发支付的场景下,管道可以显著减少服务器负载,提高系统的响应能力。   五、事务和管道的限制与注意事项 1、事务的限制 事务在使用过程中需要注意以下限制,其中包括WATCH命令和乐观锁的使用。  (1)WATCH命令 在事务中使用WATCH命令可以监视一个或多个键,如果被监视的键在事务执行过程中被其他客户端修改,事务会被中断。这是为了保证事务的一致性和避免竞态条件。  正面例子:  Jedis jedis = new Jedis("localhost", 6379); Transaction transaction = jedis.multi();  // 监视键"balance" transaction.watch("balance");  // ... 在此期间可能有其他客户端修改了"balance"键的值 ...  // 执行事务 List<Object> results = transaction.exec(); 1 2 3 4 5 6 7 8 9 10 反面例子:  Jedis jedis = new Jedis("localhost", 6379); Transaction transaction = jedis.multi();  // 监视键"balance" transaction.watch("balance");  // ... 在此期间其他客户端修改了"balance"键的值 ...  // 尝试执行事务,但由于"balance"键被修改,事务会被中断 List<Object> results = transaction.exec(); 1 2 3 4 5 6 7 8 9 10 (2)乐观锁 在处理并发更新时,可以使用乐观锁的方式。通过使用版本号或时间戳等机制,在执行命令前先检查数据是否被其他客户端修改过,从而避免并发冲突。  正面例子:  Jedis jedis = new Jedis("localhost", 6379);  // 获取当前版本号 long currentVersion = Long.parseLong(jedis.get("version"));  // 更新数据前检查版本号 if (currentVersion == Long.parseLong(jedis.get("version"))) {     Transaction transaction = jedis.multi();     transaction.set("data", "new value");     transaction.incr("version");     List<Object> results = transaction.exec(); } else {     // 数据已被其他客户端修改,需要处理冲突 } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2、管道的注意事项 使用管道时需要注意以下事项,包括管道的串行性和慎重使用。  (1)不支持事务 管道不支持事务,因此无法通过管道实现事务的原子性和一致性。如果需要事务支持,应该使用Redis的事务机制。  (2)慎用管道 管道虽然可以提高性能,但并不是在所有场景下都能带来性能提升。在某些情况下,由于管道的串行性,某些命令可能会阻塞其他命令的执行,反而降低了性能。  正面例子:  Jedis jedis = new Jedis("localhost", 6379); Pipeline pipeline = jedis.pipelined();  for (int i = 0; i < 1000; i++) {     pipeline.set("key" + i, "value" + i); }  // 执行管道中的命令并获取结果 List<Object> results = pipeline.syncAndReturnAll(); 1 2 3 4 5 6 7 8 9 反面例子:  Jedis jedis = new Jedis("localhost", 6379); Pipeline pipeline = jedis.pipelined();  for (int i = 0; i < 1000; i++) {     // 注意:此处执行了耗时的命令,可能阻塞其他命令的执行     pipeline.get("key" + i); }  // 执行管道中的命令并获取结果 List<Object> results = pipeline.syncAndReturnAll(); 1 2 3 4 5 6 7 8 9 10 六、总结 本篇博客深入探讨了Redis中的事务和管道机制,以及它们在保证数据一致性和优化性能方面的应用。  通过详细的讲解和代码示例,我们了解了事务和管道的基本概念、特性、使用方法以及适用场景。以下是本篇博客的主要内容总结:  在Redis事务部分,我们了解了事务的概念和特性。事务可以确保一系列命令的原子性、一致性、隔离性和持久性。  通过MULTI、EXEC、DISCARD和WATCH命令,我们可以管理事务的开始、提交、回滚以及监视键变化。事务适用于需要保证原子性和一致性的操作,特别是在强一致性要求的场景下。  在Redis管道部分,我们深入了解了管道的概念和优势。管道允许一次性发送多个命令到服务器,减少通信开销,提高性能。  通过PIPELINE、MULTI和EXEC命令,我们可以创建管道、添加命令,并执行管道中的命令。管道适用于批量操作和吞吐量要求较高的场景,可以显著提高Redis的性能。  在事务 vs 管道:何时使用何种部分,我们对比了事务和管道的适用场景。  事务适用于保证强一致性操作和原子性要求高的场景; 管道适用于批量操作和高吞吐量的场景。 通过示例,我们说明了如何根据业务需求选择合适的机制来满足一致性和性能的需求。  在案例研究:保证订单支付的数据一致性与性能优化部分,我们应用之前的知识解决了一个实际问题。我们展示了如何使用事务保证订单支付的数据一致性,同时如何使用管道优化支付操作的性能。这个案例充分体现了事务和管道在实际业务中的应用。  在事务和管道的限制与注意事项部分,我们指出了事务和管道的一些限制和注意事项。事务受到WATCH命令和乐观锁的限制,而管道不支持事务,并且需要在使用时慎重考虑性能影响。  通过本篇博客,我们详细探讨了Redis中的事务和管道机制,了解了它们如何在实际应用中保证数据一致性和优化性能。无论是强调一致性还是追求性能,都可以根据业务需求选择合适的机制来达到最佳效果。 ———————————————— 版权声明:本文为CSDN博主「哪 吒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/guorui_java/article/details/132747287 
  • [其他] idea的远程调试
     使用IntelliJ IDEA内置的远程调试功能  IntelliJ IDEA是一款强大的Java集成开发环境(IDE),它提供了许多实用的特性来帮助开发者提高编程效率。其中,远程调试功能使得开发者可以在不同的计算机上调试程序,这对于团队合作和远程维护项目非常有用。本文将介绍如何使用IntelliJ IDEA内置的远程调试功能。  1. 配置远程服务器  在使用远程调试之前,首先需要配置一个远程服务器。这可以通过以下步骤完成:  1. 打开IntelliJ IDEA,点击 "File" > "New" > "Project",创建一个新的项目。 2. 在 "New Project" 窗口中,选择 "Spring Initializr",然后点击 "Next"。 3. 填写项目的基本信息,如Group、Artifact、Name等,然后点击 "Next"。 4. 在 "Dependencies" 页面中,添加需要的依赖,例如 "Web" -> "Spring Web",然后点击 "Next"。 5. 确认项目设置,点击 "Finish",IntelliJ IDEA将自动创建一个新项目。  接下来,我们需要配置远程服务器的连接信息。点击 "Run" > "Edit Configurations",在弹出的窗口中点击左上角的 "+" 按钮,选择 "Remote"。  2. 配置远程调试参数  在 "Remote" 配置页面中,我们需要填写远程服务器的相关信息:  1. Name:给这个远程调试配置起一个名字,例如 "Remote Debug"。 2. Host:远程服务器的IP地址或域名。 3. Port:远程服务器的端口号,默认为8080。 4. Deployment:选择一个部署方式,例如 "Artifact",然后点击 "..." 按钮,选择刚刚创建的项目生成的jar文件。 5. Keystore:如果需要使用密钥库进行认证,点击 "..." 按钮,选择密钥库文件。如果不需要认证,可以留空。 6. Save configuration:点击 "Apply" 保存配置。  3. 开始远程调试  配置好远程调试后,我们可以像平时一样运行程序。在代码中添加断点,然后点击工具栏上的绿色虫子图标(或按下Shift + F9)启动远程调试。程序将在远程服务器上运行,并在达到断点时暂停。  4. 查看变量和日志  在远程调试过程中,我们可以查看变量值、调用堆栈以及输出日志等信息。这些信息将显示在右侧的 "Variables"、"StackTrace" 和 "Logs" 面板中。此外,我们还可以使用内置的搜索功能快速定位相关信息。  5. 结束远程调试  当程序运行完成后,我们可以点击工具栏上的红色方块图标(或按下Shift + F9)结束远程调试。同时,远程服务器上的程序也会被终止。  总结  通过以上步骤,我们可以轻松地使用IntelliJ IDEA内置的远程调试功能。这不仅可以帮助我们更高效地完成调试任务,还可以促进团队之间的协作和沟通。希望本文能对你有所帮助! 
  • [其他] 如何提高单测覆盖率
     提高单元测试覆盖率的策略  单元测试是软件开发过程中的一个重要环节,它帮助我们确保代码的正确性、稳定性和可维护性。然而,在实际开发过程中,由于时间、人力和技术限制,单元测试的覆盖率可能并不高。那么,如何提高单元测试的覆盖率呢?本文将介绍一些有效的策略。  1. 理解代码结构  首先,要提高单元测试覆盖率,需要对代码的结构有深入的理解。这包括了解代码的功能、模块划分、类之间的关系等。只有充分理解了代码结构,才能编写出有针对性的单元测试用例。  2. 使用单元测试框架  使用单元测试框架可以大大提高测试的效率和覆盖率。一个好的单元测试框架应该具备以下特点:  - 支持自动生成测试用例:框架应该能够根据代码的结构和功能,自动生成合适的测试用例。 - 支持参数化测试:通过参数化测试,可以方便地为不同的输入数据编写测试用例,从而提高测试覆盖率。 - 支持并发测试:并发测试可以帮助我们发现潜在的竞态条件问题。 - 提供详细的测试报告:测试框架应该能够清晰地展示测试结果,包括通过率、失败原因等信息。  3. 设计测试用例  设计测试用例时,应遵循以下原则:  - 尽可能覆盖所有代码路径:确保每个分支、每个条件分支都有对应的测试用例。 - 测试正常情况和异常情况:对于函数和方法,要分别测试其正常输入和异常输入的情况。同时,也要考虑到边界条件和特殊值的测试。 - 使用mock对象:对于依赖于外部资源的对象(如数据库、网络连接等),可以使用mock对象进行隔离,从而简化测试用例的设计和维护。  4. 遵循TDD(测试驱动开发)原则  TDD是一种先编写测试用例,然后编写实现代码的开发模式。这种模式可以帮助我们从一开始就关注代码的质量,确保每个功能都经过充分的测试。在TDD过程中,我们需要:  - 为每个函数或方法编写一个或多个独立的测试用例。 - 从这些测试用例出发,逐步实现代码的功能。 - 在实现代码后,运行这些测试用例,确保功能正确无误。 - 如果测试失败,分析原因并修复代码;如果测试通过,则继续添加更多的测试用例。  5. 定期评估和调整测试策略  随着项目的发展,代码结构和需求可能会发生变化。因此,我们需要定期评估现有的测试覆盖率,并根据需要调整测试策略。这可能包括添加新的测试用例、优化现有测试用例、重构代码以便于测试等。  总之,提高单元测试覆盖率是一个持续的过程,需要我们不断地学习、实践和改进。通过遵循上述策略,我们可以有效地提高单元测试的覆盖率,从而提高软件的质量和可维护性。 
  • [使用说明] 开始使用CodeArts IDE for Java
    本篇内容主要介绍使用CodeArts IDE for Java创建工程、代码补全、运行调试代码、Build构建和测试相关的主要功能。一、下载安装CodeArts IDE for JavaCodeArts IDE for Java安装要求 至少需要 2 GB RAM ,但是推荐8 GB RAM; 至少需要 2.5 GB 硬盘空间,推荐SSD; 64位Microsoft Windows 10 下载并安装CodeArts IDE for Java>>>前往CodeArts IDE for Java官方下载页面下载完成后,运行codearts-java-*.exe文件。按照安装导航的步骤,选择个人安装配置进行安装。二、登录CodeArts IDE for Java要求用户登录后才能激活并使用Java语言服务和运行调试的相关功能。打开CodeArts IDE后,可以通过点击右上角的登录入口,使用华为账号进行登录:也可以在新建Java项目时弹出的需要登录弹窗中,点击登录按钮进行登录:三、创建Java工程CodeArts IDE for Java可以通过新建工程向导界面创建Java工程,支持选择四种Java模板创建工程(构建系统:Maven/Gradle,框架:None/SpringBoot),选择创建SpringBoot工程时,可根据需求选择(可多选)相应的第三方依赖,创建成功后第三方依赖被成功写入pom.xml或build.gradle文件。新建工程界面效果如下:CodeArts IDE for Java有三种方式打开新建Java工程向导界面,方式如下:1. 点击“文件->新建->工程”菜单:2. 在欢迎界面点击“新建项目”或者“创建Java工程”:3. 使用快捷键“Alt+P”快速唤出新建工程界面。如果在新窗口创建工程(未打开任何工程),工程创建成功后会直接在当前窗口打开;如果已经打开了一个工程,并在当前窗口通过新建工程向导创建新工程,创建成功的提示弹窗中,点击→当前窗口,工程在当前窗口打开。点击→新窗口,CodeArts IDE将打开新窗口并加载已创建的工程。创建工程实例:四、语言服务初始化与日志查看加载项目时,Java语言服务会进行初始化,右下角状态栏以及消息通知弹窗会有语言服务初始化过程提示信息。语言服务初始化过程中会启动相关服务、下载依赖的Jar包及进行Indexing,此过程受计算机性能、网速等因素影响会耗费一定的时间。点击“输出”视图,切换“SmartAssist Java”,可查看更多语言服务初始化过程的日志:语言服务初始化完成之前,语言服务相关功能(如代码补全、代码重构、查看类型定义等)将不会有很好的体验,此时需耐心等待语言服务初始化完成。当右下角弹出提示“Java Language Server is ready”(Java语言服务已就绪)时(见下图),说明语言服务已初始化完毕,此时可执行文件类和main方法会出现运行按钮,我们就可以开始正常使用语言服务的全部功能。五、代码补全语言服务初始化完成后,即可使用代码补全功能。代码补全列表中包含语言服务的补全及强大的AI智能补全(带有⊕图标),见下图:六、代码运行调试语言服务初始化完成后,在可运行的文件左侧会显示运行图标,选择“Run main in DemoApplication”即可开始运行,选择“Debug main in DemoApplication”即可开始调试,终端视图中将会显示调试运行的信息。也可以点击右上角的运行按钮和调试按钮分别发起运行和调试。可在代码左侧设置断点,Debug进入断点后,可在底部运行调试视图中查看调试相关信息(变量、监视、调用堆栈),也可以对断点进行管理。监视视图和变量视图在调试发起进入断点后,同样支持代码补全能力。运行调试实例:七、构建工程CodeArts IDE for Java会在代码调试运行前自动进行工程构建,也可以通过修改相关运行配置跳过自动构建,见下图:(备注:“SkipBuild”选项默认为false,当设置为true时,发起调试可以跳过构建直接调试,适用于已构建出产物、且代码没有其他修改的情形,可提升调试运行的速度。)​我们可以通过点击相关构建菜单主动触发构建工程,“构建”视图右半部分将显示构建过程的相关信息。构建菜单入口如下:还可在构建视图的左侧视图中通过鼠标右键唤出构建菜单:​构建工程实例:八、运行单元测试用例CodeArts IDE for Java在编辑器左侧栏和底部测试视图提供单元测试运行功能。运行当前文件所有测试用例,请点击类名所在行的左侧绿色按钮,或者鼠标右键此按钮并选择“运行测试”。底部栏自动打开测试视图,展示当前运行所有用例的状态,右侧输出运行日志。​​​​运行当前单个测试用例,请点击测试用例所在行的左侧绿色按钮,或者鼠标右键此按钮并选择“运行测试”,测试视图将展示当前运行的测试用例,点击测试用例后右侧可查看该测试用例的相关运行输出。在测试视图的包名、类名或者方法名上,可以运行该包下、该类下或者某个具体的测试用例,鼠标悬停上去后会显示相关的运行调试按钮,点击后即可发起运行或调试。点击测试视图左侧工具栏的的绿色运行按钮运行可以运行此工程下的所有测试用例。单击或者右键测试视图左侧工具栏的“视图和更多操作”按钮,可以展开更多测试视图相关的操作。如清除所有结果、排序查看、切换树形或列表查看等等。点击清除所有结果,可以清除当前已运行的所有测试用例的结果,相关图标恢复未执行用例前的状态。​运行单元测试用例实例:九、Java相关设置CodeArts IDE for Java为Java相关的设置提供了单独的设置入口,点击编辑器左下角的“管理->Java助手设置”菜单可以唤出Java的设置界面:在这个设置界面中,你可以设置工程的SDK、语言级别,Maven、Gradle相关的构建工具的设置以及代码formatter规则设置等等:十、代码formatter规则设置说明CodeArts IDE for Java支持自定义代码formatter规则的校验,暂不支持formatter规则的一键导入和导出(后续会支持)。formatter规则设置好后,在编辑器中选中需要格式化的代码,通过右键唤出“格式化文档”的菜单(或者使用快捷键“Ctrl+Alt+L”)来格式化选中的代码:
  • [互动交流] 【java-sdk】使用obs的并发分段上传比直接上传速度还慢
    您好,我在使用OBS javasdk的文件上传API,使用中,我发现,对于大文件,使用官方提供的并发上传文件示例,比直接调 putObject方法速度还慢,具体代码如下:1.并发分段上传代码官方示例https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0607.html2.调用 putObject 上传文件public SaveObjectResult saveObject(String bucket, String objectKey, MultipartFile file) throws Exception { // 校验桶名称是否正确 bucket = checkBucketName(bucket); InputStream in = file.getInputStream(); SaveObjectResult saveObjectResult = new SaveObjectResult(); saveObjectResult.setOk(Boolean.FALSE); saveObjectResult.setStorageMediaType(OSSTypeEnum.OBS.name()); PutObjectResult putObjectResult = getClient().putObject(bucket, objectKey, in); if (putObjectResult != null && putObjectResult.getStatusCode() == HttpStatus.HTTP_OK) { saveObjectResult.setOk(Boolean.TRUE); saveObjectResult.setAccessUrl(putObjectResult.getObjectUrl()); } return saveObjectResult; }此处直接使用ObsCLient 的 PutObjectResult putObject(String bucketName, String objectKey, InputStream input) 方法。测试结果发现,使用并发分段上传,并没有节省时间,反而比直接使用putObject还慢,为什么会这样?怎么优化,才能让上传更快?
  • [技术干货] Spring工具类封装
    Spring工具类封装这里为了方便在非Spring管理下获取Bean对象所封装的工具类创建步骤如下在util包下创建SpringUtils工具类导入如下包import com.begin.utils.StringUtils;import org.springframework.aop.framework.AopContext;import org.springframework.beans.BeansException;import org.springframework.beans.factory.NoSuchBeanDefinitionException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;其中import com.begin.utils.StringUtils; 为已经封装好的字符串处理工具类设置Spring应用上下文环境private static ConfigurableListableBeanFactory beanFactory;private static ApplicationContext applicationContext;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{ SpringUtils.beanFactory = beanFactory;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException{ SpringUtils.applicationContext = applicationContext;}通过名称获取Bean对象 /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws org.springframework.beans.BeansException * */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) throws BeansException { return (T) beanFactory.getBean(name); }通过requiedType类型获取Bean对象 /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws org.springframework.beans.BeansException * */ public static <T> T getBean(Class<T> clz) throws BeansException { T result = (T) beanFactory.getBean(clz); return result; }判断是否包含Bean对象 /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return beanFactory.containsBean(name); }判断以给定名字注册的bean定义是一个singleton还是一个prototype /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return beanFactory.isSingleton(name); }获取注册对象的类型 /** * @param name * @return Class 注册对象的类型 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { return beanFactory.getType(name); }获取注册对象的别名 /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return beanFactory.getAliases(name); }获取aop代理对象 /** * 获取aop代理对象 * * @param invoker * @return */ @SuppressWarnings("unchecked") public static <T> T getAopProxy(T invoker) { return (T) AopContext.currentProxy(); }获取当前的环境配置 /** * 获取当前的环境配置,无配置返回null * * @return 当前的环境配置 */ public static String[] getActiveProfiles() { return applicationContext.getEnvironment().getActiveProfiles(); } /** * 获取当前的环境配置,当有多个环境配置时,只获取第一个 * * @return 当前的环境配置 */ public static String getActiveProfile() { final String[] activeProfiles = getActiveProfiles(); return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; }获取配置文件中的值 /** * 获取配置文件中的值 * * @param key 配置文件的key * @return 当前的配置文件的值 * */ public static String getRequiredProperty(String key) { return applicationContext.getEnvironment().getRequiredProperty(key); }全部代码如下package com.begin.utils.spring;import com.begin.utils.StringUtils;import org.springframework.aop.framework.AopContext;import org.springframework.beans.BeansException;import org.springframework.beans.factory.NoSuchBeanDefinitionException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;/** * spring工具类 方便在非spring管理环境中获取bean * * @author ruoyi */@Componentpublic final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware{ /** Spring应用上下文环境 */ private static ConfigurableListableBeanFactory beanFactory; private static ApplicationContext applicationContext; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { SpringUtils.beanFactory = beanFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtils.applicationContext = applicationContext; } /** * 获取对象 * * @param name * @return Object 一个以所给名字注册的bean的实例 * @throws org.springframework.beans.BeansException * */ @SuppressWarnings("unchecked") public static <T> T getBean(String name) throws BeansException { return (T) beanFactory.getBean(name); } /** * 获取类型为requiredType的对象 * * @param clz * @return * @throws org.springframework.beans.BeansException * */ public static <T> T getBean(Class<T> clz) throws BeansException { T result = (T) beanFactory.getBean(clz); return result; } /** * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true * * @param name * @return boolean */ public static boolean containsBean(String name) { return beanFactory.containsBean(name); } /** * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) * * @param name * @return boolean * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { return beanFactory.isSingleton(name); } /** * @param name * @return Class 注册对象的类型 * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { return beanFactory.getType(name); } /** * 如果给定的bean名字在bean定义中有别名,则返回这些别名 * * @param name * @return * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException * */ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { return beanFactory.getAliases(name); } /** * 获取aop代理对象 * * @param invoker * @return */ @SuppressWarnings("unchecked") public static <T> T getAopProxy(T invoker) { return (T) AopContext.currentProxy(); } /** * 获取当前的环境配置,无配置返回null * * @return 当前的环境配置 */ public static String[] getActiveProfiles() { return applicationContext.getEnvironment().getActiveProfiles(); } /** * 获取当前的环境配置,当有多个环境配置时,只获取第一个 * * @return 当前的环境配置 */ public static String getActiveProfile() { final String[] activeProfiles = getActiveProfiles(); return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; } /** * 获取配置文件中的值 * * @param key 配置文件的key * @return 当前的配置文件的值 * */ public static String getRequiredProperty(String key) { return applicationContext.getEnvironment().getRequiredProperty(key); }}
  • [技术干货] IDEA下远程调试测试环境代码
    最近在开发一个新功能,拿到需求,分析需求,代码编写,单元测试。一看完美,发测试环境。结果测试提bug了。开发环境好好的,测试环境就怎么出问题了呢 。刚好之前听说过远程调试这个事情,但是从来还没验证过,这下刚好来学习下,顺便解决bug。在学习一个新东西前得先知道这个东西是什么,干什么的。所以我先去看了他的基本概念。远程调试(Remote Debugging)是一种在开发环境中通过网络连接到远程计算机或设备进行调试的技术。它允许开发人员在运行着的远程应用程序上设置断点、观察变量和执行代码,以便更好地理解和修复问题。说人话 就是,在我本地ide里面 去调试测试环境代码 。引用大佬的一句话:远程debug可以理解为一种JVM规范,当然其不是单独的一个规范,它是属于JDWP(Java Debug Wire Protocol)协议中的一部分,该协议定义了 Java 虚拟机(JVM)与调试器之间的交互方式。通过 JDWP,开发者可以在开发工具中设置断点、查看变量、执行代码等操作,以便对运行中的 Java 程序进行调试。说直白点就是遵循了该协议的JVM,就支持debug,也支持远程debug。了解完基本概念之后,进入实际操作环节 先说下我自己的环境,本地jdk1.8,idea2023.1,测试系统是centos7.9在服务器上启动远程调试模式:需要在服务器上运行代码,并将其启动为允许远程调试的模式。常见的方法是通过在启动脚本或命令行参数中添加调试选项来实现。例如,对于 Java 代码,可以使用以下命令启动服务器应用程序:java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar your-application.jar 将在服务器上启动一个使用5005端口的远程调试会话。在 IntelliJ IDEA 中配置远程调试:打开 IntelliJ IDEA,打开项目。然后,点击菜单栏,Run--》Edit Configurations弹出窗口中点击 "+" 号,Remote配置远程调试连接参数:Name: 给调试配置取一个名称。Host: 输入服务器的 IP 地址或主机名。Port: 输入服务器上的调试端口号(如上一步中的5005)。Transport: 选择调试传输协议,通常选择 "Socket"。点击保存配置。我本地是中文环境 ,对应的就是 运行,编辑配置,然后点击加号,选择远端。剩下的都一样了。开始远程调试:点击Debug按钮,或者使用快捷键 Shift + F9 启动远程调试会话。IDEA 将连接到服务器上的应用程序。至此就可以在本地远程打断点调试测试环境代码了 。在此做一个记录也希望可以帮到大家
  • [专题汇总] 它来了,它来了,8月份技术干货它来了。
     大家好,8月份给大家整理带来的是codeArts版本最新的技术干货合集,涉及java,golang,python希望可以帮助到大家。  1.shell检查硬盘状态 https://bbs.huaweicloud.com/forum/thread-149840-1-1.html  2.Golang之sync.Pool对象池对象重用机制总结【转】 https://bbs.huaweicloud.com/forum/thread-0265128851946454378-1-1.html  3.Go defer 去掉闭包函数及用法分析【转】 https://bbs.huaweicloud.com/forum/thread-0236128851600101424-1-1.html  4.Golang中的闭包(Closures)详解【转】 https://bbs.huaweicloud.com/forum/thread-0297128851484457502-1-1.html  5.Go语言之io.ReadAtLeast函数的基本使用和原理解析【转】 https://bbs.huaweicloud.com/forum/thread-0299128851274052618-1-1.html  6.Go语言应用闭包之返回函数【转】 https://bbs.huaweicloud.com/forum/thread-0224128850654756516-1-1.html  7.一文详解Go语言io包中的discard类型【转】 https://bbs.huaweicloud.com/forum/thread-0265128850459435377-1-1.html  8.golang的tunny的用法示例教程【转】 https://bbs.huaweicloud.com/forum/thread-0263128850335015408-1-1.html  9.Go语言tunny的workerWrapper使用教程示例【转】 https://bbs.huaweicloud.com/forum/thread-0265128850197454376-1-1.html  10.GO语言实现日志切割的示例详解【转】 https://bbs.huaweicloud.com/forum/thread-0236128849958046421-1-1.html  11.一文带你了解Golang中的泛型【转】 https://bbs.huaweicloud.com/forum/thread-0263128849797446407-1-1.html  12.Go整合ElasticSearch的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0299128848232195616-1-1.html  13.Golang仿ps实现获取Linux进程信息【转】 https://bbs.huaweicloud.com/forum/thread-0286128848118160409-1-1.html  14.Go数据结构之HeapMap实现指定Key删除堆【转】 https://bbs.huaweicloud.com/forum/thread-0224128829750512487-1-1.html  15.基于Golang编写贪吃蛇游戏【转】 https://bbs.huaweicloud.com/forum/thread-0275128829535850518-1-1.html  16.用Go获取短信验证码的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0275128829460656517-1-1.html  17.golang使用sync.singleflight解决热点缓存穿透问题【转】 https://bbs.huaweicloud.com/forum/thread-0275128829216193516-1-1.html  18.vue 实现 pdf 预览功能 https://bbs.huaweicloud.com/forum/thread-0224128574304525257-1-1.html  19.Nacos部署中的一些常见问题 https://bbs.huaweicloud.com/forum/thread-0286128573822017203-1-1.html  20.用ngrok实现内网穿透,一行命令就搞定! https://bbs.huaweicloud.com/forum/thread-0224128573537080255-1-1.html  21.过滤器+slf4j(MDC)实现日志记录请求ID https://bbs.huaweicloud.com/forum/thread-0263128573250127242-1-1.html  22.阿里EasyExcel快速导出demo https://bbs.huaweicloud.com/forum/thread-0265128573093017187-1-1.html  23.业务开发时,接口不能对外暴露怎么办? https://bbs.huaweicloud.com/forum/thread-0224128572747042254-1-1.html  24.centos同步windows时间 https://bbs.huaweicloud.com/forum/thread-0286128572497851201-1-1.html  25.切面实现下单请求防重提交功能(自定义注释@repeatSubmit) https://bbs.huaweicloud.com/forum/thread-0263128572383761239-1-1.html 
  • [技术干货] spring boot中@Cacheable的使用场景和示例代码
    @Cacheable是Spring Boot中用于缓存方法结果的注解,它可以将方法的返回值缓存起来,当再次调用该方法时,如果缓存中存在对应的值,则直接从缓存中获取,而不需要再次执行方法。使用场景:对于计算量大或者耗时较长的方法,可以使用@Cacheable来提高系统性能。对于一些不经常变化的数据,可以使用@Cacheable来减少数据库查询次数。对于一些需要频繁调用的方法,可以使用@Cacheable来提高系统的响应速度。示例代码:首先,需要在pom.xml文件中添加spring-boot-starter-cache依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>然后,在Spring Boot的主配置类上添加@EnableCaching注解,开启缓存功能:@SpringBootApplication @EnableCaching public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }接下来,在需要缓存的方法上添加@Cacheable注解:import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class UserService { @Cacheable(value = "users", key = "#id") public User getUserById(Long id) { // 模拟从数据库中查询用户信息 return new User(id, "张三"); } }在上面的示例代码中,getUserById方法被@Cacheable注解修饰,表示该方法的结果将被缓存。value属性表示缓存的名称,key属性表示缓存的键,这里使用了SpEL表达式,表示以方法参数id作为缓存的键。当调用getUserById方法时,如果缓存中存在对应的值,则直接从缓存中获取,否则会执行方法并将结果存入缓存。下面再举几个@Cacheable的使用示例:在方法中使用多个参数时,可以使用SpEL表达式来指定缓存的键。例如:@Service public class UserService { @Cacheable(value = "users", key = "#id + #name") public User getUserByIdAndName(Long id, String name) { // 模拟从数据库中查询用户信息 return new User(id, name); } }可以设置缓存的过期时间,例如:@Service public class UserService { @Cacheable(value = "users", key = "#id", unless = "#result == null") public User getUserById(Long id) { // 模拟从数据库中查询用户信息 User user = new User(id, "张三"); if (user.getAge() > 18) { user.setAge(0); } return user; } }在这个示例中,我们设置了缓存的过期时间为5分钟(300秒),并且只有在查询结果不为空时才会缓存。可以在类上使用@Cacheable注解,表示该类的所有方法的结果都会被缓存。例如:@Service @Cacheable public class UserService { public User getUserById(Long id) { // 模拟从数据库中查询用户信息 return new User(id, "张三"); } public User getUserByName(String name) { // 模拟从数据库中查询用户信息 return new User(1L, name); } }在这个示例中,我们使用了@Cacheable注解修饰UserService类,表示该类的所有方法的结果都会被缓存。代码整合示例package com.nezha.controller; import com.nezha.entity.Area; import com.nezha.service.area.AreaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/area") public class AreaController { @Autowired private AreaService areaService; // @Cacheable添加缓存,相同条件的查询不再查询数据库,而是从缓存中查询 @Cacheable(cacheNames="area",key = "#id") @GetMapping("getAreaById") public Area getAreaById(Integer id) { return areaService.getAreaById(id); } // @CachePut每次都会访问数据库,并更新缓存 @CachePut(cacheNames="area",key = "#id") @PostMapping("updateAreaById") public void updateAreaById(Integer id) { areaService.updateAreaById(id); } // @CacheEvict清除缓存 @CacheEvict(cacheNames="area",key = "#id") @PostMapping("deleteAreaById") public void deleteAreaById(Integer id) { areaService.deleteAreaById(id); } }参考:cid:link_0
  • [技术干货] java中用抽象类实现多态
    在Java中,可以使用接口和抽象类来实现多态。当需要选择一个实现类时,可以通过以下步骤进行:定义一个抽象类或接口,包含需要实现的方法签名。创建多个实现类,分别实现抽象类或接口中的方法。在需要选择实现类的地方,使用抽象类或接口类型的引用作为参数,调用方法。在运行时,Java会根据传入的参数类型自动选择合适的实现类。示例代码:// 定义一个抽象类 public abstract class Animal { public abstract void makeSound(); } // 创建两个实现类 public class Dog extends Animal { @Override public void makeSound() { System.out.println("汪汪汪"); } } public class Cat extends Animal { @Override public void makeSound() { System.out.println("喵喵喵"); } } // 在需要选择实现类的地方 public class Main { public static void main(String[] args) { // 使用抽象类类型的引用作为参数 Animal myAnimal = new Dog(); myAnimal.makeSound(); // 输出:汪汪汪 myAnimal = new Cat(); myAnimal.makeSound(); // 输出:喵喵喵 } }
  • [技术干货] java实现网络连通性检测的代码
    以下是使用Java实现网络连通性检测的代码示例:import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; public class NetworkConnectivityTest { public static void main(String[] args) { String host = "www.example.com"; // 要检测的目标主机名或IP地址 try { InetAddress inetAddress = InetAddress.getByName(host); boolean isReachable = inetAddress.isReachable(5000); // 设置超时时间为5秒 if (isReachable) { System.out.println("网络连通性正常"); } else { System.out.println("网络连通性异常"); } } catch (UnknownHostException e) { System.out.println("无法解析主机名或IP地址: " + host); } catch (IOException e) { System.out.println("发生IO异常: " + e.getMessage()); } } }以上代码使用了InetAddress类的isReachable()方法来检测目标主机的网络连通性。该方法会尝试与目标主机建立连接,如果在指定的超时时间内成功建立连接,则判定为网络连通性正常;否则判定为网络连通性异常。在示例中,我们设置了超时时间为5秒,可以根据需要进行调整。如果目标主机名或IP地址无法解析,将会抛出UnknownHostException异常;如果发生其他IO异常,将会抛出IOException异常。
  • [技术干货] JAVA幂等性实现
    简单来说幂等保证了只要调用接口成功,外部多次调用对系统的影响是一致的,也就是一个请求多次重试的问题。需要考虑幂等的场景:客户端存在多次提交或者超时重试的情况;分布式架构中因网络波动采用重试机制,如Dubbo的重试机制;消息推送重试,如MQ重试;不幂等带来的影响:比如在支付场景下,消费者消费扣款消息,对一笔订单进行扣款操作,该扣款操作需要扣除100元,在不幂等的情况下,如果消费者多次发起请求,就会造成多次扣款。幂等性问题解决方案幂等问题演示:执行如下SQL,初始化金额为100;create table order_pay( id int default 0 not null primary key, amt decimal(9, 2) default 0.00 null comment '金额', status int null comment '状态:0已完成/1处理中')comment '订单支付表';INSERT INTO test.order_pak (id, amt, status) VALUES (1, 100.00, 0);pay接口:@RestController@RequestMapping("/test/order-pay")public class OrderPayController { @Autowired IOrderPayService orderPayService; @ApiOperation(httpMethod = "POST", value = "幂等性测试") @PostMapping("pay") public Resp pay(@RequestBody Req<PayReq> req) { PayReq payReq = req.getData(); OrderPay order = orderPayService.getOne(new LambdaQueryWrapper<OrderPay>().eq(OrderPay::getId, payReq.getBizId())); order.setAmt(order.getAmt().subtract(payReq.getAmt())); orderPayService.updateById(order); return Resp.success("剩余金额更新为"+order.getAmt()); }}在不控制幂等的情况下,对pay接口连续发起5次请求,每一次扣减金额为10.00:可以看到最后剩余金额更新为50 .00,我们发起一次请求,应该只扣除10.00,当遇到网络重复或系统bug在不控制幂等的情况下会导致系统进行了多次扣款。那如何进行幂等控制呢?有什么方法呢?幂等性的实现保证幂等性的措施包括但不限于:1、表单提交后按钮置灰限制客户端请求2、添加唯一索引把唯一标识作为唯一索引,在重复创建时会抛出唯一约束异常3、全局唯一ID针对业务操作和内容生产全局唯一ID,在执行操作时判断ID是否存在来判断是否已执行4、一锁二查三更新如果在流程处理过程中,业务要求不能并发执行,可以在流程执行之前根据业务ID获取锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放锁,同时也需要在入口处增加业务状态的判断,以避免对请求的多次处理。这种方式不止可用于幂等的控制,也可以防止并发操作带来的异常。@ApiOperation(httpMethod = "POST", value = "幂等性测试")@PostMapping("pay")public Resp pay(@RequestBody Req<PayReq> req) throws Exception { PayReq payReq = req.getData(); // 获取分布式锁 redisTools.lock(payReq.getBizId().toString()); OrderPay order = orderPayService.getOne(new LambdaQueryWrapper<OrderPay>().eq(OrderPay::getId, payReq.getBizId())); if (order.getStatus()==1){ order.setAmt(order.getAmt().subtract(payReq.getAmt())); order.setStatus(0); orderPayService.updateById(order); } // 释防锁 redisTools.unlock(payReq.getBizId().toString()); return Resp.success("剩余金额为"+order.getAmt());}以下是对pay接口进行并发请求5次的结果:可以看到,只有1个请求可以请求成功,另外4个请求在 redisTools.lock(payReq.getBizId().toString())获取分布式锁的步骤中抛出异常。不加锁与加锁的区别如果把分布式锁的步骤去掉会发生什么样的情况呢?@ApiOperation(httpMethod = "POST", value = "幂等性测试")@PostMapping("pay")public Resp pay(@RequestBody Req<PayReq> req) throws Exception { PayReq payReq = req.getData(); OrderPay order = orderPayService.getOne(new LambdaQueryWrapper<OrderPay>().eq(OrderPay::getId, payReq.getBizId())); if (order.getStatus()==1){ order.setAmt(order.getAmt().subtract(payReq.getAmt())); order.setStatus(0); orderPayService.updateById(order); } return Resp.success("剩余金额为"+order.getAmt());}依旧对pay接口进行并发请求5次,查看日志:5次请求都成功了,但是逻辑执行了5遍,在复杂的业务流程下可能会引发其他不必要问题。总结:在系统设计中,实现幂等的方案有很多种,要根据自身系统的特性优先选择小而巧方案,避免形成过于复杂的方案。作者:sleeper链接:https://juejin.cn/post/6891545350050414605来源:稀土掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总条数:2301 到第 页
上滑加载中