JAVA Compiler APIを利用して高速にプロパティアクセスする
JAVA Compiler APIをつかってなんか出来ないかなシリーズ第2弾!
といっても第1弾より先に試したものだけども。。メモ代わりにソース載っけます。
ちなみに第1弾はこちら↓
JAVA Compiler APIを利用してBeanのgetter、setterを不要にする - ほげにっき
概要
Java Beansのプロパティにリフレクション(イントロスペクション?)経由でアクセスすると結構に遅い。そこで、直接getter, setterを呼び出すコードを動的に生成してアクセスするだす。
ソース
例によって実験用なので実用には耐えられません。悪しからず。
public class FastPropertyUtils { public static PropertyAccessor getPropertyAccesor(Class<?> originalClass) throws Exception { String packageName = originalClass.getPackage().getName(); String simpleName = originalClass.getSimpleName() + "___PropertyAccessor"; String fullName = packageName + "." + simpleName; StringBuilder src = new StringBuilder(); src.append("package " + packageName + ";\n" + "public class " + simpleName + " implements " + PropertyAccessor.class.getName() + " {\n"); BeanInfo beanInfo = Introspector.getBeanInfo(originalClass); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); src.append(" public void set(Object bean, String property, Object value) {").append("\n"); src.append(" "+originalClass.getName()+" casted = ("+originalClass.getName()+")bean;").append("\n"); boolean first = true; for (PropertyDescriptor pd : propertyDescriptors) { if (pd.getWriteMethod() != null) { String propName = pd.getName(); String propType = pd.getPropertyType() == int.class ? // toriaezu int Integer.class.getName() : pd.getPropertyType().getName(); String setterName = pd.getWriteMethod().getName(); src.append(" " + (first ? "" : "else ") + "if (property.equals(\""+propName+"\")) {").append("\n"); src.append(" casted."+setterName+"(("+propType+")value);").append("\n"); src.append(" }").append("\n"); first = false; } } src.append(" else {").append("\n"); src.append(" throw new IllegalArgumentException();").append("\n"); src.append(" }").append("\n"); src.append(" }").append("\n"); src.append(" public Object get(Object bean, String property) {").append("\n"); src.append(" "+originalClass.getName()+" casted = ("+originalClass.getName()+")bean;").append("\n"); first = true; for (PropertyDescriptor pd : propertyDescriptors) { if (pd.getReadMethod() != null && !pd.getName().equals("class")) { String propName = pd.getName(); String propType = pd.getPropertyType().getName(); String getterName = pd.getReadMethod().getName(); src.append(" " + (first ? "" : "else ") + "if (property.equals(\""+propName+"\")) {").append("\n"); src.append(" return ("+propType+")casted."+getterName+"();").append("\n"); src.append(" }").append("\n"); first = false; } } src.append(" else {").append("\n"); src.append(" throw new IllegalArgumentException();").append("\n"); src.append(" }").append("\n"); src.append(" }").append("\n"); src.append("}\n"); System.out.println(src); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<JavaFileObject>(); JavaFileManager fileManager = new ClassFileManager(compiler, collector); try { List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>(); jfiles.add(new StringJavaFileObject(fullName, src.toString())); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, collector, null, null, jfiles); if (!task.call()) { throw new IllegalStateException("Error!"); } ClassLoader cl = fileManager.getClassLoader(null); return (PropertyAccessor) cl.loadClass(fullName).newInstance(); } finally { fileManager.close(); } } }
前回インナークラスとして使ってたいくつかのユーティリティクラスはtop levelに移動して使い回した。
↓参照↓
http://d.hatena.ne.jp/digo/20090610#1244643301
テスト
public void testSetAndGet() throws Exception { TestBean2 bean = new TestBean2(); PropertyAccessor accessor = FastPropertyUtils.getPropertyAccesor(TestBean2.class); accessor.set(bean, "hoge", "hogetest!"); assertEquals("hogetest!", bean.getHoge()); assertEquals("hogetest!", accessor.get(bean, "hoge")); accessor.set(bean, "moke", 999); assertEquals(999, bean.getMoke()); assertEquals((Integer)999, (Integer)accessor.get(bean, "moke")); }
でけた!(^o^)/ ちゃんとget, setできてます。
で速度は・・超適当計測だけど↓な結果に
FastPropertyUtils | 14941564(nanosec) |
---|---|
commons PropertyUtils | 1105767074(nanosec) |
※get,setを100000回loop
こんなに効果が!!
・・・と言いたいところだけど、前にも言った通り実はcglibのFastClassとあまり速度変わらない*1。FastClass使えるならそれを使おう。
今携わっているProjectはOSS禁止なのでもしかしたら日の目をみることもあるかもしれない。。
それにしてもcompiler APIなかなかオモロイね。なんか他にも出来そうだ。
*1:前回はFastClassより遅いと言ってましたがそれは計測ミスで、実質ほぼ同速だった