如何使用java黑魔法给一个entity生成getter,setter方法?
由于java是一门静态语言,要给一个类动态添加方法,看似是不可能的。但牛B的程序员会让任何事情发生。我只知道有两种方式可以做到,一种是字节码加强,借助asm包;另一种是运行期加强,借助javassist包。下面,尝试用第二种方法,完成一个简单的demo。
大致思路如下:先在Filter类里扫描工程得到所有实体类,通过创建自定义类加载器加载实体类 ,在加载的过程中通过javassist给每个实体类添加getter setter。
为什么要自定义类加载器呢?
我们知道,所有的类都是通过类加载器加载到内存中,类加载器的加载通过它父加载器完成。java类加载器包括:
1、启动加载器(Bootstrap ClassLoader),祖宗辈的,由c++语言实现,是jvm一部分,负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库。
2、扩展类加载器(Extension ClassLoader),爷爷辈的,负责加载JAVA_HOME\lib\ext目录中的类库。
3、应用程序类加载器(Application ClassLoader),dady辈的,负责加载用户类路径(Classpath)上所指定的类库,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
4、自定义类加载器。
如果我们使用app类加载器,就没法在加载类的时候给类动手脚了。如果我们把内存中的类当成一张蓝图,一旦这张蓝图生成,我们就更没法修改了。所以我们需要自定义加载器,在生成蓝图之前就动手动脚。
第一步:创建Filter
public class LoaderFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { MyClassLoader loader=new MyClassLoader(); //自定义加载器 //搜索文件路径 List<String> classFileList = ClassSearcher.findFiles(ClassSearcher.classPathUrl.getFile(),"*.class"); for(String str:classFileList){ System.out.println(str); if(str.startsWith("com.kyle.entity")){ //属于实体包下,则添加get,set loader.loadClass(str); } } chain.doFilter(req, resp); } @Override public void init(FilterConfig arg0) throws ServletException { } }
第二步:创建类加载器。javassist操作和java反射API操作差不多。
public class MyClassLoader extends ClassLoader{ @Override public Class<?> loadClass(String name) { try { InputStream is = getClass().getResourceAsStream(getPath(name)); if(is==null){ return super.loadClass(name); } ClassPool pool=ClassPool.getDefault(); CtClass clazz=pool.makeClass(is); CtField[] fields= clazz.getDeclaredFields(); System.out.println(Arrays.toString(fields)); for(CtField field:fields){ //add getter method System.out.println("filedname:"+field.getName()+",type:"+field.getType().getName()); String fieldname=upperFirst(field.getName()); CtMethod getter=new CtMethod(pool.get(field.getType().getName()), "get"+fieldname,null, clazz); getter.setModifiers(Modifier.PUBLIC); //访问权限 getter.setBody("{ return this."+field.getName()+"; }"); clazz.addMethod(getter); //add setter method CtMethod setter=new CtMethod(CtClass.voidType, "set"+fieldname, new CtClass[]{pool.get(field.getType().getName())}, clazz); setter.setModifiers(Modifier.PUBLIC); setter.setBody("{this."+field.getName()+"=$1;}"); clazz.addMethod(setter); } return clazz.toClass(); } catch (Exception e) { throw new RuntimeException(e); } } private String getPath(String name){ name=name.replace(".", "/"); return "/"+name+".class"; } private String upperFirst (String str){ return str.substring(0,1).toUpperCase()+str.substring(1); }寻找类文件类classSearcher代码:
public class ClassSearcher { public static URL classPathUrl = ClassSearcher.class.getResource("/"); static String lib = new File(classPathUrl.getFile()).getParent() + "/lib/"; /** * 递归查找文件 * * @param baseDirName * 查找的文件夹路径 * @param targetFileName * 需要查找的文件名 */ public static List<String> findFiles(String baseDirName,String targetFileName) { /** * 算法简述: 从某个给定的需查找的文件夹出发,搜索该文件夹的所有子文件夹及文件, * 若为文件,则进行匹配,匹配成功则加入结果集,若为子文件夹,则进队列。 队列不空,重复上述操作,队列为空,程序结束,返回结果。 */ List<String> classFiles = new ArrayList<String>(); String tempName = null; // 判断目录是否存在 File baseDir = new File(baseDirName); if (!baseDir.exists() || !baseDir.isDirectory()) { System.err.println("search error:" + baseDirName + "is not a dir!"); } else { String[] filelist = baseDir.list(); for (int i = 0; i < filelist.length; i++) { File readfile = new File(baseDirName + File.separator + filelist[i]); if (!readfile.isDirectory()) { tempName = readfile.getName(); if (ClassSearcher.wildcardMatch(targetFileName, tempName)) { String classname; String tem = readfile.getAbsoluteFile().toString().toString().replaceAll("\\\\", "/"); classname = tem.substring(tem.indexOf("/classes")+ "/classes".length(), tem.indexOf(".class")); if (classname.startsWith("/")) { classname = classname.substring(classname.indexOf("/") + 1); } classname = className(classname, "/classes"); classFiles.add(classname); } } else if (readfile.isDirectory()) { classFiles.addAll(findFiles(baseDirName + File.separator+ filelist[i], targetFileName)); } } } return classFiles; } /** * 查找jar包中的class * * @param baseDirName * jar路径 * @param includeJars * @param jarFileURL * jar文件地址 <a href="http://my.oschina.net/u/556800" * target="_blank" rel="nofollow">@return</a> */ public static List<String> findjarFiles(String baseDirName, final List<String> includeJars) { List<String> classFiles = new ArrayList<String>(); try { // 判断目录是否存在 File baseDir = new File(baseDirName); if (!baseDir.exists() || !baseDir.isDirectory()) { System.out.println("file serach error:" + baseDirName + "is not a dir!"); } else { String[] filelist = baseDir.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return includeJars.contains(name); } }); for (int i = 0; i < filelist.length; i++) { JarFile localJarFile = new JarFile(new File(baseDirName + File.separator + filelist[i])); Enumeration<JarEntry> entries = localJarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); String entryName = jarEntry.getName(); if (!jarEntry.isDirectory()&&entryName.endsWith(".class")) { String className = entryName.replaceAll("/", ".") .substring(0, entryName.length() - 6); classFiles.add(className); } } localJarFile.close(); } } } catch (IOException e) { e.printStackTrace(); } return classFiles; } @SuppressWarnings("rawtypes") public static List<Class> findInClasspathAndJars(Class clazz, List<String> includeJars) { List<String> classFileList = findFiles(classPathUrl.getFile(),"*.class"); classFileList.addAll(findjarFiles(lib,includeJars)); return extraction(clazz,classFileList); } @SuppressWarnings("rawtypes") private static List<Class> extraction(Class clazz,List<String> classFileList) { List<Class> classList = new ArrayList<Class>(); for (String classFile : classFileList) { try { Class classInFile = Class.forName(classFile); if (clazz.isAssignableFrom(classInFile)) { // if (BeanKit.isSuperclass(classInFile,clazz)) { classList.add(classInFile); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } return classList; } private static String className(String classFile, String pre) { String objStr = classFile.replaceAll("\\\\", "/"); return objStr.replaceAll("/", "."); } /** * 通配符匹配 * * @param pattern * 通配符模式 * @param str * 待匹配的字符串 <a href="http://my.oschina.net/u/556800" * target="_blank" rel="nofollow">@return</a> * 匹配成功则返回true,否则返回false */ private static boolean wildcardMatch(String pattern, String str) { int patternLength = pattern.length(); int strLength = str.length(); int strIndex = 0; char ch; for (int patternIndex = 0; patternIndex < patternLength; patternIndex++) { ch = pattern.charAt(patternIndex); if (ch == '*') { // 通配符星号*表示可以匹配任意多个字符 while (strIndex < strLength) { if (wildcardMatch(pattern.substring(patternIndex + 1), str.substring(strIndex))) { return true; } strIndex++; } } else if (ch == '?') { // 通配符问号?表示匹配任意一个字符 strIndex++; if (strIndex > strLength) { // 表示str中已经没有字符匹配?了。 return false; } } else { if ((strIndex >= strLength) || (ch != str.charAt(strIndex))) { return false; } strIndex++; } } return (strIndex == strLength); } public static List<Class> findInClasspath(Class clazz) { List<String> classFileList = findFiles(classPathUrl.getFile(),"*.class"); return extraction(clazz,classFileList); }