zoukankan      html  css  js  c++  java
  • 参考MongoRepository,为接口生成bean实现注入

    首先弄个注解,给代码个入口,这个就是mongo的@EnableMongoRepositories了。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(ProxyBeanDefinitionRegistrar.class)
    public @interface DefaultProxy {
        String[] packages() default {};
    }

    还有一个注解,类似mongo的NoRepositoryBean

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface NoProxyBean {
    
    }

    上面的ProxyBeanDefinitionRegistrar,就是入口了,在这里注册bean

    public class ProxyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
        private ResourceLoader resourceLoader;
        private Environment environment;
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
            if (annotationMetadata.getAnnotationAttributes(DefaultProxy.class.getName()) == null) {
                return;
            }
    
            ClasspathScannerProvider scanner = new ClasspathScannerProvider(new ArrayList<>(), registry);
            scanner.setEnvironment(environment);
            scanner.setResourceLoader(resourceLoader);
    
            List<BeanDefinition> beanComponentDefinitions = new ArrayList<BeanDefinition>();
            for (String basePackage : getBasePackages(annotationMetadata)) {
                Set<BeanDefinition> candidate = scanner.findCandidateComponents(basePackage);
                beanComponentDefinitions.addAll(new ArrayList<>(candidate));
            }
    
            for (BeanDefinition beanDefinition : beanComponentDefinitions) {
                RootBeanDefinition bean = new RootBeanDefinition();
                bean.setBeanClassName(ProxyServiceFactoryBean.class.getName());
                bean.setFactoryMethodName(null);
                bean.getConstructorArgumentValues().addIndexedArgumentValue(0, beanDefinition.getBeanClassName());
                registry.registerBeanDefinition(beanDefinition.getBeanClassName(), bean);
            }
        }
    
        Set<String> getBasePackages(AnnotationMetadata metadata) {
            Set<String> packages = new HashSet<>();
            Map<String, Object> attr = metadata.getAnnotationAttributes(DefaultProxy.class.getName());
            String[] pac = (String[]) attr.get("packages");
            for (String tmp : pac) {
                packages.add(tmp);
            }
            
            return packages;
        }
    }

    实现代理,是实现一个接口,在继承需要代理的类,spring-data-mongo中,这个类是SimpleMongoRepository,实现的那个接口就是自定义的实现了MongoRepository的接口(如:UserRepository)
    我们也得做一个接口,所有需要代理的接口都实现它IProxyService

    @NoProxyBean
    public interface IProxyService {
    
        void test();
    }

    做一个需要被代理的类,不然没法实现代理SimpleService,就是一个最简单的类

    public class SimpleService {
    
    }

    spring-data-mongo做代理的代码是MongoRepositoryFactoryBean,我们的类似的为

    public class ProxyServiceFactoryBean<T extends IProxyService> implements InitializingBean, FactoryBean<T>, BeanClassLoaderAware {
        private ClassLoader classLoader;
        private T repository;
        Class<? extends T> serviceInterface;
        
        public ProxyServiceFactoryBean(Class<? extends T> repositoryInterface) {
            this.serviceInterface = repositoryInterface;
        }
    
    
        @Override
        public void afterPropertiesSet() throws Exception {
            initAndReturn();
        }
    
    
        @SuppressWarnings("unchecked")
        private T initAndReturn() {
            SimpleService target = new SimpleService();
            ProxyFactory result = new ProxyFactory();
            result.setTarget(target);
            result.setInterfaces(new Class[] { serviceInterface });
    
            result.addAdvice(new ProxyInterpreter(target, serviceInterface));
    
            this.repository = (T) result.getProxy(classLoader); 
            return this.repository;
        }
        
        static class ProxyInterpreter implements MethodInterceptor {
            private final Object target;
            private final Class<?> serviceInterface;
    
            public ProxyInterpreter(Object target, Class<?> serviceInterface) {
                this.target = target;
                this.serviceInterface = serviceInterface;
            }
    
            public Object invoke(MethodInvocation invocation) throws Throwable {
    
                Object result = doInvoke(invocation);
    
                return result;
            }
    
            private Object doInvoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                Object[] arguments = invocation.getArguments();
    
                System.out.println("invoke " + method.getName() + "(), args=" + arguments + ", target=" + target + ", interface=" + serviceInterface);
                return null;
            }
        }
        
        public T getObject() {
            return (T) initAndReturn();
        }
        public Class<? extends T> getObjectType() {
            return serviceInterface;
        }
        public boolean isSingleton() {
            return true;
        }
        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    }

    InitializingBean, FactoryBean实现这两个接口在关键点,InitializingBean是注册bean后做代理,FactoryBean是在spring处理依赖注入时,判断是不是要注入的是一个FactoryBean,如果是FactoryBean会调用getObject()来生成真正需要注入的类型。如果不实现FactoryBean,启动会报错

    Description:
    
    Field proxyService in com.fzk.proxy.test.service.ProxyController required a bean of type 'com.fzk.proxy.test.service.CustomProxyService' that could not be found.
    
    Action:
    
    Consider defining a bean of type 'com.fzk.proxy.test.service.CustomProxyService' in your configuration.

    别忘了启动类加上注解

    @DefaultProxy(packages = { "com.fzk" })

    就会发现实现了IProxyService接口(并不需要实现类)可以被@Autowired,调用方法时会输出

                System.out.println("invoke " + method.getName() + "(), args=" + arguments + ", target=" + target + ", interface=" + serviceInterface);

    这只是个demo,具体怎么看情况

  • 相关阅读:
    java 集合中将元素倒序排列
    自定义AccessDeniedHandler
    如何在spring中获取request对象
    mybatis 做 insert操作的时候返回插入的那条数据的id
    Spring Security-用户密码自定义加密
    JS定义类及对象
    HTML5坦克大战(1)绘制坦克
    JSON学习【转自慕课网】
    HTML5之语音识别实例
    绚丽的倒计时(1)---画出时钟
  • 原文地址:https://www.cnblogs.com/badboyf/p/10270690.html
Copyright © 2011-2022 走看看