Java tutorial
/* * Copyright 2011-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package kr.debop4j.core.reflect; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import java.lang.reflect.Modifier; import static kr.debop4j.core.Guard.shouldNotBeNull; import static org.objectweb.asm.Opcodes.*; /** * ?? ??? , ?? ? ?? . * * @author ? ( sunghyouk.bae@gmail.com ) * @since 13. 1. 21 */ @Slf4j public abstract class ConstructorAccess<T> { @Getter boolean nonStaticMemberClass; /** Constructor for top-level classes and static nested classes. */ public abstract T newInstance(); /** Constructor for inner classes (non-static nested classes) - except static nested classes */ public abstract T newInstance(Object enclosingInstance); private static final Object syncLock = new Object(); /** ? ??? ? ?. */ @SuppressWarnings("unchecked") public static <T> ConstructorAccess<T> get(Class<T> type) { shouldNotBeNull(type, "type"); log.trace("[{}]? ??? ? .", type.getName()); Class enclosingType = type.getEnclosingClass(); boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass() && !Modifier.isStatic(type.getModifiers()); String className = type.getName(); String accessClassName = className + "ConstructorAccess"; if (accessClassName.startsWith("java.")) accessClassName = ReflectConsts.BASE_PACKAGE + "." + accessClassName; Class accessClass = null; AccessClassLoader loader = AccessClassLoader.get(type); shouldNotBeNull(loader, "loader"); synchronized (syncLock) { try { accessClass = loader.loadClass(accessClassName); } catch (ClassNotFoundException ignored) { String accessClassNameInternal = accessClassName.replace('.', '/'); String classNameInternal = className.replace('.', '/'); String enclosingClassNameInternal; if (!isNonStaticMemberClass) { enclosingClassNameInternal = null; try { type.getConstructor((Class[]) null); } catch (Exception ex) { throw new RuntimeException("[" + type.getName() + "] ? ? . ?? .", ex); } } else { enclosingClassNameInternal = enclosingType.getName().replace('.', '/'); try { type.getConstructor(enclosingType); // Inner classes should have this. } catch (Exception ex) { throw new RuntimeException( "Non-static member class ? . ?? . type=" + type.getName(), ex); } } ClassWriter cw = new ClassWriter(0); cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, ReflectConsts.CONSTRUCTOR_ACCESS_PATH, null); insertConstructor(cw); insertNewInstance(cw, classNameInternal); insertNewInstanceInner(cw, classNameInternal, enclosingClassNameInternal); cw.visitEnd(); accessClass = loader.defineClass(accessClassName, cw.toByteArray()); } } try { ConstructorAccess<T> access = (ConstructorAccess<T>) accessClass.newInstance(); access.nonStaticMemberClass = isNonStaticMemberClass; log.trace( " ??? ConstructorAccess . accessClassName=[{}]", accessClassName); return access; } catch (Exception ex) { throw new RuntimeException("Error constructing constructor access class: [" + accessClassName + "]", ex); } } /** ?? . */ static private void insertConstructor(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, ReflectConsts.CONSTRUCTOR_ACCESS_PATH, "<init>", "()V"); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } /** ? . */ static void insertNewInstance(ClassWriter cw, String classNameInternal) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitTypeInsn(NEW, classNameInternal); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", "()V"); mv.visitInsn(ARETURN); mv.visitMaxs(2, 1); mv.visitEnd(); } /** inner ? */ static void insertNewInstanceInner(ClassWriter cw, String classNameInternal, String enclosingClassNameInternal) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); mv.visitCode(); if (enclosingClassNameInternal != null) { mv.visitTypeInsn(NEW, classNameInternal); mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, enclosingClassNameInternal); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); mv.visitInsn(POP); mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", "(L" + enclosingClassNameInternal + ";)V"); mv.visitInsn(ARETURN); mv.visitMaxs(4, 2); } else { mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException"); mv.visitInsn(DUP); mv.visitLdcInsn("Not an inner class."); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "(Ljava/lang/String;)V"); mv.visitInsn(ATHROW); mv.visitMaxs(3, 2); } mv.visitEnd(); } }