001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core; 018 019import java.io.IOException; 020import java.io.InputStream; 021 022import org.springframework.lang.Nullable; 023import org.springframework.util.FileCopyUtils; 024 025/** 026 * {@code ClassLoader} that does <i>not</i> always delegate to the parent loader 027 * as normal class loaders do. This enables, for example, instrumentation to be 028 * forced in the overriding ClassLoader, or a "throwaway" class loading behavior 029 * where selected application classes are temporarily loaded in the overriding 030 * {@code ClassLoader} for introspection purposes before eventually loading an 031 * instrumented version of the class in the given parent {@code ClassLoader}. 032 * 033 * @author Rod Johnson 034 * @author Juergen Hoeller 035 * @since 2.0.1 036 */ 037public class OverridingClassLoader extends DecoratingClassLoader { 038 039 /** Packages that are excluded by default. */ 040 public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[] 041 {"java.", "javax.", "sun.", "oracle.", "javassist.", "org.aspectj.", "net.sf.cglib."}; 042 043 private static final String CLASS_FILE_SUFFIX = ".class"; 044 045 static { 046 ClassLoader.registerAsParallelCapable(); 047 } 048 049 050 @Nullable 051 private final ClassLoader overrideDelegate; 052 053 054 /** 055 * Create a new OverridingClassLoader for the given ClassLoader. 056 * @param parent the ClassLoader to build an overriding ClassLoader for 057 */ 058 public OverridingClassLoader(@Nullable ClassLoader parent) { 059 this(parent, null); 060 } 061 062 /** 063 * Create a new OverridingClassLoader for the given ClassLoader. 064 * @param parent the ClassLoader to build an overriding ClassLoader for 065 * @param overrideDelegate the ClassLoader to delegate to for overriding 066 * @since 4.3 067 */ 068 public OverridingClassLoader(@Nullable ClassLoader parent, @Nullable ClassLoader overrideDelegate) { 069 super(parent); 070 this.overrideDelegate = overrideDelegate; 071 for (String packageName : DEFAULT_EXCLUDED_PACKAGES) { 072 excludePackage(packageName); 073 } 074 } 075 076 077 @Override 078 public Class<?> loadClass(String name) throws ClassNotFoundException { 079 if (this.overrideDelegate != null && isEligibleForOverriding(name)) { 080 return this.overrideDelegate.loadClass(name); 081 } 082 return super.loadClass(name); 083 } 084 085 @Override 086 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 087 if (isEligibleForOverriding(name)) { 088 Class<?> result = loadClassForOverriding(name); 089 if (result != null) { 090 if (resolve) { 091 resolveClass(result); 092 } 093 return result; 094 } 095 } 096 return super.loadClass(name, resolve); 097 } 098 099 /** 100 * Determine whether the specified class is eligible for overriding 101 * by this class loader. 102 * @param className the class name to check 103 * @return whether the specified class is eligible 104 * @see #isExcluded 105 */ 106 protected boolean isEligibleForOverriding(String className) { 107 return !isExcluded(className); 108 } 109 110 /** 111 * Load the specified class for overriding purposes in this ClassLoader. 112 * <p>The default implementation delegates to {@link #findLoadedClass}, 113 * {@link #loadBytesForClass} and {@link #defineClass}. 114 * @param name the name of the class 115 * @return the Class object, or {@code null} if no class defined for that name 116 * @throws ClassNotFoundException if the class for the given name couldn't be loaded 117 */ 118 @Nullable 119 protected Class<?> loadClassForOverriding(String name) throws ClassNotFoundException { 120 Class<?> result = findLoadedClass(name); 121 if (result == null) { 122 byte[] bytes = loadBytesForClass(name); 123 if (bytes != null) { 124 result = defineClass(name, bytes, 0, bytes.length); 125 } 126 } 127 return result; 128 } 129 130 /** 131 * Load the defining bytes for the given class, 132 * to be turned into a Class object through a {@link #defineClass} call. 133 * <p>The default implementation delegates to {@link #openStreamForClass} 134 * and {@link #transformIfNecessary}. 135 * @param name the name of the class 136 * @return the byte content (with transformers already applied), 137 * or {@code null} if no class defined for that name 138 * @throws ClassNotFoundException if the class for the given name couldn't be loaded 139 */ 140 @Nullable 141 protected byte[] loadBytesForClass(String name) throws ClassNotFoundException { 142 InputStream is = openStreamForClass(name); 143 if (is == null) { 144 return null; 145 } 146 try { 147 // Load the raw bytes. 148 byte[] bytes = FileCopyUtils.copyToByteArray(is); 149 // Transform if necessary and use the potentially transformed bytes. 150 return transformIfNecessary(name, bytes); 151 } 152 catch (IOException ex) { 153 throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex); 154 } 155 } 156 157 /** 158 * Open an InputStream for the specified class. 159 * <p>The default implementation loads a standard class file through 160 * the parent ClassLoader's {@code getResourceAsStream} method. 161 * @param name the name of the class 162 * @return the InputStream containing the byte code for the specified class 163 */ 164 @Nullable 165 protected InputStream openStreamForClass(String name) { 166 String internalName = name.replace('.', '/') + CLASS_FILE_SUFFIX; 167 return getParent().getResourceAsStream(internalName); 168 } 169 170 171 /** 172 * Transformation hook to be implemented by subclasses. 173 * <p>The default implementation simply returns the given bytes as-is. 174 * @param name the fully-qualified name of the class being transformed 175 * @param bytes the raw bytes of the class 176 * @return the transformed bytes (never {@code null}; 177 * same as the input bytes if the transformation produced no changes) 178 */ 179 protected byte[] transformIfNecessary(String name, byte[] bytes) { 180 return bytes; 181 } 182 183}