001/* 002 * Copyright 2002-2016 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.objenesis; 018 019import org.springframework.core.SpringProperties; 020import org.springframework.objenesis.instantiator.ObjectInstantiator; 021import org.springframework.objenesis.strategy.InstantiatorStrategy; 022import org.springframework.objenesis.strategy.StdInstantiatorStrategy; 023import org.springframework.util.ConcurrentReferenceHashMap; 024 025/** 026 * Spring-specific variant of {@link ObjenesisStd} / {@link ObjenesisBase}, 027 * providing a cache based on {@code Class} keys instead of class names, 028 * and allowing for selective use of the cache. 029 * 030 * @author Juergen Hoeller 031 * @since 4.2 032 * @see #isWorthTrying() 033 * @see #newInstance(Class, boolean) 034 */ 035public class SpringObjenesis implements Objenesis { 036 037 /** 038 * System property that instructs Spring to ignore Objenesis, not even attempting 039 * to use it. Setting this flag to "true" is equivalent to letting Spring find 040 * out that Objenesis isn't working at runtime, triggering the fallback code path 041 * immediately: Most importantly, this means that all CGLIB AOP proxies will be 042 * created through regular instantiation via a default constructor. 043 */ 044 public static final String IGNORE_OBJENESIS_PROPERTY_NAME = "spring.objenesis.ignore"; 045 046 047 private final InstantiatorStrategy strategy; 048 049 private final ConcurrentReferenceHashMap<Class<?>, ObjectInstantiator<?>> cache = 050 new ConcurrentReferenceHashMap<>(); 051 052 private volatile Boolean worthTrying; 053 054 055 /** 056 * Create a new {@code SpringObjenesis} instance with the 057 * standard instantiator strategy. 058 */ 059 public SpringObjenesis() { 060 this(null); 061 } 062 063 /** 064 * Create a new {@code SpringObjenesis} instance with the 065 * given standard instantiator strategy. 066 * @param strategy the instantiator strategy to use 067 */ 068 public SpringObjenesis(InstantiatorStrategy strategy) { 069 this.strategy = (strategy != null ? strategy : new StdInstantiatorStrategy()); 070 071 // Evaluate the "spring.objenesis.ignore" property upfront... 072 if (SpringProperties.getFlag(SpringObjenesis.IGNORE_OBJENESIS_PROPERTY_NAME)) { 073 this.worthTrying = Boolean.FALSE; 074 } 075 } 076 077 078 /** 079 * Return whether this Objenesis instance is worth trying for instance creation, 080 * i.e. whether it hasn't been used yet or is known to work. 081 * <p>If the configured Objenesis instantiator strategy has been identified to not 082 * work on the current JVM at all or if the "spring.objenesis.ignore" property has 083 * been set to "true", this method returns {@code false}. 084 */ 085 public boolean isWorthTrying() { 086 return (this.worthTrying != Boolean.FALSE); 087 } 088 089 /** 090 * Create a new instance of the given class via Objenesis. 091 * @param clazz the class to create an instance of 092 * @param useCache whether to use the instantiator cache 093 * (typically {@code true} but can be set to {@code false} 094 * e.g. for reloadable classes) 095 * @return the new instance (never {@code null}) 096 * @throws ObjenesisException if instance creation failed 097 */ 098 public <T> T newInstance(Class<T> clazz, boolean useCache) { 099 if (!useCache) { 100 return newInstantiatorOf(clazz).newInstance(); 101 } 102 return getInstantiatorOf(clazz).newInstance(); 103 } 104 105 public <T> T newInstance(Class<T> clazz) { 106 return getInstantiatorOf(clazz).newInstance(); 107 } 108 109 @SuppressWarnings("unchecked") 110 public <T> ObjectInstantiator<T> getInstantiatorOf(Class<T> clazz) { 111 ObjectInstantiator<?> instantiator = this.cache.get(clazz); 112 if (instantiator == null) { 113 ObjectInstantiator<T> newInstantiator = newInstantiatorOf(clazz); 114 instantiator = this.cache.putIfAbsent(clazz, newInstantiator); 115 if (instantiator == null) { 116 instantiator = newInstantiator; 117 } 118 } 119 return (ObjectInstantiator<T>) instantiator; 120 } 121 122 protected <T> ObjectInstantiator<T> newInstantiatorOf(Class<T> clazz) { 123 Boolean currentWorthTrying = this.worthTrying; 124 try { 125 ObjectInstantiator<T> instantiator = this.strategy.newInstantiatorOf(clazz); 126 if (currentWorthTrying == null) { 127 this.worthTrying = Boolean.TRUE; 128 } 129 return instantiator; 130 } 131 catch (ObjenesisException ex) { 132 if (currentWorthTrying == null) { 133 Throwable cause = ex.getCause(); 134 if (cause instanceof ClassNotFoundException || cause instanceof IllegalAccessException) { 135 // Indicates that the chosen instantiation strategy does not work on the given JVM. 136 // Typically a failure to initialize the default SunReflectionFactoryInstantiator. 137 // Let's assume that any subsequent attempts to use Objenesis will fail as well... 138 this.worthTrying = Boolean.FALSE; 139 } 140 } 141 throw ex; 142 } 143 catch (NoClassDefFoundError err) { 144 // Happening on the production version of Google App Engine, coming out of the 145 // restricted "sun.reflect.ReflectionFactory" class... 146 if (currentWorthTrying == null) { 147 this.worthTrying = Boolean.FALSE; 148 } 149 throw new ObjenesisException(err); 150 } 151 } 152 153}