最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot Java循環(huán)依賴的解決方案

 更新時(shí)間:2025年12月29日 10:42:25   作者:WizLC  
本文詳細(xì)介紹了循環(huán)依賴的概念、常見場(chǎng)景以及多種解決方法,包括SpringApplicationEvent事件驅(qū)動(dòng)、@Lazy延遲加載、AOP切面和ObjectProvider等,同時(shí),還提供了解決循環(huán)依賴的標(biāo)準(zhǔn)回答技巧和預(yù)防措施,感興趣的朋跟隨小編一起看看吧

循環(huán)依賴詳解

一、什么是循環(huán)依賴

1.1 定義

循環(huán)依賴(Circular Dependency)指的是兩個(gè)或多個(gè)模塊之間相互引用,形成閉環(huán)的情況。簡(jiǎn)單來說,就是A依賴B,B又依賴A,或者A→B→C→A這樣的依賴鏈。

1.2 為什么會(huì)產(chǎn)生

循環(huán)依賴通常在以下情況下產(chǎn)生:

  • 架構(gòu)設(shè)計(jì)不當(dāng):模塊劃分不夠清晰,職責(zé)邊界模糊
  • 功能耦合過緊:相關(guān)功能被錯(cuò)誤地分散在不同模塊中
  • 缺乏統(tǒng)一接口:模塊間直接依賴具體實(shí)現(xiàn)而非抽象接口

1.3 危害

循環(huán)依賴會(huì)帶來以下幾個(gè)嚴(yán)重問題:

  • 編譯錯(cuò)誤:在某些語(yǔ)言中(如C++),循環(huán)依賴會(huì)導(dǎo)致編譯失敗
  • 運(yùn)行時(shí)錯(cuò)誤:模塊初始化順序不確定,可能導(dǎo)致null pointer異常
  • 維護(hù)困難:修改一個(gè)模塊可能影響多個(gè)其他模塊
  • 測(cè)試復(fù)雜:?jiǎn)卧獪y(cè)試難以獨(dú)立進(jìn)行

二、循環(huán)依賴的場(chǎng)景

2.1 后端開發(fā)中的循環(huán)依賴

以下代碼展示了一個(gè)典型的Spring Boot項(xiàng)目中用戶服務(wù)和訂單服務(wù)的循環(huán)依賴場(chǎng)景:

// UserService.java - 用戶服務(wù)實(shí)現(xiàn)
@Service
public class UserService {
    @Autowired
    private OrderService orderService; // 直接注入OrderService
    public void updateUser(Long userId, UserUpdateDTO userData) {
        // 更新用戶信息
        System.out.println("更新用戶信息: " + userId);
        // 當(dāng)用戶VIP等級(jí)變化時(shí),需要更新相關(guān)訂單的優(yōu)惠策略
        if (userData.getVipLevel() != null) {
            orderService.updateOrdersForUser(userId, userData.getVipLevel());
        }
    }
    public User getUserById(Long userId) {
        // 實(shí)際的用戶查詢邏輯
        return new User(userId, "張三", "VIP1");
    }
}
// OrderService.java - 訂單服務(wù)實(shí)現(xiàn)
@Service  
public class OrderService {
    @Autowired
    private UserService userService; // 直接注入U(xiǎn)serService
    public void updateOrdersForUser(Long userId, String vipLevel) {
        // 更新訂單邏輯
        System.out.println("更新用戶訂單,新的VIP等級(jí): " + vipLevel);
        // 訂單總額變化可能影響用戶等級(jí)
        User user = userService.getUserById(userId);
        BigDecimal totalAmount = calculateTotalOrderAmount(userId);
        String newLevel = calculateVipLevel(totalAmount);
        if (!newLevel.equals(user.getVipLevel())) {
            user.setVipLevel(newLevel);
            userService.updateUser(userId, UserUpdateDTO.builder().vipLevel(newLevel).build());
        }
    }
    private BigDecimal calculateTotalOrderAmount(Long userId) {
        // 計(jì)算用戶總訂單金額
        return new BigDecimal("5000.00");
    }
    private String calculateVipLevel(BigDecimal amount) {
        return amount.compareTo(new BigDecimal("10000")) > 0 ? "VIP2" : "VIP1";
    }
}

場(chǎng)景說明:這是一個(gè)真實(shí)的電商系統(tǒng)案例。在我之前負(fù)責(zé)的微服務(wù)電商項(xiàng)目中,用戶VIP等級(jí)會(huì)影響訂單優(yōu)惠,而訂單總額又會(huì)影響用戶VIP等級(jí)。這種雙向依賴在Spring容器啟動(dòng)時(shí)就會(huì)報(bào)錯(cuò),提示"Bean currently in creation"。

2.2 Spring MVC中的循環(huán)依賴

// UserController.java - 用戶控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private OrderController orderController; // 錯(cuò)誤的做法:控制器間相互依賴
    @PutMapping("/{userId}")
    public ResponseEntity<String> updateUser(@PathVariable Long userId, @RequestBody UserUpdateDTO dto) {
        userService.updateUser(userId, dto);
        // 更新用戶后,觸發(fā)相關(guān)訂單的重新計(jì)算
        orderController.recalculateUserOrders(userId);
        return ResponseEntity.ok("用戶更新成功");
    }
}
// OrderController.java - 訂單控制器
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private UserController userController; // 錯(cuò)誤的做法:控制器間相互依賴
    @PostMapping("/recalculate/{userId}")
    public ResponseEntity<String> recalculateUserOrders(@PathVariable Long userId) {
        orderService.recalculateOrdersForUser(userId);
        // 計(jì)算完成后,可能需要更新用戶統(tǒng)計(jì)信息
        userController.updateUserStatistics(userId);
        return ResponseEntity.ok("訂單重新計(jì)算完成");
    }
}

場(chǎng)景說明:這個(gè)案例來自我之前的一個(gè)ERP項(xiàng)目。當(dāng)時(shí)為了方便,直接在控制器層相互調(diào)用,結(jié)果造成了循環(huán)依賴。正確的做法是將共享邏輯提取到服務(wù)層處理。

2.3 Spring Data JPA中的循環(huán)依賴

// User.java - 用戶實(shí)體
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String email;
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders;
    @ManyToOne
    @JoinColumn(name = "managed_department_id")
    private Department managedDepartment;
    // getters and setters
}
// Order.java - 訂單實(shí)體
@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private BigDecimal amount;
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
    @OneToMany(mappedBy = "relatedOrder", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<OrderItem> items;
    // getters and setters
}
// Department.java - 部門實(shí)體
@Entity
@Table(name = "departments")
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "managedDepartment")
    private List<User> members;
    @OneToOne
    @JoinColumn(name = "manager_user_id")
    private User manager;
    // getters and setters
}

場(chǎng)景說明:在企業(yè)級(jí)應(yīng)用中,這種雙向關(guān)聯(lián)非常常見。用戶屬于部門,部門經(jīng)理又是一個(gè)用戶,形成一個(gè)循環(huán)。如果處理不當(dāng),序列化時(shí)就會(huì)出現(xiàn)棧溢出。我在一個(gè)HR管理系統(tǒng)中就遇到過這個(gè)問題。

三、循環(huán)依賴的解決方案

3.1 Spring ApplicationEvent事件驅(qū)動(dòng)解耦

以下代碼展示使用Spring ApplicationEvent解決循環(huán)依賴的方法:

// 領(lǐng)域事件定義
public class UserUpdatedEvent extends ApplicationEvent {
    private final Long userId;
    private final UserUpdateDTO userData;
    private final User oldUserData;
    public UserUpdatedEvent(Object source, Long userId, UserUpdateDTO userData, User oldUserData) {
        super(source);
        this.userId = userId;
        this.userData = userData;
        this.oldUserData = oldUserData;
    }
    // getters
}
// 訂單更新事件
public class OrderUpdatedEvent extends ApplicationEvent {
    private final Long userId;
    private final BigDecimal totalAmount;
    private final List<Order> updatedOrders;
    public OrderUpdatedEvent(Object source, Long userId, BigDecimal totalAmount, List<Order> updatedOrders) {
        super(source);
        this.userId = userId;
        this.totalAmount = totalAmount;
        this.updatedOrders = updatedOrders;
    }
    // getters
}
// UserServiceImpl.java - 使用事件驅(qū)動(dòng)解耦
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private UserRepository userRepository;
    @Override
    @Transactional
    public void updateUser(Long userId, UserUpdateDTO userData) {
        // 獲取舊數(shù)據(jù)
        User oldUser = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
        // 更新用戶信息
        User updatedUser = updateUserEntity(oldUser, userData);
        userRepository.save(updatedUser);
        System.out.println("更新用戶信息: " + userId);
        // 發(fā)布用戶更新事件,而不是直接調(diào)用OrderService
        UserUpdatedEvent event = new UserUpdatedEvent(this, userId, userData, oldUser);
        eventPublisher.publishEvent(event);
    }
    @Override
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
    }
    private User updateUserEntity(User user, UserUpdateDTO dto) {
        if (dto.getUsername() != null) {
            user.setUsername(dto.getUsername());
        }
        if (dto.getVipLevel() != null) {
            user.setVipLevel(dto.getVipLevel());
        }
        return user;
    }
}
// OrderServiceImpl.java - 使用事件驅(qū)動(dòng)解耦
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private OrderRepository orderRepository;
    @Override
    @Transactional
    public void updateOrdersForUser(Long userId, String vipLevel) {
        // 更新訂單邏輯
        List<Order> orders = orderRepository.findByUserId(userId);
        // 根據(jù)新的VIP等級(jí)更新訂單優(yōu)惠
        for (Order order : orders) {
            order.setDiscountRate(calculateDiscountRate(vipLevel));
        }
        orderRepository.saveAll(orders);
        // 計(jì)算用戶總訂單金額
        BigDecimal totalAmount = orders.stream()
            .map(Order::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println("更新用戶訂單,總金額: " + totalAmount);
        // 發(fā)布訂單更新事件
        OrderUpdatedEvent event = new OrderUpdatedEvent(this, userId, totalAmount, orders);
        eventPublisher.publishEvent(event);
    }
    private BigDecimal calculateDiscountRate(String vipLevel) {
        switch (vipLevel) {
            case "VIP1": return new BigDecimal("0.95");
            case "VIP2": return new BigDecimal("0.90");
            case "VIP3": return new BigDecimal("0.85");
            default: return BigDecimal.ONE;
        }
    }
}
// 事件監(jiān)聽器 - 處理跨模塊的業(yè)務(wù)邏輯
@Component
public class OrderEventListener {
    @Autowired
    private OrderService orderService;
    @EventListener
    @Async("taskExecutor") // 異步處理,避免阻塞主流程
    public void handleUserUpdated(UserUpdatedEvent event) {
        System.out.println("監(jiān)聽到用戶更新事件,用戶ID: " + event.getUserId());
        // 根據(jù)用戶更新調(diào)整相關(guān)訂單
        if (event.getUserData().getVipLevel() != null) {
            orderService.updateOrdersForUser(event.getUserId(), event.getUserData().getVipLevel());
        }
    }
}
@Component
public class UserEventListener {
    @Autowired
    private UserService userService;
    @EventListener
    @Async("taskExecutor")
    public void handleOrderUpdated(OrderUpdatedEvent event) {
        System.out.println("監(jiān)聽到訂單更新事件,用戶ID: " + event.getUserId());
        // 根據(jù)訂單總金額調(diào)整用戶等級(jí)
        String newVipLevel = calculateVipLevel(event.getTotalAmount());
        User currentUser = userService.getUserById(event.getUserId());
        if (!newVipLevel.equals(currentUser.getVipLevel())) {
            UserUpdateDTO updateDTO = UserUpdateDTO.builder()
                .vipLevel(newVipLevel)
                .build();
            userService.updateUser(event.getUserId(), updateDTO);
        }
    }
    private String calculateVipLevel(BigDecimal totalAmount) {
        if (totalAmount.compareTo(new BigDecimal("50000")) > 0) return "VIP3";
        if (totalAmount.compareTo(new BigDecimal("20000")) > 0) return "VIP2";
        if (totalAmount.compareTo(new BigDecimal("5000")) > 0) return "VIP1";
        return "普通用戶";
    }
}
// 異步配置
@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean("taskExecutor")
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

實(shí)戰(zhàn)說明:在我之前的社交電商項(xiàng)目中,用戶系統(tǒng)和商品系統(tǒng)就通過Spring事件驅(qū)動(dòng)機(jī)制解耦。用戶發(fā)布動(dòng)態(tài)時(shí),商品系統(tǒng)監(jiān)聽事件更新推薦算法;商品價(jià)格變動(dòng)時(shí),用戶系統(tǒng)監(jiān)聽事件更新購(gòu)物車。這種方式不僅解決了循環(huán)依賴,還提高了系統(tǒng)的響應(yīng)速度和可擴(kuò)展性。

3.2 Spring @Lazy延遲加載和接口抽象

以下代碼展示使用Spring的@Lazy注解和接口抽象解決循環(huán)依賴:

// 定義服務(wù)接口
public interface UserService {
    void updateUser(Long userId, UserUpdateDTO userData);
    User getUserById(Long userId);
    String getUserVipLevel(Long userId);
}
public interface OrderService {
    void updateOrdersForUser(Long userId, String vipLevel);
    BigDecimal getTotalOrderAmount(Long userId);
    List<Order> getOrdersByUserId(Long userId);
}
// UserServiceImpl.java - 使用@Lazy延遲加載
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    @Lazy // 延遲注入,避免循環(huán)依賴
    private OrderService orderService;
    @Autowired
    private UserRepository userRepository;
    @Override
    @Transactional
    public void updateUser(Long userId, UserUpdateDTO userData) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
        System.out.println("更新用戶信息: " + userId);
        // 更新用戶實(shí)體
        if (userData.getUsername() != null) {
            user.setUsername(userData.getUsername());
        }
        if (userData.getVipLevel() != null) {
            user.setVipLevel(userData.getVipLevel());
            // VIP等級(jí)變化時(shí),延遲調(diào)用訂單服務(wù)
            // orderService此時(shí)可能還未完全初始化,但@Lazy確保了延遲加載
            orderService.updateOrdersForUser(userId, userData.getVipLevel());
        }
        userRepository.save(user);
    }
    @Override
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
    }
    @Override
    public String getUserVipLevel(Long userId) {
        User user = getUserById(userId);
        return user.getVipLevel();
    }
}
// OrderServiceImpl.java - 同樣使用@Lazy延遲加載
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    @Lazy // 延遲注入
    private UserService userService;
    @Autowired
    private OrderRepository orderRepository;
    @Override
    @Transactional
    public void updateOrdersForUser(Long userId, String vipLevel) {
        List<Order> orders = orderRepository.findByUserId(userId);
        System.out.println("更新用戶訂單,新的VIP等級(jí): " + vipLevel);
        // 更新訂單的折扣率
        for (Order order : orders) {
            BigDecimal discount = calculateDiscountByVipLevel(vipLevel);
            order.setDiscountRate(discount);
            order.setFinalAmount(order.getAmount().multiply(discount));
        }
        orderRepository.saveAll(orders);
        // 檢查是否需要更新用戶等級(jí)
        BigDecimal totalAmount = getTotalOrderAmount(userId);
        String recommendedVipLevel = calculateVipLevelByAmount(totalAmount);
        // 延遲調(diào)用用戶服務(wù)
        if (!recommendedVipLevel.equals(vipLevel)) {
            UserUpdateDTO updateDTO = UserUpdateDTO.builder()
                .vipLevel(recommendedVipLevel)
                .build();
            userService.updateUser(userId, updateDTO);
        }
    }
    @Override
    public BigDecimal getTotalOrderAmount(Long userId) {
        List<Order> orders = orderRepository.findByUserId(userId);
        return orders.stream()
            .map(Order::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Override
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    private BigDecimal calculateDiscountByVipLevel(String vipLevel) {
        switch (vipLevel) {
            case "VIP1": return new BigDecimal("0.95");
            case "VIP2": return new BigDecimal("0.90");
            case "VIP3": return new BigDecimal("0.85");
            default: return BigDecimal.ONE;
        }
    }
    private String calculateVipLevelByAmount(BigDecimal amount) {
        if (amount.compareTo(new BigDecimal("100000")) > 0) return "VIP3";
        if (amount.compareTo(new BigDecimal("50000")) > 0) return "VIP2";
        if (amount.compareTo(new BigDecimal("10000")) > 0) return "VIP1";
        return "普通用戶";
    }
}
// 另一種方案:使用中間服務(wù)協(xié)調(diào)
@Service
public class UserOrderCoordinationService {
    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;
    /**
     * 協(xié)調(diào)用戶和訂單的更新邏輯
     * 避免兩個(gè)服務(wù)直接相互調(diào)用
     */
    @Transactional
    public void coordinateUserAndOrderUpdate(Long userId, UserUpdateDTO userData) {
        // 1. 先更新用戶信息
        User user = userService.getUserById(userId);
        String oldVipLevel = user.getVipLevel();
        if (userData.getVipLevel() != null) {
            userService.updateUser(userId, userData);
        }
        // 2. 根據(jù)用戶更新訂單
        if (userData.getVipLevel() != null && !userData.getVipLevel().equals(oldVipLevel)) {
            orderService.updateOrdersForUser(userId, userData.getVipLevel());
        }
        // 3. 檢查訂單總額是否需要調(diào)整用戶等級(jí)
        BigDecimal totalAmount = orderService.getTotalOrderAmount(userId);
        String recommendedLevel = calculateVipLevel(totalAmount);
        String currentLevel = userService.getUserVipLevel(userId);
        if (!recommendedLevel.equals(currentLevel)) {
            UserUpdateDTO newUserData = UserUpdateDTO.builder()
                .vipLevel(recommendedLevel)
                .build();
            userService.updateUser(userId, newUserData);
        }
    }
    private String calculateVipLevel(BigDecimal amount) {
        if (amount.compareTo(new BigDecimal("50000")) > 0) return "VIP2";
        if (amount.compareTo(new BigDecimal("10000")) > 0) return "VIP1";
        return "普通用戶";
    }
}
// 控制器使用協(xié)調(diào)服務(wù)
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserOrderCoordinationService coordinationService;
    @PutMapping("/{userId}")
    public ResponseEntity<String> updateUser(@PathVariable Long userId, 
                                           @RequestBody @Valid UserUpdateDTO userData) {
        coordinationService.coordinateUserAndOrderUpdate(userId, userData);
        return ResponseEntity.ok("用戶及關(guān)聯(lián)訂單更新成功");
    }
}

實(shí)戰(zhàn)說明:在金融項(xiàng)目中,我使用過接口隔離的原則解決支付模塊和賬戶模塊的循環(huán)依賴。支付時(shí)需要驗(yàn)證賬戶,賬戶變動(dòng)時(shí)又要觸發(fā)支付回調(diào)。通過引入IPaymentGateway接口和IAccountService接口,并使用Spring的@Lazy注解延遲加載,成功解決了這個(gè)問題。@Lazy注解是Spring提供的強(qiáng)大工具,它可以讓Bean在第一次使用時(shí)才被初始化,從而避免循環(huán)依賴問題。

3.3 Spring AOP切面和協(xié)調(diào)者模式

以下代碼展示使用Spring AOP和協(xié)調(diào)者模式解決循環(huán)依賴:

// 定義協(xié)調(diào)者接口
public interface BusinessProcessCoordinator {
    void processUserUpdate(Long userId, UserUpdateDTO userData);
    void processOrderUpdate(Long userId, OrderUpdateDTO orderData);
}
// 實(shí)現(xiàn)協(xié)調(diào)者 - 統(tǒng)一處理跨服務(wù)業(yè)務(wù)邏輯
@Service
public class UserOrderCoordinator implements BusinessProcessCoordinator {
    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private NotificationService notificationService;
    /**
     * 協(xié)調(diào)用戶更新的完整流程
     * 避免服務(wù)間的直接相互調(diào)用
     */
    @Override
    @Transactional
    public void processUserUpdate(Long userId, UserUpdateDTO userData) {
        // 1. 獲取當(dāng)前用戶狀態(tài)
        User currentUser = userService.getUserById(userId);
        String oldVipLevel = currentUser.getVipLevel();
        // 2. 更新用戶信息
        userService.updateUser(userId, userData);
        // 3. 如果VIP等級(jí)變化,處理相關(guān)訂單
        if (userData.getVipLevel() != null && !userData.getVipLevel().equals(oldVipLevel)) {
            List<Order> affectedOrders = orderService.getOrdersByUserId(userId);
            for (Order order : affectedOrders) {
                orderService.updateOrderDiscount(order.getId(), userData.getVipLevel());
            }
        }
        // 4. 發(fā)送通知(通過協(xié)調(diào)者調(diào)用,避免循環(huán)依賴)
        if (userData.getVipLevel() != null && !userData.getVipLevel().equals(oldVipLevel)) {
            notificationService.sendVipLevelChangeNotification(userId, oldVipLevel, userData.getVipLevel());
        }
    }
    @Override
    @Transactional
    public void processOrderUpdate(Long userId, OrderUpdateDTO orderData) {
        // 1. 處理訂單更新
        Order updatedOrder = orderService.updateOrder(userId, orderData);
        // 2. 重新計(jì)算用戶總消費(fèi)金額
        BigDecimal totalAmount = orderService.getTotalOrderAmount(userId);
        // 3. 根據(jù)總消費(fèi)金額調(diào)整用戶等級(jí)
        String newVipLevel = calculateVipLevel(totalAmount);
        User currentUser = userService.getUserById(userId);
        if (!newVipLevel.equals(currentUser.getVipLevel())) {
            UserUpdateDTO userUpdate = UserUpdateDTO.builder()
                .vipLevel(newVipLevel)
                .build();
            userService.updateUser(userId, userUpdate);
            // 4. 發(fā)送等級(jí)提升通知
            notificationService.sendVipLevelUpgradeNotification(userId, currentUser.getVipLevel(), newVipLevel);
        }
    }
    private String calculateVipLevel(BigDecimal totalAmount) {
        if (totalAmount.compareTo(new BigDecimal("100000")) > 0) return "VIP3";
        if (totalAmount.compareTo(new BigDecimal("50000")) > 0) return "VIP2";
        if (totalAmount.compareTo(new BigDecimal("10000")) > 0) return "VIP1";
        return "普通用戶";
    }
}
// 使用AOP切面記錄審計(jì)日志,避免在業(yè)務(wù)服務(wù)中直接調(diào)用審計(jì)服務(wù)
@Aspect
@Component
public class BusinessAuditAspect {
    @Autowired
    private AuditService auditService;
    // 用戶操作審計(jì)切面
    @Around("execution(* com.example.service.UserService.updateUser(..))")
    public Object auditUserOperation(ProceedingJoinPoint joinPoint) throws Throwable {
        Long userId = (Long) joinPoint.getArgs()[0];
        UserUpdateDTO userData = (UserUpdateDTO) joinPoint.getArgs()[1];
        // 記錄操作前狀態(tài)
        User oldUser = getUserById(userId);
        try {
            // 執(zhí)行原方法
            Object result = joinPoint.proceed();
            // 記錄審計(jì)日志(通過切面,避免循環(huán)依賴)
            auditService.logUserUpdate(userId, oldUser, userData, "SUCCESS");
            return result;
        } catch (Exception e) {
            // 記錄失敗日志
            auditService.logUserUpdate(userId, oldUser, userData, "FAILED: " + e.getMessage());
            throw e;
        }
    }
    // 訂單操作審計(jì)切面
    @Around("execution(* com.example.service.OrderService.updateOrdersForUser(..))")
    public Object auditOrderOperation(ProceedingJoinPoint joinPoint) throws Throwable {
        Long userId = (Long) joinPoint.getArgs()[0];
        String vipLevel = (String) joinPoint.getArgs()[1];
        // 記錄操作前狀態(tài)
        List<Order> oldOrders = getOrdersByUserId(userId);
        try {
            Object result = joinPoint.proceed();
            // 記錄審計(jì)日志
            auditService.logOrderUpdate(userId, oldOrders, vipLevel, "SUCCESS");
            return result;
        } catch (Exception e) {
            auditService.logOrderUpdate(userId, oldOrders, vipLevel, "FAILED: " + e.getMessage());
            throw e;
        }
    }
    // 輔助方法(實(shí)際項(xiàng)目中應(yīng)注入相應(yīng)服務(wù))
    private User getUserById(Long userId) {
        // 模擬實(shí)現(xiàn)
        return new User(userId, "用戶名", "VIP1");
    }
    private List<Order> getOrdersByUserId(Long userId) {
        // 模擬實(shí)現(xiàn)
        return Arrays.asList(new Order(1L, userId, new BigDecimal("1000")));
    }
}
// 重構(gòu)后的UserService - 只關(guān)注用戶相關(guān)業(yè)務(wù)邏輯
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepository userRepository;
    // 不再直接依賴OrderService,避免循環(huán)依賴
    @Override
    @Transactional
    public void updateUser(Long userId, UserUpdateDTO userData) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
        System.out.println("更新用戶信息: " + userId);
        // 只處理用戶相關(guān)的業(yè)務(wù)邏輯
        if (userData.getUsername() != null) {
            user.setUsername(userData.getUsername());
        }
        if (userData.getEmail() != null) {
            user.setEmail(userData.getEmail());
        }
        if (userData.getVipLevel() != null) {
            user.setVipLevel(userData.getVipLevel());
        }
        userRepository.save(user);
    }
    @Override
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
    }
    @Override
    public String getUserVipLevel(Long userId) {
        return getUserById(userId).getVipLevel();
    }
}
// 重構(gòu)后的OrderService - 只關(guān)注訂單相關(guān)業(yè)務(wù)邏輯
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderRepository orderRepository;
    // 不再直接依賴UserService,避免循環(huán)依賴
    @Override
    @Transactional
    public void updateOrdersForUser(Long userId, String vipLevel) {
        List<Order> orders = orderRepository.findByUserId(userId);
        System.out.println("更新用戶訂單折扣,VIP等級(jí): " + vipLevel);
        // 只處理訂單相關(guān)的業(yè)務(wù)邏輯
        for (Order order : orders) {
            BigDecimal discount = calculateDiscountByVipLevel(vipLevel);
            order.setDiscountRate(discount);
            order.setFinalAmount(order.getAmount().multiply(discount));
        }
        orderRepository.saveAll(orders);
    }
    @Override
    public BigDecimal getTotalOrderAmount(Long userId) {
        List<Order> orders = orderRepository.findByUserId(userId);
        return orders.stream()
            .map(Order::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Override
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    private BigDecimal calculateDiscountByVipLevel(String vipLevel) {
        switch (vipLevel) {
            case "VIP1": return new BigDecimal("0.95");
            case "VIP2": return new BigDecimal("0.90");
            case "VIP3": return new BigDecimal("0.85");
            default: return BigDecimal.ONE;
        }
    }
    public Order updateOrder(Long userId, OrderUpdateDTO orderData) {
        // 訂單更新邏輯
        return new Order();
    }
    public void updateOrderDiscount(Long orderId, String vipLevel) {
        // 更新單個(gè)訂單折扣的邏輯
    }
}
// 控制器使用協(xié)調(diào)者而不是直接調(diào)用服務(wù)
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private BusinessProcessCoordinator coordinator;
    @PutMapping("/{userId}")
    public ResponseEntity<String> updateUser(@PathVariable Long userId, 
                                           @RequestBody @Valid UserUpdateDTO userData) {
        // 通過協(xié)調(diào)者處理完整的業(yè)務(wù)流程
        coordinator.processUserUpdate(userId, userData);
        return ResponseEntity.ok("用戶更新成功");
    }
}
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    @Autowired
    private BusinessProcessCoordinator coordinator;
    @PutMapping("/user/{userId}")
    public ResponseEntity<String> updateOrdersForUser(@PathVariable Long userId, 
                                                      @RequestBody @Valid OrderUpdateDTO orderData) {
        // 通過協(xié)調(diào)者處理完整的業(yè)務(wù)流程
        coordinator.processOrderUpdate(userId, orderData);
        return ResponseEntity.ok("訂單更新成功");
    }
}

實(shí)戰(zhàn)說明:這個(gè)協(xié)調(diào)者模式是我在一個(gè)大型ERP項(xiàng)目中應(yīng)用的。原來的設(shè)計(jì)是用戶服務(wù)、訂單服務(wù)、通知服務(wù)、審計(jì)服務(wù)互相依賴,系統(tǒng)維護(hù)非常困難。重構(gòu)后引入?yún)f(xié)調(diào)者統(tǒng)一管理跨服務(wù)業(yè)務(wù)流程,使用AOP處理橫切關(guān)注點(diǎn)(如審計(jì)、日志),徹底消除了循環(huán)依賴問題。各服務(wù)職責(zé)單一,測(cè)試和維護(hù)變得非常簡(jiǎn)單。

3.4 Spring代理模式和ObjectProvider

以下代碼展示使用Spring的ObjectProvider和代理模式解決循環(huán)依賴:

// 使用ObjectProvider解決循環(huán)依賴
@Service
public class UserServiceImpl implements UserService {
    private final ObjectProvider<OrderService> orderServiceProvider;
    private final UserRepository userRepository;
    // 通過構(gòu)造函數(shù)注入ObjectProvider而不是直接注入OrderService
    public UserServiceImpl(ObjectProvider<OrderService> orderServiceProvider,
                          UserRepository userRepository) {
        this.orderServiceProvider = orderServiceProvider;
        this.userRepository = userRepository;
    }
    @Override
    @Transactional
    public void updateUser(Long userId, UserUpdateDTO userData) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
        System.out.println("更新用戶信息: " + userId);
        // 更新用戶實(shí)體
        if (userData.getVipLevel() != null) {
            user.setVipLevel(userData.getVipLevel());
        }
        if (userData.getUsername() != null) {
            user.setUsername(userData.getUsername());
        }
        userRepository.save(user);
        // 需要時(shí)才獲取OrderService實(shí)例
        if (userData.getVipLevel() != null) {
            OrderService orderService = orderServiceProvider.getObject();
            orderService.updateOrdersForUser(userId, userData.getVipLevel());
        }
    }
    @Override
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
    }
    @Override
    public String getUserVipLevel(Long userId) {
        return getUserById(userId).getVipLevel();
    }
}
// OrderService也使用ObjectProvider
@Service
public class OrderServiceImpl implements OrderService {
    private final ObjectProvider<UserService> userServiceProvider;
    private final OrderRepository orderRepository;
    public OrderServiceImpl(ObjectProvider<UserService> userServiceProvider,
                           OrderRepository orderRepository) {
        this.userServiceProvider = userServiceProvider;
        this.orderRepository = orderRepository;
    }
    @Override
    @Transactional
    public void updateOrdersForUser(Long userId, String vipLevel) {
        List<Order> orders = orderRepository.findByUserId(userId);
        System.out.println("更新用戶訂單,VIP等級(jí): " + vipLevel);
        // 更新訂單折扣
        for (Order order : orders) {
            BigDecimal discount = calculateDiscountByVipLevel(vipLevel);
            order.setDiscountRate(discount);
            order.setFinalAmount(order.getAmount().multiply(discount));
        }
        orderRepository.saveAll(orders);
        // 檢查是否需要調(diào)整用戶等級(jí)
        BigDecimal totalAmount = getTotalOrderAmount(userId);
        String recommendedLevel = calculateVipLevel(totalAmount);
        // 延遲獲取UserService實(shí)例
        String currentLevel = userServiceProvider.getObject().getUserVipLevel(userId);
        if (!recommendedLevel.equals(currentLevel)) {
            UserService userService = userServiceProvider.getObject();
            UserUpdateDTO updateDTO = UserUpdateDTO.builder()
                .vipLevel(recommendedLevel)
                .build();
            userService.updateUser(userId, updateDTO);
        }
    }
    @Override
    public BigDecimal getTotalOrderAmount(Long userId) {
        List<Order> orders = orderRepository.findByUserId(userId);
        return orders.stream()
            .map(Order::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Override
    public List<Order> getOrdersByUserId(Long userId) {
        return orderRepository.findByUserId(userId);
    }
    private BigDecimal calculateDiscountByVipLevel(String vipLevel) {
        switch (vipLevel) {
            case "VIP1": return new BigDecimal("0.95");
            case "VIP2": return new BigDecimal("0.90");
            case "VIP3": return new BigDecimal("0.85");
            default: return BigDecimal.ONE;
        }
    }
    private String calculateVipLevel(BigDecimal amount) {
        if (amount.compareTo(new BigDecimal("100000")) > 0) return "VIP3";
        if (amount.compareTo(new BigDecimal("50000")) > 0) return "VIP2";
        if (amount.compareTo(new BigDecimal("10000")) > 0) return "VIP1";
        return "普通用戶";
    }
}
// 自定義代理工廠,提供更靈活的延遲加載
@Component
public class LazyServiceProxyFactory implements BeanFactoryAware {
    private BeanFactory beanFactory;
    public <T> T createLazyProxy(Class<T> serviceClass) {
        return (T) Proxy.newProxyInstance(
            serviceClass.getClassLoader(),
            new Class[]{serviceClass},
            new LazyServiceInvocationHandler<>(serviceClass, beanFactory)
        );
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    private static class LazyServiceInvocationHandler<T> implements InvocationHandler {
        private final Class<T> serviceClass;
        private final BeanFactory beanFactory;
        private volatile T target;
        public LazyServiceInvocationHandler(Class<T> serviceClass, BeanFactory beanFactory) {
            this.serviceClass = serviceClass;
            this.beanFactory = beanFactory;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (target == null) {
                synchronized (this) {
                    if (target == null) {
                        target = beanFactory.getBean(serviceClass);
                    }
                }
            }
            return method.invoke(target, args);
        }
    }
}
// 使用自定義代理的服務(wù)
@Service
public class UserServiceWithCustomProxy implements UserService {
    private final LazyServiceProxyFactory proxyFactory;
    private final OrderService orderService; // 使用代理對(duì)象
    private final UserRepository userRepository;
    public UserServiceWithCustomProxy(LazyServiceProxyFactory proxyFactory,
                                     UserRepository userRepository) {
        this.proxyFactory = proxyFactory;
        this.orderService = proxyFactory.createLazyProxy(OrderService.class);
        this.userRepository = userRepository;
    }
    @Override
    @Transactional
    public void updateUser(Long userId, UserUpdateDTO userData) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
        System.out.println("更新用戶信息: " + userId);
        // 更新用戶信息
        if (userData.getVipLevel() != null) {
            user.setVipLevel(userData.getVipLevel());
        }
        userRepository.save(user);
        // 代理對(duì)象會(huì)在第一次調(diào)用時(shí)才真正初始化OrderService
        if (userData.getVipLevel() != null) {
            orderService.updateOrdersForUser(userId, userData.getVipLevel());
        }
    }
    @Override
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用戶不存在"));
    }
    @Override
    public String getUserVipLevel(Long userId) {
        return getUserById(userId).getVipLevel();
    }
}

實(shí)戰(zhàn)說明:在我處理的一個(gè)微服務(wù)項(xiàng)目中,訂單服務(wù)和庫(kù)存服務(wù)存在嚴(yán)重的循環(huán)依賴問題。通過使用Spring的ObjectProvider和自定義代理模式,我們實(shí)現(xiàn)了真正的按需加載,服務(wù)只有在真正被調(diào)用時(shí)才會(huì)被初始化,徹底避免了啟動(dòng)時(shí)的循環(huán)依賴問題。這種方案特別適合于大型微服務(wù)架構(gòu),其中服務(wù)間的依賴關(guān)系復(fù)雜,但又必須保持解耦。

四、面試回答技巧

4.1 面試官提問時(shí)的標(biāo)準(zhǔn)回答結(jié)構(gòu)

面試官:你遇到過循環(huán)依賴嗎?是怎么解決的?

標(biāo)準(zhǔn)回答

"是的,我在實(shí)際開發(fā)中確實(shí)遇到過循環(huán)依賴問題。讓我從三個(gè)方面來回答:

第一,我遇到的循環(huán)依賴場(chǎng)景
在我之前負(fù)責(zé)的Spring Boot電商項(xiàng)目中,用戶服務(wù)(UserService)和訂單服務(wù)(OrderService)存在循環(huán)依賴。用戶VIP等級(jí)變化需要更新訂單優(yōu)惠策略,而訂單總額變化又要影響用戶VIP等級(jí)。這導(dǎo)致了Spring容器啟動(dòng)時(shí)出現(xiàn)’Bean currently in creation’的錯(cuò)誤。

第二,我的解決思路和方案
我采用了Spring ApplicationEvent事件驅(qū)動(dòng)架構(gòu)來解決這個(gè)問題的。具體做法是:

  1. 定義了UserUpdatedEvent和OrderUpdatedEvent領(lǐng)域事件
  2. 使用ApplicationEventPublisher發(fā)布事件,而不是直接調(diào)用其他服務(wù)
  3. 創(chuàng)建了對(duì)應(yīng)的事件監(jiān)聽器處理跨服務(wù)業(yè)務(wù)邏輯
  4. 配置了異步執(zhí)行器(@Async)提高系統(tǒng)性能

第三,解決后的效果

  • 消除了循環(huán)依賴,Spring容器可以正常啟動(dòng)
  • 提高了系統(tǒng)的可擴(kuò)展性,后續(xù)其他服務(wù)也可以訂閱這些事件
  • 降低了耦合度,各服務(wù)可以獨(dú)立測(cè)試和部署
  • 通過異步處理提升了系統(tǒng)的響應(yīng)速度"

4.2 體現(xiàn)技術(shù)深度的回答要點(diǎn)

1. 展現(xiàn)理論深度
“循環(huán)依賴本質(zhì)上是違反了依賴倒置原則,高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴抽象。在Spring生態(tài)中,我們可以通過多種技術(shù)手段來解決:@Lazy延遲加載、ApplicationEvent事件驅(qū)動(dòng)、ObjectProvider延遲注入、AOP切面處理、以及協(xié)調(diào)者模式等。關(guān)鍵是要理解Spring容器的Bean生命周期和依賴注入機(jī)制。”

2. 展現(xiàn)實(shí)踐經(jīng)驗(yàn)
"在Spring Boot項(xiàng)目中,我處理過幾種不同的循環(huán)依賴場(chǎng)景:

  • 服務(wù)層循環(huán)依賴:使用@Lazy注解或事件驅(qū)動(dòng)解決
  • 實(shí)體類雙向關(guān)聯(lián):使用@JsonIgnore避免序列化問題,或者使用DTO模式
  • 控制器層循環(huán)依賴:通過協(xié)調(diào)者模式重構(gòu),將業(yè)務(wù)邏輯下沉到服務(wù)層
  • 跨模塊循環(huán)依賴:引入領(lǐng)域事件和事件總線,實(shí)現(xiàn)模塊解耦

每種場(chǎng)景都有最適合的解決方案,關(guān)鍵是要根據(jù)具體的業(yè)務(wù)需求和技術(shù)架構(gòu)來選擇。"

3. 展現(xiàn)架構(gòu)思維
"預(yù)防循環(huán)依賴比解決更重要。在Spring項(xiàng)目設(shè)計(jì)階段,我會(huì):

  1. 遵循DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),按業(yè)務(wù)邊界劃分限界上下文
  2. 建立清晰的依賴關(guān)系圖,確保依賴方向是單向的
  3. 使用Spring Boot Actuator的beans端點(diǎn)監(jiān)控Bean依賴關(guān)系
  4. 在CI/CD流程中加入依賴關(guān)系檢查
  5. 團(tuán)隊(duì)代碼審查時(shí)重點(diǎn)關(guān)注模塊間依賴設(shè)計(jì)

我習(xí)慣用ArchUnit這樣的架構(gòu)測(cè)試工具來確保架構(gòu)規(guī)則的執(zhí)行。"

4.3 可能的追問及回答

追問:如果業(yè)務(wù)邏輯必須要有循環(huán)依賴怎么辦?

回答
"這種情況下,我會(huì)根據(jù)Spring框架的特性來處理:

  1. 重新審視業(yè)務(wù)流程:看看是否可以通過調(diào)整業(yè)務(wù)邏輯來避免,比如引入最終一致性概念
  2. 使用Spring技術(shù)解耦
    • @Lazy延遲加載解決服務(wù)層循環(huán)依賴
    • ApplicationEvent事件驅(qū)動(dòng)實(shí)現(xiàn)異步解耦
    • @TransactionalEventListener確保事務(wù)一致性
    • ObjectProvider實(shí)現(xiàn)延遲注入
  3. 協(xié)調(diào)者模式:引入BusinessProcessCoordinator統(tǒng)一管理復(fù)雜業(yè)務(wù)流程
  4. 分布式事務(wù)處理:如果是微服務(wù)場(chǎng)景,考慮使用Saga模式或TCC模式

關(guān)鍵是要將循環(huán)依賴的控制權(quán)收歸到一個(gè)協(xié)調(diào)者手中,讓各個(gè)Spring Bean都依賴這個(gè)協(xié)調(diào)者,而不是互相依賴。"

追問:如何預(yù)防循環(huán)依賴的產(chǎn)生?

回答
"我會(huì)在幾個(gè)方面著手,特別是在Spring Boot項(xiàng)目中:

  • 架構(gòu)設(shè)計(jì)階段
    • 使用DDD劃分限界上下文,確保依賴方向正確
    • 繪制Spring Bean依賴關(guān)系圖,確保沒有環(huán)形依賴
    • 定義清晰的分層架構(gòu):Controller → Service → Repository
  • 代碼規(guī)范階段
    • 建立團(tuán)隊(duì)代碼規(guī)范,禁止@Service層之間的直接依賴
    • 要求使用接口編程,面向抽象而非具體實(shí)現(xiàn)
  • 工具支持
    • 使用ArchUnit編寫架構(gòu)測(cè)試,自動(dòng)檢測(cè)循環(huán)依賴
    • 配置IDE插件(如SonarLint)實(shí)時(shí)檢查依賴關(guān)系
    • 使用Spring Boot Actuator的/beans端點(diǎn)監(jiān)控Bean依賴
  • 持續(xù)監(jiān)控
    • 在CI/CD流程中加入依賴關(guān)系檢查
    • 定期使用jdeps或類似工具分析項(xiàng)目依賴
    • 使用Maven/Gradle的依賴分析插件"

4.4 體現(xiàn)解決問題能力的回答模板

"遇到Spring循環(huán)依賴問題時(shí),我的解決思路是:分析問題根源 → 設(shè)計(jì)解決方案 → 實(shí)施改造 → 驗(yàn)證效果。

在之前的Spring Boot電商項(xiàng)目中,我不僅解決了用戶服務(wù)和訂單服務(wù)的循環(huán)依賴問題,還做了以下系統(tǒng)性改進(jìn):

  1. 建立Spring依賴管理規(guī)范:制定了@Service層依賴規(guī)范,要求使用接口編程和@Lazy注解
  2. 引入架構(gòu)測(cè)試:使用ArchUnit編寫自動(dòng)化測(cè)試,防止新的循環(huán)依賴產(chǎn)生
  3. 完善監(jiān)控體系:配置Spring Boot Actuator,定期檢查Bean依賴關(guān)系
  4. 團(tuán)隊(duì)培訓(xùn):組織Spring最佳實(shí)踐分享,提升團(tuán)隊(duì)整體架構(gòu)意識(shí)

這種系統(tǒng)性的問題解決思路,讓我在后續(xù)項(xiàng)目中能夠更好地規(guī)避技術(shù)債務(wù),確保Spring應(yīng)用的健康性和可維護(hù)性。"

總結(jié)

循環(huán)依賴是軟件開發(fā)中常見但嚴(yán)重的問題。通過合理的設(shè)計(jì)模式選擇、架構(gòu)重構(gòu)和技術(shù)手段應(yīng)用,我們不僅可以解決已有的循環(huán)依賴問題,更重要的是從源頭上預(yù)防這類問題的產(chǎn)生。在實(shí)際項(xiàng)目中,要根據(jù)具體的業(yè)務(wù)場(chǎng)景和技術(shù)棧選擇最適合的解決方案,并建立長(zhǎng)期的管理機(jī)制來維護(hù)代碼庫(kù)的健康度。

到此這篇關(guān)于Spring Boot Java循環(huán)依賴詳解的文章就介紹到這了,更多相關(guān)Spring Boot Java循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • shiro整合springboot前后端分離

    shiro整合springboot前后端分離

    這篇文章主要介紹了shiro整合springboot前后端分離,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • SpringBoot生成License的實(shí)現(xiàn)示例

    SpringBoot生成License的實(shí)現(xiàn)示例

    License指的是版權(quán)許可證,那么對(duì)于SpringBoot項(xiàng)目,如何增加License呢?本文就來介紹一下,感興趣的可以了解一下
    2021-06-06
  • java中讀寫Properties屬性文件公用方法詳解

    java中讀寫Properties屬性文件公用方法詳解

    在項(xiàng)目開發(fā)中我們會(huì)將很多環(huán)境特定的變量定義到一個(gè)配置文件中,比如properties文件,把數(shù)據(jù)庫(kù)的用戶名和密碼存放到此屬性文件中。下面這篇文章就主要介紹了java中讀寫Properties屬性文件公用方法,需要的朋友可以參考借鑒。
    2017-01-01
  • Java連接MySQL數(shù)據(jù)庫(kù)命令行程序過程

    Java連接MySQL數(shù)據(jù)庫(kù)命令行程序過程

    SQL編程包括兩種形式,一種是過程化編程,主要通過數(shù)據(jù)庫(kù)交互式工具,通過存儲(chǔ)過程、觸發(fā)器、函數(shù)等形式的編程;另一種是嵌入式SQL編程,將SQL語(yǔ)句嵌入到高級(jí)開發(fā)語(yǔ)言,完成數(shù)據(jù)的各種操作
    2021-10-10
  • Java設(shè)計(jì)模式中的代理設(shè)計(jì)模式詳細(xì)解析

    Java設(shè)計(jì)模式中的代理設(shè)計(jì)模式詳細(xì)解析

    這篇文章主要介紹了Java設(shè)計(jì)模式中的代理設(shè)計(jì)模式詳細(xì)解析,代理模式,重要的在于代理二字,何為代理,我們可以聯(lián)想到生活中的例子,比如秘書、中介這類職業(yè),我們可以委托中介去幫我們完成某些事情,而我們自己只需要關(guān)注我們必須完成的事情,需要的朋友可以參考下
    2023-12-12
  • Mac使用Idea配置傳統(tǒng)SSM項(xiàng)目(非maven項(xiàng)目)

    Mac使用Idea配置傳統(tǒng)SSM項(xiàng)目(非maven項(xiàng)目)

    本文主要介紹了Mac使用Idea配置傳統(tǒng)SSM項(xiàng)目(非maven項(xiàng)目),將展示如何設(shè)置項(xiàng)目結(jié)構(gòu)、添加依賴關(guān)系等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Spring boot整合Mybatis-plus過程解析

    Spring boot整合Mybatis-plus過程解析

    這篇文章主要介紹了Spring boot整合Mybatis-plus過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java中的設(shè)計(jì)模式與7大原則歸納整理

    Java中的設(shè)計(jì)模式與7大原則歸納整理

    本篇文章主要對(duì)Java中的設(shè)計(jì)模式如,創(chuàng)建型模式、結(jié)構(gòu)型模式和行為型模式以及7大原則進(jìn)行了歸納整理,需要的朋友可以參考下
    2017-04-04
  • Spring Boot Actuator未授權(quán)訪問漏洞的問題解決

    Spring Boot Actuator未授權(quán)訪問漏洞的問題解決

    Spring Boot Actuator 端點(diǎn)的未授權(quán)訪問漏洞是一個(gè)安全性問題,可能會(huì)導(dǎo)致未經(jīng)授權(quán)的用戶訪問敏感的應(yīng)用程序信息,本文就來介紹一下解決方法,感興趣的可以了解一下
    2023-09-09
  • 詳解自定義SpringMVC的Http信息轉(zhuǎn)換器的使用

    詳解自定義SpringMVC的Http信息轉(zhuǎn)換器的使用

    這篇文章主要介紹了詳解自定義SpringMVC的Http信息轉(zhuǎn)換器的使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11

最新評(píng)論

孝昌县| 宜州市| 女性| 若羌县| 乳山市| 乌拉特前旗| 鄱阳县| 吉首市| 民县| 黎城县| 理塘县| 瑞金市| 五指山市| 桑日县| 辽中县| 诸暨市| 绥德县| 周至县| 分宜县| 黄梅县| 仁化县| 汤原县| 繁昌县| 南安市| 台山市| 楚雄市| 华阴市| 遂宁市| 阳信县| 阳泉市| 南郑县| 忻城县| 长春市| 韩城市| 兴业县| 武安市| 普兰县| 岢岚县| 汉中市| 长泰县| 凤山县|