Spring boot–自定义类扫描器
前言
之前一直在写RPC框架是基于Spring的框架。通过扫描接口并将其注入代理工厂的方式是采用了Spring 自定义标签的方式。最近一直在玩Spring cloud,所以也希望自己的RPC框架能够直接在Spring boot上面运行。所以将其修改了一下。其实改成基于Spring boot框架的RPC主要是要修改两个地方。第一个地方是将在Spring 配置文件中的< bean >改成基于Java 的配置。
第二个就是自定义的类扫描器。
ClassPathBeanDefinitionScanner简介
用过Mybatis的小伙伴应该知道我们扫描包是在启动类上面加一个@MapperScan(“com.xxx.xxx.dao”)这种方式。
其实Mybatis 的Mapper注册器(ClassPathMapperScanner) 是同过继承ClassPathBeanDefinitionScanner,并且自定义了过滤器规则来实现的。那我只要仿造Mybatis的方式自己写一个就行
ImportBeanDefinitionRegistrar简介
spring官方就是用这种方式,实现了@Component、@Service等注解的动态注入机制。定义一个ImportBeanDefinitionRegistrar的实现类,然后在有@Configuration注解的配置类上使用@Import导入
自定义注册过程
我们需要定义3个个东西
- 第一个是自定义注解 用于启动类
- 第二个是一个实现ImportBeanDefinitionRegistrar的类 用于自定义注解通过@Import
- 第三个就是自定义类扫描器 实现我们对于接口的自定义注入
自定义注解
package com.ljl.rpc.annotation;
import com.ljl.rpc.handle.ClientPackageScan;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ClientPackageScan.class)
@Documented
/*
* 标注哪个包作为RPC消费接口 自定义注入
* */
public @interface RPCClientPackage {
String[] basePackage() default {};
}
实现ImportBeanDefinitionRegistrar
package com.ljl.rpc.handle;
import com.ljl.rpc.annotation.RPCClientPackage;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
/**
* Created by Administrator on 2019/9/10 0010.
*/
public class ClientPackageScan implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(RPCClientPackage.class.getName()));
String[] basePackages = annoAttrs.getStringArray("basePackage");
//自定义的 包扫描器
ClientPackageScanHandle scanHandle = new ClientPackageScanHandle(beanDefinitionRegistry,false);
//扫描指定路径下的接口
scanHandle.doScan(basePackages);
}
}
自定义类扫
这里添加工程bean是和原来的思路一样的
package com.ljl.rpc.handle;
import com.ljl.rpc.ProxyFactory.MethodProxyFactory;
import com.ljl.rpc.annotation.RPCClient;
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;
/**
* Created by Administrator on 2019/9/10 0010.
*
*/
public class ClientPackageScanHandle extends ClassPathBeanDefinitionScanner {
public ClientPackageScanHandle(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
//添加过滤条件
addIncludeFilter(new AnnotationTypeFilter(RPCClient.class));
//调用spring的扫描
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
if(beanDefinitionHolders.size() != 0){
//给扫描出来的接口添加上代理对象
processBeanDefinitions(beanDefinitionHolders);
}
return beanDefinitionHolders;
}
/**
* 给扫描出来的接口添加上代理对象
* @param beanDefinitions
*/
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//拿到接口的全路径名称
String beanClassName = definition.getBeanClassName();
//设置属性 即所对应的消费接口
try {
definition.getPropertyValues().add("interfaceClass", Class.forName(beanClassName));
//设置Calss 即代理工厂
definition.setBeanClass(MethodProxyFactory.class);
//按 照查找Bean的Class的类型
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}
使用
@SpringBootApplication
@RPCClientPackage(basePackage = "com.ljl.server.service")
public class LjlRpcServerApplication {
public static void main(String[] args) {
SpringApplication.run(LjlRpcServerApplication.class, args);
}
}
总结
我们可以发现Spring boot 真的很厉害,化繁为简。把许许多多的配置文件都省下来了,代码也美观了很多。
我们可以迁移之Spring boot 之后我们只需要配置这一点东西 加上几个注解就能完成一个第三方框架的引入。
zookeeper:
hosts: 127.0.0.1:2181
baseackage : com.ljl.server.service.Impl
netty:
port: 8885
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/15311.html