需要实现的功能是使用指定的 SDK
开发包来访问其他服务的资源,这种场景其实和Mapper
、Feign
类似。所以,希望也通过接口上配置注解的方式实现。
所以在本文使用了三个注解:
@RkproblemMapperScan
:自定义的注解,用于指定要扫描的包名;@RkproblemMapper
:自定义的注解,用于标记当前接口需要被代理;@RequestMapping
:SpringMvc
的注解,用于标记当前方法需要被代理,以及代理的 url
。创建一个注解,添加在接口上表示当前接口需要被扫描代理。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Component
@Indexed
public @interface RkproblemMapper {
}
创建一个开启功能的注解,可以指定需要扫描 @RkproblemMapper
注解的包名。
注解中引入了 RkproblemMapperRegister
类,将进行 Bean
的代理和注册。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({RkproblemMapperRegister.class})
public @interface RkproblemMapperScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
RkproblemMapperRegister
类用于注册 Bean
,它将解析 @RkproblemMapperScan
注解的配置,然后根据配置扫描 @RkproblemMapper
注解,进行 Bean
的注册。
ImportBeanDefinitionRegistrar
的实现类使用 @Import
注解则会调用接口方法,将其中要注册的类注册成 bean
;
ResourceLoaderAware
获取资源加载器,可以获得外部资源文件;
ResourceLoaderAware
获取 Bean
的类加载器,用于加载代理类。
public class RkproblemMapperRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware {
private ClassLoader classLoader;
private ResourceLoader resourceLoader;
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(RkproblemMapperScan.class.getName(), true));
Set<String> packages = new HashSet();
if (attributes != null) {
this.addPackages(packages, attributes.getStringArray("value"));
this.addPackages(packages, attributes.getStringArray("basePackages"));
this.addClasses(packages, attributes.getStringArray("basePackageClasses"));
if (packages.isEmpty()) {
packages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
}
RkproblemMapperScanner scanner = new RkproblemMapperScanner(registry, this.classLoader);
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
scanner.doScan(StringUtils.toStringArray(packages));
}
private void addPackages(Set<String> packages, String[] values) {
if (values != null) {
Collections.addAll(packages, values);
}
}
private void addClasses(Set<String> packages, String[] values) {
if (values != null) {
String[] var3 = values;
int var4 = values.length;
for(int var5 = 0; var5 < var4; ++var5) {
String value = var3[var5];
packages.add(ClassUtils.getPackageName(value));
}
}
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
扫描 @RkproblemMapper
注解,创建 BeanDefinition
信息。
public class RkproblemMapperScanner extends ClassPathBeanDefinitionScanner {
private ClassLoader classLoader;
public RkproblemMapperScanner(BeanDefinitionRegistry registry, ClassLoader classLoader) {
super(registry, false);
this.classLoader = classLoader;
this.addIncludeFilter(new AnnotationTypeFilter(RkproblemMapper.class));
}
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
if (beanDefinition.getMetadata().isInterface()) {
try {
Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), this.classLoader);
return !target.isAnnotation();
} catch (Exception var3) {
this.logger.error("load class exception:", var3);
}
}
return false;
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No @RkproblemMapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
Iterator var3 = beanDefinitions.iterator();
while(var3.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating RkproblemBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' Interface");
}
definition.getConstructorArgumentValues().addGenericArgumentValue(Objects.requireNonNull(definition.getBeanClassName()));
definition.setBeanClass(RkproblemRequestFactoryBean.class);
}
}
}
用于存储接口方法中一些简要信息的实体类。
public class MethodInformation {
private String url;
private Type resultType;
public MethodInformation() {
}
public MethodInformation(String url, Type resultType) {
this.url = url;
this.resultType = resultType;
}
public static MethodInformation build(String url, Type resultType) {
return new MethodInformation(url, resultType);
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public Type getResultType() {
return this.resultType;
}
public void setResultType(Type resultType) {
this.resultType = resultType;
}
}
解析接口方法的信息,创建代理对象。
public class RkproblemRequestFactoryBean<T> implements SmartFactoryBean<T>, ApplicationContextAware {
private Class<T> rkproblemMapperInterface;
private ApplicationContext applicationContext;
private RkproblemHelperProvider provider;
private static final Map<String, Map<String, MethodInformation>> interfaceProcessorMap = new ConcurrentHashMap(4);
public RkproblemRequestFactoryBean(Class<T> rkproblemMapperInterface) {
this.rkproblemMapperInterface = rkproblemMapperInterface;
}
public T getObject() {
this.checkBeanIsInterface();
return Proxy.newProxyInstance(this.rkproblemMapperInterface.getClassLoader(), new Class[]{this.rkproblemMapperInterface}, new RkproblemRequestHandler(this.provider, this.getInterfaceInformationMap()));
}
private Map<String, MethodInformation> getInterfaceInformationMap() {
if (!interfaceProcessorMap.containsKey(this.rkproblemMapperInterface.getCanonicalName())) {
Map<String, MethodInformation> methodMap = new LinkedHashMap();
Method[] var2 = this.rkproblemMapperInterface.getMethods();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
Method method = var2[var4];
RequestMapping mapping = (RequestMapping)method.getAnnotation(RequestMapping.class);
if (mapping != null) {
methodMap.put(method.toString(), MethodInformation.build(mapping.value()[0], method.getGenericReturnType()));
}
}
interfaceProcessorMap.put(this.rkproblemMapperInterface.getCanonicalName(), methodMap);
}
return (Map)interfaceProcessorMap.get(this.rkproblemMapperInterface.getCanonicalName());
}
private void checkBeanIsInterface() {
Assert.isTrue(this.rkproblemMapperInterface.isInterface(), "@RkproblemMapper 只能作用在接口类型上!");
}
public boolean isSingleton() {
return false;
}
public boolean isPrototype() {
return true;
}
public Class<T> getObjectType() {
return this.rkproblemMapperInterface;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (Objects.isNull(this.provider)) {
this.provider = (RkproblemHelperProvider)this.applicationContext.getBean(RkproblemHelperProvider.class);
}
}
}
代理类的处理逻辑,每个被代理的方法都会在 invoke
方法中被执行。
public class RkproblemRequestHandler implements InvocationHandler {
private final Map<String, MethodInformation> methodInformationInvokes;
private RkproblemHelperProvider provider;
public RkproblemRequestHandler(RkproblemHelperProvider provider, Map<String, MethodInformation> methodInformationInvokes) {
this.provider = provider;
this.methodInformationInvokes = methodInformationInvokes;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
if ("toString".equals(method.getName())) {
return "proxy$" + method.getDeclaringClass();
} else {
Object body = args.length == 1 ? args[0] : (args.length > 1 ? Arrays.asList(args) : null);
MethodInformation methodInformation = (MethodInformation)this.methodInformationInvokes.get(method.toString());
return this.provider.apiHeader(methodInformation, body);
}
}
}