参考MongoRepository,为接口生成bean实现注入
Posted badboyf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了参考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,具体怎么看情况
以上是关于参考MongoRepository,为接口生成bean实现注入的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 MongoRepository 接口更新 mongo db 中的特定字段?
使用spring data mongo 多数据源如何切换 使用注解形式 不影响使用 MongoRepository 接口方案。
解决SpringBoot 整合 MongoDB,继承MongoRepository的Repo接口无法注入异常
解决SpringBoot 整合 MongoDB,继承MongoRepository的Repo接口无法注入异常