本篇文章旨在实现一个最简单的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 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 getFishinger(Class> clazz);
}
第四步,钓鱼者需要从鱼竿工厂得到了一杆鱼竿,所以我们需要声明一个鱼竿工厂。
public interface FishingFactory {
FishingRod getFishingRod();
}
第五步,文章开头提到了,钓鱼者不会使用这种鱼竿,需要钓鱼高手替他完成钓鱼的过程。此处会用到JDK动态代理的特性,不熟悉的小伙伴可以去百度一下再回来继续往下看。我们先用JDK动态代理编写一个钓鱼高手类来拦截钓鱼者的钓鱼过程,代码如下:
public class FisherProxy implements InvocationHandler{
private FishingRod rod;
@SuppressWarnings("unchecked")
public static T newInstance(Class> clazz, FishingRod rod){
FisherProxy proxy = new FisherProxy();
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 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 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类中的打印代码重写为访问数据库方法即可。