Record the getMapper() of the SqlSession object of mybatis to dynamically generate the proxy class

Self-study, for recording

1. Use Javassist to dynamically generate classes [ssist: auxiliary]

The meaning of Javassist to java

Javassist is a library for Java bytecode manipulation that provides a convenient way to dynamically modify classes or generate new classes at Java runtime. Through Javassist, developers can dynamically generate Java programs without writing Java source code, and can modify existing Java programs. This operation is useful for some classes that need to be dynamically generated or modified at runtime. The application scenarios of classes are very meaningful, such as AOP framework, ORM framework and Java server container.

Why use javassist

DaoImpl itself does not involve the business logic layer [it is in the persistence layer]. It only performs CURD on the data, so it is often repeated.

So a class library like javassist is needed to operate on bytecode

The reflection mechanism dynamically obtains class information and methods and properties on objects at runtime, and calls them. Mainly implemented through classes under the Java.lang.reflect package.

Javassist is a bytecode editing framework that allows you to directly manipulate and modify Java bytecode without having to parse the class structure at runtime like reflection. Javassist modifies the bytecode during compilation and can implement functions such as AOP.

Key point: Javassist can create class bytecode files and let jvm read them. The proxy mechanism needs to comply with specific naming rules.

2. Encapsulate the GenerateDaoProxy tool class

Note: mybatis has re-encapsulated Javassist [built-in Javassist, only one jar package]


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 has implemented the GenerateDaoProxy proxy mode

3. Understanding of mybaits.getMapper()

Here is a summary of why using SqlSession object.getMapper(AccountDao.class) can achieve dynamic generation of proxy classes

First follow the naming convention: the namespace of the Mapper file must be the fully qualified class name of the Dao.class class, and the id is the method name of the class.

So you can get the corresponding unique SqlId

Then get the corresponding Sql object through sqlSession.getConfiguration().getMappedStatement(sqlId)

Then you can get the Curd type corresponding to Sql through the getSqlCommandType of the Sql object.

Then splice the corresponding return value according to the Curd type, and finally load the dynamically spliced ​​Class template into the JVM and instantiate it.

Guess you like

Origin blog.csdn.net/lfeishumomol/article/details/133530323