-
【问题来源】 汉口银行【问题简要】 座席二次开发问题【问题类别】 CC-Gateway【AICC解决方案版本】 AICC版本:AICC 22.200 UAP版本:UAP9600 V100R005C00SPC113 CTI版本: ICD V300R008C25spc012【期望解决时间】 尽快【问题现象描述】调用签入接口,服务返回状态码405,调用参数、代码如下图所示:调用接口的地址:https://ip:port/agentgateway/resource/onlineagent/{agentid}代码截图:入参截图:
-
大家好本次整理的是codeArts6月份干货合集,希望可以帮到大家。 1.Go语言五大主流web框架-转载 https://bbs.huaweicloud.com/forum/thread-0213121661901680008-1-1.html 2.Vue中实现自定义excel下载-转载 https://bbs.huaweicloud.com/forum/thread-0278121662059656002-1-1.html 3.如何查看自己的操作系统是否支持 64 位架构 https://bbs.huaweicloud.com/forum/thread-0257121682060865007-1-1.html 4.Solr和Elasticsearch对比 https://bbs.huaweicloud.com/forum/thread-0253122347814807015-1-1.html 5.linux关于sed流编辑器那些事 https://bbs.huaweicloud.com/forum/thread-02116122519740471010-1-1.html 6.JS中的事件监听-转载 https://bbs.huaweicloud.com/forum/thread-0253122779497415032-1-1.html 7.浅谈DNS域名解析的过程-转载 https://bbs.huaweicloud.com/forum/thread-0253122779866795033-1-1.html 8.【服务器】利用树莓派搭建 web 服务器-无需公网IP-转载 https://bbs.huaweicloud.com/forum/thread-02106122779940418023-1-1.html 9.【vue3】使用canvas-转载 https://bbs.huaweicloud.com/forum/thread-0234122780116390011-1-1.html 10.前端面试八股文(超详细)-转载 https://bbs.huaweicloud.com/forum/thread-02116122780328089022-1-1.html 11.python数组和矩阵的区别 https://bbs.huaweicloud.com/forum/thread-0219123321311972030-1-1.html 12.Python NLP开发之实现聊天机器人【转】 https://bbs.huaweicloud.com/forum/thread-0253123321507470064-1-1.html 13.使用vbs脚本来监控windows服务器上的应用程序(不存在就启动)【转】 https://bbs.huaweicloud.com/forum/thread-02117123384453066001-1-1.html 14.vbs ShellExecute运行外部程序时如何判断外部程序执行成功与否【转】 https://bbs.huaweicloud.com/forum/thread-0212123384557753002-1-1.html 15.cmd批量ping命令的实现【转】 https://bbs.huaweicloud.com/forum/thread-0226123384655183003-1-1.html 16.批处理文件bat启动redis及任何软件【转】 https://bbs.huaweicloud.com/forum/thread-0212123384741335003-1-1.html 17.cmd实现清理系统垃圾的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0284123384865623001-1-1.html 18.cmd常用命令大全【转】 https://bbs.huaweicloud.com/forum/thread-0226123385078380004-1-1.html 19.window使用bat批处理文件执行cmd命令【转】 https://bbs.huaweicloud.com/forum/thread-0215123385522817002-1-1.html 20.DOS批处理字符串截取【转】 https://bbs.huaweicloud.com/forum/thread-02117123385670322002-1-1.html 21.批处理删除指定文件或文件夹的实现【转】 https://bbs.huaweicloud.com/forum/thread-02100123387214334003-1-1.html 22.批处理字符串截取的实现【转】 https://bbs.huaweicloud.com/forum/thread-0248123387845375003-1-1.html 23.Python中选择排序的实现与优化【转】 https://bbs.huaweicloud.com/forum/thread-02105123388234406002-1-1.html 24.flask+pymysql实现Web端操作数据库的项目实践【转】 https://bbs.huaweicloud.com/forum/thread-0248123388970328005-1-1.html 25.深入理解Python中的Contextlib库【转】 https://bbs.huaweicloud.com/forum/thread-02117123389149341007-1-1.html
-
使用HashSet判断主键是否存在 HashSet 实现 Set 接口,由哈希表(实际上是 HashMap )实现,但不保证 set 的迭代顺序,并允许使用 null 元素。HashSet 的时间复杂度跟 HashMap 一致,如果没有哈希冲突则时间复杂度为 O(1) ,如果存在哈希冲突则时间复杂度不超过 O(n) 。所以,在日常编码中,可以使用 HashSet 判断主键是否存在。 案例:给定一个字符串(不一定全为字母),请返回第一个重复出现的字符。 /** 查找第一个重复字符 */ public static char findFirstRepeatedChar(String string) { // 检查空字符串 if (Objects.isNull(string) || string.isEmpty()) { return null; } // 查找重复字符 char[] charArray = string.toCharArray(); Set charSet = new HashSet<>(charArray.length); for (char ch : charArray) { if (charSet.contains(ch)) { return ch; } charSet.add(ch); } // 默认返回为空 return null; } 复制 其中,由于 Set 的 add 函数有个特性——如果添加的元素已经再集合中存在,则会返回 false 。可以简化代码为: if (!charSet.add(ch)) { return ch; } 复制 使用HashMap存取键值映射关系 简单来说,HashMap 由数组和链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。如果定位到的数组位置不含链表,那么查找、添加等操作很快,仅需一次寻址即可,其时间复杂度为 O(1) ;如果定位到的数组包含链表,对于添加操作,其时间复杂度为 O(n) ——首先遍历链表,存在即覆盖,不存在则新增;对于查找操作来讲,仍需要遍历链表,然后通过key对象的 equals 方法逐一对比查找。从性能上考虑, HashMap 中的链表出现越少,即哈希冲突越少,性能也就越好。所以,在日常编码中,可以使用 HashMap 存取键值映射关系。 案例:给定菜单记录列表,每条菜单记录中包含父菜单标识(根菜单的父菜单标识为 null ),构建出整个菜单树。 /** 菜单DO类 */ @Setter @Getter @ToString public static class MenuDO { /** 菜单标识 */ private Long id; /** 菜单父标识 */ private Long parentId; /** 菜单名称 */ private String name; /** 菜单链接 */ private String url; } /** 菜单VO类 */ @Setter @Getter @ToString public static class MenuVO { /** 菜单标识 */ private Long id; /** 菜单名称 */ private String name; /** 菜单链接 */ private String url; /** 子菜单列表 */ private List<MenuVO> childList; } /** 构建菜单树函数 */ public static List<MenuVO> buildMenuTree(List<MenuDO> menuList) { // 检查列表为空 if (CollectionUtils.isEmpty(menuList)) { return Collections.emptyList(); } // 依次处理菜单 int menuSize = menuList.size(); List<MenuVO> rootList = new ArrayList<>(menuSize); Map<Long, MenuVO> menuMap = new HashMap<>(menuSize); for (MenuDO menuDO : menuList) { // 赋值菜单对象 Long menuId = menuDO.getId(); MenuVO menu = menuMap.get(menuId); if (Objects.isNull(menu)) { menu = new MenuVO(); menu.setChildList(new ArrayList<>()); menuMap.put(menuId, menu); } menu.setId(menuDO.getId()); menu.setName(menuDO.getName()); menu.setUrl(menuDO.getUrl()); // 根据父标识处理 Long parentId = menuDO.getParentId(); if (Objects.nonNull(parentId)) { // 构建父菜单对象 MenuVO parentMenu = menuMap.get(parentId); if (Objects.isNull(parentMenu)) { parentMenu = new MenuVO(); parentMenu.setId(parentId); parentMenu.setChildList(new ArrayList<>()); menuMap.put(parentId, parentMenu); } // 添加子菜单对象 parentMenu.getChildList().add(menu); } else { // 添加根菜单对象 rootList.add(menu); } } // 返回根菜单列表 return rootList; } 复制 使用 ThreadLocal 存储线程专有对象 ThreadLocal 提供了线程专有对象,可以在整个线程生命周期中随时取用,极大地方便了一些逻辑的实现。 常见的 ThreadLocal 用法主要有两种: 1、保存线程上下文对象,避免多层级参数传递; 2、保存非线程安全对象,避免多线程并发调用。 保存线程上下文对象,避免多层级参数传递 这里,以 PageHelper 插件的源代码中的分页参数设置与使用为例说明。 设置分页参数代码: /** 分页方法类 */ public abstract class PageMethod { /** 本地分页 */ protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>(); /** 设置分页参数 */ protected static void setLocalPage(Page page) { LOCAL_PAGE.set(page); } /** 获取分页参数 */ public static <T> Page<T> getLocalPage() { return LOCAL_PAGE.get(); } /** 开始分页 */ public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) { Page<E> page = new Page<E>(pageNum, pageSize, count); page.setReasonable(reasonable); page.setPageSizeZero(pageSizeZero); Page<E> oldPage = getLocalPage(); if (oldPage != null && oldPage.isOrderByOnly()) { page.setOrderBy(oldPage.getOrderBy()); } setLocalPage(page); return page; } } 复制 使用分页参数代码: /** 虚辅助方言类 */ public abstract class AbstractHelperDialect extends AbstractDialect implements Constant { /** 获取本地分页 */ public <T> Page<T> getLocalPage() { return PageHelper.getLocalPage(); } /** 获取分页SQL */ @Override public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) { String sql = boundSql.getSql(); Page page = getLocalPage(); String orderBy = page.getOrderBy(); if (StringUtil.isNotEmpty(orderBy)) { pageKey.update(orderBy); sql = OrderByParser.converToOrderBySql(sql, orderBy); } if (page.isOrderByOnly()) { return sql; } return getPageSql(sql, page, pageKey); } ... } 复制 使用分页插件代码: /** 查询用户函数 */ public PageInfo<UserDO> queryUser(UserQuery userQuery, int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); List<UserDO> userList = userDAO.queryUser(userQuery); PageInfo<UserDO> pageInfo = new PageInfo<>(userList); return pageInfo; } 复制 如果要把分页参数通过函数参数逐级传给查询语句,除非修改 MyBatis 相关接口函数,否则是不可能实现的。 保存非线程安全对象,避免多线程并发调用 在写日期格式化工具函数时,首先想到的写法如下: /** 日期模式 */ private static final String DATE_PATTERN = "yyyy-MM-dd"; /** 格式化日期函数 */ public static String formatDate(Date date) { return new SimpleDateFormat(DATE_PATTERN).format(date); } 复制 其中,每次调用都要初始化 DateFormat 导致性能较低,把 DateFormat 定义成常量后的写法如下: /** 日期格式 */ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); /** 格式化日期函数 */ public static String formatDate(Date date) { return DATE_FORMAT.format(date); } 复制 由于 SimpleDateFormat 是非线程安全的,当多线程同时调用 formatDate 函数时,会导致返回结果与预期不一致。如果采用 ThreadLocal 定义线程专有对象,优化后的代码如下: /** 本地日期格式 */ private static final ThreadLocal<DateFormat> LOCAL_DATE_FORMAT = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; /** 格式化日期函数 */ public static String formatDate(Date date) { return LOCAL_DATE_FORMAT.get().format(date); } 复制 这是在没有线程安全的日期格式化工具类之前的实现方法。在 JDK8 以后,建议使用 DateTimeFormatter 代替 SimpleDateFormat ,因为 SimpleDateFormat 是线程不安全的,而 DateTimeFormatter 是线程安全的。当然,也可以采用第三方提供的线程安全日期格式化函数,比如 apache 的 DateFormatUtils 工具类。 注意:ThreadLocal 有一定的内存泄露的风险,尽量在业务代码结束前调用 remove 函数进行数据清除。 使用 Pair 实现成对结果的返回 在 C/C++ 语言中, Pair (对)是将两个数据类型组成一个数据类型的容器,比如 std::pair 。 Pair 主要有两种用途: 1、把 key 和 value 放在一起成对处理,主要用于 Map 中返回名值对,比如 Map 中的 Entry 类; 2、当一个函数需要返回两个结果时,可以使用 Pair 来避免定义过多的数据模型类。 第一种用途比较常见,这里主要说明第二种用途。 定义模型类实现成对结果的返回 函数实现代码: /** 点和距离类 */ @Setter @Getter @ToString @AllArgsConstructor public static class PointAndDistance { /** 点 */ private Point point; /** 距离 */ private Double distance; } /** 获取最近点和距离 */ public static PointAndDistance getNearestPointAndDistance(Point point, Point[] points) { // 检查点数组为空 if (ArrayUtils.isEmpty(points)) { return null; } // 获取最近点和距离 Point nearestPoint = points[0]; double nearestDistance = getDistance(point, points[0]); for (int i = 1; i < points.length; i++) { double distance = getDistance(point, point[i]); if (distance < nearestDistance) { nearestDistance = distance; nearestPoint = point[i]; } } // 返回最近点和距离 return new PointAndDistance(nearestPoint, nearestDistance); } 复制 函数使用案例: Point point = ...; Point[] points = ...; PointAndDistance pointAndDistance = getNearestPointAndDistance(point, points); if (Objects.nonNull(pointAndDistance)) { Point point = pointAndDistance.getPoint(); Double distance = pointAndDistance.getDistance(); ... } 复制 使用 Pair 类实现成对结果的返回 在 JDK 中,没有提供原生的 Pair 数据结构,也可以使用 Map::Entry 代替。不过, Apache 的 commons-lang3 包中的 Pair 类更为好用,下面便以 Pair 类进行举例说明。 函数实现代码: /** 获取最近点和距离 */ public static Pair<Point, Double> getNearestPointAndDistance(Point point, Point[] points) { // 检查点数组为空 if (ArrayUtils.isEmpty(points)) { return null; } // 获取最近点和距离 Point nearestPoint = points[0]; double nearestDistance = getDistance(point, points[0]); for (int i = 1; i < points.length; i++) { double distance = getDistance(point, point[i]); if (distance < nearestDistance) { nearestDistance = distance; nearestPoint = point[i]; } } // 返回最近点和距离 return Pair.of(nearestPoint, nearestDistance); } 复制 函数使用案例: Point point = ...; Point[] points = ...; Pair<Point, Double> pair = getNearestPointAndDistance(point, points); if (Objects.nonNull(pair)) { Point point = pair.getLeft(); Double distance = pair.getRight(); ... } 复制 定义 Enum 类实现取值和描述 在 C++、Java 等计算机编程语言中,枚举类型(Enum)是一种特殊数据类型,能够为一个变量定义一组预定义的常量。在使用枚举类型的时候,枚举类型变量取值必须为其预定义的取值之一。 用 class 关键字实现的枚举类型 在 JDK5 之前, Java 语言不支持枚举类型,只能用类(class)来模拟实现枚举类型。 /** 订单状态枚举 */ public final class OrderStatus { /** 属性相关 */ /** 状态取值 */ private final int value; /** 状态描述 */ private final String description; /** 常量相关 */ /** 已创建(1) */ public static final OrderStatus CREATED = new OrderStatus(1, "已创建"); /** 进行中(2) */ public static final OrderStatus PROCESSING = new OrderStatus(2, "进行中"); /** 已完成(3) */ public static final OrderStatus FINISHED = new OrderStatus(3, "已完成"); /** 构造函数 */ private OrderStatus(int value, String description) { this.value = value; this.description = description; } /** 获取状态取值 */ public int getValue() { return value; } /** 获取状态描述 */ public String getDescription() { return description; } } 复制 用 enum 关键字实现的枚举类型 JDK5 提供了一种新的类型—— Java 的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常量使用,这是一种非常有用的功能。 /** 订单状态枚举 */ public enum OrderStatus { /** 常量相关 */ /** 已创建(1) */ CREATED(1, "已创建"), /** 进行中(2) */ PROCESSING(2, "进行中"), /** 已完成(3) */ FINISHED(3, "已完成"); /** 属性相关 */ /** 状态取值 */ private final int value; /** 状态描述 */ private final String description; /** 构造函数 */ private OrderStatus(int value, String description) { this.value = value; this.description = description; } /** 获取状态取值 */ public int getValue() { return value; } /** 获取状态描述 */ public String getDescription() { return description; } } 复制 其实,Enum 类型就是一个语法糖,编译器帮我们做了语法的解析和编译。通过反编译,可以看到 Java 枚举编译后实际上是生成了一个类,该类继承了 java.lang.Enum<E> ,并添加了 values()、valueOf() 等枚举类型通用方法。 定义 Holder 类实现参数的输出 在很多语言中,函数的参数都有输入(in)、输出(out)和输入输出(inout)之分。在 C/C++ 语言中,可以用对象的引用(&)来实现函数参数的输出(out)和输入输出(inout)。但在 Java 语言中,虽然没有提供对象引用类似的功能,但是可以通过修改参数的字段值来实现函数参数的输出(out)和输入输出(inout)。这里,我们叫这种输出参数对应的数据结构为Holder(支撑)类。 Holder 类实现代码: /** 长整型支撑类 */ @Getter @Setter @ToString public class LongHolder { /** 长整型取值 */ private long value; /** 构造函数 */ public LongHolder() {} /** 构造函数 */ public LongHolder(long value) { this.value = value; } } 复制 Holder 类使用案例: /** 静态常量 */ /** 页面数量 */ private static final int PAGE_COUNT = 100; /** 最大数量 */ private static final int MAX_COUNT = 1000; /** 处理过期订单 */ public void handleExpiredOrder() { LongHolder minIdHolder = new LongHolder(0L); for (int pageIndex = 0; pageIndex < PAGE_COUNT; pageIndex++) { if (!handleExpiredOrder(pageIndex, minIdHolder)) { break; } } } /** 处理过期订单 */ private boolean handleExpiredOrder(int pageIndex, LongHolder minIdHolder) { // 获取最小标识 Long minId = minIdHolder.getValue(); // 查询过期订单(按id从小到大排序) List<OrderDO> orderList = orderDAO.queryExpired(minId, MAX_COUNT); if (CollectionUtils.isEmpty(taskTagList)) { return false; } // 设置最小标识 int orderSize = orderList.size(); minId = orderList.get(orderSize - 1).getId(); minIdHolder.setValue(minId); // 依次处理订单 for (OrderDO order : orderList) { ... } // 判断还有订单 return orderSize >= PAGE_SIZE; } 复制 其实,可以实现一个泛型支撑类,适用于更多的数据类型。 定义 Union 类实现数据体的共存 在 C/C++ 语言中,联合体(union),又称共用体,类似结构体(struct)的一种数据结构。联合体(union)和结构体(struct)一样,可以包含很多种数据类型和变量,两者区别如下: 1、结构体(struct)中所有变量是“共存”的,同时所有变量都生效,各个变量占据不同的内存空间; 2、联合体(union)中是各变量是“互斥”的,同时只有一个变量生效,所有变量占据同一块内存空间。 当多个数据需要共享内存或者多个数据每次只取其一时,可以采用联合体(union)。 在Java语言中,没有联合体(union)和结构体(struct)概念,只有类(class)的概念。众所众知,结构体(struct)可以用类(class)来实现。其实,联合体(union)也可以用类(class)来实现。但是,这个类不具备“多个数据需要共享内存”的功能,只具备“多个数据每次只取其一”的功能。 这里,以微信协议的客户消息为例说明。根据我多年来的接口协议封装经验,主要有以下两种实现方式。 使用函数方式实现 Union Union 类实现: /** 客户消息类 */ @ToString public class CustomerMessage { /** 属性相关 */ /** 消息类型 */ private String msgType; /** 目标用户 */ private String toUser; /** 共用体相关 */ /** 新闻内容 */ private News news; ... /** 常量相关 */ /** 新闻消息 */ public static final String MSG_TYPE_NEWS = "news"; ... /** 构造函数 */ public CustomerMessage() {} /** 构造函数 */ public CustomerMessage(String toUser) { this.toUser = toUser; } /** 构造函数 */ public CustomerMessage(String toUser, News news) { this.toUser = toUser; this.msgType = MSG_TYPE_NEWS; this.news = news; } /** 清除消息内容 */ private void removeMsgContent() { // 检查消息类型 if (Objects.isNull(msgType)) { return; } // 清除消息内容 if (MSG_TYPE_NEWS.equals(msgType)) { news = null; } else if (...) { ... } msgType = null; } /** 检查消息类型 */ private void checkMsgType(String msgType) { // 检查消息类型 if (Objects.isNull(msgType)) { throw new IllegalArgumentException("消息类型为空"); } // 比较消息类型 if (!Objects.equals(msgType, this.msgType)) { throw new IllegalArgumentException("消息类型不匹配"); } } /** 设置消息类型函数 */ public void setMsgType(String msgType) { // 清除消息内容 removeMsgContent(); // 检查消息类型 if (Objects.isNull(msgType)) { throw new IllegalArgumentException("消息类型为空"); } // 赋值消息内容 this.msgType = msgType; if (MSG_TYPE_NEWS.equals(msgType)) { news = new News(); } else if (...) { ... } else { throw new IllegalArgumentException("消息类型不支持"); } } /** 获取消息类型 */ public String getMsgType() { // 检查消息类型 if (Objects.isNull(msgType)) { throw new IllegalArgumentException("消息类型无效"); } // 返回消息类型 return this.msgType; } /** 设置新闻 */ public void setNews(News news) { // 清除消息内容 removeMsgContent(); // 赋值消息内容 this.msgType = MSG_TYPE_NEWS; this.news = news; } /** 获取新闻 */ public News getNews() { // 检查消息类型 checkMsgType(MSG_TYPE_NEWS); // 返回消息内容 return this.news; } ... } 复制 Union 类使用: String accessToken = ...; String toUser = ...; List<Article> articleList = ...; News news = new News(articleList); CustomerMessage customerMessage = new CustomerMessage(toUser, news); wechatApi.sendCustomerMessage(accessToken, customerMessage); 复制 主要优缺点: 优点:更贴近 C/C++ 语言的联合体(union); 缺点:实现逻辑较为复杂,参数类型验证较多。 使用继承方式实现 Union Union 类实现: /** 客户消息类 */ @Getter @Setter @ToString public abstract class CustomerMessage { /** 属性相关 */ /** 消息类型 */ private String msgType; /** 目标用户 */ private String toUser; /** 常量相关 */ /** 新闻消息 */ public static final String MSG_TYPE_NEWS = "news"; ... /** 构造函数 */ public CustomerMessage(String msgType) { this.msgType = msgType; } /** 构造函数 */ public CustomerMessage(String msgType, String toUser) { this.msgType = msgType; this.toUser = toUser; } } /** 新闻客户消息类 */ @Getter @Setter @ToString(callSuper = true) public class NewsCustomerMessage extends CustomerMessage { /** 属性相关 */ /** 新闻内容 */ private News news; /** 构造函数 */ public NewsCustomerMessage() { super(MSG_TYPE_NEWS); } /** 构造函数 */ public NewsCustomerMessage(String toUser, News news) { super(MSG_TYPE_NEWS, toUser); this.news = news; } } 复制 Union 类使用: String accessToken = ...; String toUser = ...; List<Article> articleList = ...; News news = new News(articleList); CustomerMessage customerMessage = new NewsCustomerMessage(toUser, news); wechatApi.sendCustomerMessage(accessToken, customerMessage); 复制 主要优缺点: 优点:使用虚基类和子类进行拆分,各个子类对象的概念明确; 缺点:与 C/C++ 语言的联合体(union)差别大,但是功能上大体一致。 在 C/C++ 语言中,联合体并不包括联合体当前的数据类型。但在上面实现的 Java 联合体中,已经包含了联合体对应的数据类型。所以,从严格意义上说, Java 联合体并不是真正的联合体,只是一个具备“多个数据每次只取其一”功能的类。 使用泛型屏蔽类型的差异性 在 C++ 语言中,有个很好用的模板(template)功能,可以编写带有参数化类型的通用版本,让编译器自动生成针对不同类型的具体版本。而在 Java 语言中,也有一个类似的功能叫泛型(generic)。在编写类和方法的时候,一般使用的是具体的类型,而用泛型可以使类型参数化,这样就可以编写更通用的代码。 许多人都认为, C++ 模板(template)和 Java 泛型(generic)两个概念是等价的,其实实现机制是完全不同的。 C++ 模板是一套宏指令集,编译器会针对每一种类型创建一份模板代码副本; Java 泛型的实现基于"类型擦除"概念,本质上是一种进行类型限制的语法糖。 泛型类 以支撑类为例,定义泛型的通用支撑类: /** 通用支撑类 */ @Getter @Setter @ToString public class GenericHolder<T> { /** 通用取值 */ private T value; /** 构造函数 */ public GenericHolder() {} /** 构造函数 */ public GenericHolder(T value) { this.value = value; } } 复制 泛型接口 定义泛型的数据提供者接口: /** 数据提供者接口 */ public interface DataProvider<T> { /** 获取数据函数 */ public T getData(); } 复制 泛型方法 定义泛型的浅拷贝函数: /** 浅拷贝函数 */ public static <T> T shallowCopy(Object source, Class<T> clazz) throws BeansException { // 判断源对象 if (Objects.isNull(source)) { return null; } // 新建目标对象 T target; try { target = clazz.newInstance(); } catch (Exception e) { throw new BeansException("新建类实例异常", e); } // 拷贝对象属性 BeanUtils.copyProperties(source, target); // 返回目标对象 return target; } 复制 泛型通配符 泛型通配符一般是使用"?"代替具体的类型实参,可以把"?"看成所有类型的父类。当具体类型不确定的时候,可以使用泛型通配符 "?";当不需要使用类型的具体功能,只使用Object类中的功能时,可以使用泛型通配符 "?"。 /** 打印取值函数 */ public static void printValue(GenericHolder<?> holder) { System.out.println(holder.getValue()); } /** 主函数 */ public static void main(String[] args) { printValue(new GenericHolder<>(12345)); printValue(new GenericHolder<>("abcde")); } 复制 在 Java 规范中,不建议使用泛型通配符"?",上面函数可以改为: /** 打印取值函数 */ public static <T> void printValue(GenericHolder<T> holder) { System.out.println(holder.getValue()); } 复制 泛型上下界 在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。泛型上下界的声明,必须与泛型的声明放在一起 。 上界通配符(extends): 上界通配符为 ”extends ”,可以接受其指定类型或其子类作为泛参。其还有一种特殊的形式,可以指定其不仅要是指定类型的子类,而且还要实现某些接口。例如: List<? extends A> 表明这是 A 某个具体子类的 List ,保存的对象必须是A或A的子类。对于 List<? extends A> 列表,不能添加 A 或 A 的子类对象,只能获取A的对象。 下界通配符(super): 下界通配符为”super”,可以接受其指定类型或其父类作为泛参。例如:List<? super A> 表明这是 A 某个具体父类的 List ,保存的对象必须是 A 或 A 的超类。对于 List<? super A> 列表,能够添加 A 或 A 的子类对象,但只能获取 Object 的对象。 PECS(Producer Extends Consumer Super)原则:作为生产者提供数据(往外读取)时,适合用上界通配符(extends);作为消费者消费数据(往里写入)时,适合用下界通配符(super)。 在日常编码中,比较常用的是上界通配符(extends),用于限定泛型类型的父类。例子代码如下: /** 数字支撑类 */ @Getter @Setter @ToString public class NumberHolder<T extends Number> { /** 通用取值 */ private T value; /** 构造函数 */ public NumberHolder() {} /** 构造函数 */ public NumberHolder(T value) { this.value = value; } } /** 打印取值函数 */ public static <T extends Number> void printValue(GenericHolder<T> holder) { System.out.println(holder.getValue()); } ———————————————— 版权声明:本文为CSDN博主「栾还是恋」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/2301_78008478/article/details/131367225
-
@RequestParam 和 @param 都是用于在代码中提供参数的注解,但在不同的编程语言和框架中的具体使用方式和含义可能会有所不同。@RequestParam: @RequestParam 是 Spring 框架中的一个注解,用于映射 HTTP 请求中的参数到方法的参数上。通常在处理 HTTP 请求时,我们需要从请求的 URL 或请求体中获取参数值,然后将其传递给方法进行处理。使用 @RequestParam 注解可以方便地将请求中的参数绑定到方法的参数上。使用 @RequestParam 注解时,通常需要指定参数的名称,可以通过参数名进行匹配和绑定。示例(Java Spring):@GetMapping("/hello") public String sayHello(@RequestParam("name") String name) { return "Hello, " + name + "!"; }在上述示例中,@RequestParam("name") 表示从请求中获取名为 "name" 的参数,并将其绑定到方法的 name 参数上。@param: @param 是一种用于方法注释的标记,用于说明方法参数的含义和用途。通常在编写代码时,我们会为方法添加注释,来提供方法的说明和参数的作用。使用 @param 注解可以清晰地说明方法参数的含义,以便开发者更好地理解和使用方法。示例(Java):/** * 计算两个数的和 * * @param a 第一个数 * @param b 第二个数 * @return 两个数的和 */ public int sum(int a, int b) { return a + b; }在上述示例中,@param a 第一个数 和 @param b 第二个数 表示方法 sum 的两个参数的含义和作用。需要注意的是,@param 注解是一种注释方式,它不会被直接编译和执行,而是用于提供开发文档的信息和提示。
-
@Data 是一个注解,它通常用于简化 Java 类的编写。它是 Lombok 提供的一个注解,通过在类上使用 @Data 注解,可以自动为类生成一些常用的方法,如 getter、setter、toString、equals 和 hashCode 等。使用 @Data 注解可以避免手动编写这些常用方法,减少了代码的冗余,提高了开发效率。以下是 @Data 注解提供的功能:Getter 和 Setter 方法:使用 @Data 注解后,会自动为类的每个字段生成对应的 getter 和 setter 方法。toString 方法:@Data 注解会为类生成一个默认的 toString 方法,用于返回类的字符串表示。该方法会包含类的所有字段和它们的值。equals 和 hashCode 方法:@Data 注解会生成 equals 和 hashCode 方法,用于比较对象的相等性。这些方法会考虑类的所有字段,并根据它们的值计算结果。默认构造方法:若类没有显式定义构造方法,@Data 注解会为类生成一个默认的无参构造方法。使用示例:import lombok.Data; @Data public class MyClass { private String name; private int age; // 可以添加其他自定义方法 }使用 @Data 注解后,编译器会自动为 MyClass 类生成 getter、setter、toString、equals、hashCode 和默认构造方法。需要注意的是,当使用 @Data 注解时,需要事先在项目中引入 Lombok 的依赖,以便让编译器在编译时自动处理注解并生成相应的代码。
-
Dubbo和Feign都是Java中的服务调用框架,但它们之间有一些关键的区别。本文将介绍Dubbo和Feign的主要特点、适用场景以及优缺点。 1. 功能定位 Dubbo是一个高性能、轻量级的RPC(远程过程调用)框架,主要用于微服务架构中的服务治理。它提供了负载均衡、容错、服务注册与发现等功能,帮助开发者快速构建分布式系统。 Feign是一个声明式的Web服务客户端,它使得编写HTTP客户端变得更简单。Feign主要关注于简化HTTP请求的发送,提供了丰富的注解来描述服务接口和请求参数,而不需要关心底层的网络通信细节。 2. 适用场景 Dubbo适用于微服务架构中的服务治理,例如在分布式系统中实现服务的调用、负载均衡、容错等。Dubbo可以帮助开发者更好地管理微服务之间的依赖关系,提高系统的可扩展性和可靠性。 Feign适用于RESTful风格的微服务架构,特别是在Spring Cloud环境中。Feign可以作为Spring Boot应用程序的一部分,通过简单的注解配置即可实现对其他服务的调用。Feign特别适合于构建基于HTTP的客户端,例如调用其他服务的API接口。 3. 优缺点 Dubbo的优点: - 性能较高:Dubbo采用了多种优化策略,如线程池、序列化优化等,提高了服务的响应速度和吞吐量。 - 功能丰富:Dubbo提供了丰富的功能模块,如负载均衡、容错、服务注册与发现等,方便开发者进行微服务治理。 - 社区活跃:Dubbo拥有庞大的用户群体和活跃的社区支持,问题解决较快。 Dubbo的缺点: - 学习成本较高:Dubbo的概念和技术较为复杂,需要一定的学习成本才能掌握。 - 配置繁琐:Dubbo的配置文件较多,需要花费一定时间进行配置。 Feign的优点: - 简单易用:Feign的注解配置方式使得编写HTTP客户端变得非常简单,无需关心底层的网络通信细节。 - 集成方便:Feign可以很好地与Spring Cloud等微服务框架集成,方便开发者进行微服务调用。 - 社区活跃:Feign拥有活跃的社区支持,问题解决较快。 Feign的缺点: - 不适用于所有场景:Feign主要适用于RESTful风格的微服务架构,对于非RESTful的服务调用可能不太适用。 - 功能有限:虽然Feign提供了丰富的注解,但其功能相对于Dubbo来说还是有限的,无法满足所有的需求。 总结:Dubbo和Feign都是Java中优秀的服务调用框架,它们各自有不同的特点和适用场景。在实际项目中,可以根据具体需求选择合适的框架进行使用。
-
1. 相关概念本地缓存:程序运行时,为了提高运行的速度,CPU可以不直接跟内存进行通信,而是先将内存中的数据读到内部缓存,然后再进行操作。这样会提高效率,但是我们不知道本地缓存中的修改何时会回写到共享内存中;内存可见性:可见性的意思是当一个线程修改了一个共享变量时,其他的线程能够读到这个修改的值。(这句话我当初理解的有问题,导致我在修改的代码的时候掉进一个很大的坑里) ;共享内存:JVM在执行Java程序时会 把它管理的内存划分成几个不同的数据区域。这些区域都有着各自的用途,以及创建和销毁的时间。具体其中有部分数据区域是所有线程共享的,这部分数据区域称之为共享内存,共享内存中的变量叫做共享变量。 其中堆是最大的一块共享内存,也是垃圾收集器重点管理的区域。基本上我们所有的实例对象都是在堆上创建的。而方法区主要是用来存储加载过的类信息、常量和静态变量等。内存可见性就是针对于共享内存而言的。 缓存一致性协议:简单来说,就是在多核CPU中一个共享变量被其中一个线程修改且回写到内存中之后,其他的CPU中缓存的这个共享变量就会被置为invalid。在下次读的时候就会更新这个缓存。 原子操作:不可被中断的一个或一系列操作。2. 内存可见性的相关实现2.1 volatilevolatile相较于synchronized而言,使用的代价和成本更低,因为它不会因为线程上下文的切换和调度。 volatile的实现原理当我们使用volatile关键字修饰一个变量之后,在程序运行时,它会导致以下两件事:1. 本地缓存的数据会立马回写到共享内存中; 2. 这个回写会导致其他CPU缓存中这个内存地址对应的数据无效,在下次读的时候就需要更新本地缓存。 volatile的内存语义使用volatile 修饰变量,实际是就对变量的单个读/单个写做了同步。相信很多人都只到volatile++不是原子操作。下面我们通过一些等效代码来分析原因。 单个读写首先我们来理解一下单个读写是什么意思。 public class AtomicVariable { volatile int v; //单次读 public int getA() { return v; } //单次写 public void setA(int v) { this.v = v; }}这里的代码等效于: public class AtomicVariable { volatile int v; //单次读 public synchronized int getA() { return v; } //单次写 public synchronized void setA(int v) { this.v = v; }}通过以上的代码我们可以看出来,针对于volatile修饰的变量,单次的读写,其原子性是可以得到保证的。也就是说,在对于单个线程而言,当读volatile变量时,这个变量肯定是最近修改的值。在写这个变量时,它可以保证下次线程读的这个值是最新的。那对于volatile++的操作为什么不行呢? 首先我们得明白volatile++是一个复合操作,它可以转换成volatile = volatile + 1。即先读了volatile,然后进行加1操作,在写给volatile。这里只有第一步和第三步可以保证原子性,而第二步做不到。我们可以通过代码展示一下。 public class AtomicVariableTest { public volatile int a = 0; @Test public void atomicOperation() throws InterruptedException { List<Thread> threads = new ArrayList<Thread>(); for (int i = 0; i < 4; i++) { threads.add(new Thread(() -> { for (int j = 0; j < 100000; j++) { int tmp = a + 1; a = tmp; } })); } for(Thread t: threads){ t.start(); } for(Thread t: threads){ t.join(); } }}399748399749399750399751399752399753399754399755399756399757从结果来看,这里的读写操作并没有得到同步。 2.2 Synchronizedsynchronized主要是通过锁来实现同步的,而在java中每一个多对象都可以作为锁。具体的表现有以下几种形式:* 对于普通同步方法,锁是当前实例对象;public synchronized void set(int v) { ... }对于静态同步方法,锁是当前类的Class对象;public synchronized static void staticMethod(){ ... }对于同步方法块, 所以Synchronized括号里配置的对象。``` Object ob = new Object(); synchronized(ob){ .... }当一个线程试图运行同步代码块时,它必须先获得锁。在同步代码块执行完之后或抛出异常之后,它必须释放锁。 synchronized用的锁是存储在对象头中的,而锁的状态又分为以下几种: 1. 重量级锁状态2. 轻量级锁状态3. 偏向锁状态4. 无锁状态 锁的级别依次递减,锁可以升级但是不可以降级。关于这几种状态,与本文主题没有太大关联,感兴趣的小伙伴可以再去了解。 synchronized可以保证同步方法或同步代码块一次只能由一个线程访问执行,同时能够保证对共享变量操作的可见性。更改上面的代码:public class AtomicVariableTest { public int a = 0;@Test public void atomicOperation() throws InterruptedException {List<Thread> threads = new ArrayList<Thread>();for (int i = 0; i < 4; i++) { threads.add(new Thread(() -> { for (int j = 0; j < 100000; j++) { increment(); System.out.println(Thread.currentThread().getName() + " " + a); } }));}for(Thread t: threads){ t.start();}for(Thread t: threads){ t.join();}}public synchronized void increment(){ a++; } }这里的代码便得到了同步,结果为400000. **小结**:上面大概说了一下volatile与sychronized,现在做一下总结。首先,相同点大致有两点1. 二者都可以保证可见性,即当前线程的修改,对其他线程下次的读写可见;2. 二者对指令的重排序都有一定的限制。 而不同点则有以下几个方面: 1. sychronized 可以做到同步(操作的原子性),而仅靠volatile做不到;2. sychronized 可以修饰代码块或者方法,而volatile只能修饰单个变量;3. volatile不会造成线程阻塞,而synchronzied会。## 3. 原子操作的实现原理 CPU实现原子操作主要有两种方式: **1. 总线加锁****2. 缓存加锁**### 3.1 使用总线锁保证原子性  我们通过上面的图理解一下总线锁是如何实现原子性的。假设现在共享内存中有一个值a = 1,CPU1到CPU3都要执行`int tmp =a + 1; a = tmp; `这样的操作。那么共享内存中的a就会被多个处理器同时进行操作。这样的多改写操作就不是原子性的,操作完之后的值可能不是我们想要的。原因很简单,多个CPU都从自己的本地缓存中读取了变量a,然后进行改写操作,而本地缓存中的值可能还是旧值。改写完之后再分别回写,那么内存中值就是最后一次写的。 而总线锁就是保证了一个CPU在对共享内存中的变量进行读改写操作时,其他的CPU不能操作缓存了该共享变量内存地址的缓存。上列中,就是一个CPU对共享变量a进行了读改写操作,其他的CPU就不能操作本地缓存中的a。 总线锁就是在CPU1对变量a进行读改写操作时,会在总线输出一个LOCK #信号,之后共享内存就会被CPU1**独占**,其他处理器的总线请求都会被阻塞。这样子就可以保证对变量的读改写操作是原子性的了。但是使用这种方式会导致对于部分变量的读改写,会让其他CPU无法读写共享内存中的其他变量,开销过大。 ### 3.2 使用缓存锁来保证原子性 其实对于一些频繁操作的内存,各个CPU会将其缓存在本地缓存中。那么对于这些内存或变量,我们只要本证他的读改写在各个CPU的缓存之间是原子性的即可,并不需要声明总线锁,带来过大的开销。 **缓存锁**是通过内存一致性协议来实现的,即当某个CPU修改了共享内存中个某个变量并回写之后,其他CPU会让缓存了这块内存数据的缓存失效,在下次读的时候就是最新的值了。 ## 4. Java实现原子操作 所谓原子操作就是指一个线程在执行一系列操作的时候,需要保证其使用的共享变量不会给其他的线程读改写。Java实现原子操作的方式有两种,一个是使用**锁**,另一个是使用**循环CAS**。 ### 4.1 使用循环CAS来实现原子操作Java的中的CAS(Compare And Swap)操作可以保证操作的原子性。CAS操作就是用期望值跟旧值进行比较,如果相同则会将旧值更新成新值,且保证这一过程的原子性。根据这一特性,Java可以实现操作的原子性。且看先下面的Demo: ```java public class CommonTest { private AtomicInteger a = new AtomicInteger(0); @Test public void test() throws InterruptedException { List<Thread> threads = new ArrayList<Thread>(); for (int i = 0; i < 3; i++) { threads.add(new Thread(new CounterRunnable())); } for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } } class CounterRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100000; i++) { increment(); } } private void increment() { while (true) { int i = a.get(); boolean changed = a.compareAndSet(i, ++i); if (changed) { System.out.println(a.get()); break; } } } }}输出结果为300000相较于锁,CAS可以高效地实现原子操作,但是在使用它的时候也得注意一些可能遇到的问题。 CAS 实现原子操作可能遇到的三大问题ABA问题:ABA问题就是当状态连续改变之后,CPU可能感知不到,例如变量从A到B,然后再从B变成A。这个时候,实际上变量是改变过了,但是CAS进行检查的时候会发现他的值并没有改变。循环开销过大:在上面的代码样例中,如果一直比较失败的话,线程会一直在那里执行,资源也不会释放,这样的开销是比较大的;只能保证一个变量的原子操作:由CAS的使用方式,我们可以看出来CAS只能对单个变量进行原子性的操作。 4.2 通过锁实现原子操作Java锁机制保证了只有获得了锁的线程才能执行锁定的内存区域,同时能保证操作的原子性。但是相比较与CAS而言,一般情况下他的开销更大。使用时需要慎重。 参考:《Java 并发编程的艺术》 方腾飞,魏鹏,程晓明 《深入理解Java虚拟机》 陶志华Java多线程之原子操作转自 https://zhuanlan.zhihu.com/p/96244543
-
XML文件生成:import java.io.File;import java.io.FileOutputStream;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.output.Format;import org.jdom2.output.XMLOutputter;public class JDOMCreat {public static void main(String[] args) {Element bookstore = new Element("bookstore");Element book = new Element("book");book.setAttribute("id","1");book.addContent(new Element("name").setText("哈利波特"));book.addContent(new Element("author").setText("罗琳"));book.addContent(new Element("year").setText("2018"));bookstore.addContent(book);//新建文档Document doc = new Document(bookstore);//读取格式Format format = Format.getCompactFormat();format.setEncoding("UTF-8");//设置xml文件缩进4个空格format.setIndent(" ");//建一个xml输出工厂XMLOutputter xmlout = new XMLOutputter(format);try {//将其写好的文本给工厂xmlout.output(doc, new FileOutputStream("D:/book.xml"));} catch (Exception e) {// TODO: handle exception}}}XML文件读取 import java.io.File;import java.util.List;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.input.SAXBuilder;public class JdomSax {public static void main(String[] args) {//新建构造器解析xmlSAXBuilder sax = new SAXBuilder();Document doc;try {//获取xml文档doc = sax.build(new File("D:/book.xml"));//获得根节点Element bookstore = doc.getRootElement();//获得根节点下的节点数据List<Element> books = bookstore.getChildren();for(Element book: books) {//获得属性值book.getAttributeValue("id");List<Element> bookitems = book.getChildren();for(Element item:bookitems) {System.out.println(item.getName()); System.out.println(item.getText());}}} catch (Exception e) {// TODO: handle exception}}}原文链接:https://blog.csdn.net/HappySSweet/article/details/81668140
-
一、解析xml内容xml文件内容:<?xml version="1.0" encoding="utf-8"?> <RETVAL success='true'> <Shop><sid>1</sid><name>北京鑫和易通贸易有限公司</name></Shop> <Shop><sid>2</sid><name>张家口市金九福汽车服务有限公司</name></Shop> </RETVAL>解析的java代码import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.SAXBuilder; import org.xml.sax.InputSource; public Class Test{ public static List test() throws Exception{ List result = new ArrayList(); SAXBuilder builder = new SAXBuilder(); StringReader read = new StringReader(xmlDoc); InputSource source = new InputSource(read); Document document = builder.build(source); Element root = document.getRootElement();// 获得根节点 String ifSuccess = root.getAttributeValue("success"); if (!"true".equals(ifSuccess)) {// 不成功直接返回 throw new Exception("从接口获取全部经销商数据时不成功!"); } List<Element> childrenList = root.getChildren(); for (Element e : childrenList) { Map elementMap = new HashMap<String, String>(); elementMap.put("sid", e.getChildText("sid")); // 经销商id elementMap.put("name", e.getChildText("name")); // 经销商名称 result.add(elementMap); } return result; } }二、解析和操作xml文件xml文件内容<?xml version="1.0" encoding="UTF-8"?> <root> <person id="1"> <username>张三</username> <password>123123</password> </person> <person id="2"> <username>1111111112</username> <password>password2</password> </person> </root>JDOM构建document对象的方法Document build(java.io.File file) //This builds a document from the supplied filename. Document build(org.xml.sax.InputSource in) //This builds a document from the supplied input source. Document build(java.io.InputStream in) //This builds a document from the supplied input stream. Document build(java.io.InputStream in, java.lang.String systemId) //This builds a document from the supplied input stream. Document build(java.io.Reader characterStream) //This builds a document from the supplied Reader. Document build(java.io.Reader characterStream, java.lang.String systemId) //This builds a document from the supplied Reader. Document build(java.lang.String systemId) //This builds a document from the supplied URI. Document build(java.net.URL url) //This builds a document from the supplied URL.解析、新增、修改、删除xml节点属性的javapackage test; import java.io.FileWriter; import java.util.List; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.SAXBuilder; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; public class Test { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { SAXBuilder builder = new SAXBuilder(); //Document doc = builder.build(new File("src/test.xml")); //Document doc = builder.build(new FileInputStream("src/test.xml")); //Document doc = builder.build(new FileReader("src/test.xml")); //Document doc = builder.build(new URL("http://localhost:8080/jdomTest/test.xml")); Document doc = builder.build("src/test.xml"); //readXmlFile(doc); //addXmlElement(doc); //updateXmlElement(doc); deleteXmlElement(doc); } /** * 解析xml文件 * @throws Exception */ public static void readXmlFile(Document doc) throws Exception { Element root = doc.getRootElement(); //获取根元素 System.out.println("---获取第一个子节点和子节点下面的节点信息------"); Element e = root.getChild("person"); //获取第一个子元素 System.out.println("person的属性id的值:"+e.getAttributeValue("id")); //获取person的属性值 for(Element el: e.getChildren()){ System.out.println(el.getText());//第一次输入张三 第二次输出123123 System.out.println(el.getChildText("username"));//这里这么写会是null System.out.println(el.getChildText("password"));//这里这么写会是null } System.out.println("---直接在根节点下就遍历所有的子节点---"); for(Element el: root.getChildren()){ System.out.println(el.getText());//这里输出4行空格 System.out.println(el.getChildText("username"));//输出张三 & 1111111112 System.out.println(el.getChildText("password"));//输出123123 & password2 } } /** * 新增节点 * @throws Exception */ public static void addXmlElement(Document doc) throws Exception { Element root = doc.getRootElement(); //获取根元素 Element newEle = new Element("person");//设置新增的person的信息 newEle.setAttribute("id","88888"); Element chiledEle = new Element("username"); //设置username的信息 chiledEle.setText("addUser"); newEle.addContent(chiledEle); Element chiledEle2 = new Element("password"); //设置password的信息 chiledEle2.setText("addPassword"); newEle.addContent(chiledEle2); root.addContent(newEle); XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getCompactFormat().setEncoding("GBK"));//设置UTF-8编码,理论上来说应该不会有乱码,但是出现了乱码,故设置为GBK out.output(doc, new FileWriter("src/test.xml")); //写文件 } /** * 更新节点 * @param doc * @throws Exception */ public static void updateXmlElement(Document doc) throws Exception { Element root = doc.getRootElement(); //获取根元素 //循环person元素并修改其id属性的值 for(Element el : root.getChildren("person")){ el.setAttribute("id","haha"); } //循环设置username和password的文本值和添加属性 for(Element el: root.getChildren()){ el.getChild("username").setAttribute("nameVal", "add_val").setText("update_text"); el.getChild("password").setAttribute("passVal", "add_val").setText("update_text"); } XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getCompactFormat().setEncoding("GBK"));//设置UTF-8编码,理论上来说应该不会有乱码,但是出现了乱码,故设置为GBK out.output(doc, new FileWriter("src/test.xml")); //写文件 } /** * 删除节点和属性 * @param doc * @throws Exception */ public static void deleteXmlElement(Document doc) throws Exception { Element root = doc.getRootElement(); //获取根元素 List<Element> personList = root.getChildren("person"); //循环person元素,删除person的id为1的id属性以及username子节点 for(Element el : personList){ if(null!=el.getAttribute("id") && "1".equals(el.getAttribute("id").getValue())){ el.removeAttribute("id"); el.removeChild("username"); } } //循环person元素,删除person的id为2的节点 for(int i=0; i<personList.size(); i++){ Element el = personList.get(i); if(null!=el.getAttribute("id") && "2".equals(el.getAttribute("id").getValue())){ root.removeContent(el);//从root节点上删除该节点 //警告:此处删除了节点可能会使personList的长度发生变化而发生越界错误,故不能写成for(int i=0,len=personList.size(); i<len; i++) } } XMLOutputter out = new XMLOutputter(); out.setFormat(Format.getCompactFormat().setEncoding("GBK"));//设置UTF-8编码,理论上来说应该不会有乱码,但是出现了乱码,故设置为GBK out.output(doc, new FileWriter("src/test.xml")); //写文件 } } 原文链接:https://www.cnblogs.com/yangzhilong/p/2955087.html
-
使用io流完成request传参,获取<xml>,具体代码如下:public Object getParameter(HttpServletRequest request, HttpServletResponse response){request.setCharacterEncoding("UTF-8");//设置字符格式response.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");response.setHeader("Access-Control-Allow-Origin", "*");// achieve input streamInputStream in = request.getInputStream();ByteArrayOutputStream out = new ByteArrayOutputStream();// length of stringbyte[] buffer = new byte[1024];int len = 0;// achieve data from inwhile ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);}in.close();out.close();//获取流对象内容String content = new String(out.toByteArray(), "utf-8");//获取json对象JSONObject jsonObject = JSONObject.parseObject(XmltoJsonUtil.xml2JSON(content));//获取xml对象JSONObject result_xml = jsonObject.getJSONObject("xml");// 获取<xml>中某个对象具体的值JSONArray return_code = result_xml.getJSONArray("return_code");String code = return_code.get(0).toString();System.out.println("code:"+code);}//<xml>转为json格式public class XmltoJsonUtil {public static String xml2JSON(String xml) {JSONObject obj = new JSONObject();try {InputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));SAXBuilder sb = new SAXBuilder();Document doc = sb.build(is);Element root = doc.getRootElement();obj.put(root.getName(), iterateElement(root));return obj.toString();} catch (Exception e) {e.printStackTrace();return null;}}eg:输入的<xml>对象为:<xml><return_code>SUCCESS</return_code><appid><![CDATA[wx2421b1c4370ec43b]]></appid><mch_id><![CDATA[10000100]]></mch_id><nonce_str><![CDATA[TeqClE3i0mvn3DrK]]></nonce_str></xml>输出结果为:code:SUCCESS原文链接:https://www.cnblogs.com/qqzhulu/p/10371696.html
-
环境准备 xml文件 <?xml version="1.0" encoding="UTF-8"?> <root> <category id="1000" name="Laptops" discount="20130701:0.99"> <saleDate date="20130630"> <product id="100012" cost="2900" saleCount="40" salePrice="4099"> ThinkPadE430_1 </product> <product id="100013" cost="2900" saleCount="55" salePrice="3399"> LenovoG480A_1 </product> <product id="100014" cost="2900" saleCount="63" salePrice="2900"> HaseeA488-Isd3_1 </product> </saleDate> <saleDate date="20130701"> <product id="100012" cost="2900" saleCount="48" salePrice="4099"> ThinkPadE430_1_1 </product> <product id="100013" cost="2900" saleCount="55" salePrice="3399"> LenovoG480A_1_1 </product> <product id="100014" cost="2900" saleCount="63" salePrice="2900"> HaseeA488-Isd3_1_1 </product> </saleDate> </category> <category id="2000" name="CPU" discount="none"> <saleDate date="20130630"> <product id="100012" cost="2900" saleCount="40" salePrice="4099"> ThinkPadE430_2 </product> <product id="100013" cost="2900" saleCount="55" salePrice="3399"> LenovoG480A </product> <product id="100014" cost="2900" saleCount="63" salePrice="2900"> HaseeA488-Isd3 </product> </saleDate> <saleDate date="20130701"> <product id="100012" cost="2900" saleCount="48" salePrice="4099"> ThinkPadE430 </product> <product id="100013" cost="2900" saleCount="55" salePrice="3399"> LenovoG480A </product> <product id="100014" cost="2900" saleCount="63" salePrice="2900"> HaseeA488-Isd3 </product> </saleDate> </category> </root> 实体类 public class Category { private String id; private String name; private String discount; public Category() { } public Category(String id, String name, String discount) { this.id = id; this.name = name; this.discount = discount; } public String getId() { return id; } public String getName() { return name; } public String getDiscount() { return discount; } public void setId(String id) { this.id = id; } public void setName(String name) { this.name = name; } public void setDiscount(String discount) { this.discount = discount; } @Override public String toString() { return "Category{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", discount='" + discount + '\'' + '}'; } } public class Product { private String id; private String cost; private String saleCount; private String salePrice; private Category category; private String date; public Product() { } public Product(String id, String cost, String saleCount, String salePrice, Category category, String date) { this.id = id; this.cost = cost; this.saleCount = saleCount; this.salePrice = salePrice; this.category = category; this.date = date; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCost() { return cost; } public void setCost(String cost) { this.cost = cost; } public String getSaleCount() { return saleCount; } public void setSaleCount(String saleCount) { this.saleCount = saleCount; } public String getSalePrice() { return salePrice; } public void setSalePrice(String salePrice) { this.salePrice = salePrice; } public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } @Override public String toString() { return "Product{" + "id='" + id + '\'' + ", cost='" + cost + '\'' + ", saleCount='" + saleCount + '\'' + ", salePrice='" + salePrice + '\'' + ", category=" + category + ", date='" + date + '\'' + '}'; } } 业务方法 public List<Product> getProducts() { List<Product> list = new ArrayList<>(); try { Reader resourceAsReader = Resources.getResourceAsReader("SalesRecords.xml"); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(resourceAsReader); Element rootElement = document.getRootElement(); Iterator<Element> elementIterator = rootElement.elementIterator(); int count = 0; for (Iterator iterator = elementIterator; iterator.hasNext(); ) { Element element = (Element) iterator.next(); Iterator<Element> elementIterator1 = element.elementIterator(); for (Iterator iterator1 = elementIterator1; iterator1.hasNext(); ) { Element element1 = (Element) iterator1.next(); List<Element> elements = element1.elements(); Attribute attribute = element1.attribute(0); for (Element element2 : elements) { List<Attribute> list_Attributes = element2.attributes(); count++; Product product = new Product(); for (int i = 0; i < list_Attributes.size(); i++) { if (i == 0) product.setId(list_Attributes.get(i).getValue()); if (i == 1) product.setCost(list_Attributes.get(i).getValue()); if (i == 2) product.setSaleCount(list_Attributes.get(i).getValue()); if (i == 3) product.setSalePrice(list_Attributes.get(i).getValue()); if (attribute.getName().equals("date"))product.setDate(attribute.getValue()); if (count <=6){ product.setCategory(getCategorys().get(0)); } else { product.setCategory(getCategorys().get(1)); } } list.add(product); } } } } catch (IOException | DocumentException ioException) { throw new RuntimeException(ioException); } return list; } public List<Category> getCategorys() { List<Category> list = new ArrayList<Category>(); try { Reader resourceAsReader = Resources.getResourceAsReader("SalesRecords.xml"); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(resourceAsReader); Element rootElement = document.getRootElement(); List<Element> elements = rootElement.elements(); list = elements.stream().map(element -> { Category category = new Category(); for (int i = 0; i < element.attributeCount(); i++) { Attribute attribute = element.attribute(i); String value = attribute.getValue(); if (i == 0) { category.setId(value); } else if (i == 1) { category.setName(value); } else if (i == 2) { category.setDiscount(value); } } return category; }).collect(Collectors.toList()); } catch (IOException | DocumentException ioException) { throw new RuntimeException(ioException); } return list; } 计算每天的利润,并且转成json格式 import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baizhi.entity.Product; import com.baizhi.service.SalesRecordsService; import com.baizhi.service.TotalService; import com.google.gson.JsonObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; @Service public class TotalServiceImpl implements TotalService { @Autowired SalesRecordsService salesRecordsService; @Override public JSONObject totalArraySale() { List<Product> products = salesRecordsService.getProducts(); double total_20130630 = 0.0; double total_20130701 = 0.0; double total_20130630_None = 0.0; double total_20130701_None = 0.0; JSONObject jsonObject = new JSONObject(); for (Product product : products) { String discount = product.getCategory().getDiscount(); // count 销售数量 String saleCount = product.getSaleCount(); int count = Integer.parseInt(saleCount); // cost 销售成本价 String costf = product.getCost(); int cost = Integer.parseInt(costf); // salePrice 销售价格 String salePricef = product.getSalePrice(); int salePrice = Integer.parseInt(salePricef); if (product.getDate().equals("20130630")){ if (!discount.equals("none")){ // discountDigital 打折数字 String discountDigital = discount.substring(discount.indexOf(":")+1, discount.length()); total_20130630+=(salePrice-cost)*(1-Double.parseDouble(discountDigital))*count; }else{ total_20130630_None+=(salePrice-cost)*count; } }else if (product.getDate().equals("20130701")){ if (!discount.equals("none")){ // discountDigital 打折数字 String discountDigital = discount.substring(discount.indexOf(":")+1, discount.length()); total_20130701+=(salePrice-cost)*(1-Double.parseDouble(discountDigital))*count; }else{ total_20130701_None+=(salePrice-cost)*count; } } } BigDecimal bigDecimal = new BigDecimal(total_20130630 + total_20130630_None); double sale_7 = new BigDecimal(total_20130701 + total_20130701_None).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); BigDecimal bigDecimal1 = bigDecimal.setScale(2, RoundingMode.HALF_EVEN); double sale = bigDecimal1.doubleValue(); jsonObject.put("20130630",sale); jsonObject.put("20130701",sale_7); return jsonObject; } @Override public JSON totalAllSale() { JSONObject object = new JSONObject(); JSONObject jsonObject = totalArraySale(); Double o1 = (Double)jsonObject.get("20130630"); Double o2 = (Double)jsonObject.get("20130701"); long o3 = Math.round(o1 + o2); object.put("fit",o3); return object; } } ———————————————— 原文链接:https://blog.csdn.net/hacker_world/article/details/119064628
-
大体思路:**先将报文转化为List< HashMap>集合,在将HashMap转化为具体实体类。entity:@Datapublic class ClearRoutingClearTask {private String index;private String taskType;private Long priority;}第一种xml报文:/*** xml报文解析成list集合* @param xml* @return* @throws DocumentException*/import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.jdom.JDOMException;import org.jdom.input.SAXBuilder;import org.springframework.stereotype.Component;public static List< HashMap> TransferSubTask(String xml) throws DocumentException {List> subList = new ArrayList<>();Document document = DocumentHelper.parseText(xml);Element clearTaskElt = document.getRootElement();Iterator iterator = clearTaskElt.elementIterator(“clear-task”);while (iterator.hasNext()) {Element clearSubTaskElt = (Element) iterator.next();String index = clearSubTaskElt.elementTextTrim(“index”);String taskType = clearSubTaskElt.elementTextTrim(“task-type”);String priority = clearSubTaskElt.elementTextTrim(“priority”);HashMap subTaskTemplateMap = new HashMap<>();subTaskTemplateMap.put(“index”, index);subTaskTemplateMap.put(“taskType”, taskType);subTaskTemplateMap.put(“priority”, priority);subList.add(subTaskTemplateMap);}return subList;}测试类:@Testpublic void test6() throws AppException, DocumentException, InvocationTargetException, IllegalAccessException {List clearRoutingClearTasks = new ArrayList<>();String str ="{\n" +" “createTime”: “2021-07-27T11:17:48.000+0800”,\n" +" “remark”: “销售费”,\n" +" “updateTime”: “2021-07-27T11:17:48.000+0800”,\n" +" “clearRoutingKey”: “R1001-187501-0”,\n" +" “clearTask”: “ 1209021 2209032”,\n" +" “returnType”: “0”\n" +“}”;JSONObject jsonObject = JSONObject.parseObject(str);System.out.println(“jsonObject-----”+jsonObject);ClearRoutingConfigDO clearRoutingConfigDO = JSON.toJavaObject(jsonObject,ClearRoutingConfigDO.class);System.out.println(“clearRoutingConfigDO------”+clearRoutingConfigDO);String clearTask = clearRoutingConfigDO.getClearTask();List> beans = XmlEntityConvertHelper.TransferSubTask(clearTask);for (HashMap bean : beans) {ClearRoutingClearTask clearRoutingClearTask = new ClearRoutingClearTask();BeanUtils.populate(clearRoutingClearTask, bean);clearRoutingClearTasks.add(clearRoutingClearTask);}}第二种xml格式:< routing-clear-judge name="证金债一级外部买入RIV2004O">< organization>1< /organization>< new-process>1< /new-process>< bank-settlement-mode>condition1< /bank-settlement-mode>< exchange-settlement-mode>0< /exchange-settlement-mode>< registration-institution>0< /registration-institution>< counterparty>0< /counterparty>< /routing-clear-judge>解析方法:/*** xml报文解析成清算任务* @param xml* @return*/public static ClearRoutingClearTypeConfig TransferClearType(String xml) throws IOException, JDOMException {ClearRoutingClearTypeConfig clearRoutingClearTypeConfig = new ClearRoutingClearTypeConfig();Map map = new HashMap<>();//将xml中的回车和换行去掉if (xml != null && !xml.equals("")) {Pattern p = compile("\r|\n");Matcher m = p.matcher(xml);xml = m.replaceAll("");//创建一个新的字符串StringReader read = new StringReader(xml);//创建新的输入源SAX解析器将使用 InputSource对象来确定如何读取 XML输入InputSource source = new InputSource(read);//创建一个新的SAXBuilderSAXBuilder sb = new SAXBuilder();//通过输入源构造一个Documentorg.jdom.Document doc = sb.build(source);//取的根元素org.jdom.Element root = doc.getRootElement();System.out.println(root.getName());//输出根元素的名称(测试)//得到根元素所有子元素的集合List node = root.getChildren();org.jdom.Element et = null;//循环依次得到子元素for (int i = 0; i < node.size(); i++) {et = (org.jdom.Element) node.get(i);map.put(et.getName(), et.getValue());}//将map集合中的数据取出clearRoutingClearTypeConfig.setOrganization(String.valueOf(map.get(“organization”)));clearRoutingClearTypeConfig.setNewProcess(String.valueOf(map.get(“new-process”)));clearRoutingClearTypeConfig.setBankSettlementMode(String.valueOf(map.get(“bank-settlement-mode”)));clearRoutingClearTypeConfig.setExchangeSettlementMode(String.valueOf(map.get(“exchange-settlement-mode”)));clearRoutingClearTypeConfig.setRegistrationInstitution(String.valueOf(map.get(“registration-institution”)));clearRoutingClearTypeConfig.setCounterParty(String.valueOf(map.get(“counterparty”)));clearRoutingClearTypeConfig.setIsTgPay(String.valueOf(map.get(“is-tg-pay”)));}return clearRoutingClearTypeConfig;}————————————————原文链接:https://blog.csdn.net/qq_42817320/article/details/119645944
-
需要引入dom4j包 <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> /** * xml工具 * @author tangbin * @date 2021年5月8日 */ public class XMLUtils { /** * 将xml格式处理为jsonObject对象 * @param resString xml格式 * @return * @throws UnsupportedEncodingException * @throws DocumentException */ public static JSONObject xmlHandle(String resString) throws UnsupportedEncodingException, DocumentException { // 读取输入流 SAXReader reader = new SAXReader(); Document document = null; document = reader.read(new ByteArrayInputStream(resString.getBytes("UTF-8"))); // 得到xml根元素 Element root = document.getRootElement(); JSONObject resJsonObject = elementHandle(root); return resJsonObject; } /** * 递归取每一层数据 * @param element * @return */ private static JSONObject elementHandle(Element element) { JSONObject resultJsonObject = new JSONObject(); // 得到根元素的所有子节点 @SuppressWarnings("unchecked") List<Element> elementList = element.elements(); // 遍历所有子节点 for (Element e : elementList){ String name = e.getName(); String text = e.getText(); if(e.hasContent() && !e.isTextOnly()) { //有下一层 resultJsonObject.element(name, elementHandle(e)); } else { resultJsonObject.element(name, text); } } return resultJsonObject; } ———————————————— 原文链接:https://blog.csdn.net/qqtbqq/article/details/117785938
-
JSON格式化 json对比 包com.alibaba.fastjson:1.2.56 对象转为字符串: JSONObject.toJSONString(userEntity); 集合转为字符串: JSONArray.toJSONString(odfList); 字符串转为对象 JSONObject.parseObject(str, SendNoteResultBO.class); 字符串转为集合 JSONArray.parseArray(str, ArgsInOdfDto.class); 1.将map对象转为为Json字符串:JSON.toJSONString String json1 = JSON.toJSONString(userEntity); String json2 = JSONObject.toJSONString(userEntity); 2.将Json字符串专为对象:JSON.parseObject SendNoteResultBO resultBO = JSON.parseObject(response.getData(), SendNoteResultBO.class); 3,字符串与JSON集合 JSON JSONArray 4.object对象转为java对象 RootCertificateResult rootCertificateResult = JSON.parseObject( JSON.toJSONString(object对象实例), new TypeReference<java对象类型 RootCertificateResult>() { } ); ———————————————— 原文链接:https://blog.csdn.net/weixin_44635157/article/details/107360529
-
依赖 # pom.xml <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>1.1.1.RELEASE</version> </dependency> 配置文件 # application.yml spring: kafka: bootstrap-servers: 192.168.1.117:9092 producer: # 重试次数 retries: 3 # 批量发送的消息数量 batch-size: 16384 # 32MB的批处理缓冲区 buffer-memory: 33554432 consumer: # 默认消费者组 group-id: etl # 最早未被消费的offset auto-offset-reset: earliest # 批量一次最大拉取数据量 max-poll-records: 1000 # 自动提交 auto-commit-interval: 1000 enable-auto-commit: true topicName: topic2: topic2Name topic5: topic5Name 配置 // KafkaConfiguration.java package kafka_test; import com.google.common.collect.Maps; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; import org.springframework.kafka.config.KafkaListenerContainerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.core.ProducerFactory; import java.util.Map; /** * @author tianshl * @version 2017/9/1 下午04:07 */ @Configuration @EnableKafka public class KafkaConfiguration { @Value("${spring.kafka.bootstrap-servers}") private String bootstrapServers; @Value("${spring.kafka.consumer.enable-auto-commit}") private Boolean autoCommit; @Value("${spring.kafka.consumer.auto-commit-interval}") private Integer autoCommitInterval; @Value("${spring.kafka.consumer.group-id}") private String groupId; @Value("${spring.kafka.consumer.max-poll-records}") private Integer maxPollRecords; @Value("${spring.kafka.consumer.auto-offset-reset}") private String autoOffsetReset; @Value("${spring.kafka.producer.retries}") private Integer retries; @Value("${spring.kafka.producer.batch-size}") private Integer batchSize; @Value("${spring.kafka.producer.buffer-memory}") private Integer bufferMemory; /** * 生产者配置信息 */ @Bean public Map<String, Object> producerConfigs() { Map<String, Object> props = Maps.newHashMap(); props.put(ProducerConfig.ACKS_CONFIG, "0"); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ProducerConfig.RETRIES_CONFIG, retries); props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize); props.put(ProducerConfig.LINGER_MS_CONFIG, 1); props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); return props; } /** * 生产者工厂 */ @Bean public ProducerFactory<String, String> producerFactory() { return new DefaultKafkaProducerFactory<>(producerConfigs()); } /** * 生产者模板 */ @Bean public KafkaTemplate<String, String> kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } /** * 消费者配置信息 */ @Bean public Map<String, Object> consumerConfigs() { Map<String, Object> props = Maps.newHashMap(); props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 120000); props.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, 180000); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return props; } /** * 消费者批量工厂 */ @Bean public KafkaListenerContainerFactory<?> batchFactory() { ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfigs())); //设置为批量消费,每个批次数量在Kafka配置参数中设置ConsumerConfig.MAX_POLL_RECORDS_CONFIG factory.setBatchListener(true); return factory; } } 生产者 // KafkaProducer.java package kafka_test; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Component; import java.util.List; /** * @author tianshl * @version 2017/9/1 下午05:03 */ @Component public class KafkaProducer { private final KafkaTemplate<String, String> kafkaTemplate; public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate) { this.kafkaTemplate = kafkaTemplate; } /** * 向topic中发送消息 */ public void send (String topic, String msg) { kafkaTemplate.send(topic, msg); } /** * 向topic中发送消息 */ public void send (String topic, List<String> msgs) { msgs.forEach(msg -> kafkaTemplate.send(topic, msg)); } } 消费者 // KafkaConsumer.java package kafka_test; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.List; /** * @author tianshl * @version 2017/9/1 下午05:21 */ @Component public class KafkaConsumer { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 监听topic1主题,单条消费 */ @KafkaListener(topics = "topic1") public void listen0(ConsumerRecord<String, String> record) { consumer(record); } /** * 监听topic2主题,单条消费 */ @KafkaListener(topics = "${topicName.topic2}") public void listen1(ConsumerRecord<String, String> record) { consumer(record); } /** * 监听topic3和topic4,单条消费 */ @KafkaListener(topics = {"topic3", "topic4"}) public void listen2(ConsumerRecord<String, String> record) { consumer(record); } /** * 监听topic5,批量消费 */ @KafkaListener(topics = "${topicName.topic2}", containerFactory = "batchFactory") public void listen2(List<ConsumerRecord<String, String>> records) { batchConsumer(records); } /** * 单条消费 */ public void consumer(ConsumerRecord<String, String> record) { logger.debug("主题:{}, 内容: {}", record.topic(), record.value()); } /** * 批量消费 */ public void batchConsumer(List<ConsumerRecord<String, String>> records) { records.forEach(record -> consumer(record)); } } ———————————————— 原文链接:https://blog.csdn.net/xiaobuding007/article/details/82699758
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签