Javassist是⼀个开源的分析、编辑和创建Java字节码的类库。是由东京⼯业⼤学的数学和计算机科学 系的 Shigeru Chiba (千叶 滋)所创建的。它已加⼊了开放源代码JBoss 应⽤服务器项⽬,通过使⽤ Javassist对字节码操作为JBoss实现动态"AOP"框架。
Javassist的使⽤:我们要使⽤javassist,⾸先要引⼊它的依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.1-GA</version>
</dependency>
样例代码:
@Test
public void testGenerateFirstClass() throws Exception {
// 获取类池,这个类池就是用来给我生成class的
ClassPool pool = ClassPool.getDefault();
// 制造类(需要告诉javassist,类名是啥)
CtClass ctClass = pool.makeClass("com.xiaofan.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.xiaofan.bank.dao.impl.AccountDaoImpl");
// 创建对象
Object obj = clazz.newInstance();
// 获取AccountDaoImpl中的insert方法
Method insertMethod = clazz.getDeclaredMethod("insert");
// 调用方法insert
insertMethod.invoke(obj);
}
运⾏要注意:加两个参数,要不然会有异常。
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
使⽤Javassist⽣成DaoImpl类
@Test
public void testGenerateImpl() throws Exception {
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类
CtClass ctClass = pool.makeClass("com.xiaofan.bank.dao.impl.AccountDaoImpl");
// 制造接口
CtClass ctInterface = pool.makeInterface("com.xiaofan.bank.dao.AccountDao");
// 添加接口到类中
ctClass.addInterface(ctInterface); // AccountDaoImpl implements AccountDao
// 实现接口中的方法
// 制造方法
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();
}
/*针对接口 实现接口的实现类 */
@Test
public void testGenerateAccountDaoImpl() throws Exception {
// 获取类池
ClassPool pool = ClassPool.getDefault();
// 制造类
CtClass ctClass = pool.makeClass("com.xiaofan.bank.dao.impl.AccountDaoImpl");
// 制造接口
CtClass ctInterface = pool.makeInterface("com.xiaofan.bank.dao.AccountDao");
// 实现接口
ctClass.addInterface(ctInterface);
// 实现接口中所有的方法
Method[] methods = AccountDao.class.getDeclaredMethods();
// System.out.println("接口中方法的个数"+methods.length);
Arrays.stream(methods).forEach(method -> {
// method是接口中的抽象方法
// 把method抽象方法给实现了。
try {
// public void delete(){}
// public int update(String actno, Double balance){}
StringBuilder methodCode = new StringBuilder();//对方法进行拼串,
methodCode.append("public "); // 追加修饰符列表
methodCode.append(method.getReturnType().getName()); // 追加返回值类型
methodCode.append(" ");
methodCode.append(method.getName()); //追加方法名
methodCode.append("(");
// 拼接参数 String actno, Double balance ,参数可能拥有多个!
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("aaaaa");
accountDao.delete();
accountDao.update("aaaa", 1000.0);
accountDao.selectByActno("aaaa");
}
使⽤Javassist⽣成DaoImpl类
/**
* 工具类:可以动态的生成DAO的实现类。(或者说可以动态生成DAO的代理类)
* 注意注意注意注意注意!!!!!!:
* 凡是使用GenerateDaoProxy的,SQLMapper.xml映射文件中namespace必须是dao接口的全名,id必须是dao接口中的方法名。
*/
public class GenerateDaoProxy { // GenerateDaoProxy是mybatis框架的开发者写的。
/**
* 生成dao接口实现类,并且将实现类的对象创建出来并返回。
* @param daoInterface dao接口
* @return dao接口实现类的实例化对象。
*/
public static Object generate(SqlSession sqlSession, Class daoInterface){
// 类池
ClassPool pool = ClassPool.getDefault();
// 制造类(com.powernode.bank.dao.AccountDao --> com.powernode.bank.dao.AccountDaoProxy)
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.bank.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("}");
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;
}
}