模拟MyBatis、OpenFeign。通过定义接口,加上特定的注解,即可完成对应的功能,不用关心细节实现。 核心技术:动态代理 + spring自启动
定义相关注解
自定义代理配置类
/**
* 启动类注解
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MyInvocationAutoConfig.class, MyProxyBeanPostProcessor.class})
public @interface MyInvocationAutoConfiguration {
/**
* 配置类所在路径
* @return
*/
String basePackages();
}
代理类标注注解
/**
* 标注代理类
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyInvocation {
}
代理方法注解
代理实现类
public class MyInvocationHandler implements FactoryBean<Object>, InvocationHandler {
private final Class<?> targetClass;
public MyInvocationHandler(Class<?> targetClass) {
Assert.isTrue(targetClass.isInterface(),"only interface");
this.targetClass = targetClass;
// 创建config
}
@Override
public Object getObject() {
return Proxy.newProxyInstance(targetClass.getClassLoader(), new Class[]{targetClass},this);
}
@Override
public Class<?> getObjectType() {
return Object.class;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
StandardMethodMetadata methodMetadata = new StandardMethodMetadata(method);
if (methodMetadata.isAbstract()){
System.out.println("MyInvocationHandler: " + method.getName());
return doInvoke(method,args);
}
Class<?> declaringClass = method.getDeclaringClass();
// 执行接口只的default方法不走代理
return MethodHandles.privateLookupIn(declaringClass, MethodHandles.lookup())
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
}
private Object doInvoke(Method method,Object[] args){
return "MyInvocationHandler";
}
}
核心配置类
解析注解配置类
/**
* 导入 自定义BeanDefinitionPostProcessor读取注解配置属性。读取路径下的所有类查看是否有对应注解的类,
* 有的话创建对应的RootBeanDefinition。并设置beforeInstantiationResolved=true
* 导入 自定义InstantiationAwareBeanPostProcessor
*/
@Configuration
public class MyInvocationAutoConfig implements ApplicationContextAware, InitializingBean, EnvironmentCapable {
private ApplicationContext applicationContext;
private BeanDefinitionRegistry registry;
private Environment environment;
private final String resourcePattern = "**/*.class";
private ResourcePatternResolver resourcePatternResolver;
private MetadataReaderFactory metadataReaderFactory;
private final BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.registry = (BeanDefinitionRegistry) applicationContext;
}
@NonNull
@Override
public final Environment getEnvironment() {
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
return this.environment;
}
@Override
public void afterPropertiesSet() throws Exception {
initMyInvocation();
}
public void initMyInvocation(){
// 获取所有注册了的bean
String[] beanNameList = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class);
for (String name : beanNameList) {
if (!ScopedProxyUtils.isScopedTarget(name)) {
// 获取标注了MyInvocationAutoConfiguration的配置类。然后解析MyInvocationAutoConfiguration的属性。然后扫描
MyInvocationAutoConfiguration myInvocationAutoConfiguration = applicationContext.findAnnotationOnBean(name, MyInvocationAutoConfiguration.class);
if (myInvocationAutoConfiguration != null) {
// 扫描对应路径下的类,如果有MyInvocation封装为RootBeanDefinition
String basePackages = myInvocationAutoConfiguration.basePackages();
Assert.hasText(basePackages,"basePackages不为空");
scan(basePackages);
}
}
}
}
public void scan(String basePackage){
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
for (Resource resource : resources) {
String filename = resource.getFilename();
if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
// Ignore CGLIB-generated classes in the classpath
continue;
}
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
BeanDefinition beanDefinition = new ScannedGenericBeanDefinition(metadataReader);
String beanName = this.beanNameGenerator.generateBeanName(beanDefinition, (BeanDefinitionRegistry) applicationContext);
if (isCandidateComponent(metadataReader)) {
// 找到符合条件的 创建对应的BeanDefinition同时设置beforeInstantiationResolved为true
// 注册BeanDefinition到容器
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(beanName);
rootBeanDefinition.setBeanClassName(beanDefinition.getBeanClassName());
Field beforeInstantiationResolved = ReflectionUtils.findField(RootBeanDefinition.class,
"beforeInstantiationResolved", Boolean.class);
if (beforeInstantiationResolved != null){
ReflectionUtils.makeAccessible(beforeInstantiationResolved);
ReflectionUtils.setField(beforeInstantiationResolved,rootBeanDefinition,true);
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder,registry);
}
} else {
// 不是符合条件的类
}
}
catch (FileNotFoundException ex) {
ex.printStackTrace();
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
}
protected String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(getEnvironment().resolveRequiredPlaceholders(basePackage));
}
private ResourcePatternResolver getResourcePatternResolver() {
if (this.resourcePatternResolver == null) {
this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
}
return this.resourcePatternResolver;
}
public final MetadataReaderFactory getMetadataReaderFactory() {
if (this.metadataReaderFactory == null) {
this.metadataReaderFactory = new CachingMetadataReaderFactory();
}
return this.metadataReaderFactory;
}
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
return classMetadata.isInterface() && annotationMetadata.isAnnotated(MyInvocation.class.getName());
}
}
创建对应类型的代理类
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]) 中会调用resolveBeforeInstantiation创建代理类。
/**
* 实例化有@MyInvocation的代理类
*/
public class MyProxyBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// 判断是否有@MyInvocation注解
MyInvocation myInvocation = beanClass.getAnnotation(MyInvocation.class);
if (myInvocation != null){
try {
MyInvocationHandler invocationHandler = new MyInvocationHandler(beanClass);
return invocationHandler.getObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 创建代理对象
return null;
}
}
标注代理
@MyInvocation
public interface MyInvocationService {
void service();
default void say(){
System.out.println("say");
}
}
启动类+获取对应的接口实现类
@MyInvocationAutoConfiguration(basePackages = "com.example.springbootinvocation.invocation")
@SpringBootApplication
public class SpringBootInvocationApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootInvocationApplication.class, args);
MyInvocationService service = applicationContext.getBean(MyInvocationService.class);
service.service();
service.say();
applicationContext.close();
}
}
总结
通过spring+动态代理实现自动扫描有注解的接口自动生成对象的代理类。对于平常代理里使用的一些Http请求,可以通过动态代理实现封装。不过有现有的框架OpenFeign可以使用,OpenFeign的实现了SpringMVC的一些注解,使用起来更方便。在使用的时候需要配置一下不使用注册中心即可以实现使用OpenFeign只为Http请求,不用整复杂的那一套。