实现动态代理接口并注入到IOC容器的第二种方式:ImportBeanDefinitionRegistrar+ClassPathBeanDefinitionScanner

准备工作:

  1. 编写扫描包注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 使用import的方式导入
@Import(AutoMapperScanImportBeanDefinitionRegistrar.class)
public @interface MapperScan {
    @AliasFor("value")
    String[] basePackage() default {};

    @AliasFor("basePackage")
    String[] value() default {};
}
  1. 编写注解,用来识别哪些接口需要被动态代理(类似于@Repository
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName NeedProxy
 * @Description TODO
 * @Author Silwings
 * @Date 2021/3/7 15:57
 * @Version V1.0
 **/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedProxy {
    String value() default "";
}
  1. 编写公共接口(类似于@Mapper)
/**
 * @ClassName Repository
 * @Description 泛型用来声明实体类类型(参考MyBatis)
 * @Author Silwings
 * @Date 2021/3/7 15:58
 * @Version V1.0
 **/
public interface Repository<T> {
	// 示例方法.
   String  print();
}
  1. 编写公共接口的默认实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @ClassName DefaultRepository
 * @Description TODO
 * @Author Silwings
 * @Date 2021/3/7 16:00
 * @Version V1.0
 **/
public class DefaultRepository<T> implements Repository<T> , InvocationHandler {
	// 这里声明一个Class,用来接收接口声明的泛型实际类型的class,T是声明的实体类类型
    private Class<T> clazz;

    public DefaultRepository(Class<T> interfaceType) {
        // 获取当前类上的泛型类型
        ParameterizedType parameterizedType = (ParameterizedType) interfaceType.getGenericInterfaces()[0];
        // 获取泛型对应的真实类型(泛型真实类型在很多场合需要使用)
        Type[] actualType = parameterizedType.getActualTypeArguments();
        // 取数组的第一个,肯定是T的类型,即实体类类型(如果有多个,递增角标即可)
        // 需要注意,继承BaseRepository的接口不能定义泛型,否则会出现类型转换异常
        this.clazz = (Class<T>) actualType[0];
    }

    @Override
    public String print() {
        // 示例方法的默认实现
        System.out.println(clazz);
        return clazz.getName();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Object 方法,走原生方法,比如hashCode()
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this,args);
        }
        // 其它走本地代理
        return method.invoke(this, args);
    }
}
  1. 默认实现完成后,需要使用FactoryBean来构建它.
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Proxy;

/**
 * @ClassName RepositoryFactory
 * @Description FactoryBean是一种特殊的Bean,其返回的对象不是指定类的一个实例,其返回的是该工厂Bean的getObject方法所返回的对象
 * @Author Silwings
 * @Date 2021/3/7 16:01
 * @Version V1.0
 **/
public class RepositoryFactory<T> implements FactoryBean<T> {

    /**
     * 构建DefaultRepository需要使用的参数
     */
    private Class<T> interfaceType;

    public RepositoryFactory(Class<T> interfaceType) {
        this.interfaceType = interfaceType;
    }

    @Override
    public T getObject() throws Exception {
        // 因为DefaultRepository需要Class<T>作为参数,所以该类包含一个Claa<T>的成员,通过构造函数初始化
        return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[]{interfaceType},
                new DefaultRepository<>(interfaceType));
    }

    @Override
    public Class<?> getObjectType() {
        // 该方法返回的getObject()方法返回对象的类型,这里是基于interfaceType生成的代理对象,所以类型就是interfaceType
        return interfaceType;
    }
}

核心工作

该方法的核心是需要继承和实现ClassPathBeanDefinitionScannerImportBeanDefinitionRegistrar.通过自定义类扫描器完成类的扫描工作

  1. 继承ClassPathBeanDefinitionScanner
import com.silwings.mapperdemo.anno.Repository;
import com.silwings.mapperdemo.repository.RepositoryFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Set;

/**
 * @ClassName AutoMapperScanClassPathBeanDefinitionScanner
 * @Description TODO
 * @Author Silwings
 * @Date 2021/3/13 10:00
 * @Version V1.0
 **/
public class AutoMapperScanClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

    public AutoMapperScanClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }
    
 	/**
     * description: 负责对接口代理进行定义
     * version: 1.0
     * date: 2021/3/13 11:04
     * author: Silwings
     *
     * @param beanDefinitionHolderSet
     * @return void
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 添加过滤器,只扫描添加了NeedProxy注解的类
        addIncludeFilter(new AnnotationTypeFilter(NeedProxy.class));
        Set<BeanDefinitionHolder> beanDefinitionHolderSet = super.doScan(basePackages);
        // 对扫描到的数据进行代理处理
        processBeanDefinitions(beanDefinitionHolderSet);
        return beanDefinitionHolderSet;
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitionHolderSet) {
        beanDefinitionHolderSet.forEach(e -> {
            // 设置工厂等操作需要基于GenericBeanDefinition,BeanDefinitionHolder是其子类
            GenericBeanDefinition definition = (GenericBeanDefinition) e.getBeanDefinition();
            // 获取接口的全路径名称
            String beanClassName = definition.getBeanClassName();
            // 设置构造函数参数
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
            // 设置工厂
            definition.setBeanClass(RepositoryFactory.class);
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        });
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
}
  1. 实现ImportBeanDefinitionRegistrar,这样可以对自定义的类路径扫描器创建对象进行扫描
import com.silwings.mapperdemo.anno.MapperScan;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @ClassName AutoMapperScanImportBeanDefinitionRegistrar
 * @Description TODO
 * @Author Silwings
 * @Date 2021/3/13 9:59
 * @Version V1.0
 **/
public class AutoMapperScanImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取MapperScan注解属性信息
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        // 获取注解的属性值,拿到定义的扫描路径
        String[] basePackages = annotationAttributes.getStringArray("basePackage");
        // 使用自定义扫描器扫描
        AutoMapperScanClassPathBeanDefinitionScanner scanner = new AutoMapperScanClassPathBeanDefinitionScanner(registry, false);
        scanner.doScan(basePackages);
    }
}

使用:

  1. 在启动类上添加MapperScan注解,并指定需要扫描哪个路径
@SpringBootApplication
@MapperScan({"com.silwings.mapperdemo.mapper"})
public class MapperApplication {
    public static void main(String[] args) {
        SpringApplication.run(MapperApplication.class, args);
    }
}
  1. 继承Repository
@NeedProxy
public interface Test03 extends Repository<User> {
}
  1. 编写controller
@RestController
@RequestMapping("/my")
public class TestController {

    private Test03 test03;

    @Autowired
    public TestController(Test03 test03) {
        this.test03 = test03;
    }

    @GetMapping("/test03")
    public String test03() {
        Objects.requireNonNull(test03, "你的代码怎么又报错啦!");
        System.out.println("测试 getClass() = " + test03.getClass());
        System.out.println("测试 hashCode() = " + test03.hashCode());
        return test03.print();
    }

}
  1. 请求localhost:8080/my/test03.
  1. 控制台打印
测试 getClass() = class com.sun.proxy.$Proxy49
测试 hashCode() = 1018602505
class com.silwings.mapperdemo.bean.User
  1. 请求响应结果
com.silwings.mapperdemo.bean.User

说明已经成功对接口进行代理并注入到了Spring容器,同时像hashCode()这种Object的方法也可以正常执行.