简单模拟spring + java 动态代理实现mybatis的接口代理demo

1,047 阅读2分钟

纯粹作为个人记录,不喜勿喷。

如有解释不对的地方,这其中并没有误人子弟的意思,恳请指正。

整体思路

问题:
    1:接口是不能被实例化的。
    2:在没有具体实例类下,如何通过接口做到像实例类一样的方式调用方法。
    3:要如何通过spring的方式生成具体的bean。
    
解决:
    1:实现BeanDefinitionRegistryPostProcessor,ResourceLoaderAware,ApplicationContextAware的三个spring的接口。
    2:扫描特定的类路径的包
    3:实现FactoryBean接口,配合sping的启动,java动态代理就是在这儿的getObject()方法中完成。
    4:修改beanDefinition的beanClass为代理工厂bean。
    5:手动生成代理接口的beanDefinitionHolder注册到beanFactory内,以便spring实现自动创建bean。

代码:

生成beanDefinitionHolder

code

@Component

public class TestBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {

private ApplicationContext applicationContext;

private ResourceLoader resourceLoader;

private String packagePath;

private MetadataReaderFactory metadataReaderFactory;

private ResourcePatternResolver resourcePatternResolver;

private Environment environment;

private BeanNameGenerator beanNameGenerator;

private ScopeMetadataResolver scopeMetadataResolver;

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

    Set<BeanDefinition> beanDefinitions = scannerPackages();
    for (BeanDefinition beanDefinition : beanDefinitions) {

        GenericBeanDefinition definition = (GenericBeanDefinition) beanDefinition;

        // 获取beanName
        String beanName = this.beanNameGenerator.generateBeanName(beanDefinition, registry);

        // 设置FactoryBean属性
        definition.getPropertyValues().add("rpcInterface", beanDefinition.getBeanClassName());

        // 把beanClass修改为工程
        definition.setBeanClass(ServiceFactory.class);

        // beanHolder
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definition, beanName);

        // 注册
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }
}

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

}

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

    // 环境变量
    this.environment = applicationContext.getEnvironment();
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;

    // 解析类元数据
    this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);

    // 针对类批量扫描包解析,但是需要对占位符进行处理
    this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);

    this.beanNameGenerator = new AnnotationBeanNameGenerator();
    this.scopeMetadataResolver = new AnnotationScopeMetadataResolver();
}

// 解析包下的类
private Set<BeanDefinition> scannerPackages() {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();

    try {
        String classPath = "classpath*:" + resolveBasePackage() + "/**/*.class";
        Resource[] resources = this.resourcePatternResolver.getResources(classPath);
        for (Resource resource : resources) {

            MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);

            // 设置scope属性值
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(sbd);
            sbd.setScope(scopeMetadata.getScopeName());

            sbd.setResource(resource);
            sbd.setSource(resource);

            candidates.add(sbd);
        }

    } catch (Exception e) {

    }

    return candidates;
}

// 解析占位符,并且把 "." 改成 "/"
private String resolveBasePackage() {
    return ClassUtils.convertClassNameToResourcePath(this.environment.resolveRequiredPlaceholders(packagePath));
}

public void setPackagePath(String packagePath) {
    this.packagePath = packagePath;
}

}

TestBeanDefinitionRegistr实现了BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware这3个接口,目的是获得spring的ApplicationContext,ResourceLoader以及在spring启动的过程中,会自动调用postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,以便我们可以对spring的BeanDefinition做个性化的处理。 目前我们的类资源加载,动态生成BeanDefinition都是在这个地方作为入口处理。

实现代理的FactoryBean

code

public class ServiceFactory implements FactoryBean {

private Class<T> rpcInterface;

@Override
public T getObject() throws Exception {
    return (T) Proxy.newProxyInstance(this.rpcInterface.getClassLoader(), new Class[]{this.rpcInterface}, new MyInvocationHandler());
}

@Override
public Class<?> getObjectType() {
    return rpcInterface;
}

@Override
public boolean isSingleton() {
    return true;
}

public void setRpcInterface(Class<T> rpcInterface) {
    this.rpcInterface = rpcInterface;
}

class MyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是代理类");
        return "result test";
    }
}

}

ServiceFactory 就是我们的工厂Bean,代理类的生成就是在这个地方实现的。 主要是使用了java JDK动态代理。

代理的接口

public interface BookService {

void book();

}

启动类

code

public class Test { public static void main (String[] args) {

    try {
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/common/server.xml");

        BookService service = context.getBean("bookService", BookService.class);
        service.book();

    } catch (Exception e) {
        System.out.println(e);
    }
}

}

server.xml的配置

code

<bean id="testBeanDefinitionRegistry" class="com.demo.service.mybatisDemo.TestBeanDefinitionRegistry">
    <property name="packagePath" value="com.demo.service.mybatisDemo.interfaces"></property>
</bean>

结果展示

用途

在代理类invoke的地方其实也可以用来实现简单的RPC,作为netty的client端。