In the previous article, @import introduced the principle of ImportSelector and its application in SpringBoot automatic assembly technology:

Application of @import in SpringBoot automatic assembly technology

ImportBeanDefinitionRegistrar, introduced this article tell @ Import, as well as references in Mybatis, this article will write a Mybatis chestnuts, later will finish writing the content of the Spring source.

First take a look at ImportBeanDefinitionRegistrar this interface:

public interface ImportBeanDefinitionRegistrar {
   void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
Copy the code

This interface has a BeanDefinitionRegistry, which does not return values, compared to the ImportSelector interface. If you need BeanDefinitionRegistry, you can use it to do whatever you want.

Mybatis framework mybatis framework myBatis framework

First a ImportBeanDefinitionRegistrar custom, to realize its registerBeanDefinitions:

package importbeandefinitionregistry.registrar;

import importbeandefinitionregistry.factorybean.MyMapperFactoryBean;
import importbeandefinitionregistry.myannotation.MyMapperScan;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
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.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/ * * * custom ImportBeanDefinitionRegistrar * /
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware {
   private static final String VALUE = "value";
   private ResourceLoader resourceLoader;

   /** * provides annotation information (importingClassMetadata) and a registry *@param importingClassMetadata annotation metadata of the importing class
    * @param registry current bean definition registry
    */
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      // Fetch the relevant annotation information (package name), scan it, construct the object by proxy, and register it in the bean container
      AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(
            importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName()));
      if(null! =annotationAttributes){for(String path : getScanPath(annotationAttributes)){
            // Use a FactoryBean to dynamically generate proxy objects for the interface
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyMapperFactoryBean.class);
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            // Pass the full path of the interface as an argument to FactoryBean to generate the proxy object
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(path);
            // Register beanDefinition with the container
            registry.registerBeanDefinition(lowerFirst(path.substring(path.lastIndexOf(".") +1)),beanDefinition); }}}@Override
   public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourceLoader = resourceLoader;
   }

   /** * get the full path of the interface under the package in the annotation *@paramAnnotationAttributes annotation *@returnThe full interface path under the package in the annotation */
   private List<String> getScanPath(AnnotationAttributes annotationAttributes){
      List<String> scanPath = new ArrayList<>();
      for(String basePackage:annotationAttributes.getStringArray(VALUE)){
         ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
         MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resolver);
         try {
            Resource[] resources = resolver.getResources("classpath*:" + basePackage.replace('. '.'/') + "/**/*.class");
            for(Resource resource:resources){ MetadataReader metadataReader = readerFactory.getMetadataReader(resource); scanPath.add(metadataReader.getClassMetadata().getClassName()); }}catch(IOException e) { e.printStackTrace(); }}return scanPath;
   }

   /** ** the first letter is smaller than *@paramOldStr is the string * to convert@returnThe converted string */
   private String lowerFirst(String oldStr){
      char[] charArray = oldStr.toCharArray();
      charArray[0] + =32;
      returnString.valueOf(charArray); }}Copy the code

This custom ImportBeanDefinitionRegistrar did some what? AnnotationMetadata is used to retrieve AnnotationMetadata parameters, that is, packets that need to be scanned, and then to scan those packets. Take a look at custom MapperScan

@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface MyMapperScan {
   String[] value() default {};
}
Copy the code

When we use Mybatis, are through to call the method of Mapper, and then write SQL to query the database, but we define mapper is interface, and interface how can be directly called? The mapper we call is not an interface, but a proxy object. Here’s how to generate proxy objects for these mappers:

package importbeandefinitionregistry.factorybean;

import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** * Custom FactoryBean **@author cy
 */
public class MyMapperFactoryBean  implements FactoryBean ,InvocationHandler{

   private Class interfaceClass;

   public MyMapperFactoryBean(Class interfaceClass){
      this.interfaceClass = interfaceClass;
   }

   / * * * *@returnReturn the proxy object *@throwsThe Exception Exception * /
   @Override
   public Object getObject(a) throws Exception {
      return Proxy.newProxyInstance(getClass().getClassLoader(),new Class[]{interfaceClass},this);
   }

   / * * * *@returnReturns the class * /
   @Override
   publicClass<? > getObjectType() {return interfaceClass;
   }

   /** * Print out the SQL * in the annotation@paramThe proxy agent *@param* method method@param* args parameter@return object
    * @throwsThrowable exception * /
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      String sql = method.getDeclaredAnnotation(Select.class).value()[0];
      / / print the SQL
      System.out.println(sql);
      //JDBC to query the database and return the result set
      return "Ha ha ha ha."; }}Copy the code

Here we use the FactoryBean interface, which we won’t discuss in this article, but which takes a class, generates a proxy object for that class, and returns it. So that we in the custom ImportBeanDefinitionRegistrar resulting object is a proxy object, rather than the interface, then through the method provided by the registry parameters, the proxy object to register in the spring container (in beanDefinitionMap, Instantiate the bean and register it with the Spring container at a later stage.

In the invoke method, you actually get the @select parameter information, the SQL, which is simply printed out here. Now that we have got SQL here, we can query the database through JDBC, and finally return the results, and finally call the method of Mapper to get the result set you query.

Here is mapper, a simple query statement:

public interface UserMapper {

   @Select("select * from user limit 1")
   String queryUser(a);

}
Copy the code

Configure class, scan mapper package:

@Configuration
@MyMapperScan("importbeandefinitionregistry.mapper")
public class MyConfiguration {}Copy the code

Finally, write the test class:

public class DataSourceTest {

   public static void main(String[] args) {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
		UserMapper userMapper = (UserMapper) context.getBean("userMapper"); String s = userMapper.queryUser(); System.out.println(s); }}Copy the code

Execution Result:

You can see the method printed in the Invoke method and the final output – >” hahaha”

The entire catalog:

Write today @ Import introduce annotations ImportBeanDefinitionRegistrar to written a short version of the mybatis framework, feel good can have a try on their own oh.

The spring source code article is now the fourth article, because it will bea separate list of some of the most important points in Spring, the subsequent article will write about the bean creation process, if you feel good, you can like the bookmark. Welcome to follow the spring source code article.