Preface:

Recently, in the process of learning Netty, follow the ideas of predecessors to use Netty as the underlying communication to develop a very awesome, the first (actual super garbage) Netty Rpc Demo. Why not call the framework Demo, a good framework is very long to develop and optimize, can not do without the full commitment of the big guys, my level of novice, at best Demo. You need to manually configure a map of the interface to the implementation class, something like this:

    @Bean("handlerMap")
    public Map<String, Object> handlerMap(a){
        Map<String, Object> handlerMap = new ConcurrentHashMap<String, Object>();
        handlerMap.put("com.jdkcb.mystarter.service.PersonService".new PersonServiceImpl());
        return handlerMap;
    }
Copy the code

When I confidently presented the code to the seniors, the seniors were very patient (and mercilessly) to point out the problem. Indeed, when the number of interface classes is very large, the configuration of Map is a very troublesome thing, so I went back to think hard, sleep very sweet, seven seven forty-nine minutes later, a light in my head, thought of the day before Mybatis configuration of multiple data sources saw a note, @mapperscan

Ohohohohohoh, this is my moment, and it’s you.

Next, we’ll emulate the Mybatis implementation to create an annotation scanner that scans and registers our custom annotations as Spring-managed beans.

Soldiers go into battle and get to work. (Ok, this is actually a meme, which I guess a lot of people don’t get)

Actual code:

To scan our custom annotations, we need to have a custom annotation first. To small two, on the custom annotations, combined with my personal needs, I will customize it as NRpcServer, code as follows:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NRpcServer {
    // The name of the service used to invoke the specified service name during RPC
    String name(a) default "";
    String value(a) default "";
}


Copy the code

@target ({elementType. TYPE, elementType. METHOD}) {Target({elementType. TYPE, elementType. METHOD});

HHH, you’ve caught me. I can’t write, I can’t copy. Since it’s a copy of @mapperscan (), just click on @mapper and see what it adds

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Mapper {
}

Copy the code

Nice, get rid of the annotations that we don’t need, the attributes that we don’t need, and change the name to our own annotations. (funny)

That @mapper is how to scan it, through @mapPERScan this annotation, I think, here we estimate can reach an agreement, copy a, space is limited, I will not post Mybatis code, need to know friends can open Mybatis source view.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
// The annotation in Spring loads the corresponding class
@Import(NRpcScannerRegistrar.class)// This is our key, and it's actually the class that does the scanning
@Documented
public @interface NRpcScan {

    String[] basePackage() default {};

}

Copy the code

Of course, the annotation itself is nothing, but what is the core of it? The answer is the CLASS NRpcScannerRegistrar, which is actually the class that we entrust to filter the scanning of annotations.

Create a new code NRpcScannerRegistrar class and inherit ImportBeanDefinitionRegistrar ResourceLoaderAware the two interfaces.

Where: ResourceLoaderAware is a tag interface for injecting ResourceLoader through the ApplicationContext context

The code is as follows:

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) {

        // Get the attributes and values of all annotations
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(NRpcScan.class.getName()));
        // Get the basePackage value
        String[] basePackages = annoAttrs.getStringArray("basePackage");
        // If the basePackage scan path is not set, the value below the corresponding package is scanned
        if(basePackages.length == 0){
            basePackages = new String[]{((StandardAnnotationMetadata) annotationMetadata).getIntrospectedClass().getPackage().getName()};
        }

        // Custom package scanner
        FindNRpcServiceClassPathScanHandle scanHandle = new  FindNRpcServiceClassPathScanHandle(beanDefinitionRegistry,false);

        if(resourceLoader ! =null){
            scanHandle.setResourceLoader(resourceLoader);
        }
        // Inject by name
        scanHandle.setBeanNameGenerator(new RpcBeanNameGenerator());
        // Scan the interface in the specified pathSet<BeanDefinitionHolder> beanDefinitionHolders = scanHandle.doScan(basePackages); }}Copy the code

Here involves a FindNRpcServiceClassPathScanHandle class, it is our custom package scanner, we can add our filter conditions in the scanner.

public class FindNRpcServiceClassPathScanHandle extends ClassPathBeanDefinitionScanner {

    public FindNRpcServiceClassPathScanHandle(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // Add a filter condition, where only @nrpcServer annotations will be scanned
        addIncludeFilter(new AnnotationTypeFilter(NRpcServer.class));
        // Call spring's scan
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        returnbeanDefinitionHolders; }}Copy the code

As many readers here will almost certainly realize, I’m not really doing anything, and all the core SAO operations are done by the implementation provided by Spring. In fact, the core code is actually in

super.doScan(basePackages);

In my demo, the map mentioned before is actually generated automatically in this step. Because this time we mainly talk about the implementation of the package scanner, so we will remove that part of the logic.

Meanwhile, the RpcBeanNameGenerator class implements the injection of the specified bean by name. All it does is get the value set by the attribute in the annotation. The code is as follows:

public class RpcBeanNameGenerator extends AnnotationBeanNameGenerator {

    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        // Get the name from the custom annotation
        String name = getNameByServiceFindAnntation(definition,registry);
        if(name ! =null&&!"".equals(name)){
            return name;
        }
        // Take the superclass method
        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;
            }
            // Get the value of annotation name and return it
            return annotation.name();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

At this point, it’s almost done.

Testing:

First we’ll prepare a PersonService class and print our @nrpcServer annotation as follows:

@NRpcServer(name="PersonService")
public class PersonService {

    public String getName(a){
        return "helloword"; }}Copy the code

Then add the @nrpcScan annotation to the Springboot boot class and specify the packages we want to scan

@NRpcScan(basePackage = {"com.jdkcb.mybatisstuday.service"})
Copy the code

Create a new Controller for the test as follows:

@RestController
public class TestController {

    @Autowired
    @Qualifier("PersonService")
    private PersonService personService;

    @RequestMapping("test")
    public String getName(a){
        returnpersonService.getName(); }}Copy the code

Type http://127.0.0.1:8080/test in your browser

Output results:

helloword
Copy the code

At this point, the real work is done.

Finally, hello everyone, I am Han shu, hum, pay attention to me, you have good fruit to eat (akimbo).

Remember to click “like” before you go

Wait a minute:

Welcome to my Github download (welcome star) :

Github.com/hanshuaikan…