模仿myBatis整合Spring实现扫描接口进行动态代理

本文实现扫描对应包下接口,使用JDK动态代理进行接口代理

一丶JDK动态代理的接口

/**
 * 该注解标志需要代理的接口,类似mybatis的@Mapper注解,也可以扫描的时候扫描所有接口,不进行筛选
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DemoRegistry {
}

/**
 * 进行动态代理的接口,@DemoRegistry代表筛选的条件
 */
@DemoRegistry
public interface DemoBean {

    String hello();

}

/**
 * 接口的动态代理实现
 */
public class DemoInvocationHandler implements InvocationHandler {

    public static Object build(Class<?> type) {
        // 创建代理对象,参数分别为类加载器,需要的接口Class对应(因为代理的type就是接口,所以直接new一个class数组就行),具体的代理实现
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new DemoInvocationHandler());
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 自己实现对应的代理逻辑
        // 这里实现为如果方法定义的class为Object,就直接调用这个对象的方法
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
        }
        // 方法返回值为String,就返回一个hell word,代理的具体逻辑可以自己实现
        Class<?> returnType = method.getReturnType();
        if (returnType == String.class) {
            return "hell word";
        }
        return null;
    }
}

/**
 * 在Spring中想给接口注入对应的代理实现可以使用FactoryBean,在注册BeanDefinition的时候设置接口对应的beanClass为该FactoryBean即可
 */
public class DemoFactoryBean<T> implements FactoryBean<T> {

    /**
     * 代理的接口类型
     */
    public final Class<T> type;

    public DemoFactoryBean(Class<T> type) {
        this.type = type;
    }

    @Override
    public T getObject() throws Exception {
        // 获取代理对象,使用JDK动态代理
        return (T) DemoInvocationHandler.build(type);
    }

    @Override
    public Class<T> getObjectType() {
        // 获取对象的类型
        return type;
    }

}

二丶接口扫描及注册BeanDefinition

/**
 * 启动接口扫描的注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// @Import注解是Spring提供的导入对应的类的注解,通过导入具体实现了ImportBeanDefinitionRegistrar接口的类可以自己注册BeanDefinition
@Import({DemoRegistrarTest.class})
public @interface EnableDemo {

    /**
     * 扫描的包路径
     */
    String[] value();
}

/**
 * 注册BeanDefinition
 */
public class DemoRegistrarTest implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取类上的EnableDemo注解的属性
        Map<String, Object> map = importingClassMetadata.getAnnotationAttributes(EnableDemo.class.getName());
        // 类扫描器
        MyClassScan scan = new MyClassScan(registry);
        // 从注解配置中获取扫描的包路径进行扫描
        int scanCount = scan.scan((String[]) map.get("value"));
        System.out.println(scanCount);
    }
}

/**
 * 自定义扫描器
 */
public class MyClassScan extends ClassPathBeanDefinitionScanner {

    public MyClassScan(BeanDefinitionRegistry registry) {
        super(registry, false);
        // 注册筛选器
        registerFilters();
    }

    public void registerFilters() {
        // 筛选只有包含DemoRegistry注解的接口
        addIncludeFilter(new AnnotationTypeFilter(DemoRegistry.class));
    }

    /**
     * 这个必须要重写,spring提供的ClassPathBeanDefinitionScanner默认是不要接口的,不重写这个方法,获取不到接口
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        // 是不是接口且是独立的bean(不依赖其他bean)
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 进行扫描,获取BeanDefinitionHolder
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        // 循环替换beanDefinition中的beanClass为FactoryBean,不进行替换的话,beanClass是接口,Spring无法通过接口得到Bean对象
        for (BeanDefinitionHolder definitionHolder : beanDefinitionHolders) {
            // 获取beanDefinition
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definitionHolder.getBeanDefinition();
            // 获取扫描出的接口的beanClassName
            String beanClassName = beanDefinition.getBeanClassName();
            // 设置BeanClass为DemoFactoryBean
            beanDefinition.setBeanClass(DemoFactoryBean.class);
            // 添加构造方法的参数,DemoFactoryBean的构造方法需要Class对象,这里设置的是string的参数,spring会使用Class.forName加载
            // 成DemoFactoryBean中构造方法需要的类参数
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
        }
        return beanDefinitionHolders;
    }

}

三丶测试

/**
 * 测试代码
 */
@Service
public class DemoInterfaceTest {

    /**
     * 注入对象
     */
    @Autowired
    private DemoBean demoBean;

    @PostConstruct
    public void init() {
        // 执行方法,这里就会执行代理中的逻辑
        System.out.println(demoBean.hello());
        System.out.println(demoBean.toString());
        // 输出
        // hell word
        // com.example.cachedemo.factory.DemoInvocationHandler@30bf26df()
    }
}

你可能感兴趣的:(java,spring,mybatis)