Mybatis基本原理及整合Spring

Mybatis基本原理及整合Spring

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis 以及与Spring的整合过程中需要解决以下问题

  • xml文件中的sql语句如何与接口映射
  • mapper接口如何进行实例化
  • 如何将mapper对象交给到spring ioc中进行管理

在这片文章中,我们先解决后两个问题,当然我们也应该先大体了解一下第一个问题是如何解决的,其问题的解决与第二个问题息息相关。其实不难解决:

  • 首先通过一个读入流将配置xml文件读入,然后交给一个类去解析成一个一个相关配置,最后将解析结果存入到一个对象中(有几个比较重要属性:数据源相关配置,mapper相关位置)
    • 在解析过程中,mapper的解析十分重要,mybatis会为将每个mapper接口存入到配置中
    • 同时mapper接口和对应的mappersqlxml文件是在同一个路径下的,因此我们只需将配置sql语句的sql解析并存起来,因为sql配置中每个sql语句都具有一个id属性与接口方法名字相对应,因此当执行接口中的方法时,再拿对应的sql语句出来交给特定的执行器进行处理
  • 创建一个工厂类根据配置中的创建一个操作类,这个类在Mybatis中称为SqlSession
  1. mapper接口如何进行实例化?

通过动态代理实现

  1. 如何将mapper对象交给到spring ioc中进行管理

将动态代理生成的代理对象注册到ioc中,但是我们常用的注册方法通常是现成的一个类,而代理对象是我们动态生成的,没有确定的一个类,这又怎么解决呢?这就不得不提到一个spring中特殊的bean----> factoryBean,其使用的泛型,每个factorybean都对应着一个对象,我们可以通过实现factorybean接口,将我们动态生成的对象返回就可以了

一、重要接口

1.1. BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

	/*
	1. BeanFactoryPostProcessor:提供给开发者一个接口,在调用此接口时 项目中所有的bean都已经被加载,但是未实例化,在此接口中我们可以获取指定的beanDefinition,对其进行修改或者添加属性值
	2. BeanDefinitionRegistryPostProcessor继承BeanFactoryPostProcessor,提供了一个注册接口,我们可以通过此接口加载注册我们自己的bean
	*/
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
1.2. ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar和BeanDefinitionRegistryPostProcessor功能上相似

ImportBeanDefinitionRegistrar同样可以注册新的bean,但是其是**@Import注册配套使用的**,他除了提供BeanDefinitionRegistry对象之外,还提供了AnnotationMetadata对象

AnnotationMetadata:继承ClassMetadata, AnnotatedTypeMetadata两个接口,因此其存储了类的元信息和类上注解的信息

ImportBeanDefinitionRegistrar侧重于根据@Import修饰的类的信息和类上其他注解的信息来 注册相关的bean

public interface ImportBeanDefinitionRegistrar {
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {
		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}
}
1.3. FactoryBean
//factoryBean.getObject()返回的对象实际上已经完成了实例化和初始化,只是想利用ioc的自动注入等其他功能
public interface FactoryBean<T> {

	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    //继承FactoryBean实现getObject方法,返回我们创建的对象即可将其放入到ioc中管理
	@Nullable
	T getObject() throws Exception;
	
	@Nullable
	Class<?> getObjectType();

	default boolean isSingleton() {
		return true;
	}
}

二、Mapper代理类生成逻辑

  • 因为mapper接口及相关sql语句是在之前被解析出来了,因此需要一个类来维护这些mapper接口,这个类就是MapperRegistry

  • 存在一个mapper生成工厂创建指定的生产对象,这个工厂是MapperProxyFactory。采用工厂方法模式

  • 基于接口实现动态代理,采用了jdk动态代理,需要一个类实现InvocationHandler接口,这个类是MapperProxy

2.1. MapperRegistry

从字面意思上看他是一个mapper的注册中心,其主要保存 mapper接口与代理工厂的关系

mapperRegistry是保存在了Configuration类中,Configuration类就是我们之前从xml文件中解析出来的配置,另外他还根据业务需要提供了一些方法工具类

public class MapperRegistry {

  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);//从已经注册的mapper中获取指定的代理工厂
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {//通过代理工厂创建  mapper接口的代理实例
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
	//***省略addMapper等方法
}

2.2. MapperProxyFactory
//工厂方法模式,将生成具体产品的任务分发给具体的产品工厂
//此处主要是基于jdk动态代理 创建对象
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;//要生成的mapper类(也就是接口)
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);//jdk动态代理  生成代理对象
  }

  public T newInstance(SqlSession sqlSession) {
    //创建代理类MapperProxy其实现了InvocationHandler接口
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  //省略getter setter。。。。
}
2.3. MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {

  //省略。。。。。
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //具体的与sql绑定并执行
  }
  //省略。。。。。
}

三、spring整合mybatis

  • 将动态生成的代理类交给spring管理->MapperFactoryBean(实现了FactoryBean接口)
  • 在spring启动过程中根据配置扫描指定的mapper并创建生成mapperFactoryBean
3.1. MapperFactoryBean
//将对应的MapperFactoryBean放入spring中,spring将会调用getObject方法将返回的实例放入spirng ioc中管理
//继承自SqlSessionDaoSupport其增加了对sqlSession的支持
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  private Class<T> mapperInterface;

  private boolean addToConfig = true;//是否将扫描到的mapperInterface注册到configuration中

  public MapperFactoryBean() {}

  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  
  //调用了sqlSession中的getmapper方法
  //调用链:sqlSession.getMapper()->configuration.getmapper()->mapperRegistry.getmapper->mapperProxyFactory.newInstance()[jdk动态代理生成]
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

   //本类继承自SqlSessionDaoSupport,而SqlSessionDaoSupport又继承自DaoSupport
    /**
    DaoSupport是一个抽象类,实现了InitializingBean接口,我们可能对DaoSuppport不熟悉,但是对JDBCDaoSupport熟悉,JDBCDaoSupport同样是一个抽象类,继承自DaoSupport,在非Mybatis项目中,我们可能会在dao实现了继承JDBCDaoSupport,通过xml配置的形式将datasouce注入到此类中,从而可以使用JDBCDaoSupport中的jdbcTemplate
    
    **/
  @Override
  protected void checkDaoConfig() {
      super.checkDaoConfig();

      notNull(this.mapperInterface, "Property 'mapperInterface' is required");

      Configuration configuration = getSqlSession().getConfiguration();
      if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
              configuration.addMapper(this.mapperInterface);
          } catch (Exception e) {
              logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
              throw new IllegalArgumentException(e);
          } finally {
              ErrorContext.instance().reset();
          }
      }
  }
  /**
   * 省略。。。。
   */
}
3.2. MapperScannerConfigurer

两个问题:

  1. 有了MapperFactoryBean,我们难道要一个一个的像这样注入
<bean id="xxxxMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
     <property name="mapperInterface" value="xxxxxMapper"/>
</bean>

​ 当我们的mapper多的时候我们该如何自动将其装配呢

答:通过实现BeanDefinitionRegistryPostProcessor接口中的方法,通过扫描指定路径下的mapper接口,并创建相对应的beanDefinition,并将其注册到ioc中

这样我们就可以只将这个实现了BeanDefinitionRegistryPostProcessor接口的类注入就可以了。这个类就是MapperScannerConfigurer。我们可以这样配置:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
     <property name="basePackage" value="com.lmt.mapper"/>
</bean>

**实现:**MapperScannerConfigurer会借助spring中的bean扫描器ClassPathBeanDefinitionScanner,根据 相关配置比如basePackage扫描出相关mapper接口,并创建相关的FactoryBean实例

//实现了BeanDefinitionRegistryPostProcessor,spring将会在所有的bean已经被注册之后而没有实例化之前进行调用ioc中实现了BeanDefinitionRegistryPostProcessor接口的相关方法
public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

  private String basePackage;

  private boolean addToConfig = true;

  private String lazyInitialization;

  private SqlSessionFactory sqlSessionFactory;

  private SqlSessionTemplate sqlSessionTemplate;

  private String sqlSessionFactoryBeanName;

  private String sqlSessionTemplateBeanName;

  private Class<? extends Annotation> annotationClass;

  private Class<?> markerInterface;

  private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;

  private ApplicationContext applicationContext;

  private String beanName;

  private boolean processPropertyPlaceHolders;

  private BeanNameGenerator nameGenerator;


  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) { //是否对本类 具有占位符的属性进行处理比如Value(${})这种
      processPropertyPlaceHolders();
    }
	//创建类路径下的mapper扫描器,ClassPathMapperScanner继承自ClassPathBeanDefinitionScanner
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    //事实上下面这些信息在执行的时候因为没有配置都是null的【可能是因为有些还没有在spring中初始化化无法注入】,这就会使得ClassPathMapperScanner创建的BeanDefinition没有对应的类的属性值,直接导致创建的MapperFactoryBean没有sqlSession等属性值,影响数据库的操作。那怎么办呢,
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    scanner.registerFilters();//设置scanner的扫描策略
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
	
  //此方法中获取了容器中所有的PropertyResourceConfigurer,因为在执行本方法postProcessBeanDefinitionRegistry的时候,还未执行此方法中获取了容器中所有的PropertyResourceConfigurer类中BeanFactoryPostProcessor接口定义的方法,也就无法对【所有的BeanDefintion中的 bean的相关属性值进行设置,所提模拟了一个Spring工厂环境,并将本类注册到其中,最后执行PropertyResourceConfigurer的postProcessBeanFactory方法,从而获取相关属性的值
  private void processPropertyPlaceHolders() {
      //获取spring中的PropertyResourceConfigurer对象
      Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class,false, false);

      
  	 if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext){
         //获取本类对应的BeanDefinition
      BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory()
          .getBeanDefinition(beanName);
	 //模拟一个新的factory
      DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
      factory.registerBeanDefinition(beanName, mapperScannerBean);//注册本类的beanDefintion
		//执行PropertyResourceConfigurer的postProcessBeanFactory接口方法
      for (PropertyResourceConfigurer prc : prcs.values()) {
        prc.postProcessBeanFactory(factory);//PropertyResourceConfigurer会获取环境中的值并将相关属性放入到本类的BeanDefintion中
      }
	 //获取属性值
      PropertyValues values = mapperScannerBean.getPropertyValues();
	 //依次更新属性信息
      this.basePackage = updatePropertyValue("basePackage", values);
      this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values);
      this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values);
      this.lazyInitialization = updatePropertyValue("lazyInitialization", values);
    }
    this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
    this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
        .map(getEnvironment()::resolvePlaceholders).orElse(null);
    this.sqlSessionTemplateBeanName = Optional.ofNullable(this.sqlSessionTemplateBeanName)
        .map(getEnvironment()::resolvePlaceholders).orElse(null);
    this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
        .orElse(null);
  }
  //省略。。。。。
}

ClassPathMapperScanner类比较庞大我们只关注其扫描后的处理部分

//ClassPathMapperScanner继承自ClassPathBeanDefinitionScanner,ClassPathBeanDefinitionScanner会根据配置扫描出类并将其封装成BeanDefinition,最后注册到spring中,最后将所有的BeanDefinition交给子类也就是ClassPathMapperScanner处理,下面就是处理方法
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    //遍历
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      //将definition的bean类设置为MapperFactoryBean,也就是 spring中FactoryBean以实现动态生成代理类
      definition.setBeanClass(this.mapperFactoryBeanClass);
//--------------------------------------------------------------------------
	  /*设置 FactoryBean的属性初始值 addToConfig、sqlSessionFactory、sqlSessionTemplate,此时存在两种情况:
       1. 这些属性信息可能都不在本类(ClassPathMapperScanner)中存在的,原因是此时这些类还没在spring中实例化,那么就无法设置MapperFactoryBean的初始值.
       2.这些对象已经在spring中初始化,MapperScannerConfigurer在初始化的时候可能已经被注入了这些属性信息,将其交给此类也就是ClassPathMapperScanner就可以了,并且在此处可以将MapperFactoryBean的值初始化成功。
       
       那第一种情况下,Spring创建的对象MapperFactoryBean没有SqlSession对象,将直接影响数据库的操作,怎么办呢?看下面:将注入方式设置为类型注入
       */
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
      boolean explicitFactoryUsed = false;//是否已经初始化的标志
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory",
            new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        definition.getPropertyValues().add("sqlSessionTemplate",
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }
 //----------------------------------------------------------------------------------
	
      if (!explicitFactoryUsed) {
         //如果无法获取FactoryBean所需属性的值,则可以将BeanDefinition设置为根据属性类型进行注入,这就表示在FactoryBean在实例化的时候会根据类型进行注入
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

四、spring整合Mybatis对注解@MapperScan的支持

​ 我们已经有了MapperScannerConfigurer类帮助我们扫描并注册相关的类,但是我们仍需要在配置文件中配置MapperScannerConfigurer作为spring的bean,那是否有其他方式简化此步骤呢?答案当然有:@MapperScan

4.1. @MapperScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)//重点
@Repeatable(MapperScans.class)
public @interface MapperScan {
  String[] value() default {};
  String[] basePackages() default {};
  /**
   * 省略
   */
}

当spring在启动过程中扫描到标注到配置类上的@MapperScan的注解时(实际上是ConfigurationClassPostProcessor类进行的扫描),发现其内存在@Import注解,则会将import注解导入的类进行处理。

4.2. MapperScannerRegistrar

MapperScannerRegistrar继承自ImportBeanDefinitionRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      //获取MapperScan注解所标注类上注解的属性值
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
        //根据属性值 beanDefinition
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
          generateBaseBeanName(importingClassMetadata, 0));
    }
  }

  void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) {
	//建造者模式 创建一个builder,目标defintion为MapperScannerConfigurer
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    //从注解中获取属性值并添加到添加beanDefintiotn对应的属性中--------------------------
    builder.addPropertyValue("processPropertyPlaceHolders", true);

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }

    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }

    String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
      builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }
     //basePackage信息
    List<String> basePackages = new ArrayList<>();
  basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
  basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));
  basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));
    if (basePackages.isEmpty()) {
      basePackages.add(getDefaultBasePackage(annoMeta));
    }

    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
 //-------------------------------------------------------------------
	//注册此beanDefintion spring最终会将注册的beanDefintion实例化
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }
  /**
   * 省略
   */
}

这是一种常见的注解化模式

五、模仿

自定义一个基于FactoryBean的基于接口自动生成代理对象的组件

5.1. LMTProxy
/**
 * @author Li Mengtong
 * @time 2021/8/8 10:52
 * @Description  代理对象
 */
public class LMTProxy<T> implements InvocationHandler {
    private Class<T> lmtInterface;

    public LMTProxy(Class<T> lmtInterface) {
        this.lmtInterface = lmtInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            System.out.println("\n-------------------------------------------------");
            System.out.println("调用" + lmtInterface.getName() + " : " + method.getName() + "方法");
            System.out.println("参数为:");
            if (args != null)
                Arrays.stream(args).forEach(System.out::println);

            Class<?> returnType = method.getReturnType();
            System.out.println("返回类型:" + returnType.getName());
            System.out.println("-------------------------------------------------\n");
            if (method.getName().equals("getName")) {
                return "你好";
            }
            return null;
        }
    }
}
5.2. LMTFactory

代理对象建造工厂

public class LMTFactory<T> {
    private Class<T> lmtInterface;

    public LMTFactory(Class<T> lmtInterface) {
        this.lmtInterface = lmtInterface;
    }

    public T newInstance() {
        final LMTProxy<T> lmtProxy = new LMTProxy<>(lmtInterface);
        return (T) Proxy.newProxyInstance(lmtInterface.getClassLoader(), new Class[]{lmtInterface}, lmtProxy);
    }

    public Class<T> getLmtInterface() {
        return lmtInterface;
    }

    public void setLmtInterface(Class<T> lmtInterface) {
        this.lmtInterface = lmtInterface;
    }
}
5.3. LMTFactoryBean

基于FactoryBean,实现工厂方法模式下将对象注册至ioc的方法

public class LMTFactoryBean<T> implements FactoryBean<T> {

    private Class<T> lmtInterface;

    private LMTSqlExecutor sqlExecutor;//仅仅是为了测试 类型注入功能,没有实际功能

    @Override
    public T getObject() throws Exception {
        LMTFactory<T> lmtFactory = new LMTFactory<>(lmtInterface);
        return lmtFactory.newInstance();
    }

    @Override
    public Class<?> getObjectType() {
        return lmtInterface;
    }

    public Class<T> getLmtInterface() {
        return lmtInterface;
    }

    public void setLmtInterface(Class<T> lmtInterface) {
        this.lmtInterface = lmtInterface;
    }

    public LMTSqlExecutor getSqlExecutor() {
        return sqlExecutor;
    }

    public void setSqlExecutor(LMTSqlExecutor sqlExecutor) {
        System.out.println("LMTSqlExector is being injected into the LMTFactoryBean");
        this.sqlExecutor = sqlExecutor;
    }
}
5.4. FtBeanImportConfigurer

用来创建指定的FactoryBean类

public class FtBeanImportConfigurer implements BeanDefinitionRegistryPostProcessor {

    public static final String IMPORTINTERFACES_ATTRIBUTE_NAME = "ImportInterfaces";

    private Class<?>[] ImportInterfaces;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if (ImportInterfaces != null) {
            for (Class inf : ImportInterfaces) {
                if (inf.isInterface()) {
                    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                    beanDefinition.setBeanClass(LMTFactoryBean.class);
                    beanDefinition.getPropertyValues().addPropertyValue("lmtInterface", inf);
                    beanDefinition.setLazyInit(false);
                    beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);//重点:设置为AUTOWIRE_BY_TYPE模式,Spring可以对FactoryBean的属性类型进行分析,并可将可注入的属性进行注入
                    registry.registerBeanDefinition(inf.getName(), beanDefinition);
                } else {
                    System.out.println("-------- " + inf.getName() + "is not a interface -------------");
                }
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    public Class<?>[] getImportInterfaces() {
        return ImportInterfaces;
    }

    public void setImportInterfaces(Class<?>[] importInterfaces) {
        ImportInterfaces = importInterfaces;
    }
}

5.5. 注解化

有了以上几点,我们模仿mybatis建立了一个自己的基于jdk动态代理生成代理对象的小demo,我们可以继续模仿@MapperScan进行注解化配置

5.5.1. FtBeanImportRegister
public class FtBeanImportRegister implements ImportBeanDefinitionRegistrar {//实现ImportBeanDefinitionRegistrar以实现指定类的注册
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取注解信息
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableLmt.class.getName());
        Class<?>[] interfaces = null;
        for (String key : annotationAttributes.keySet()) {
            if (key.equals("value")) {
                Object o = annotationAttributes.get(key);
                if (o instanceof Class[]) {
                    interfaces = (Class<?>[]) o;
                }
            }
        }
        if (interfaces == null) {
            return;
        }
        // 注册FtBeanImportConfigurer的defintion
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(FtBeanImportConfigurer.class);
        beanDefinition.getPropertyValues().add(FtBeanImportConfigurer.IMPORTINTERFACES_ATTRIBUTE_NAME, interfaces);
        registry.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition);
    }
}
5.5.2. EnableLmt
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FtBeanImportRegister.class)//导入FtBeanImportRegister
public @interface EnableLmt {
    Class<?>[] value();//需要导入的接口
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值