软件编程
位置:首页>> 软件编程>> java编程>> spring boot 动态生成接口实现类的场景分析

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

作者:mysgk  发布时间:2022-12-14 05:09:33 

标签:spring,boot,接口,实现类

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

比如 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<T> implements FactoryBean<T>, InvocationHandler {

private Class<T> interfaceClass;

public Class<T> getInterfaceClass() {
return interfaceClass;
}

public void setInterfaceClass(Class<T> 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<Class<?>> 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);

}

项目结构:

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

来源:https://www.cnblogs.com/mysgk/p/15607900.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com