Mybatis源码---重写一个最简单的Mybatis架构实现(一)

     本篇文章旨在实现一个最简单的Mybatis架构。我们想象这样一个场景:有个钓鱼者想得到一条特殊的鱼,这种鱼特殊需要使用某种特殊的鱼竿才能钓上来,于是就有了有了钓鱼的需求。钓鱼者去鱼竿工厂去买了一杆钓这种鱼的鱼竿,却发现自己并不会用这种鱼竿。于是,他托鱼竿工厂给自己联系一个钓鱼高手帮自己钓鱼,自己可以提供鱼竿。然后,鱼竿工厂帮他联系了一个钓鱼高手,钓鱼者把鱼竿交给他,钓鱼高手替钓鱼者钓到了鱼。在这个场景中,我们提炼出几个关键角色:钓鱼者、鱼竿工厂、鱼竿、钓鱼高手。基于这个场景,我们开始实现一个自己的Mybatis。

 第一步, 我们需要创建一些鱼类,代码如下:

@Fish
public class Liyu {

}

 可以看到,我们用@Fish注解来声明Liyu类是一个代表鱼类的类。@Fish注解代码如下:

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Fish {
  
	String value() default "";
}

第二步,我们需要创建一个钓鱼者。

@Fisher
public interface LiyuFisher{
    
   public List<Liyu> fishing();
 
}

 可以看到,我们用@Fisher注解来声明LiyuFisher类是一个代表钓鱼者的类。@Fisher注解代码如下:

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Fisher {
  
	String value() default "";
}

第三步,因为鱼的种类各种各样,鱼竿的类型也各种各样。但是,鱼竿具有共同的功能,就是钓鱼。同时,钓鱼工厂是通过鱼竿来匹配合适的钓鱼高手的。那么,我们可以抽象出一个鱼竿接口来,代码如下:

public interface FishingRod {
   
   /*
    * 钓鱼方法
    * @param bait 鱼饵
    * @return Object 鱼对象
    */

   public Object fishing(String bait);

   /**
    * 通过鱼竿匹配钓鱼高手的方法
    * @parm clazz 钓鱼者类
    */
   public <T> T getFishinger(Class<?> clazz);
    
}

  第四步,钓鱼者需要从鱼竿工厂得到了一杆鱼竿,所以我们需要声明一个鱼竿工厂。

public interface FishingFactory {

	FishingRod getFishingRod();
}

第五步,文章开头提到了,钓鱼者不会使用这种鱼竿,需要钓鱼高手替他完成钓鱼的过程。此处会用到JDK动态代理的特性,不熟悉的小伙伴可以去百度一下再回来继续往下看。我们先用JDK动态代理编写一个钓鱼高手类来拦截钓鱼者的钓鱼过程,代码如下:

public class FisherProxy<T> implements InvocationHandler{
	
	private FishingRod rod; 
	
	@SuppressWarnings("unchecked")
	public static <T> T newInstance(Class<?> clazz, FishingRod rod){
		FisherProxy<T> proxy = new FisherProxy<T>();
		proxy.rod = rod;
		return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, proxy);
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		retyurn rod.fishing(null);
	}
}

  在上面的代码中,我们声明了一个钓鱼高手类,让它实现了InvocationHandler接口的invoke()方法来拦截钓鱼者钓鱼的过程,这样做的好处是钓鱼者的钓鱼过程会被钓鱼高手拦截下来替他完成。也就是说,钓鱼者不需要亲自去钓鱼了,钓鱼高手替他完成了钓鱼过程。

第六步,我们给鱼竿接口一个默认的实现类:

public class DefaultFishingRod implements FishingRod {
  
    //通过鱼竿来匹配合适的钓鱼高手

	public <T> T getFishinger(Class<?> clazz) {
		return FisherProxy.newInstance(clazz, this);
	}
    
    //钓鱼过程

	public Object fishing(String bait) {
		System.out.println("钓到鱼啦!"+bait);
		return null;
	}
}

第七步,我们给鱼竿工厂一个默认的实现类: 

public class DefaultFishingerFactory implements FishingFactory {

	public FishingRod getFishingRod() {
		return new DefaultFishingRod();
	}

}

 现在,已经可以实现文章开头的需求了。代码如下:

  //实例化一个鱼竿工厂
  FishingFactory factory = new DefaultFishingerFactory();
  //得到一杆鱼竿
  FishingRod rod = factory.getFishingRod();
  //根据鱼竿匹配并得到钓鱼高手
  LiyuFisher fisher = rod.getFishinger(LiyuFisher.class);
  //完成钓鱼
  fisher.fishing();

 执行这段代码,控制台打印:钓到鱼啦!。

 以上,就是最简单的Mybatis的实现过程了。钓鱼者就是Mapper接口,鱼竿工厂就是SqlSessionFactory,鱼竿就是SqlSession,鱼就是我们要从数据库取的数据。接下来是扩展内容:

1.声明一个注解表示鱼饵:

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Bait {
  
  String value() default "";
	
  BaitType type() default BaitType.SELECT;
	
  public enum BaitType{
     SELECT,INSERT,DELETE,UPDATE;
  }
}

 2.把LiyuFisher接口改造一下,代码如下:

@Fisher
public interface LiyuFisher{
    
	@Bait(value = "select * from fishPool",type=BaitType.SELECT)
	public List<Liyu> fishing();
}

3.把FishingerProxy的invoke()方法改造成这样:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Assert.notNull(method.getAnnotation(Bait.class),"Biat不能为空");
    String bait = method.getAnnotation(Bait.class).value();
    return rod.fishing(bait);
}

4.再次执行钓鱼代码,控制台打印:钓到鱼啦!select * from fishPool

5.如果需要进一步改造,只要把DefaultFishingRod类中的打印代码重写为访问数据库方法即可。

猜你喜欢

转载自blog.csdn.net/qq_28802119/article/details/83381816