• This article to achieve the function
Following Mybatis in conjunction with Spring, custom annotations are implemented by implementing queries to the specified interface method database.
The expected input is as follows;

@Sl4j
public class AnnotationRunner {
  public static void main(String[] args) {
    //
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(MyConfig.class);
      context.refresh();
      CustomerQuery bean = context.getBean("customerQuery", CustomerQuery.class); System.out.println(bean.query()); }}Copy the code

Just care about the last step bean.query(). This method is not actually implemented, but we changed it when we instantiated the bean.


The expected output is a query to the database that returns information for all customers.


To achieve the above functions, decomposed into the following several sub-problems
  1. This interface is left to Spring to manage and generate how the bean is implemented.
  2. How to add the default implementation to the generated bean, query the database.




Create the required classes first

public interface CustomerQuery {
  String query();
}Copy the code

@Configuration
@MyScanner
public class MyConfig {
}Copy the code



Custom annotation creation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({MyImportSelector.class})
public @interface MyScanner {
}Copy the code

A new @import annotation is required. It is equivalent to an import element in XML, allowing you to add config. XML /ImportSelector Implementation class/ImportBeanDefinitionRegistrar class is a very normal component classes. What it does is it processes the BeanDefinition when it’s put into the map. Handling the implementation is done through MyImportSelector.


This is interspersed with a spring bean creation process that you need to understand to create beans on the fly. Before creating a bean, there is a BeanDefinition that describes the bean, and then the bean is created based on that description. All beandefinitions are placed in a map, where k is name and V is BeanDefinition.


Raises MyBenDefinitionRegistrar, according to the above @ the explanation of the Import, the class needs to implement ImportBeanDefinitionRegistrar. This class will then be called by Spring. To achieve the creation of custom beans.

public class MyBenDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
  @Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(CustomerQuery.class);
    GenericBeanDefinition beanDefinition = (GenericBeanDefinition) bdb.getBeanDefinition();
    beanDefinition
        .getConstructorArgumentValues()
        .addGenericArgumentValue(beanDefinition.getBeanClassName());
//    System.out.println("-- -- -- -- >" + beanDefinition.getBeanClassName());
    beanDefinition.setBeanClass(MyFactoryBean.class);
    registry.registerBeanDefinition("customerQuery",beanDefinition); }}Copy the code

BeanDefinitionRegistry: According to the source code annotation, Chinese meaning. This interface contains classes for beanDefinitions build information that are responsible for putting the corresponding BeanDefinitions into the map, a process called Registry.
BeanDefinitionBuilder: builder BeanDefinition, by calling the build method, generating various BeanDefinition
There are many different types of BeanDefinition. Check out the implementation class of BeanDefinition
At this point, we need to change the bean definition so that the class we get by name is the one we specify. Set beanClassName. The name here is actually CustomerQuery.
beanDefinition.setBeanClass(MyFactoryBean.class); Modified the corresponding BeanClass, and here is the factory class that we generated, which is actually the implementation class. I’ll leave it to the details below.
registry.registerBeanDefinition(“customerQuery”,beanDefinition); Call the registration method.


A concrete implementation of MyFactoryBean

public class MyFactoryBean<T> implements FactoryBean {
  private Class clazz;


  public MyFactoryBean(Class<T> clazz) {
    this.clazz = clazz;
  }

  @Override
  public Object getObject() throws Exception {
    returnProxy.newProxyInstance( this.getClass().getClassLoader(), new Class[] {CustomerQuery.class}, (proxy, method, args) -> queryCustomer()); } @Override public Class<? >getObjectType() {
    return clazz;
  }

  public String queryCustomer(){
      String name = null;
      try {
          Statement statement = HikariCPDataSource.getConnection().createStatement();
          ResultSet resultSet = statement.executeQuery("select name from customer;");
resultSet.next();
          name = resultSet.getString(1);
      } catch (SQLException e) {
          e.printStackTrace();
      }
      returnname; }}Copy the code

Define a FactoryBean, call getObject when instantiating the bean, and then we make a proxy class here.



Create the corresponding DataSource class that connects to the database

public class DataSource {
  private static HikariConfig config;
  private static HikariDataSource dataSource;
static  {
    config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://localhost:3306/test? serverTimezone=UTC");
    config.setDriverClassName("com.mysql.jdbc.Driver");
    config.setUsername("root");
    config.setPassword("admin123");
    dataSource = new HikariDataSource(config);
  }
  public static Connection getConnection() {
    Connection connection = null;
    try {
      connection = dataSource.getConnection();
    } catch (SQLException e) {
      e.printStackTrace();
    }
    returnconnection; }}Copy the code
Perform the initial method to see if the results are as expected.



If someone really needs it, they will consider uploading the code to Git.