SpringData JPA審計功能(@CreatedDate與@LastModifiedDate)實現(xiàn)
引言
在企業(yè)級應(yīng)用開發(fā)中,數(shù)據(jù)審計是一項至關(guān)重要的功能。所謂數(shù)據(jù)審計,是指對數(shù)據(jù)的創(chuàng)建、修改等操作進行跟蹤記錄,以便于后續(xù)的數(shù)據(jù)分析、問題追蹤和安全審核。Spring Data JPA提供了強大的審計功能,通過簡單的注解配置,即可實現(xiàn)對實體創(chuàng)建時間、最后修改時間、創(chuàng)建人和修改人的自動記錄。本文將深入探討Spring Data JPA的審計功能,重點介紹@CreatedDate與@LastModifiedDate注解的實現(xiàn)原理及使用方法,幫助開發(fā)者構(gòu)建健壯的數(shù)據(jù)審計系統(tǒng)。
一、Spring Data JPA審計功能概述
Spring Data JPA的審計功能是通過實體生命周期事件和AOP切面實現(xiàn)的。它可以在實體被持久化和更新時,自動填充審計字段,從而避免了手動設(shè)置這些值的繁瑣工作。
1.1 核心審計注解
Spring Data JPA提供了四個核心的審計注解:
@CreatedDate:標(biāo)記實體創(chuàng)建時間字段@LastModifiedDate:標(biāo)記實體最后修改時間字段@CreatedBy:標(biāo)記實體創(chuàng)建者字段@LastModifiedBy:標(biāo)記實體最后修改者字段
這些注解都位于org.springframework.data.annotation包中,是Spring Data通用的審計注解,不僅限于JPA使用。
1.2 審計功能的工作原理
Spring Data JPA的審計功能主要通過以下幾個組件協(xié)同工作:
AuditingEntityListener:JPA實體監(jiān)聽器,負(fù)責(zé)捕獲實體的生命周期事件AuditingHandler:處理審計信息的填充邏輯DateTimeProvider:提供當(dāng)前時間的接口AuditorAware:提供當(dāng)前用戶信息的接口
當(dāng)啟用審計功能后,每當(dāng)實體被創(chuàng)建或更新,AuditingEntityListener會捕獲相應(yīng)的事件,并調(diào)用AuditingHandler對標(biāo)記了審計注解的字段進行填充。
二、基礎(chǔ)配置
要使用Spring Data JPA的審計功能,首先需要進行必要的配置。
2.1 啟用JPA審計功能
在Spring Boot應(yīng)用中,通過@EnableJpaAuditing注解啟用JPA審計功能:
package com.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
/**
* JPA審計功能配置類
*/
@Configuration
@EnableJpaAuditing // 啟用JPA審計功能
public class JpaAuditingConfig {
// 可以在這里配置審計相關(guān)的Bean
}
2.2 創(chuàng)建審計基類
通常,我們會創(chuàng)建一個包含審計字段的基類,讓需要審計的實體繼承這個基類:
package com.example.entity;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
/**
* 包含審計字段的基礎(chǔ)實體類
*/
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class) // 注冊實體監(jiān)聽器
public abstract class AuditableEntity {
@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
// Getter和Setter方法
public LocalDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = createdDate;
}
public LocalDateTime getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(LocalDateTime lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}
在上面的代碼中:
@MappedSuperclass注解表明這是一個映射超類,其字段將被映射到子類的表中@EntityListeners(AuditingEntityListener.class)注冊了JPA實體監(jiān)聽器,用于捕獲實體的生命周期事件@CreatedDate標(biāo)記了實體創(chuàng)建時間字段@LastModifiedDate標(biāo)記了實體最后修改時間字段updatable = false確保createdDate字段在更新時不會被修改
三、實現(xiàn)時間審計
時間審計是最基本的審計功能,涉及到@CreatedDate和@LastModifiedDate注解的使用。
3.1 使用審計基類
創(chuàng)建業(yè)務(wù)實體類并繼承審計基類:
package com.example.entity;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* 產(chǎn)品實體類
*/
@Entity
@Table(name = "tb_product")
public class Product extends AuditableEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "description")
private String description;
@Column(name = "price", precision = 10, scale = 2)
private BigDecimal price;
@Column(name = "stock")
private Integer stock;
// Getter和Setter方法省略
}
繼承AuditableEntity后,Product實體將自動擁有createdDate和lastModifiedDate字段,這些字段會在實體創(chuàng)建和更新時自動填充。
3.2 不使用基類的審計實現(xiàn)
如果因為某些原因不想使用繼承,也可以直接在實體類中使用審計注解:
package com.example.entity;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 訂單實體類
*/
@Entity
@Table(name = "tb_order")
@EntityListeners(AuditingEntityListener.class) // 直接在實體類上注冊監(jiān)聽器
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number", nullable = false, unique = true)
private String orderNumber;
@Column(name = "customer_id")
private Long customerId;
@Column(name = "total_amount", precision = 10, scale = 2)
private BigDecimal totalAmount;
@Column(name = "status")
private String status;
@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
// Getter和Setter方法省略
}
3.3 自定義日期時間提供者
如果需要自定義日期時間的提供方式,可以實現(xiàn)DateTimeProvider接口:
package com.example.audit;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAccessor;
import java.util.Optional;
/**
* 自定義日期時間提供者
*/
@Component
public class CustomDateTimeProvider implements DateTimeProvider {
@Override
public Optional<TemporalAccessor> getNow() {
// 使用特定時區(qū)的當(dāng)前時間
return Optional.of(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
}
}
然后在配置類中注冊這個提供者:
package com.example.config;
import com.example.audit.CustomDateTimeProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
/**
* JPA審計功能配置類
*/
@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider") // 指定日期時間提供者
public class JpaAuditingConfig {
@Bean
public DateTimeProvider dateTimeProvider() {
return new CustomDateTimeProvider();
}
}
四、實現(xiàn)用戶審計
除了時間審計,還可以實現(xiàn)用戶審計,即記錄創(chuàng)建和修改實體的用戶。
4.1 擴展審計基類
擴展審計基類,添加用戶審計字段:
package com.example.entity;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
/**
* 包含完整審計字段的基礎(chǔ)實體類
*/
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class FullAuditableEntity {
@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
@CreatedBy
@Column(name = "created_by", updatable = false)
private String createdBy;
@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;
// Getter和Setter方法省略
}
4.2 實現(xiàn)AuditorAware接口
要使用@CreatedBy和@LastModifiedBy注解,需要實現(xiàn)AuditorAware接口,提供當(dāng)前用戶信息:
package com.example.audit;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* 當(dāng)前用戶提供者
*/
@Component
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// 從Spring Security上下文中獲取當(dāng)前用戶
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.of("anonymousUser");
}
return Optional.of(authentication.getName());
}
}
然后在配置類中注冊這個提供者:
package com.example.config;
import com.example.audit.CustomDateTimeProvider;
import com.example.audit.SpringSecurityAuditorAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
/**
* JPA審計功能配置類
*/
@Configuration
@EnableJpaAuditing(
dateTimeProviderRef = "dateTimeProvider",
auditorAwareRef = "auditorAware" // 指定用戶提供者
)
public class JpaAuditingConfig {
@Bean
public DateTimeProvider dateTimeProvider() {
return new CustomDateTimeProvider();
}
@Bean
public AuditorAware<String> auditorAware() {
return new SpringSecurityAuditorAware();
}
}
4.3 使用完整審計實體
創(chuàng)建業(yè)務(wù)實體并繼承完整審計基類:
package com.example.entity;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* 訂單實體類
*/
@Entity
@Table(name = "tb_order")
public class Order extends FullAuditableEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number", nullable = false, unique = true)
private String orderNumber;
@Column(name = "customer_id")
private Long customerId;
@Column(name = "total_amount", precision = 10, scale = 2)
private BigDecimal totalAmount;
@Column(name = "status")
private String status;
// Getter和Setter方法省略
}
五、實際應(yīng)用場景
Spring Data JPA的審計功能在實際開發(fā)中有廣泛的應(yīng)用場景。
5.1 數(shù)據(jù)版本控制
結(jié)合版本控制字段,實現(xiàn)樂觀鎖和數(shù)據(jù)版本追蹤:
package com.example.entity;
import javax.persistence.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
/**
* 文檔實體類
*/
@Entity
@Table(name = "tb_document")
@EntityListeners(AuditingEntityListener.class)
public class Document {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "title")
private String title;
@Column(name = "content", columnDefinition = "TEXT")
private String content;
@Version // 版本控制字段
@Column(name = "version")
private Long version;
@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
// Getter和Setter方法省略
}
5.2 審計日志記錄
利用實體監(jiān)聽器,實現(xiàn)更詳細的審計日志記錄:
package com.example.listener;
import com.example.entity.AuditLog;
import com.example.entity.Product;
import com.example.repository.AuditLogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import java.time.LocalDateTime;
/**
* 產(chǎn)品審計監(jiān)聽器
*/
@Component
public class ProductAuditListener {
@Autowired
private AuditLogRepository auditLogRepository;
private static final ThreadLocal<Product> originalState = new ThreadLocal<>();
@PrePersist
public void prePersist(Product product) {
// 新建產(chǎn)品前的操作
}
@PostPersist
public void postPersist(Product product) {
// 記錄產(chǎn)品創(chuàng)建日志
AuditLog log = new AuditLog();
log.setEntityType("Product");
log.setEntityId(product.getId().toString());
log.setAction("CREATE");
log.setTimestamp(LocalDateTime.now());
log.setDetails("Created product: " + product.getName());
auditLogRepository.save(log);
}
@PreUpdate
public void preUpdate(Product product) {
// 保存產(chǎn)品原始狀態(tài)
Product original = new Product();
// 復(fù)制product的屬性到original
originalState.set(original);
}
@PostUpdate
public void postUpdate(Product product) {
// 獲取原始狀態(tài)
Product original = originalState.get();
// 記錄產(chǎn)品更新日志
AuditLog log = new AuditLog();
log.setEntityType("Product");
log.setEntityId(product.getId().toString());
log.setAction("UPDATE");
log.setTimestamp(LocalDateTime.now());
// 構(gòu)建變更信息
StringBuilder changes = new StringBuilder();
if (!product.getName().equals(original.getName())) {
changes.append("Name changed from '")
.append(original.getName())
.append("' to '")
.append(product.getName())
.append("'. ");
}
// 其他字段變更檢查...
log.setDetails(changes.toString());
auditLogRepository.save(log);
// 清理ThreadLocal
originalState.remove();
}
}
要啟用這個監(jiān)聽器,需要在Product實體上注冊:
@Entity
@Table(name = "tb_product")
@EntityListeners({AuditingEntityListener.class, ProductAuditListener.class})
public class Product extends AuditableEntity {
// 實體內(nèi)容
}
5.3 多租戶審計
在多租戶系統(tǒng)中,結(jié)合審計功能實現(xiàn)租戶數(shù)據(jù)隔離:
package com.example.entity;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
/**
* 多租戶審計基類
*/
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class TenantAuditableEntity {
@Column(name = "tenant_id", nullable = false, updatable = false)
private String tenantId;
@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;
@CreatedBy
@Column(name = "created_by", updatable = false)
private String createdBy;
@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;
// Getter和Setter方法省略
}
使用多租戶審計實體:
package com.example.entity;
import javax.persistence.*;
/**
* 客戶實體類
*/
@Entity
@Table(name = "tb_customer")
public class Customer extends TenantAuditableEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Column(name = "phone")
private String phone;
// Getter和Setter方法省略
}
六、高級技巧
Spring Data JPA審計功能還有一些高級用法,可以滿足更復(fù)雜的審計需求。
6.1 條件審計
有時候我們只希望在特定條件下進行審計??梢酝ㄟ^自定義實體監(jiān)聽器實現(xiàn):
package com.example.listener;
import com.example.entity.User;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
/**
* 條件審計監(jiān)聽器
*/
public class ConditionalAuditListener {
@PrePersist
public void touchForCreate(Object target) {
// 只對激活狀態(tài)的用戶進行審計
if (target instanceof User) {
User user = (User) target;
if (user.isActive()) {
setCreatedDate(user);
}
}
}
@PreUpdate
public void touchForUpdate(Object target) {
// 只對激活狀態(tài)的用戶進行審計
if (target instanceof User) {
User user = (User) target;
if (user.isActive()) {
setLastModifiedDate(user);
}
}
}
private void setCreatedDate(Object target) {
setFieldValue(target, CreatedDate.class, LocalDateTime.now());
}
private void setLastModifiedDate(Object target) {
setFieldValue(target, LastModifiedDate.class, LocalDateTime.now());
}
private void setFieldValue(Object target, Class annotation, Object value) {
try {
for (Field field : target.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(annotation)) {
field.setAccessible(true);
field.set(target, value);
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to set auditing field", e);
}
}
}
6.2 自定義審計注解
可以創(chuàng)建自定義審計注解,實現(xiàn)更靈活的審計邏輯:
package com.example.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義審計注解:記錄字段的歷史值
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TrackChanges {
String value() default "";
}
然后實現(xiàn)對應(yīng)的監(jiān)聽器處理這個注解:
package com.example.listener;
import com.example.annotation.TrackChanges;
import com.example.entity.FieldChangeLog;
import com.example.repository.FieldChangeLogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.PreUpdate;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 字段變更跟蹤監(jiān)聽器
*/
public class FieldChangeTrackingListener {
@Autowired
private FieldChangeLogRepository changeLogRepository;
@PreUpdate
public void preUpdate(Object entity) {
try {
// 獲取實體ID
Long entityId = getEntityId(entity);
String entityType = entity.getClass().getSimpleName();
// 查找標(biāo)記了@TrackChanges的字段
for (Field field : entity.getClass().getDeclaredFields()) {
TrackChanges annotation = field.getAnnotation(TrackChanges.class);
if (annotation != null) {
field.setAccessible(true);
// 獲取字段新值
Object newValue = field.get(entity);
// 從數(shù)據(jù)庫獲取原始實體和字段舊值
Object originalEntity = loadOriginalEntity(entityId, entity.getClass());
field.setAccessible(true);
Object oldValue = field.get(originalEntity);
// 如果值發(fā)生變化,記錄日志
if (!Objects.equals(oldValue, newValue)) {
FieldChangeLog changeLog = new FieldChangeLog();
changeLog.setEntityType(entityType);
changeLog.setEntityId(entityId);
changeLog.setFieldName(field.getName());
changeLog.setOldValue(oldValue != null ? oldValue.toString() : null);
changeLog.setNewValue(newValue != null ? newValue.toString() : null);
changeLog.setChangedAt(LocalDateTime.now());
changeLogRepository.save(changeLog);
}
}
}
} catch (Exception e) {
throw new RuntimeException("Failed to track field changes", e);
}
}
private Long getEntityId(Object entity) throws Exception {
// 獲取實體ID的邏輯
// ...
return null;
}
private Object loadOriginalEntity(Long entityId, Class<?> entityClass) {
// 從數(shù)據(jù)庫加載原始實體的邏輯
// ...
return null;
}
}
總結(jié)
Spring Data JPA的審計功能提供了一種強大而靈活的機制,用于自動跟蹤實體的創(chuàng)建和修改信息。通過使用@CreatedDate和@LastModifiedDate注解,開發(fā)者可以輕松地實現(xiàn)時間審計;結(jié)合@CreatedBy和@LastModifiedBy注解以及AuditorAware接口,還可以實現(xiàn)用戶審計。這些功能大大簡化了審計系統(tǒng)的開發(fā)工作,使開發(fā)者能夠?qū)W⒂跇I(yè)務(wù)邏輯的實現(xiàn)。
在實際應(yīng)用中,審計功能可以與版本控制、詳細日志記錄和多租戶系統(tǒng)等場景結(jié)合,滿足不同的業(yè)務(wù)需求。通過自定義實體監(jiān)聽器和審計注解,還可以實現(xiàn)更加復(fù)雜和靈活的審計邏輯??傊?,Spring Data JPA的審計功能是構(gòu)建健壯企業(yè)級應(yīng)用的重要組成部分,對于提高系統(tǒng)的可追溯性和安全性具有重要意義。
到此這篇關(guān)于SpringData JPA審計功能(@CreatedDate與@LastModifiedDate)實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringData JPA審計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程利用socket多線程訪問服務(wù)器文件代碼示例
這篇文章主要介紹了Java編程利用socket多線程訪問服務(wù)器文件代碼示例,具有一定參考價值,需要的朋友可以了解下。2017-10-10
從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細教程
這篇文章主要介紹了從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細教程,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
nacos配置中心遠程調(diào)用讀取不到配置文件的解決
這篇文章主要介紹了nacos配置中心遠程調(diào)用讀取不到配置文件的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教。2022-01-01
一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J
本文主要介紹了一文了解Java Log框架徹底搞懂Log4J,Log4J2,LogBack,SLF4J,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
解決IDEA使用springBoot創(chuàng)建項目,lombok標(biāo)注實體類后編譯無報錯,但是運行時報錯問題
文章詳細描述了在使用lombok的@Data注解標(biāo)注實體類時遇到編譯無誤但運行時報錯的問題,分析了可能的原因,并提供了解決步驟2025-01-01
Java網(wǎng)絡(luò)編程之URL+URLconnection使用方法示例
這篇文章主要介紹了Java網(wǎng)絡(luò)編程之URL+URLconnection使用方法示例,還是比較不錯的,這里分享給大家,供需要的朋友參考。2017-11-11
基于Java HttpClient和Htmlparser實現(xiàn)網(wǎng)絡(luò)爬蟲代碼
這篇文章主要介紹了基于Java HttpClient和Htmlparser實現(xiàn)網(wǎng)絡(luò)爬蟲代碼的相關(guān)資料,需要的朋友可以參考下2015-12-12
SpringCloud Zuul和Gateway的實例代碼(搭建方式)
本文主要介紹了SpringCloudZuul和SpringCloudGateway的簡單示例,SpringCloudGateway是推薦使用的API網(wǎng)關(guān)解決方案,基于SpringFramework5和ProjectReactor構(gòu)建,具有更高的性能和吞吐量2025-02-02
使用JSCH框架通過跳轉(zhuǎn)機訪問其他節(jié)點的方法
下面小編就為大家分享一篇使用JSCH框架通過跳轉(zhuǎn)機訪問其他節(jié)點的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12

