Предисловие:
В процессе изучения Netty в последнее время я следовал идеям предшественников и использовал Netty в качестве основного средства коммуникации для разработки очень крутой, первой во вселенной (фактически супер-мусора) Netty Rpc Demo. Почему бы не назвать фреймворк Demo?Хороший фреймворк требует очень много времени на разработку и оптимизацию, и это неотделимо от полной самоотдачи больших парней.Для новичка моего уровня это в лучшем случае называется demo. Ну, без лишних слов, исходная идея состоит в том, чтобы вручную настроить карту сопоставления между интерфейсами и классами реализации, как показано ниже:
@Bean("handlerMap")
public Map<String, Object> handlerMap(){
Map<String, Object> handlerMap = new ConcurrentHashMap<String, Object>();
handlerMap.put("com.jdkcb.mystarter.service.PersonService",new PersonServiceImpl());
return handlerMap;
}
Не распыляйте больших парней Когда я уверенно передал код старшим, старшие были очень терпеливы (неуправляемы) и указали на проблему, которую я сделал. Действительно, когда количество интерфейсных классов очень велико, настраивать карту в одиночку очень хлопотно, поэтому я вернулся, чтобы хорошенько подумать, и очень хорошо выспался.Через семь, семь, сорок девять минут вспыхнула лампочка. моя голова, и я подумал о позавчерашнем аннотации, которую я видел, когда писал Mybatis для настройки нескольких источников данных, @MapperScan
охохохохохох, это мой эксклюзивный момент, поэтому я решил, что это ты.
Далее мы будем имитировать реализацию Mybatis, чтобы создать сканер аннотаций для сканирования и регистрации наших пользовательских аннотаций как bean-компонентов, управляемых Spring.
Солдаты отправляются на поле боя и начинают работать непосредственно. (Ну, это на самом деле стебель, я думаю, многие люди не могут его получить)
Код боя:
Поскольку мы хотим сканировать наши пользовательские аннотации, сначала у нас должна быть пользовательская аннотация. Давай, Xiao Er, поставь пользовательскую аннотацию, совмещенную с моими личными потребностями, я настрою ее как NRpcServer, код такой:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NRpcServer {
//服务的名称,用来RPC的时候调用指定名称的 服务
String name() default "";
String value() default "";
}
Однако у меня есть вопрос, как вы узнали, что нужно добавить аннотации @Target({ElementType.TYPE, ElementType.METHOD})?
ххх,кажется нашел.Не могу написать.Не могу скопировать.Поскольку это имитация аннотации @MapperScan(), просто нажмите на аннотацию @Mapper и посмотрите какие аннотации добавлены к нему, да? , эй-эй
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Mapper {
}
Хорошо, удалите аннотации, которые нам не нужны, атрибуты, которые мы не используем, и измените имя на наши собственные аннотации. (смешной)
Как сканируется @Mapper?Я думаю, что с помощью аннотации @MapperScan мы можем прийти к соглашению.копировать один, Место ограничено, я не буду выкладывать код Mybatis позже, друзья, которым нужно знать, могут открыть исходный код mybatis для просмотра.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
//spring中的注解,加载对应的类
@Import(NRpcScannerRegistrar.class)//这个是我们的关键,实际上也是由这个类来扫描的
@Documented
public @interface NRpcScan {
String[] basePackage() default {};
}
Конечно, сама по себе эта аннотация — ничто, где основная часть? Ответ заключается в том, что на классе NRpcScannerRegistrar, собственно, сканирующая фильтрация наших аннотаций в основном реализуется этим классом.
Создайте новый код класса NRpcScannerRegistrar и наследуйте два интерфейса ImportBeanDefinitionRegistrar, ResourceLoaderAware.
Где: ResourceLoaderAware — это интерфейс маркера, используемый для внедрения ResourceLoader через контекст ApplicationContext.
код показывает, как показано ниже:
public class NRpcScannerRegistrar 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(NRpcScan.class.getName()));
//获取到basePackage的值
String[] basePackages = annoAttrs.getStringArray("basePackage");
//如果没有设置basePackage 扫描路径,就扫描对应包下面的值
if(basePackages.length == 0){
basePackages = new String[]{((StandardAnnotationMetadata) annotationMetadata).getIntrospectedClass().getPackage().getName()};
}
//自定义的包扫描器
FindNRpcServiceClassPathScanHandle scanHandle = new FindNRpcServiceClassPathScanHandle(beanDefinitionRegistry,false);
if(resourceLoader != null){
scanHandle.setResourceLoader(resourceLoader);
}
//这里实现的是根据名称来注入
scanHandle.setBeanNameGenerator(new RpcBeanNameGenerator());
//扫描指定路径下的接口
Set<BeanDefinitionHolder> beanDefinitionHolders = scanHandle.doScan(basePackages);
}
}
Это включает в себя класс FindNRpcServiceClassPathScanHandle, который является нашим настраиваемым сканером пакетов, и мы можем добавить наши условия фильтрации к этому сканеру.
public class FindNRpcServiceClassPathScanHandle extends ClassPathBeanDefinitionScanner {
public FindNRpcServiceClassPathScanHandle(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
//添加过滤条件,这里是只添加了@NRpcServer的注解才会被扫描到
addIncludeFilter(new AnnotationTypeFilter(NRpcServer.class));
//调用spring的扫描
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
return beanDefinitionHolders;
}
}
Когда вы увидите это, многие читатели почти обнаружат, что я на самом деле ничего не делаю.Все основные операции выполняются реализацией, предоставленной Spring.На самом деле, да, основной код на самом деле находится в
super.doScan(basePackages);
В этом предложении мы фактически выполнили вторичную обработку в соответствии с результатами сканирования Spring, В моей демонстрации упомянутая ранее карта фактически автоматически генерируется на этом этапе, потому что на этот раз мы в основном говорим о реализации сканера пакетов, так что часть логики была удалена.
В то же время класс RpcBeanNameGenerator в приведенном выше коде реализует внедрение указанного bean-компонента в соответствии с нашим именем, что мы фактически делаем здесь, так это получаем значение, установленное атрибутом в аннотации. код показывает, как показано ниже:
public class RpcBeanNameGenerator extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
//从自定义注解中拿name
String name = getNameByServiceFindAnntation(definition,registry);
if(name != null && !"".equals(name)){
return name;
}
//走父类的方法
return super.generateBeanName(definition, registry);
}
private String getNameByServiceFindAnntation(BeanDefinition definition, BeanDefinitionRegistry registry) {
String beanClassName = definition.getBeanClassName();
try {
Class<?> aClass = Class.forName(beanClassName);
NRpcServer annotation = aClass.getAnnotation(NRpcServer.class);
if(annotation == null){
return null;
}
//获取到注解name的值并返回
return annotation.name();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
На данный момент это почти сделано.
контрольная работа:
Во-первых, мы готовим класс PersonService и аннотируем его нашим @NRpcServer, Код выглядит следующим образом:
@NRpcServer(name="PersonService")
public class PersonService {
public String getName(){
return "helloword";
}
}
Затем добавьте аннотацию @NRpcScan в класс запуска Springboot и укажите пакеты, которые нам нужно сканировать.
@NRpcScan(basePackage = {"com.jdkcb.mybatisstuday.service"})
Создайте новый контроллер для тестирования, код выглядит следующим образом:
@RestController
public class TestController {
@Autowired
@Qualifier("PersonService")
private PersonService personService;
@RequestMapping("test")
public String getName(){
return personService.getName();
}
}
Введите http://127.0.0.1:8080/test в браузере.
Выходной результат:
helloword
На данный момент, даже если настоящий успех достигнут.
В конце концов, всем привет, я Хань Шу, гудите, следуйте за мной и ешьте привет фрукты (акимбо).
Не забудьте поставить лайк перед уходом~
Подождите минутку:
Соответствующий исходный код можно загрузить на мой github (добро пожаловать звездочка):