在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应的注解,并不需要实现对应的逻辑。
比如 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 动态生成接口实现类的文章就介绍到这了,更多相关spring boot 接口实现类内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!