模拟mybatis实现接口依赖注入

一、前言

这一节基于前面两节mybatis 中Mapper注入spring源码分析与mybatis 接口依赖注入源码分析的理论,手动实现将接口放入Spring工厂中,并且能够依赖注入。

二、实践

这里先把已写好的类贴出来,下面会一一介绍。这篇文章的两个目的如下。

image.png

目的一:将TestInterface 接口放入Spring的工厂中.

@Component
public interface TestInterface {
    void test();
}

目的二:当依赖注入成功后,执行test()方法的时候能够打印一段话:调用test方法成功。

    @Autowired
    public void test(TestInterface testInterface) {
      testInterface.test();
    }

2.1 接口放入Spring中

我们可以参考mybatis中的ClassPathMapperScanner类的实现,ClassPathMapperScanner就可以将接口放入Spring中。默认的ClassPathBeanDefinitionScanner只能将非抽象类放入Spring中,接口是不能放入的,因此为了满足接口能放入Spring中,我们需要重写ClassPathBeanDefinitionScanner。具体的类如下。

public class TestScanner extends ClassPathBeanDefinitionScanner {
    public TestScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
    @Override
    public Set doScan(String... basePackages) {
        Set beanDefinitions = super.doScan(basePackages);
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitions) {
            GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
            // 构造函数的入参 这里的BeanClassName 其实就是接口的className
            genericBeanDefinition.
                    getConstructorArgumentValues().
                    addGenericArgumentValue(genericBeanDefinition.getBeanClassName());
            //供spring实例化代理对象使用
            genericBeanDefinition.setBeanClass(TestFactoryBean.class);
        }
        return beanDefinitions;
    }

    /**
     *
     * @param beanDefinition
     * @return beanDefinition 是接口 && 独立的类,则返回true
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
}

2.2 代理类

2.2.1 TestFactoryBean

这个类就是模仿mybatisMapperFactoryBean,主要作用是能够让Spring实例化代理对象。

public class TestFactoryBean implements FactoryBean {
    private Class testClass;
    /**
     * 
     * @param tClass  TestInterface 接口class
     */
    public TestFactoryBean(Class tClass) {
        this.testClass = tClass;
    }

    /**
     * 这个方法将会在依赖注入之前调用。
     * @return
     * @throws Exception
     */
    @Override
    public T getObject() throws Exception {
        return (T) Proxy.newProxyInstance(testClass.getClassLoader(), new Class[]{testClass}, new TestProxy());
    }

    @Override
    public Class getObjectType() {
        return testClass;
    }

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

2.2.2 java 动态代理

这个类参考MapperProxy.

public class TestProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // getDeclaringClass method 方法在哪个类class中
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        System.out.println("调用"+method.getName()+"方法成功");
        // 执行sql 操作。
        return "";
    }

}

2.3 配置类

@Configuration
public class Config {
    @Autowired
    public void init(DefaultListableBeanFactory factory) throws Exception {
        TestScanner testScanner = new TestScanner(factory);
        // 指定扫描哪个包下的class
        testScanner.doScan("com.mytijian.order.test");
    }
    @Autowired
    public void test(TestInterface testInterface) {
      testInterface.test();
    }
}

三、结果

image.png

四、总结

其实这三篇文章关键的点还是ClassPathBeanDefinitionScanner 类、Java动态代理、FactoryBean ,只要抓住这三个点就可以了,代码的实现倒是其次,就好像你写一个字我写一个字,字形虽不同但意思却是一样的。

你可能感兴趣的:(模拟mybatis实现接口依赖注入)