mybatisのSqlSessionオブジェクトのgetMapper()を記録し、プロキシクラスを動的に生成します。

自習用、記録用

1. Javassist を使用してクラスを動的に生成する [ssist:auxiliary]

JavaにとってのJavassistの意味

Javassist は、Java バイトコード操作用のライブラリであり、Java ランタイムでクラスを動的に変更したり、新しいクラスを生成したりする便利な方法を提供します。Javassist を使用すると、開発者は Java ソース コードを記述せずに Java プログラムを動的に生成したり、既存の Java プログラムを変更したりできます。この操作は、実行時に動的に生成または変更する必要がある一部のクラスに役立ちます。クラスのアプリケーション シナリオは非常に意味があります。 AOP フレームワーク、ORM フレームワーク、および Java サーバー コンテナとして。

Javassist を使用する理由

DaoImpl 自体はビジネス ロジック層には関与せず (永続化層にあります)、データに対して CURD を実行するだけなので、頻繁に繰り返されます。

したがって、バイトコードを操作するにはjavassistのようなクラスライブラリが必要です

リフレクション メカニズムは、実行時にクラス情報とオブジェクトのメソッドとプロパティを動的に取得し、それらを呼び出します。主に Java.lang.reflect パッケージのクラスを通じて実装されます。

Javassist は、リフレクションのように実行時にクラス構造を解析することなく、Java バイトコードを直接操作および変更できるバイトコード編集フレームワークです。Javassist はコンパイル中にバイトコードを変更し、AOP などの機能を実装できます。

キーポイント: Javassist はクラスのバイトコード ファイルを作成し、jvm にそれらを読み取らせることができますが、プロキシ メカニズムは特定の命名規則に準拠する必要があります。

2. GenerateDaoProxy ツール クラスをカプセル化します。

注: mybatis は再カプセル化された Javassist [組み込み Javassist、1 つの jar パッケージのみ]


public class GenerateDaoProxy {
    /**
     * 这个工具类是框架的开发者提供的
     * 开发者可以给使用者规定传进哪些参数
     * 通过session..getConfiguration().getMappedStatement() 可以获取SqlSessionFactoryBuilder.build() 解析的mybatis.xml文件 中的Mapper 的sql集合。
     * 传进接口,返回实现所有方法的类
     * @param daoInterface 接口
     * @return Impl类
     */
    public static Object generate(SqlSession session, Class daoInterface)  {
        //类池
        ClassPool pool = ClassPool.getDefault();
        //制造类
        CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
        //制造接口
        CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        ctClass.addInterface(ctInterface);

        Method[] declaredMethods = daoInterface.getDeclaredMethods();

        Arrays.stream(declaredMethods).forEach(method -> {
            try {
                StringBuffer methodCode = new StringBuffer();
                //添加修饰符
                methodCode.append("public ");
                //添加返回值
                methodCode.append(method.getReturnType().getName()+" ");
                methodCode.append(method.getName());
                methodCode.append("(");

                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    methodCode.append(parameterTypes[i].getName()+" ");
                    methodCode.append("arg"+i);
                    if (i!= parameterTypes.length-1){
                        methodCode.append(",");
                    }
                }
                methodCode.append("){");
                /**
                 * 括号中间需要写对应的session.insert或session.select方法
                 * 要想使用GenerateDaoProxy工具类就需要采用对应的规范,即namespace名为Dao的全限定类名,sqlId为对应Dao的接口方法
                 */
                String sqlId = daoInterface.getName()+"."+method.getName();
//                在SqlsessionFactoryBuilder.build() 解析mybatis.xml文件的时候,会将所有的Mapper文件中的sql语句存储在getMappedStatement中
                SqlCommandType sqlCommandType = session.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
                methodCode.append("org.apache.ibatis.session.SqlSession session = utils.SqlSessionUtil.getSqlSession();");

                if(sqlCommandType == SqlCommandType.INSERT){

                }
                if(sqlCommandType == SqlCommandType.DELETE){

                }
                if(sqlCommandType == SqlCommandType.UPDATE){
                    methodCode.append("return session.update(\""+sqlId+"\", arg0);");
                }
                if(sqlCommandType == SqlCommandType.SELECT){
                    String resultType = method.getReturnType().getName();
                    methodCode.append("return ("+resultType+")session.selectOne(\""+sqlId+"\", arg0);");
                }

                methodCode.append("}");

                System.out.println(methodCode.toString());
                CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
                ctClass.addMethod(ctMethod);
            } catch (CannotCompileException e) {
                e.printStackTrace();
            }
        });

        Object obj = null;
        try {
            Class<?> toClass = ctClass.toClass();
            obj = toClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return obj;
    }
}

mybatis は GenerateDaoProxy プロキシ モードを実装しました

3. mybaits.getMapper() の理解

SqlSession object.getMapper(AccountDao.class) を使用することでプロキシ クラスの動的な生成が実現できる理由の概要を次に示します。

まず、命名規則に従います。Mapper ファイルの名前空間は、Dao.class クラスの完全修飾クラス名である必要があり、ID はクラスのメソッド名です。

したがって、対応する一意のSqlIdを取得できます

次に、sqlSession.getConfiguration().getMappedStatement(sqlId) を通じて、対応する SQL オブジェクトを取得します。

次に、Sql オブジェクトの getSqlCommandType を通じて、Sql に対応する Curd 型を取得できます。

次に、Curd 型に従って対応する戻り値を結合し、最後に動的に結合されたクラス テンプレートを JVM にロードしてインスタンス化します。

おすすめ

転載: blog.csdn.net/lfeishumomol/article/details/133530323