使用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);