如何动态代理Controller的接口并注册到SpringMVC中

1、实现对Controller接口的动态代理

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
	String value() default "";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyControllerMethod {
    String value() default "";
    Class beanType() default Object.class;
    String methodName() default "";
}
@RestController
public interface TestMyController {

    @GetMapping("/my/hello")
    @MyControllerMethod(beanType = TestMyService.class, methodName = "hello")
    String hello();
}

@Service
public class TestMyService {

    String hello(){
        return "hello";
    }
}

/**
 * 用于Spring动态代理注入自定义接口
 * https://www.jianshu.com/p/e2e19fcf97fc
 * https://blog.csdn.net/lichuangcsdn/article/details/89694363
 * https://blog.csdn.net/liyantianmin/article/details/81047373
 * open-feign源码: FeignClientsRegistrar https://blog.csdn.net/sinat_29899265/article/details/86577997
 * 
 * 	BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 
 * 		区别在于后者需要@Import导入类,前者需要@Component
 */
@Component
@Slf4j
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		System.out.println("BeanDefinitionRegistryPostProcessor,postProcessBeanDefinitionRegistry");

		// 通过反射获取需要代理的接口的clazz列表
		Set<Class<?>> clazzSet = scannerPackages("io.github.ygsama.microservice");
		for (Class beanClazz : clazzSet) {
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
			GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            // 这里可以给该对象的属性注入对应的实例。mybatis就在这里注入了dataSource和sqlSessionFactory,
            // definition.getPropertyValues().add("interfaceType", beanClazz),BeanClass需要提供setter
            // definition.getConstructorArgumentValues(),BeanClass需要提供包含该属性的构造方法,否则会注入失败
			definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);
			definition.getConstructorArgumentValues().addGenericArgumentValue(applicationContext);

			// 注意,这里的BeanClass是生成Bean实例的工厂,不是Bean本身。
			// FactoryBean是一种特殊的Bean,其返回的对象不是指定类的一个实例,
			// 其返回的是该工厂Bean的getObject方法所返回的对象。
			definition.setBeanClass(ServiceFactory.class);

			//这里采用的是byType方式注入,类似的还有byName等
			definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);

			String simpleName = beanClazz.getSimpleName();
			log.info("beanClazz.getSimpleName(): {}", simpleName);
			log.info("GenericBeanDefinition: {}", definition);

			registry.registerBeanDefinition(beanClazz.getSimpleName(), definition);
		}
	}

	private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
	private MetadataReaderFactory metadataReaderFactory;

	/**
	 * 根据包路径获取包及子包下的所有类
	 *
	 * @param basePackage basePackage
	 * @return Set<Class < ?>> Set<Class<?>>
	 */
	private Set<Class<?>> scannerPackages(String basePackage) {

		Set<Class<?>> set = new LinkedHashSet<>();
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + DEFAULT_RESOURCE_PATTERN;
		try {
			Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
			for (Resource resource : resources) {
				if (resource.isReadable()) {
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
					String className = metadataReader.getClassMetadata().getClassName();
					Class<?> clazz;
					try {
						clazz = Class.forName(className);
						if (clazz.isInterface() && clazz.getAnnotation(MyController.class) != null) {
							set.add(clazz);
						}
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return set;
	}

	protected String resolveBasePackage(String basePackage) {
		return ClassUtils.convertClassNameToResourcePath(this.getEnvironment().resolveRequiredPlaceholders(basePackage));
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

	}

	private ResourcePatternResolver resourcePatternResolver;

	private ApplicationContext applicationContext;

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	private Environment getEnvironment() {
		return applicationContext.getEnvironment();
	}

}
/**
 * 接口实例工厂,这里主要是用于提供接口的实例对象
 */
public class ServiceFactory<T> implements FactoryBean<T> {

	private Class<T> interfaceType;
	private ApplicationContext applicationContext;

	public ServiceFactory(Class<T> interfaceType, ApplicationContext applicationContext) {
		this.interfaceType = interfaceType;
		this.applicationContext = applicationContext;
	}

	@Override
	public T getObject() throws Exception {
		// 这里主要是创建接口对应的实例,便于注入到spring容器中
		InvocationHandler handler = new ServiceProxy<>(interfaceType,applicationContext);
		return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[] {interfaceType},handler);
	}

	@Override
	public Class<T> getObjectType() {
		return interfaceType;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}
}
/**
 * 动态代理,需要注意的是,这里用到的是JDK自带的动态代理,代理对象只能是接口,不能是类
 */
public class ServiceProxy<T> implements InvocationHandler {

	private Class<T> interfaceType;
	private ApplicationContext applicationContext;

	public ServiceProxy(Class<T> intefaceType, ApplicationContext applicationContext) {
		this.interfaceType = interfaceType;
		this.applicationContext = applicationContext;

	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用前,args = " + args);
        MyControllerMethod annotation = method.getAnnotation(MyControllerMethod.class);
        Object bean = applicationContext.getBean(annotation.beanType());
        Method beanMethod = ReflectionUtils.findMethod(bean.getClass(), annotation.methodName(), method.getParameterTypes());
        beanMethod.setAccessible(true);
        Object result = ReflectionUtils.invokeMethod(beanMethod, bean, args);
        System.out.println("调用后,result = " + result);
		return result;
	}
}

2、将代理对象注入SpringMVC中

新版本的Spring不需要这一步,注入到spring容器后mvc框架会自己解析。
老版本的Spring有其他问题的话,需要手动移除或者注入到handlermapping里

    /**
     * 注册Controller
     */
    public static void registerController(String controllerBeanName) {
        RequestMappingHandlerMapping rmhm = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
        if (rmhm != null) {
            try {
                Object controller = applicationContext.getBean(controllerBeanName);
                if (controller == null) {
                    return;
                }
                Map<Method, RequestMappingInfo> methods = MethodIntrospector.selectMethods(controller.getClass(),
                        (MethodIntrospector.MetadataLookup<RequestMappingInfo>) method -> {
                            try {
                                RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
                                RequestMappingInfo.Builder mappping = RequestMappingInfo
                                        .paths((requestMapping.path()))
                                        .methods(requestMapping.method())
                                        .params(requestMapping.params())
                                        .headers(requestMapping.headers())
                                        .consumes(requestMapping.consumes())
                                        .produces(requestMapping.produces())
                                        .mappingName(requestMapping.name());
                                return mappping.build();
                            } catch (Throwable ex) {
                                throw new IllegalStateException("Invalid mapping on handler class [" +
                                        controller.getClass().getName() + "]: " + method, ex);
                            }
                        });

                Method rmhmMethod = rmhm.getClass().getSuperclass().getSuperclass().
                        getDeclaredMethod("registerHandlerMethod", new Class[]{Object.class, Method.class, Object.class});
                rmhmMethod.setAccessible(true);

                methods.forEach((method, mapping) -> {
                    try {
                        rmhmMethod.invoke(rmhm, new Object[]{controller, method, mapping});
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

参考:
基于FeignClient的Controller自动生成注入机制
基于FeignClient的Controller自动生成注入-github源码

原创文章 95 获赞 219 访问量 29万+

猜你喜欢

转载自blog.csdn.net/zimou5581/article/details/99548519