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源码