六、使用javassist生成类

使用junit测试

1.使用javassist动态生成类

package com.powernode.javassist;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.junit.Test;

import java.lang.reflect.Method;

public class JavassistTest {
    @Test
    public void testGenerateFirstClass() throws Exception{
        // 获取类池:生成class的
        ClassPool pool = ClassPool.getDefault();
        // 制造类(需要告诉javassist类名)
        CtClass ctClass = pool.makeClass("com.powernode.bank.dao.impl.AccountDaoImpl");
        // 制造方法
        String methodCode = "public void insert(){System.out.println(123);}";
        CtMethod ctMethod = CtMethod.make(methodCode, ctClass);
        // 将方法添加到类中
        ctClass.addMethod(ctMethod);
        // 在内存中生成class
        ctClass.toClass();


        // 类加载到JVM中,返回AccountDaoImpl类的字节码
        Class<?> clazz = Class.forName("com.powernode.bank.dao.impl.AccountDaoImpl");
        // 创建对象
        Object obj = clazz.newInstance();
        // 获取AccountDaoImpl中的insert方法
        Method insertMethod = clazz.getDeclaredMethod("insert");
        // 调用方法insert
        insertMethod.invoke(obj);
    }
}

2.使用javassist生成类并实现接口

接口

package com.powernode.bank.dao;
public interface AccountDao {
    void delete();
}
@Test
public void testGenerateImpl() throws Exception{
      // 获取类池
      ClassPool pool = ClassPool.getDefault();
      // 制造类
      CtClass ctClass = pool.makeClass("com.powernode,bank.dao.impl.AccountDaoImpl");
      // 制造接口
      CtClass ctInterface = pool.makeInterface("com.powernode.bank.dao.AccountDao");
      // 添加接口到类中
      ctClass.addInterface(ctInterface);
      // 实现接口中的方法
      // 制造方法
      CtMethod ctMethod = CtMethod.make("public void delete(){System.out.println(\"hello delete\");}", ctClass);
      // 将方法添加到类中
      ctClass.addMethod(ctMethod);
      // 在内存中生成类,并将类加载到JVM中
      Class<?> clazz = ctClass.toClass();
      AccountDao accountDao = (AccountDao) clazz.newInstance();
      accountDao.delete();
}

3.使用javassist动态实现接口中所有方法

package com.powernode.bank.dao;

public interface AccountDao {
    void delete();
    int insert(String actno);
    int update(String actno, Double balance);
    String selectByActno(String actno);
}
@Test
public void testGenerateAccountDaoImpl() throws Exception{
    // 获取类池
    ClassPool pool = ClassPool.getDefault();
    // 制造类
    CtClass ctClass = pool.makeClass("com.powernode,bank.dao.impl.AccountDaoImpl");
    // 制造接口
    CtClass ctInterface = pool.makeInterface("com.powernode.bank.dao.AccountDao");
    // 实现接口
    ctClass.addInterface(ctInterface);
    // 实现接口中的所有方法
    // 获取接口中的所有方法
    Method[] methods = AccountDao.class.getDeclaredMethods();
    Arrays.stream(methods).forEach(method -> {
        // method是接口中的抽象方法
        // 把method的抽象方法实现
        try {
            // 拼接方法
            StringBuilder methodCode = new StringBuilder();
            // 追加修饰符列表
            methodCode.append("public ");
            // 追加返回值类型
            methodCode.append(method.getReturnType().getName());
            // 追加空格
            methodCode.append(" ");
            // 追加方法名
            methodCode.append(method.getName());
            // 追加小括号
            methodCode.append("(");
            // 追加参数
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                methodCode.append(parameterType.getName());
                methodCode.append(" ");
                methodCode.append("arg" + i);
                if (i != parameterTypes.length -1) {
                    methodCode.append(",");
                }

            }
            methodCode.append("){System.out.println(11111);");
            // 动态添加return语句
            String returnTypeSimpleName = method.getReturnType().getSimpleName();
            if ("void".equals(returnTypeSimpleName)) {

            }else if ("int".equals(returnTypeSimpleName)){
                methodCode.append("return 1;");
            }else if ("String".equals(returnTypeSimpleName)){
                methodCode.append("return \"hello\";");
            }
            methodCode.append("}");
            System.out.println(methodCode);
            CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
            // 将方法添加到类中
            ctClass.addMethod(ctMethod);
        } catch (Exception e) {
            e.printStackTrace();
        }


    });

    // 在内存中生成class,并将类加载到JVM中
    Class<?> clazz = ctClass.toClass();
    // 创建对象
    // 调用方法
    AccountDao accountDao = (AccountDao) clazz.newInstance();
    accountDao.insert("aaaa");
    accountDao.delete();
    accountDao.update("aaaa",1000.0);
    accountDao.selectByActno("aaaa");
}

4.在(五)mybatis-004-web中编写工具类GenerateDaoProxy(自动实现DAO层接口)

修改AccountMapper.xml文件:
namespace必须是dao接口的全限定名称,id必须是dao接口中的方法名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.powernode.dao.AccountDao">
    <select id="selectByActno" resultType="com.powernode.pojo.Account">
        select * from t_act where actno = #{actno}
    </select>

    <update id="updateByActno">
        update t_act set balance = #{balance} where actno = #{actno}
    </update>
</mapper>

com.powernode.utils包下

package com.powernode.utils;

import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 工具类
 * 可以动态生成DAO的实现类
 */
public class GenerateDaoProxy {

    /**
     * 生成dao接口实现类,并且将实现类的对象创建出来并返回
     * @param daoInterface dao接口
     * @return dao接口实现类的实例化对象
     */
    public static Object generate(SqlSession sqlSession, Class daoInterface){
        // 类池
        ClassPool pool = ClassPool.getDefault();
        // 制造类
        CtClass ctClass = pool.makeClass(daoInterface.getName() + "Proxy");
        // 制造接口
        CtClass ctInterface = pool.makeInterface(daoInterface.getName());
        // 实现接口
        ctClass.addInterface(ctInterface);
        // 实现接口中所有的方法
        Method[] methods = daoInterface.getDeclaredMethods();
        Arrays.stream(methods).forEach(method ->{
            // method是抽象方法
            // 将method抽象方法进行实现
            try {
                // Account selectByActno(String actno);
                // public Account selectByActno(String actno){ 代码; }
                StringBuilder methodCode = new StringBuilder();
                methodCode.append("public ");
                methodCode.append(method.getReturnType().getName());
                methodCode.append(" ");
                methodCode.append(method.getName());
                methodCode.append("(");
                // 需要方法的形式参数列表
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    methodCode.append(parameterType.getName());
                    methodCode.append(" ");
                    methodCode.append("arg" + i);
                    if(i != parameterTypes.length - 1){
                        methodCode.append(",");
                    }
                }
                methodCode.append(")");
                methodCode.append("{");
                // 需要方法体当中的代码
                methodCode.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.utils.SqlSessionUtil.openSession();");
                // 需要知道是什么类型的sql语句
                // sql语句的id是框架使用者提供的,具有多变性。对于框架的开发人员来说。我不知道。
                // 既然我框架开发者不知道sqlId,怎么办呢?mybatis框架的开发者于是就出台了一个规定:凡是使用GenerateDaoProxy机制的。
                // sqlId都不能随便写。namespace必须是dao接口的全限定名称。id必须是dao接口中方法名。
                String sqlId = daoInterface.getName() + "." + method.getName();
                SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType();
                if (sqlCommandType == SqlCommandType.INSERT) {

                }
                if (sqlCommandType == SqlCommandType.DELETE) {

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

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

        // 创建对象
        Object obj = null;
        try {
            Class<?> clazz = ctClass.toClass();
            obj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }
}

最后在业务层AccountServiceImpl引用Dao的时候改一下即可:

// private AccountDao accountDao = new AccountDaoImpl();
private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(), AccountDao.class);

5.MyBatis中接口代理机制及使用

在mybatis当中,mybatis提供了相关的机制。也可以动态为我们生成dao接口的实现类。(代理类:dao接口的代理)
mybatis当中实际上采用了代理模式。在内存中生成dao接口的代理类,然后创建代理类的实例。
使用mybatis的这种代理机制的前提:SqlMapper.xml文件中namespace必须是dao接口的全限定名称,id必须是dao接口中的方法名。
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

// private AccountDao accountDao = new AccountDaoImpl();
// 这是自己封装的。
// private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.openSession(), AccountDao.class);
	// AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
private AccountDao accountDao = SqlSessionUtil.openSession().getMapper(AccountDao.class);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值