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

	前文中提到通过策略模式代替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;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介这是一门使用Java语言,SpringBoot框架,从0开发一个RESTful API应用,接近企业级的项目(我的云音乐),课程包含了基础内容,高级内容,项目封装,项目重构等知识,99%代码为手写;因为这是项目课程;所以不会深入到源码讲解某个知识点,以及原理,但会粗略的讲解下基础原理;主要是讲解如何使用系统功能,流行的第三方框架,第三方服务,完成接近企业级项目,目的是让大家,学到真正的企业级项目开发技术。适用人群刚刚毕业的学生想提高职场竞争力想学从零开发SpringBoot项目想提升SpringBoot项目开发技术想学习SpringBoot项目架构技术想学习企业级项目开发技术就是想学习SpringBoot开发能学到什么从0开发一个似企业级项目学会能做出市面上90%通用API快速增加1到2年实际开发经验刚毕业学完后能找到满意的工作已经工作学完后最高涨薪30%课程信息全课程目前是82章,155小时,每节视频都经过精心剪辑。在线学习分辨率最高1080P课程知识点1~11章:学习方法,项目架构,编码规范,Postman使用方法,Git和Github版本控制12~16章:搭建开发环境,快速入门SpringBoot框架17~20章:快速入门MySQL数据库21~30章:MyBatis,登录注册,找回密码,发送短信,发送邮件,企业级接口配置31~41章:实现歌单,歌单标签,音乐,列表分页,视频,评论,好友功能42~48章:阿里云OSS,话题,MyBatis-plus,应用监控49~53章:Redis使用,集成Redis,SpringCache,HTTP缓存54~58章:Elasticsearch使用,集成Elasticsearch,使用ES搜索59~61章:商城,集成支付宝SDK,支付宝支付62~64章:常用哈希和加密算法,接口加密和签名65~67章:实时挤掉用户,企业级项目测试环境,企业级接口文档68~69章:SpringBoot全站HTTPS,自签证书,申请免费证书70~73章:云MySQL数据库,云Redis数据库使用,轻量级应用部署环境,域名解析74~80章:Docker使用,生产级Kubernetes集群,域名解析,集群全站HTTPS81~82章:增强和重构项目,课程总结,后续学习计划
### 回答1: 在 Spring 中,可以使用 AOP(面向切面编程)来实现接口防刷的功能。下面是一个示例实现: 1. 在项目的 pom.xml 文件中加入 AOP 的依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 2. 创建一个切面,用于处理接口的防刷逻辑,例如: ``` @Aspect @Component public class AntiBrushAspect { @Autowired private RedisTemplate redisTemplate; @Pointcut("@annotation(com.example.annotation.AntiBrush)") public void antiBrushPointcut() { } @Around("antiBrushPointcut()") public Object doAntiBrush(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); AntiBrush antiBrush = method.getAnnotation(AntiBrush.class); int frequency = antiBrush.frequency(); long time = antiBrush.time(); String key = getKey(joinPoint, method); // 检查请求频率是否超过限制 if (checkFrequency(key, frequency, time)) { return Result.fail("请求过于频繁"); } try { return joinPoint.proceed(); } finally { // 更新请求频率 updateFrequency(key, frequency, time); } } private String getKey(ProceedingJoinPoint joinPoint, Method method) { // 获取请求的 IP 地址 String ip = RequestUtil.getIpAddr(); // 获取请求的方法名 String methodName = method.getName(); // 生成 Redis 的键 return String.format("anti_brush:%s:%s", ip, methodName); } private boolean checkFrequency(String key, int frequency, long time) { // 获取当前时间 long now = System.currentTimeMillis(); // 获取 Redis 中的记录 List<Long> records = (List<Long>) redisTemplate.opsForValue(). ### 回答2: Spring Boot提供了AOP(Aspect Oriented Programming,面向切面编程)的支持,可以通过切面实现接口防刷的功能。 首先,我们需要创建一个切面,用于在接口调用时进行处理。可以通过使用`@Aspect`来声明这个一个切面,使用`@Component`将其注册为Spring Bean。 ```java @Aspect @Component public class RateLimitAspect { // 存储接口调用次数的缓存,这里使用ConcurrentHashMap private Map<String, AtomicInteger> counterMap = new ConcurrentHashMap<>(); // 定义切入点,这里设置为所有带有@RequestMapping注解的方法 @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public void requestMappingPointcut() {} // 定义环绕通知,用于在接口调用前进行处理 @Around("requestMappingPointcut()") public Object handleRateLimit(ProceedingJoinPoint joinPoint) throws Throwable { // 获取请求的URL String url = getRequestUrl(joinPoint); // 检查接口调用次数是否超过阈值 if (isRateLimitExceeded(url)) { throw new RuntimeException("接口调用频率超过限制"); } // 接口调用次数加1 incrementRateLimit(url); // 执行接口方法 return joinPoint.proceed(); } // 获取请求的URL private String getRequestUrl(ProceedingJoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); RequestMapping annotation = method.getAnnotation(RequestMapping.class); return annotation.value()[0]; } // 检查接口调用次数是否超过阈值 private boolean isRateLimitExceeded(String url) { AtomicInteger counter = counterMap.get(url); return counter != null && counter.get() >= 5; // 假设阈值为5 } // 接口调用次数加1 private void incrementRateLimit(String url) { counterMap.computeIfAbsent(url, k -> new AtomicInteger()).incrementAndGet(); } } ``` 在切面中,我们使用了一个Map来存储接口调用的次数,使用ConcurrentHashMap保证线程安全。在`handleRateLimit`方法中,我们首先通过`getRequestUrl`方法获取请求的URL,然后通过`isRateLimitExceeded`方法判断接口是否超过了阈值,如果超过则抛出异常。最后,我们通过`incrementRateLimit`方法将接口调用次数加1。 请注意,以上示例仅供参考,具体的实现方式可能因具体需求而有所不同。接口防刷的实现可能需要考虑更多的因素,比如IP限制、令牌桶算法等。 ### 回答3: Spring Boot提供了AOP(面向切面编程)的支持,通过使用AOP可以很方便地实现接口防刷的功能。 首先,在Spring Boot项目中添加依赖,包括spring-boot-starter-aop和spring-boot-starter-web。 然后,创建一个切面,用于实现接口防刷的逻辑。在该上加上@Aspect注解,表示这是一个切面。然后可以定义一个切点,用于指定需要进行拦截的接口方法。 示例代码如下: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; @Aspect @Component public class InterfaceLimiterAspect { private static final Map<String, Integer> frequencyMap = new HashMap<>(); /** * 定义切点,对需要进行拦截的接口方法进行限制 */ @Before("execution(* com.example.controller.*.*(..))") public void limitInterfaceFrequency(JoinPoint joinPoint) throws Exception { // 获取接口方法的名称 String methodName = joinPoint.getSignature().toLongString(); // 判断是否已经达到了阈值 if (frequencyMap.containsKey(methodName) && frequencyMap.get(methodName) >= 10) { throw new Exception("接口频率超过限制"); } else { // 将接口方法加入到频率统计中 frequencyMap.put(methodName, frequencyMap.getOrDefault(methodName, 0) + 1); } } } ``` 上述示例代码中,切面InterfaceLimiterAspect用于实现接口防刷的逻辑。在@Before注解中,通过execution表达式指定了需要进行拦截的接口方法,这里是拦截了com.example.controller包下的所有方法。 在切面方法中,通过JoinPoint对象获取到当前正在执行的接口方法的名称,然后在frequencyMap中判断该接口方法的访问频率是否超过限制,如果超过限制,则抛出异常;如果没有超过限制,则将接口方法的访问次数加1。 最后,在Spring Boot的启动中添加@EnableAspectJAutoProxy注解,开启AOP的自动代理功能,使得切面生效。 以上就是一个简单的Spring Boot接口防刷的切面实现。需要注意的是,该示例中只是简单地通过接口方法的访问次数进行限制,并没有考虑到并发访问的情况。在实际开发中,可能需要结合其他的技术或工具来实现更精确的接口访问频率限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值