spring boot 动态生成接口实现类的场景分析

网友投稿 1616 2022-11-19

spring boot 动态生成接口实现类的场景分析

spring boot 动态生成接口实现类的场景分析

目录一: 定义注解二: 建立动态代理类三: 注入spring容器四: 编写-五: 新建测试类

在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑。

比如 mybatis和feign: 在 mybatis 中,我们只需要定义对应的mapper接口;在 feign 中,我们只需要定义对应业务系统中的接口即可。

那么在这种场景下,具体的业务逻辑时怎么执行的呢,其实原理都是动态代理。

我们这里不具体介绍动态代理,主要看一下它在springboot项目中的实际应用,下面我们模仿feign来实现一个调用三方接口的 httpclient。

一: 定义注解

package com.mysgk.blogdemo.annotation;

public @interface MyHttpClient {

}

二: 建立动态代理类

package com.mysgk.blogdemo.proxy;

import org.springframework.beans.factory.FactoryBean;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class RibbonAopProxyFactory implements FactoryBean, InvocationHandler {

private Class interfaceClass;

public Class getInterfaceClass() {

return interfaceClass;

}

public void setInterfaceClass(Class interfaceClass) {

this.interfaceClass = interfaceClass;

}

@Override

public T getObject() throws Exception {

return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{interfaceClass}, this);

}

@Override

public Class> getObjectType() {

return interfaceClass;

}

@Override

public boolean isSingleton() {

return true;

}

/**

真正执行的方法

*/

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

return "invoke " + proxy.getClass().getName() + "." + method.getName() + " , do anything ..";

}

}

三: 注入spring容器

package com.mysgk.blogdemo.start;

import cn.hutool.core.util.ClassUtil;

import cn.hutool.core.util.StrUtil;

import com.mysgk.blogdemo.annotation.MyHttpClient;

import com.mysgk.blogdemo.proxy.RibbonAopProxyFactory;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

import org.springframework.beans.factory.support.GenericBeanDefinition;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.stereotype.Component;

import java.util.Set;

@Component

public class ScanHttpClients implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

private final Logger logger = LoggerFactory.getLogger(ScanHttpClients.class);

private ApplicationContext ctx;

public void run(BeanDefinitionRegistry registry) {

Set> scanPackage = ClassUtil.scanPackageByAnnotation("com.mysgk", MyHttpClient.class);

for (Class> cls : scanPackage) {

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(cls);

GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();

definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName());

definition.setBeanClass(RibbonAopProxyFactory.class);

definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);

String beanName = StrUtil.removePreAndLowerFirst(cls.getSimpleName(), 0) + "RibbonClient";

registry.registerBeanDefinition(beanName, definition);

}

}

@Override

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

run(registry);

}

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

}

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.ctx = ctx;

}

}

四: 编写-

package com.mysgk.blogdemo.aop;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpEntity;

import org.springframework.http.HttpMethod;

import org.springframework.http.ResponseEntity;

import org.springframework.stereotype.Component;

import org.springframework.web.client.RestTemplate;

@Component

@Aspect

public class InterceptAnnotation {

@Autowired

private RestTemplate ribbonLoadBalanced;

@Pointcut("@annotation(com.mysgk.blogdemo.annotation.MyHttpClient)")

public void execute() {

}

@Around("execute()")

public Object interceptAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {

/**

* 此处省略 获取 url, httpMethod, requestEntity, responseType 等参数的处理过程

*/

ResponseEntity> exchange = ribbonLoadBalanced.exchange("url", HttpMethod.GET, HttpEntity.EMPTY, Object.class);

return exchange.getBody();

}

}

五: 新建测试类

package com.mysgk.blogdemo.client;

import com.mysgk.blogdemo.annotation.MyHttpClient;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

@MyHttpClient

public interface MyHttpClientTest {

@PostMapping(value = "test/t1")

Object test(String param);

}

项目结构:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Android 探究View onAttachedToWindow 触发时机
下一篇:C#实现网页加载后将页面截取成长图片 | Playwright版
相关文章

 发表评论

暂时没有评论,来抢沙发吧~