Springboot 获取接口多个实现类bean 并调用的方式

本文探讨了如何利用策略模式替代switch-case结构,并介绍了在Spring Boot环境下实现该模式遇到的问题及解决方案。文章详细讨论了不同注入方式的局限性和替代方案,包括使用反射获取Bean实例的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

	前文中提到通过策略模式代替switch-case的方式,在实际使用中将接口通过bean获得,然后去调用即可;

实际操作中发现,springboot在通过@Autowired 或者@Resource 方式注入时会出现部分问题;

针对以上替代switch-case形式的策略模式会产生很多接口的实现类,注入时需要确定bean的具体类型使用@Qualifier+@Autowired 或者 @Resource (name="beanName")的方式时均只能获得一个bean 不能满足使用接口去调用的方式;
以下是百度到的其他解决方式,未能成功,不排除我本身代码问题

// 1、使用 List<ServiceInterface> beanNames的方式
// 或者使用@Resource 注入
// 或者使用 Map<String,Class>的方式 前者为beanName 后者为具体实现类;
@Autowired
private List<ServiceInterface> serviceInterfaces;

public static void main(String[] args) {
	int param= 1;
	for (HandleMapClientOperateService serviceInterface : serviceInterfaces) {
	    if(serviceInterface.isDo(param)){
	        serviceInterface.doIt();
	    }
	}
}

另一种基于反射形式获取beans,也是参考另一个大佬的实现;

	public static void main(String[] args) throws Exception {
		List<ServerInterface> serviceInterfaces= new ArrayList<>();
		List<Class> clazzs = getAllInterfaceAchieveClass(HandleMapClientOperateService.class);
		for (Class clazz : clazzs) {
		    serviceInterfaces.add((ServerInterface) clazz.newInstance());
		}
		//打印Class对象
		for (ServerInterfacecla : serviceInterfaces) {
		    if (cla.isDo(1)) {
		        cla.doIt(1);
		    }
		}
	}
	
	@SneakyThrows
	public static List<HandleMapClientOperateService> getServiceImpl() {
		List<HandleMapClientOperateService> services = new ArrayList<>();
		List<Class> clazzs = getAllInterfaceAchieveClass(HandleMapClientOperateService.class);
		for (Class clazz : clazzs) {
		    services.add((HandleMapClientOperateService) clazz.newInstance());
		}
		return services;
	}
	
	public static List<Class> getAllInterfaceAchieveClass(Class clazz) {
		ArrayList<Class> list = new ArrayList<>();
		//判断是否是接口
		if (clazz.isInterface()) {
		    try {
		        ArrayList<Class> allClass = getAllClassByPath(clazz.getPackage().getName());
		        /**
		         * 循环判断路径下的所有类是否实现了指定的接口
		         * 并且排除接口类自己
		         */
		        for (int i = 0; i < allClass.size(); i++) {
		
		            //排除抽象类
		            if (Modifier.isAbstract(allClass.get(i).getModifiers())) {
		                continue;
		            }
		            //判断是不是同一个接口
		            if (clazz.isAssignableFrom(allClass.get(i))) {
		                if (!clazz.equals(allClass.get(i))) {
		                    list.add(allClass.get(i));
		                }
		            }
		        }
		    } catch (Exception e) {
		        System.out.println("出现异常");
		    }
		}
		return list;
	}
	
	/**
	* 从指定路径下获取所有类
	*
	* @return
	*/
	public static ArrayList<Class> getAllClassByPath(String packagename) {
		ArrayList<Class> list = new ArrayList<>();
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		String path = packagename.replace('.', '/');
		try {
		    ArrayList<File> fileList = new ArrayList<>();
		    Enumeration<URL> enumeration = classLoader.getResources(path);
		    while (enumeration.hasMoreElements()) {
		        URL url = enumeration.nextElement();
		        fileList.add(new File(url.getFile()));
		    }
		    for (int i = 0; i < fileList.size(); i++) {
		        list.addAll(findClass(fileList.get(i), packagename));
		    }
		} catch (IOException e) {
		    e.printStackTrace();
		}
		return list;
	}
	
	private static ArrayList<Class> findClass(File file, String packagename) {
		ArrayList<Class> list = new ArrayList<>();
		if (!file.exists()) {
		    return list;
		}
		File[] files = file.listFiles();
		for (File file2 : files) {
		    if (file2.isDirectory()) {
		        assert !file2.getName().contains(".");//添加断言用于判断
		        ArrayList<Class> arrayList = findClass(file2, packagename + "." + file2.getName());
		        list.addAll(arrayList);
		    } else if (file2.getName().endsWith(".class")) {
		        try {
		            //保存的类文件不需要后缀.class
		            list.add(Class.forName(packagename + '.' + file2.getName().substring(0,
		                    file2.getName().length() - 6)));
		        } catch (ClassNotFoundException e) {
		            e.printStackTrace();
		        }
		    }
		}
		return list;
	}

另外在以上方式中,发现其他的service 无法注入到方法中,通过 implements ApplicationContextAware 的方式,获取bean;

<think>好的,我现在需要帮助用户解决在Spring Boot普通类中获取Bean实例的问题。用户已经提供了一些引用资料,我需要结合这些信息来整理出一个清晰的回答步骤。 首先,根据引用1和引用2,用户提到了通过实现ApplicationContextAware接口获取ApplicationContext。这是常见的方法,我记得ApplicationContextAware接口一个setApplicationContext方法,可以用来注入ApplicationContext实例。然后,通过这个上下文就可以获取Bean了。需要创建一个工具类,比如SpringContextUtil,来保存ApplicationContext,提供静态方法获取Bean。 然后引用3中的例子展示了如何从工具类中根据类名获取Bean实例。用户的问题是在普通类中如何调用这些方法,所以可能需要演示如何在普通类中调用SpringContextUtil.getBean()方法。 接下来,我需要考虑其他方法,比如使用@PostConstruct注解或者@Autowired静态变量,但可能这些方法有局限性或者需要额外的配置。比如,@Autowired需要Spring管理该类,而普通类可能没有被Spring管理,所以这种方法可能不适用。另外,用户提到的启动类设置ApplicationContext也是一种方式,但可能不如工具类方便。 还要注意线程安全问题,确保ApplicationContext的初始化正确。另外,要提醒用户避免在非Spring管理的类中频繁获取Bean,因为这可能违反IoC原则,导致代码耦合。 可能需要分步骤说明,首先介绍实现ApplicationContextAware接口的方法,然后提供工具类代码示例,再展示如何在普通类中使用这个工具类获取Bean。同时,指出其他方法,比较它们的优缺点,最后给出注意事项。 需要检查代码示例是否正确,比如工具类的实现,静态变量和方法的正确性。确保ApplicationContext在set的时候正确赋值,且工具类的方法能够正确获取Bean实例。 最后,生成相关问题,比如如何注入Bean到非Spring管理的类,或者ApplicationContextAware的工作原理,以及如何解决可能的空指针问题。</think>在Spring Boot项目中,从普通类(非Spring管理的类)中获取Bean实例,可以通过以下两种主要方法实现: ### 一、通过实现ApplicationContextAware接口(推荐方法) 1. **创建工具类**:定义实现`ApplicationContextAware`接口的上下文工具类 ```java @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext context) { if (applicationContext == null) { applicationContext = context; } } // 根据名称获取Bean public static Object getBean(String name) { return applicationContext.getBean(name); } // 根据类型获取Bean public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); } // 根据名称+类型获取Bean public static <T> T getBean(String name, Class<T> clazz) { return applicationContext.getBean(name, clazz); } } ``` 2. **普通类中调用**:在任意普通类中通过静态方法获取Bean ```java public class CommonClass { public void doSomething() { // 通过类型获取 UserService userService = SpringContextUtil.getBean(UserService.class); // 通过名称获取 JWTConfig jwtConfig = (JWTConfig) SpringContextUtil.getBean("jwtConfig"); } } ``` ### 二、通过启动类设置上下文(备用方案) 在Spring Boot启动类中设置静态变量: ```java @SpringBootApplication public class Application { private static ApplicationContext context; public static void main(String[] args) { context = SpringApplication.run(Application.class, args); } public static ApplicationContext getContext() { return context; } } ``` ### 三、注意事项 1. **初始化时机**:需确保Spring容器初始化完成后再调用获取Bean的方法[^2] 2. **线程安全**:`ApplicationContext`在应用启动后是只读的,多线程访问安全 3. **设计规范**:应优先考虑依赖注入,仅在特殊场景使用该方法[^3] ### 四、性能优化建议 对于高频调用Bean,建议在工具类中添加缓存机制: ```java private static Map<Class<?>, Object> beanCache = new ConcurrentHashMap<>(); public static <T> T getBeanWithCache(Class<T> clazz) { return (T) beanCache.computeIfAbsent(clazz, k -> applicationContext.getBean(clazz)); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值