如何使用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文件地址*/
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
* 待匹配的字符串
* 匹配成功则返回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);
}

自动生成getter setter的更多相关文章

  1. [图解教程]Eclipse不可不知的用法之一:自动生成Getter、Setter和构造方法

    [图解教程]Eclipse不可不知的用法之一:自动生成Getter.Setter和构造方法 关键词:Getters and Setters.getter和setter方法.Constructor us ...

  2. 基于注解处理器开发自动生成getter和setter方法的插件

    昨天无意中,逛到了lombok的网站,并看到了首页的5分钟视频,视频中的作者只是在实体类中写了几个字段,就可以自动编译为含setter.getter.toString()等方法的class文件.看着挺 ...

  3. android studio: 快捷键生成getter/setter方法时自动加m的问题

    平时使用Android Studio 在写实体类的时候,习惯给实体类的成员变量前面加上一个"m" 修饰符表示这是一个成员变量,这也是搞java的一种约定俗成的写法,本来这是没有问题 ...

  4. Spring 自动生成getter和setter方法 tostring方法

    添加maven依赖 <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency& ...

  5. 自动生成getter,setter方法的插件lombok

    1.在InteiliJ IDEA上安装lombok插件,并重启 . 2.在pom.xml文件中添加依赖 <dependency>    <groupId>org.project ...

  6. Eclipse 自动生成getter 和 setter

    示例: public String View;//右击View > Source > Generate Getters and Setters... public String view; ...

  7. STS 自动生成 getter 和 setter

    1.Source --  Generate Getters and Setters

  8. python自动生成bean类

    近期在学习python,一直在和java做对比,目前没有发现有通过字段自动生成getter setter方法,故此自己写了一个类,可以通过__init__方法传入类名和字段数组,再调用内部的方法,就可 ...

  9. mapstruct 的 mapstruct-processor 自动生成的 Impl 文件中未设置属性值(时好时坏)

    配置依赖和注解处理器 ... <properties> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version ...

随机推荐

  1. Xadmin弹出窗口

    Xadmin弹出窗口 需求分析: 1.在添加页面的一对多和多对多字段后面加上+,点击+后,能显示出添加相应字段的窗口 2.提交后窗口关闭,添加的内容显示到当前页面 1.判断出当前字段是否为Foreig ...

  2. 整理的最全 python常见面试题(基本必考)

    整理的最全 python常见面试题(基本必考) python 2018-05-17 作者 大蛇王 1.大数据的文件读取 ① 利用生成器generator ②迭代器进行迭代遍历:for line in ...

  3. (百度)centos7上安装apache指南

    https://jingyan.baidu.com/album/c843ea0bb5ff3977931e4a14.html?picindex=1 原文就不拷贝了.留个网址

  4. 受欢迎的牛[HAOI2006]

    --BZOJ1051 Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 ​ 种关系是具有传递性的,如果A认为B受欢迎, ...

  5. windows Sql server performance monitor

    对于sql server 性能的监控主要从2个方面: 1. sql server自带的监控 Management->SQL Server Logs->Activity Monitor 在这 ...

  6. IJ配置项目的TOMCAT

    参考文档: IJ里配置TOMCAT http://jingyan.baidu.com/album/0a52e3f43d9f69bf62ed72f9.html?picindex=11 源发行版1.8 需 ...

  7. turtle库的学习笔记

    (1)turtle使用pen来绘制图形 pendown()  放下画笔,移动到指定点后继续绘制 penup()   提起画笔,用于另起一个地方绘制时使用 pensize(width)   设置画笔线条 ...

  8. linux分区划分

  9. 11. English vocabulary 英语词汇量

    11. English vocabulary 英语词汇量 (1) The exact number of English words is not known.The large dictionari ...

  10. webpack Cannot find module 'webpack/schemas/WebpackOptions.json'

    webpack-dev-server版本的问题 一直在解决这个问题,最后竟然发现...安装2.9.1版本就可以了 npm install webpack-dev-server@2.9.1