SpringBoot與GraphQL集成的實(shí)現(xiàn)示例
引言
今天想和大家聊聊 Spring Boot 與 GraphQL 的集成。GraphQL 是一種用于 API 的查詢語(yǔ)言,它提供了一種更高效、更靈活的方式來(lái)獲取數(shù)據(jù),是現(xiàn)代 API 開(kāi)發(fā)的重要技術(shù)。在 Spring Boot 應(yīng)用中,合理使用 GraphQL 可以提高 API 的靈活性和性能。
1. GraphQL 基礎(chǔ)知識(shí)
1.1 GraphQL 核心概念
- 查詢 (Query):用于獲取數(shù)據(jù)的操作,類(lèi)似于 RESTful API 中的 GET 請(qǐng)求。
- 變更 (Mutation):用于修改數(shù)據(jù)的操作,類(lèi)似于 RESTful API 中的 POST、PUT、DELETE 請(qǐng)求。
- 訂閱 (Subscription):用于實(shí)時(shí)獲取數(shù)據(jù)的操作,基于 WebSocket 實(shí)現(xiàn)。
- 類(lèi)型 (Type):定義數(shù)據(jù)的結(jié)構(gòu),包括對(duì)象類(lèi)型、標(biāo)量類(lèi)型等。
- 字段 (Field):類(lèi)型中的屬性,每個(gè)字段都有一個(gè)類(lèi)型。
- 參數(shù) (Argument):字段可以接受參數(shù),用于過(guò)濾或排序數(shù)據(jù)。
1.2 GraphQL 優(yōu)勢(shì)
- 按需獲取數(shù)據(jù):客戶端可以精確指定需要的字段,避免過(guò)度獲取數(shù)據(jù)。
- 強(qiáng)類(lèi)型:GraphQL 使用類(lèi)型系統(tǒng)定義 API,提供強(qiáng)類(lèi)型檢查。
- 單一端點(diǎn):所有請(qǐng)求都通過(guò)單一端點(diǎn)發(fā)送,簡(jiǎn)化 API 管理。
- 實(shí)時(shí)數(shù)據(jù):支持訂閱操作,提供實(shí)時(shí)數(shù)據(jù)更新。
- 自我描述:GraphQL API 可以通過(guò) introspection 查詢自身的結(jié)構(gòu)。
1.3 GraphQL 與 RESTful API 的比較
- 數(shù)據(jù)獲取:GraphQL 允許客戶端按需獲取數(shù)據(jù),而 RESTful API 通常返回固定結(jié)構(gòu)的數(shù)據(jù)。
- API 設(shè)計(jì):GraphQL 使用單一端點(diǎn),而 RESTful API 使用多個(gè)端點(diǎn)。
- 版本控制:GraphQL 可以通過(guò)添加字段來(lái)擴(kuò)展 API,而 RESTful API 通常需要版本控制。
- 錯(cuò)誤處理:GraphQL 返回標(biāo)準(zhǔn)的錯(cuò)誤格式,而 RESTful API 使用 HTTP 狀態(tài)碼。
2. Spring Boot 與 GraphQL 集成
2.1 依賴配置
在 Maven 項(xiàng)目中添加 GraphQL 依賴:
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>2.2 定義 schema
創(chuàng)建 src/main/resources/graphql/schema.graphqls 文件:
type Query {
user(id: ID!): User
users: [User!]!
}
type Mutation {
createUser(name: String!, email: String!, age: Int): User
updateUser(id: ID!, name: String, email: String, age: Int): User
deleteUser(id: ID!): Boolean
}
type User {
id: ID!
name: String!
email: String!
age: Int
}2.3 實(shí)現(xiàn) resolver
實(shí)現(xiàn) GraphQL 的 resolver:
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserRepository userRepository;
public UserResolver(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User user(Long id) {
return userRepository.findById(id).orElse(null);
}
public List<User> users() {
return userRepository.findAll();
}
}
@Component
public class UserMutationResolver implements GraphQLMutationResolver {
private final UserRepository userRepository;
public UserMutationResolver(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(String name, String email, Integer age) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setAge(age);
return userRepository.save(user);
}
public User updateUser(Long id, String name, String email, Integer age) {
User user = userRepository.findById(id).orElse(null);
if (user != null) {
if (name != null) {
user.setName(name);
}
if (email != null) {
user.setEmail(email);
}
if (age != null) {
user.setAge(age);
}
return userRepository.save(user);
}
return null;
}
public boolean deleteUser(Long id) {
userRepository.deleteById(id);
return true;
}
}2.4 配置 GraphQL
在 application.yml 文件中配置 GraphQL:
graphql:
servlet:
mapping: /graphql
enabled: true
corsEnabled: true2.5 測(cè)試 GraphQL
啟動(dòng)應(yīng)用,訪問(wèn) http://localhost:8080/graphiql,使用 GraphiQL 工具測(cè)試 GraphQL API:
# 查詢單個(gè)用戶
query {
user(id: 1) {
id
name
email
age
}
}
# 查詢所有用戶
query {
users {
id
name
email
age
}
}
# 創(chuàng)建用戶
mutation {
createUser(name: "Alex", email: "alex@example.com", age: 30) {
id
name
email
age
}
}
# 更新用戶
mutation {
updateUser(id: 1, name: "Alex Updated", age: 31) {
id
name
email
age
}
}
# 刪除用戶
mutation {
deleteUser(id: 1)
}3. Spring Boot 與 GraphQL 高級(jí)集成
3.1 自定義標(biāo)量類(lèi)型
實(shí)現(xiàn)自定義標(biāo)量類(lèi)型:
public class DateTimeScalarType extends GraphQLScalarType {
public DateTimeScalarType() {
super("DateTime", "DateTime scalar type", new Coercing<Instant, String>() {
@Override
public String serialize(Object dataFetcherResult) throws CoercingSerializeException {
if (dataFetcherResult instanceof Instant) {
return dataFetcherResult.toString();
}
throw new CoercingSerializeException("Expected a Instant object");
}
@Override
public Instant parseValue(Object input) throws CoercingParseValueException {
if (input instanceof String) {
return Instant.parse((String) input);
}
throw new CoercingParseValueException("Expected a String");
}
@Override
public Instant parseLiteral(Object input) throws CoercingParseLiteralException {
if (input instanceof StringValue) {
return Instant.parse(((StringValue) input).getValue());
}
throw new CoercingParseLiteralException("Expected a StringValue");
}
});
}
}
@Configuration
public class GraphQLConfig {
@Bean
public GraphQLScalarType dateTimeScalar() {
return new DateTimeScalarType();
}
}在 schema 中使用自定義標(biāo)量類(lèi)型:
sCALAR DateTime
type User {
id: ID!
name: String!
email: String!
age: Int
createdAt: DateTime!
updatedAt: DateTime!
}3.2 數(shù)據(jù)加載器
使用數(shù)據(jù)加載器,減少 N+1 查詢問(wèn)題:
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserRepository userRepository;
private final DataLoaderRegistry dataLoaderRegistry;
public UserResolver(UserRepository userRepository, DataLoaderRegistry dataLoaderRegistry) {
this.userRepository = userRepository;
this.dataLoaderRegistry = dataLoaderRegistry;
}
public CompletableFuture<User> user(Long id) {
DataLoader<Long, User> dataLoader = dataLoaderRegistry.getDataLoader("userLoader");
return dataLoader.load(id);
}
public List<User> users() {
return userRepository.findAll();
}
}
@Configuration
public class DataLoaderConfig {
@Bean
public DataLoaderRegistry dataLoaderRegistry(UserRepository userRepository) {
DataLoaderRegistry registry = new DataLoaderRegistry();
DataLoader<Long, User> userLoader = DataLoader.newDataLoader((List<Long> ids) -> {
List<User> users = userRepository.findAllById(ids);
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
return CompletableFuture.completedFuture(
ids.stream().map(userMap::get).collect(Collectors.toList())
);
});
registry.register("userLoader", userLoader);
return registry;
}
}3.3 認(rèn)證和授權(quán)
實(shí)現(xiàn) GraphQL 的認(rèn)證和授權(quán):
@Component
public class GraphQLContextBuilder implements GraphQLServletContextBuilder {
private final AuthenticationManager authenticationManager;
public GraphQLContextBuilder(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public GraphQLContext build(HttpServletRequest request, HttpServletResponse response) {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
try {
UsernamePasswordAuthenticationToken authentication = JwtUtil.parseToken(token);
Authentication authenticated = authenticationManager.authenticate(authentication);
SecurityContextHolder.getContext().setAuthentication(authenticated);
} catch (Exception e) {
SecurityContextHolder.clearContext();
}
}
return new DefaultGraphQLServletContext(request, response, null);
}
}
@Component
public class UserMutationResolver implements GraphQLMutationResolver {
private final UserRepository userRepository;
public UserMutationResolver(UserRepository userRepository) {
this.userRepository = userRepository;
}
@PreAuthorize("hasRole('ADMIN')")
public User createUser(String name, String email, Integer age) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setAge(age);
return userRepository.save(user);
}
// 其他方法...
}3.4 錯(cuò)誤處理
實(shí)現(xiàn) GraphQL 的錯(cuò)誤處理:
@Component
public class CustomGraphQLErrorHandler implements GraphQLErrorHandler {
@Override
public List<GraphQLError> processErrors(List<GraphQLError> errors) {
List<GraphQLError> processedErrors = new ArrayList<>();
for (GraphQLError error : errors) {
if (error instanceof ExceptionWhileDataFetching) {
ExceptionWhileDataFetching exceptionError = (ExceptionWhileDataFetching) error;
if (exceptionError.getException() instanceof RuntimeException) {
processedErrors.add(new CustomGraphQLError(error.getLocations(), error.getPath(), exceptionError.getException().getMessage()));
} else {
processedErrors.add(error);
}
} else {
processedErrors.add(error);
}
}
return processedErrors;
}
private static class CustomGraphQLError implements GraphQLError {
private final List<SourceLocation> locations;
private final List<Object> path;
private final String message;
public CustomGraphQLError(List<SourceLocation> locations, List<Object> path, String message) {
this.locations = locations;
this.path = path;
this.message = message;
}
@Override
public String getMessage() {
return message;
}
@Override
public List<SourceLocation> getLocations() {
return locations;
}
@Override
public List<Object> getPath() {
return path;
}
@Override
public Map<String, Object> getExtensions() {
return null;
}
}
}4. Spring Boot 與 GraphQL 最佳實(shí)踐
4.1 Schema 設(shè)計(jì)
- 合理設(shè)計(jì)類(lèi)型:根據(jù)業(yè)務(wù)需求,合理設(shè)計(jì) GraphQL 類(lèi)型,避免類(lèi)型過(guò)于復(fù)雜。
- 使用接口和聯(lián)合類(lèi)型:對(duì)于具有共同特征的類(lèi)型,使用接口或聯(lián)合類(lèi)型。
- 添加描述:為類(lèi)型和字段添加描述,提高 API 的可讀性。
- 使用枚舉類(lèi)型:對(duì)于有限的取值范圍,使用枚舉類(lèi)型。
4.2 Resolver 實(shí)現(xiàn)
- 使用數(shù)據(jù)加載器:使用數(shù)據(jù)加載器,減少 N+1 查詢問(wèn)題。
- 異步處理:對(duì)于耗時(shí)的操作,使用異步處理,提高系統(tǒng)的并發(fā)能力。
- 錯(cuò)誤處理:實(shí)現(xiàn)合理的錯(cuò)誤處理,返回清晰的錯(cuò)誤信息。
- 緩存:對(duì)于頻繁訪問(wèn)的數(shù)據(jù),使用緩存,提高查詢性能。
4.3 性能優(yōu)化
- 批量查詢:使用數(shù)據(jù)加載器,實(shí)現(xiàn)批量查詢,減少數(shù)據(jù)庫(kù)查詢次數(shù)。
- 字段級(jí)權(quán)限:實(shí)現(xiàn)字段級(jí)權(quán)限,只返回用戶有權(quán)訪問(wèn)的字段。
- 查詢復(fù)雜度分析:分析查詢的復(fù)雜度,防止惡意查詢。
- 緩存:使用緩存,減少重復(fù)計(jì)算和數(shù)據(jù)庫(kù)查詢。
4.4 監(jiān)控和運(yùn)維
- 監(jiān)控 GraphQL 查詢:監(jiān)控 GraphQL 查詢的執(zhí)行時(shí)間、頻率等指標(biāo)。
- 日志記錄:記錄 GraphQL 查詢的日志,便于問(wèn)題定位和分析。
- 查詢分析:分析 GraphQL 查詢的性能,找出慢查詢。
- 安全檢查:定期檢查 GraphQL API 的安全性,防止安全漏洞。
5. Spring Boot 與 GraphQL 實(shí)戰(zhàn)案例
5.1 實(shí)現(xiàn)用戶服務(wù)
5.1.1 配置文件
application.yml:
graphql:
servlet:
mapping: /graphql
enabled: true
corsEnabled: true5.1.2 代碼實(shí)現(xiàn)
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private Integer age;
private Instant createdAt;
private Instant updatedAt;
// getter 和 setter 方法
}
public interface UserRepository extends JpaRepository<User, Long> {
}
@Component
public class UserResolver implements GraphQLQueryResolver {
private final UserRepository userRepository;
public UserResolver(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User user(Long id) {
return userRepository.findById(id).orElse(null);
}
public List<User> users() {
return userRepository.findAll();
}
}
@Component
public class UserMutationResolver implements GraphQLMutationResolver {
private final UserRepository userRepository;
public UserMutationResolver(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(String name, String email, Integer age) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setAge(age);
user.setCreatedAt(Instant.now());
user.setUpdatedAt(Instant.now());
return userRepository.save(user);
}
public User updateUser(Long id, String name, String email, Integer age) {
User user = userRepository.findById(id).orElse(null);
if (user != null) {
if (name != null) {
user.setName(name);
}
if (email != null) {
user.setEmail(email);
}
if (age != null) {
user.setAge(age);
}
user.setUpdatedAt(Instant.now());
return userRepository.save(user);
}
return null;
}
public boolean deleteUser(Long id) {
userRepository.deleteById(id);
return true;
}
}5.2 實(shí)現(xiàn)訂單服務(wù)
5.2.1 代碼實(shí)現(xiàn)
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private Long productId;
private Integer quantity;
private Double price;
private String status;
private Instant createdAt;
private Instant updatedAt;
// getter 和 setter 方法
}
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
}
@Component
public class OrderResolver implements GraphQLQueryResolver {
private final OrderRepository orderRepository;
private final UserRepository userRepository;
public OrderResolver(OrderRepository orderRepository, UserRepository userRepository) {
this.orderRepository = orderRepository;
this.userRepository = userRepository;
}
public Order order(Long id) {
return orderRepository.findById(id).orElse(null);
}
public List<Order> orders() {
return orderRepository.findAll();
}
public List<Order> ordersByUser(Long userId) {
return orderRepository.findByUserId(userId);
}
public User user(Order order) {
return userRepository.findById(order.getUserId()).orElse(null);
}
}
@Component
public class OrderMutationResolver implements GraphQLMutationResolver {
private final OrderRepository orderRepository;
public OrderMutationResolver(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public Order createOrder(Long userId, Long productId, Integer quantity, Double price) {
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setPrice(price);
order.setStatus("CREATED");
order.setCreatedAt(Instant.now());
order.setUpdatedAt(Instant.now());
return orderRepository.save(order);
}
public Order updateOrder(Long id, String status) {
Order order = orderRepository.findById(id).orElse(null);
if (order != null) {
order.setStatus(status);
order.setUpdatedAt(Instant.now());
return orderRepository.save(order);
}
return null;
}
public boolean deleteOrder(Long id) {
orderRepository.deleteById(id);
return true;
}
}6. 常見(jiàn)問(wèn)題與解決方案
6.1 N+1 查詢問(wèn)題
問(wèn)題:GraphQL 查詢導(dǎo)致 N+1 查詢問(wèn)題,影響性能。
解決方案:
- 使用數(shù)據(jù)加載器:使用數(shù)據(jù)加載器,實(shí)現(xiàn)批量查詢,減少數(shù)據(jù)庫(kù)查詢次數(shù)。
- 預(yù)加載關(guān)聯(lián)數(shù)據(jù):在 resolver 中預(yù)加載關(guān)聯(lián)數(shù)據(jù),減少后續(xù)查詢。
- 使用緩存:對(duì)于頻繁訪問(wèn)的數(shù)據(jù),使用緩存,減少數(shù)據(jù)庫(kù)查詢。
6.2 查詢復(fù)雜度問(wèn)題
問(wèn)題:復(fù)雜的 GraphQL 查詢可能導(dǎo)致系統(tǒng)性能下降。
解決方案:
- 查詢復(fù)雜度分析:分析查詢的復(fù)雜度,設(shè)置合理的復(fù)雜度限制。
- 字段級(jí)權(quán)限:實(shí)現(xiàn)字段級(jí)權(quán)限,只返回用戶有權(quán)訪問(wèn)的字段。
- 分頁(yè):對(duì)于大型列表,實(shí)現(xiàn)分頁(yè),減少一次性返回的數(shù)據(jù)量。
6.3 錯(cuò)誤處理問(wèn)題
問(wèn)題:GraphQL 錯(cuò)誤處理不當(dāng),導(dǎo)致錯(cuò)誤信息不清晰。
解決方案:
- 自定義錯(cuò)誤處理:實(shí)現(xiàn)自定義錯(cuò)誤處理,返回清晰的錯(cuò)誤信息。
- 錯(cuò)誤分類(lèi):對(duì)錯(cuò)誤進(jìn)行分類(lèi),便于客戶端處理。
- 日志記錄:記錄詳細(xì)的錯(cuò)誤日志,便于問(wèn)題定位和分析。
6.4 安全性問(wèn)題
問(wèn)題:GraphQL API 存在安全漏洞,可能被攻擊。
解決方案:
- 認(rèn)證和授權(quán):實(shí)現(xiàn)認(rèn)證和授權(quán),確保只有授權(quán)的用戶可以訪問(wèn) API。
- 查詢限制:限制查詢的深度和復(fù)雜度,防止惡意查詢。
- 輸入驗(yàn)證:對(duì)輸入進(jìn)行驗(yàn)證,防止注入攻擊。
- CORS 配置:合理配置 CORS,防止跨域攻擊。
7. 未來(lái)發(fā)展趨勢(shì)
7.1 GraphQL 與微服務(wù)集成
GraphQL 正在加強(qiáng)與微服務(wù)的集成,如:
- GraphQL Federation:允許將多個(gè) GraphQL 服務(wù)組合成一個(gè)統(tǒng)一的 API。
- Apollo Gateway:提供 GraphQL 服務(wù)的網(wǎng)關(guān),支持服務(wù)發(fā)現(xiàn)和負(fù)載均衡。
- GraphQL Mesh:將現(xiàn)有的 RESTful API、gRPC 服務(wù)等轉(zhuǎn)換為 GraphQL API。
7.2 GraphQL 與實(shí)時(shí)數(shù)據(jù)
GraphQL 正在加強(qiáng)對(duì)實(shí)時(shí)數(shù)據(jù)的支持,如:
- 訂閱改進(jìn):改進(jìn)訂閱機(jī)制,提供更可靠的實(shí)時(shí)數(shù)據(jù)更新。
- WebSocket 優(yōu)化:優(yōu)化 WebSocket 連接,提高實(shí)時(shí)數(shù)據(jù)的傳輸效率。
- 與事件驅(qū)動(dòng)架構(gòu)集成:與事件驅(qū)動(dòng)架構(gòu)集成,提供更靈活的實(shí)時(shí)數(shù)據(jù)處理。
7.3 GraphQL 工具鏈的改進(jìn)
GraphQL 的工具鏈正在不斷改進(jìn),如:
- 更強(qiáng)大的 IDE 支持:提供更強(qiáng)大的 IDE 支持,如代碼補(bǔ)全、錯(cuò)誤檢查等。
- 更豐富的客戶端庫(kù):提供更豐富的客戶端庫(kù),支持更多的編程語(yǔ)言和框架。
- 更好的監(jiān)控工具:提供更好的監(jiān)控工具,便于監(jiān)控和分析 GraphQL API 的性能。
總結(jié)
Spring Boot 與 GraphQL 的集成是現(xiàn)代 API 開(kāi)發(fā)的重要組成部分,它可以提高 API 的靈活性和性能。在實(shí)際項(xiàng)目中,我們應(yīng)該根據(jù)具體的業(yè)務(wù)需求和系統(tǒng)特點(diǎn),合理配置和使用 GraphQL,充分發(fā)揮它的優(yōu)勢(shì)。
記住,GraphQL 是一種工具,我們應(yīng)該根據(jù)具體的場(chǎng)景選擇合適的使用方式。最重要的是,保持代碼的簡(jiǎn)潔和可維護(hù)性,這其實(shí)可以更優(yōu)雅一點(diǎn)。
別叫我大神,叫我 Alex 就好。如果有任何問(wèn)題或建議,歡迎在評(píng)論區(qū)留言,我會(huì)認(rèn)真回復(fù)每一條評(píng)論。
參考資料
- GraphQL 官方文檔
- Spring Boot 官方文檔
- 《GraphQL 實(shí)戰(zhàn)》
- 《Spring Boot 實(shí)戰(zhàn)》
到此這篇關(guān)于SpringBoot與GraphQL集成的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot集成GraphQL 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot集成mybatisplus實(shí)例詳解
這篇文章主要介紹了springboot集成mybatisplus實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Java 中的 equals 和 hashCode 方法關(guān)系與正確重寫(xiě)
在Java中,equals和 hashCode方法是Object類(lèi)的核心方法,廣泛用于對(duì)象比較和哈希集合(如 HashMap、HashSet)的操作,本文深入剖析equals和hashCode方法的關(guān)系、契約、正確重寫(xiě)方式及實(shí)踐案例,感興趣的朋友一起看看吧2025-09-09
SpringBoot集成ZXing實(shí)現(xiàn)二維碼的生成與讀取功能
本教程將詳細(xì)講解如何在 Spring Boot 項(xiàng)目中集成 ZXing 庫(kù)實(shí)現(xiàn)二維碼的生成(返回 Base64 編碼)和讀取(解析圖片的二維碼)功能,并覆蓋常見(jiàn)異常處理、參數(shù)優(yōu)化等實(shí)戰(zhàn)要點(diǎn),適合 Java 開(kāi)發(fā)新手快速上手,需要的朋友可以參考下2026-03-03
java用類(lèi)加載器的5種方式讀取.properties文件
這篇文章主要介紹了java用類(lèi)加載器的5種方式讀取.properties文件,詳細(xì)的介紹了這5種方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
springboot中如何實(shí)現(xiàn)kafa指定offset消費(fèi)
這篇文章主要介紹了springboot中如何實(shí)現(xiàn)kafa指定offset消費(fèi),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
Java8內(nèi)存模型PermGen Metaspace實(shí)例解析
這篇文章主要介紹了Java8內(nèi)存模型PermGen Metaspace實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
一文詳細(xì)解析Java?8?Stream?API中的flatMap方法
這篇文章主要介紹了Java?8?Stream?API中的flatMap方法的相關(guān)資料,flatMap方法是Java?StreamAPI中的重要中間操作,用于將流中的每個(gè)元素轉(zhuǎn)換為一個(gè)新的流,并將多個(gè)流合并為一個(gè)單一的流,常用于處理嵌套集合和一對(duì)多映射,需要的朋友可以參考下2024-12-12

