自定义自动代理+spring

203 阅读3分钟

模拟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请求,不用整复杂的那一套。