3030import net .bytebuddy .implementation .bytecode .assign .Assigner ;
3131
3232import java .lang .annotation .*;
33+ import java .lang .reflect .InvocationTargetException ;
3334import java .util .Arrays ;
35+ import java .util .List ;
3436
35- import static net .bytebuddy .matcher .ElementMatchers .named ;
37+ import static net .bytebuddy .matcher .ElementMatchers .* ;
3638
3739/**
3840 * Parameters that are annotated with this annotation are assigned an instance of an auxiliary proxy type that allows calling
102104 */
103105 Class <?>[] constructorParameters () default {};
104106
107+ /**
108+ * Specifies a class to resolve a constructor of the proxied type to use for instantiation if
109+ * {@link Instantiation#CONSTRUCTOR} is used. Note that the specified class will be loaded and instantiated by
110+ * Byte Buddy in order to resolve the constructor. For this, the specified class requires a public
111+ * default constructor.
112+ *
113+ * @return The type of the {@link ConstructorResolver} to use.
114+ */
115+ Class <? extends ConstructorResolver > constructorResolver () default ConstructorResolver .Default .class ;
116+
105117 /**
106118 * Determines the type that is implemented by the proxy. When this value is set to its default value
107119 * {@code void}, the proxy is created as an instance of the parameter's type. When it is set to
112124 */
113125 Class <?> proxyType () default void .class ;
114126
127+ /**
128+ * A constructor resolver is responsible to specify the constructor to be used for creating a proxy.
129+ */
130+ interface ConstructorResolver {
131+
132+ /**
133+ * Resolves the constructor to be used.
134+ *
135+ * @param proxiedType The type being proxied.
136+ * @param constructorParameters The types being specified on the annotation.
137+ * @return The constructor to invoke with default arguments for instantiation.
138+ */
139+ MethodDescription .InDefinedShape resolve (TypeDescription proxiedType , List <TypeDescription > constructorParameters );
140+
141+ /**
142+ * A default constructor resolver that attempts to resolve a constructor with the given argument types.
143+ */
144+ class Default implements ConstructorResolver {
145+
146+ /**
147+ * {@inheritDoc}
148+ */
149+ public MethodDescription .InDefinedShape resolve (TypeDescription proxiedType , List <TypeDescription > constructorParameters ) {
150+ if (proxiedType .isInterface ()) {
151+ return TypeDescription .ForLoadedType .of (Object .class ).getDeclaredMethods ()
152+ .filter (isConstructor ())
153+ .getOnly ();
154+ }
155+ MethodList <MethodDescription .InDefinedShape > candidates = proxiedType .getDeclaredMethods ().filter (isConstructor ()
156+ .and (not (isPrivate ()))
157+ .and (takesArguments (constructorParameters )));
158+ if (candidates .size () == 1 ) {
159+ return candidates .getOnly ();
160+ } else {
161+ throw new IllegalStateException ("Did not discover exactly one constructor on " + proxiedType + " with parameters " + constructorParameters );
162+ }
163+ }
164+ }
165+ }
166+
115167 /**
116168 * Determines the instantiation of the proxy type.
117169 *
@@ -125,12 +177,32 @@ enum Instantiation {
125177 */
126178 CONSTRUCTOR {
127179 @ Override
128- protected StackManipulation proxyFor (TypeDescription parameterType ,
180+ protected StackManipulation proxyFor (TypeDescription proxyType ,
129181 Implementation .Target implementationTarget ,
130182 AnnotationDescription .Loadable <Super > annotation ) {
131- return new TypeProxy .ForSuperMethodByConstructor (parameterType ,
183+ MethodDescription .InDefinedShape constructor ;
184+ try {
185+ @ SuppressWarnings ("unchecked" )
186+ ConstructorResolver constructorResolver = (ConstructorResolver ) annotation .getValue (CONSTRUCTOR_RESOLVER )
187+ .load (ConstructorResolver .class .getClassLoader ())
188+ .resolve (Class .class )
189+ .getConstructor ()
190+ .newInstance ();
191+ constructor = constructorResolver .resolve (
192+ proxyType ,
193+ Arrays .asList (annotation .getValue (CONSTRUCTOR_PARAMETERS ).resolve (TypeDescription [].class )));
194+ } catch (NoSuchMethodException exception ) {
195+ throw new IllegalStateException ("No default constructor specified by " + annotation .getValue (CONSTRUCTOR_RESOLVER )
196+ .resolve (TypeDescription .class )
197+ .getName (), exception );
198+ } catch (InvocationTargetException exception ) {
199+ throw new IllegalStateException ("Failed to resolve constructor specified by " + annotation , exception .getTargetException ());
200+ } catch (Exception exception ) {
201+ throw new IllegalStateException ("Failed to resolve constructor specified by " + annotation , exception );
202+ }
203+ return new TypeProxy .ForSuperMethodByConstructor (proxyType ,
204+ constructor ,
132205 implementationTarget ,
133- Arrays .asList (annotation .getValue (CONSTRUCTOR_PARAMETERS ).resolve (TypeDescription [].class )),
134206 annotation .getValue (IGNORE_FINALIZER ).resolve (Boolean .class ),
135207 annotation .getValue (SERIALIZABLE_PROXY ).resolve (Boolean .class ));
136208 }
@@ -142,10 +214,10 @@ protected StackManipulation proxyFor(TypeDescription parameterType,
142214 */
143215 UNSAFE {
144216 @ Override
145- protected StackManipulation proxyFor (TypeDescription parameterType ,
217+ protected StackManipulation proxyFor (TypeDescription proxyType ,
146218 Implementation .Target implementationTarget ,
147219 AnnotationDescription .Loadable <Super > annotation ) {
148- return new TypeProxy .ForSuperMethodByReflectionFactory (parameterType ,
220+ return new TypeProxy .ForSuperMethodByReflectionFactory (proxyType ,
149221 implementationTarget ,
150222 annotation .getValue (IGNORE_FINALIZER ).resolve (Boolean .class ),
151223 annotation .getValue (SERIALIZABLE_PROXY ).resolve (Boolean .class ));
@@ -167,6 +239,11 @@ protected StackManipulation proxyFor(TypeDescription parameterType,
167239 */
168240 private static final MethodDescription .InDefinedShape CONSTRUCTOR_PARAMETERS ;
169241
242+ /**
243+ * A reference to the constructor parameters resolver method.
244+ */
245+ private static final MethodDescription .InDefinedShape CONSTRUCTOR_RESOLVER ;
246+
170247 /*
171248 * Extracts method references to the annotation methods.
172249 */
@@ -175,18 +252,19 @@ protected StackManipulation proxyFor(TypeDescription parameterType,
175252 IGNORE_FINALIZER = annotationProperties .filter (named ("ignoreFinalizer" )).getOnly ();
176253 SERIALIZABLE_PROXY = annotationProperties .filter (named ("serializableProxy" )).getOnly ();
177254 CONSTRUCTOR_PARAMETERS = annotationProperties .filter (named ("constructorParameters" )).getOnly ();
255+ CONSTRUCTOR_RESOLVER = annotationProperties .filter (named ("constructorResolver" )).getOnly ();
178256 }
179257
180258 /**
181259 * Creates a stack manipulation which loads a {@code super}-call proxy onto the stack.
182260 *
183- * @param parameterType The type of the parameter that was annotated with
261+ * @param proxyType The type of the proxy that is bound to the parameter annotated by
184262 * {@link net.bytebuddy.implementation.bind.annotation.Super}
185263 * @param implementationTarget The implementation target for the currently created type.
186264 * @param annotation The annotation that caused this method call.
187265 * @return A stack manipulation representing this instance's instantiation strategy.
188266 */
189- protected abstract StackManipulation proxyFor (TypeDescription parameterType ,
267+ protected abstract StackManipulation proxyFor (TypeDescription proxyType ,
190268 Implementation .Target implementationTarget ,
191269 AnnotationDescription .Loadable <Super > annotation );
192270 }
0 commit comments