You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring中不同Maven模块下事务实现类如何@Autowired调用默认服务实现

解决方案:跨Maven模块调用默认实现类(无直接依赖场景)

Alright, let's break down how to make your transactional implementation call the default one, even though they're in separate Maven modules with no direct dependency. Since you're using Spring (evidenced by the @Service annotation), I'll focus on Spring-friendly approaches first, plus a Java-native option for non-Spring scenarios.


1. Spring Bean Dynamic Lookup (Best for Your Use Case)

This leverages Spring's application context to find the default implementation at runtime, no direct module dependency needed.

Step 1: Define the Interface in Your API Module

package my.app.core.api;

public interface MyService {
    boolean process();
}

Step 2: Default Implementation Module (my-app-core-impl)

Give the bean a distinct name so we can reliably look it up later:

package my.app.core.impl;

import my.app.core.api.MyService;
import org.springframework.stereotype.Service;

@Service("defaultMyService") // Explicit bean name avoids ambiguity
public class MyServiceImpl implements MyService {
    @Override
    public boolean process() {
        System.out.println("Running default business logic");
        return true;
    }
}

Step 3: Transactional Implementation Module (my-app-core-transactional-impl)

Inject the Spring context and dynamically fetch the default bean:

package my.app.core.transactional.impl;

import my.app.core.api.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyTransactionalServiceImpl implements MyService {

    private final MyService defaultService;

    @Autowired
    public MyTransactionalServiceImpl(ApplicationContext context) {
        // Fetch the default implementation by its bean name
        this.defaultService = context.getBean("defaultMyService", MyService.class);
    }

    @Override
    @Transactional
    public boolean process() {
        System.out.println("Starting transaction...");
        boolean result = defaultService.process(); // Delegate to default logic
        System.out.println("Committing transaction...");
        return result;
    }
}

Key Notes

  • Make sure both modules are scanned by Spring: Add their base packages to your application's @ComponentScan or use Spring Boot's auto-scanning if they're under your main application's package hierarchy.
  • For safety, add fallback logic if the bean isn't found (e.g., use context.getBeansOfType(MyService.class) to filter out the transactional implementation itself and pick the remaining one).

2. Java SPI (Service Provider Interface)

If you need a Java-native solution that works outside Spring, SPI lets you discover implementations at runtime via a configuration file.

Step 1: Interface in API Module (Same as Above)

Step 2: Default Implementation Module

  • Create a file at src/main/resources/META-INF/services/my.app.core.api.MyService with this content:
    my.app.core.impl.MyServiceImpl
    
  • The implementation class (no @Service needed if not using Spring):
    package my.app.core.impl;
    
    import my.app.core.api.MyService;
    
    public class MyServiceImpl implements MyService {
        @Override
        public boolean process() {
            System.out.println("Running default business logic");
            return true;
        }
    }
    

Step 3: Transactional Implementation Module

Use ServiceLoader to load the default implementation:

package my.app.core.transactional.impl;

import my.app.core.api.MyService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;

import java.util.Iterator;
import java.util.ServiceLoader;

@Service
public class MyTransactionalServiceImpl implements MyService {

    private final MyService defaultService;

    public MyTransactionalServiceImpl() {
        ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class);
        Iterator<MyService> iterator = loader.iterator();
        
        // Filter out the transactional implementation itself if needed
        this.defaultService = iterator.next();
    }

    @Override
    @Transactional
    public boolean process() {
        System.out.println("Starting transaction...");
        boolean result = defaultService.process();
        System.out.println("Committing transaction...");
        return result;
    }
}

Key Notes

  • SPI loads all registered implementations, so add logic to exclude your transactional class if multiple implementations exist.
  • In Spring, you can combine this with @PostConstruct to initialize the default service after the bean is created.

3. Reflection (Last Resort)

Only use this if the above options don't fit—hardcoding class names makes your code brittle:

package my.app.core.transactional.impl;

import my.app.core.api.MyService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;

@Service
public class MyTransactionalServiceImpl implements MyService {

    private final MyService defaultService;

    public MyTransactionalServiceImpl() {
        try {
            Class<?> defaultClass = Class.forName("my.app.core.impl.MyServiceImpl");
            this.defaultService = (MyService) defaultClass.getConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Failed to load default MyService implementation", e);
        }
    }

    @Override
    @Transactional
    public boolean process() {
        System.out.println("Starting transaction...");
        boolean result = defaultService.process();
        System.out.println("Committing transaction...");
        return result;
    }
}

Key Notes

  • This breaks if the default implementation's package or class name changes.
  • Gets complicated quickly if the default class has constructor dependencies.

Final Recommendation

Go with Spring Bean Dynamic Lookup first—it's the most maintainable and fits perfectly with your existing Spring setup. Use SPI if you need a framework-agnostic solution, and avoid reflection unless you have no other choice.

内容的提问来源于stack exchange,提问作者JiKra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:30:59