SpringBoot如何动态代理接口并注入到IOC容器?方式 二:ImportBeanDefinitionRegistrar+ClassPathBeanDefinitionScanner

实现动态代理接口并注入到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 {};
    }
    
  2. 编写注解,用来识别哪些接口需要被动态代理(类似于@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 "";
    }
    
  3. 编写公共接口(类似于@Mapper)

    /**
     * @ClassName Repository
     * @Description 泛型用来声明实体类类型(参考MyBatis)
     * @Author Silwings
     * @Date 2021/3/7 15:58
     * @Version V1.0
     **/
    public interface Repository<T> {
    	// 示例方法.
       String  print();
    }
    
  4. 编写公共接口的默认实现

    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);
        }
    }
    
  5. 默认实现完成后,需要使用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();
        }
    }
    
  2. 实现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);
        }
    }
    
  2. 继承Repository

    @NeedProxy
    public interface Test03 extends Repository<User> {
    }
    
  3. 编写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();
        }
    
    }
    
  4. 请求localhost:8080/my/test03.

    1. 控制台打印

      测试 getClass() = class com.sun.proxy.$Proxy49
      测试 hashCode() = 1018602505
      class com.silwings.mapperdemo.bean.User
      
    2. 请求响应结果

      com.silwings.mapperdemo.bean.User
      

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

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值