初探Spoon(附上用spoon检查HideUtilityClassConstructor)
来源:互联网 发布:sql设置字段默认值 编辑:程序博客网 时间:2024/06/06 00:19
最近收到友人的委托,帮忙研究下spoon,尝试下用spoon来实现一个工具类的检查,于是做了个demo,以供参考。
首先说下工具类的一些规范约束,又或者是最佳实践,以下是checkstyle网站上面的定义:
http://checkstyle.sourceforge.net/config_design.html#HideUtilityClassConstructor
HideUtilityClassConstructor Description
Since Checkstyle 3.1
Makes sure that utility classes (classes that contain only static
methods or fields in their API) do not have a public constructor.Rationale: Instantiating utility classes does not make sense. Hence
the constructors should either be private or (if you want to allow
subclassing) protected. A common mistake is forgetting to hide the
default constructor.If you make the constructor protected you may want to consider the
following constructor implementation technique to disallow
instantiating subclasses:public class StringUtils // not final to allow subclassing {
protected StringUtils() {
// prevents calls from subclass
throw new UnsupportedOperationException();
}public static int count(char c, String s) { // ...} }
其实宗旨很简单,对于一个工具类来说最好的做法是:
- 所有方法都是静态的
不需要一个公共的构造方法(并没有任何理由去实例化一个工具类),如何做?
1)隐藏默认的构造方法(如果在类的修饰前是public 则默认构造函数访问权限是 public ,如果 没有显示采用public修饰,则 默认构造函数的访问权限是 friendly),将其设置为private2)如果构造方法是protected的,则建议在构造方法里面抛出异常,以防止子类被实例化
protected StringUtils() { // prevents calls from subclass throw new UnsupportedOperationException(); }
同时也科普下访问权限的作用域:
下图引用自博文:http://blog.csdn.net/fatherican/article/details/6876596
那么,什么是spoon?上官网介绍:
http://spoon.gforge.inria.fr/architecture_test.htmlSpoon is a library to analyze, transform, rewrite, transpile Java source code (incl Java 8). It parses source files to build a well-designed AST with powerful analysis and transformation API.
简单滴说,就是使用了抽象语法树AST(abstract syntax tree)的方式去拆解一个java源码(有点像反射机制做的东西?),并提取里面的元素,后续做一些我们想要做的操作,例如代码格式检查,xxx,xxx,xxx(反正我只用到格式检查)….
然后看到关于codean alysis的介绍:
http://spoon.gforge.inria.fr/first_analysis_processor.html
不错,这正是我想要的,我看了下例子,主要说的是检查try {…} catch {…}代码块里面的catch代码块是不是没有做任何的处理,那既然能检查try catch代码块,是不是也能检查方法的作用域呢?我把 spoon的jar包下了下来,反编译看了下源码,果然有!
只要在processor的类上集成了AbstractProcessor ,注意枚举类型是CtClass的话,就会在解析到构造函数的时候,就会触发processor的处理
public class ClassProcessor extends AbstractProcessor<CtClass>
接着完善processor,实现父类的方法public void process(CtClass paramE),然后后用paramE.hasModifier来判断方法的作用域,是不是很简单?! So easy! 妈妈再也不用担心我的代码不规范了!
/** * If this class is only a utility class, you should make the class final and define a private constructor */ @Override public void process(CtClass paramE) { System.out.println(">>>>>>>>> Start to check the [" + paramE.getActualClass().getName() +"] Class definition <<<<<<<<<<<<<<"); if(!paramE.hasModifier(ModifierKind.FINAL)) { System.out.println("Utility Class is recommanded to be defined as FINAL!!!"); System.out.println(); }else { System.out.println("Utility Class is FINAL now. : )"); } System.out.println(">>>>>>>>> Start to check the Class definition <<<<<<<<<<<<<<"); Set<CtConstructor> set = paramE.getConstructors(); int counter = 0; for(CtConstructor constructor : set) { if(constructor.hasModifier(ModifierKind.PUBLIC) || constructor.hasModifier(ModifierKind.PROTECTED)) { getFactory().getEnvironment().report(this, Level.WARN, paramE, "Utility Class Constructor is accessible!"); counter ++; System.out.println("------------------- " +counter+ " ---------------------"); System.out.println("Utility Class Constructor is accessible! It should be defined as private! Constructor is :"); System.out.println(constructor.toString()); System.out.println("------------------------------------------"); } } }
接着在main函数调用new Launcher().run(params), 就大功告成了
public static void main( String[] args ) { String[] params = {"-i","J:\\eclipse-workspace\\Spoon\\src\\main\\java\\com\\nathan\\Spoon\\Utils","-p","com.nathan.Spoon.ClassProcessor"}; new Launcher().run(params); }
至于new Launcher().run参数的定义,可以参考源代码可以看到
/* */ public void printUsage() {/* 121 */ this.commandLineArgs = new String[] { "--help" };/* 122 */ processArguments();/* */ }
可以输入help来查看参数定义,很贴心有木有,心都要化了:
String[] params = {"--help"}; new Launcher().run(params);
结果很清晰了,就是说,你需要输入什么参数,首先在前面告知你需要输入参数的类型,如
[(-i|–input) ]
List of path to sources files.
[(-p|–processors) ]
List of processor’s qualified name to be used.:
Spoon version 5.7.0Usage: java <launcher name> [option(s)]Options : [-h|--help] [--tabs] Use tabulations instead of spaces in the generated code (use spaces by default). [--tabsize <tabsize>] Define tabulation size. (default: 4) [--level <level>] Level of the ouput messages about what spoon is doing. Default value is ALL level. (default: OFF) [--with-imports] Enable imports in generated files. [--compliance <compliance>] Java source code compliance level (1,2,3,4,5, 6, 7 or 8). (default: 8) [--encoding <encoding>] Forces the compiler to use a specific encoding (UTF-8, UTF-16, ...). (default: UTF-8) [(-i|--input) <input>] List of path to sources files. [(-p|--processors) <processors>] List of processor's qualified name to be used. [(-t|--template) <template>] List of path to templates java files. [(-o|--output) <output>] Specify where to place generated java files. (default: spooned) [--source-classpath <source-classpath>] An optional classpath to be passed to the internal Java compiler when building or compiling the input sources. [--template-classpath <template-classpath>] An optional classpath to be passed to the internal Java compiler when building the template sources. [(-d|--destination) <destination>] An optional destination directory for the generated class files. (default: spooned-classes) [--output-type <output-type>] States how to print the processed source code: nooutput|classes|compilationunits (default: classes) [--compile] Compiles the resulting classes (after transformation) to bytecode. [--precompile] [experimental] Enable pre-compilation of input source files before processing. The compiled classes will be added to the classpath. [--buildOnlyOutdatedFiles] Set Spoon to build only the source files that have been modified since the latest source code generation, for performance purpose. Note that this option requires to have the --ouput-type option not set to none. This option is not appropriate to all kinds of processing. In particular processings that implement or rely on a global analysis should avoid this option because the processor will only have access to the outdated source code (the files modified since the latest processing). [--lines] Set Spoon to try to preserve the original line numbers when generating the source code (may lead to human-unfriendly formatting). [-x|--noclasspath] Does not assume a full classpath [-g|--gui] Show spoon model after processing [-r|--no-copy-resources] Disable the copy of resources from source to destination folder. [-c|--enable-comments] Adds all code comments in the Spoon AST (Javadoc, line-based comments), rewrites them when pretty-printing. [(-f|--generate-files) <generate-files>] Only generate the given fully qualified java classes (separated by ':' if multiple are given). [-a|--disable-model-self-checks] Disables checks made on the AST (hashcode violation, method's signature violation and parent violation). Default: false.
接下来我们看一下代码的执行效果,console的输出,格式不太好看,凑合凑合:
>>>>>>>>> Start to check the [com.nathan.Spoon.Utils.DateUtil] Class definition <<<<<<<<<<<<<<Utility Class is recommanded to be defined as FINAL!!!>>>>>>>>> Start to check the Class definition <<<<<<<<<<<<<<------------------- 1 ---------------------Utility Class Constructor is accessible! It should be defined as private! Constructor is :public DateUtil() { super();}------------------------------------------------------------- 2 ---------------------Utility Class Constructor is accessible! It should be defined as private! Constructor is :public DateUtil(java.lang.String utilName) { super(); this.utilName = utilName;}------------------------------------------>>>>>>>>> Start to check the [com.nathan.Spoon.Utils.DateUtil2] Class definition <<<<<<<<<<<<<<Utility Class is FINAL now. : )>>>>>>>>> Start to check the Class definition <<<<<<<<<<<<<<------------------- 1 ---------------------Utility Class Constructor is accessible! It should be defined as private! Constructor is :protected DateUtil2() { super();}------------------------------------------
最后附上源代码,学无止境,不要在不可以做更多事情的时候却什么都不做,共勉!
Spoon demo的 githun地址,点我点我!
- 初探Spoon(附上用spoon检查HideUtilityClassConstructor)
- Kettle Spoon初探-简单说
- 用源码运行Spoon
- spoon实战
- kettle spoon
- Kettle Spoon
- I lost my spoon
- kettle spoon pentaho
- spoon学习1
- kettle spoon excel
- Spoon Devil Love Arithmetic
- spoon测试指定设备
- Spoon etl汉化
- Kettle Spoon入门教程
- spoon新手入门教程
- kettle 启动spoon一闪而过
- Spoon工具的使用
- 玲珑杯 1005 Spoon Devil's RP Test(水题)
- 数据结构实验之栈:行编辑器
- 《 Android应用setContentView与LayoutInflater加载解析机制源码分析》观后感
- 网络爬虫中Jsoup请求url
- 费马小定理
- ETL学习笔记之一:ETL是什么?
- 初探Spoon(附上用spoon检查HideUtilityClassConstructor)
- html-覆盖在div上的虚影
- stream 排序
- HDU 2899 Strange fuction(二分,三分)
- (转)数学之美番外篇:平凡而又神奇的贝叶斯方法
- Java线程的状态转换关系
- 设计模式-《设计模式那点事》代码
- CSS3中的盒布局
- 实现二进制代码块的复制