{"record":{"id":"c70309fe063963dd","repo":"spring-projects/spring-framework","slug":"an-advice-method-can-never-be-a-constructor","errorCode":null,"errorMessage":"An advice method can never be a constructor","messagePattern":"An advice method can never be a constructor","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java","lineNumber":290,"sourceCode":"\t\t\t\t\t\tthis.numberOfRemainingUnboundArguments + \" argument(s) could not be bound\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// convention for failing is to return null, allowing participation in a chain of responsibility\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * An advice method can never be a constructor in Spring.\n\t * @return {@code null}\n\t * @throws UnsupportedOperationException if\n\t * {@link #setRaiseExceptions(boolean) raiseExceptions} has been set to {@code true}\n\t */\n\t@Override\n\tpublic String @Nullable [] getParameterNames(Constructor<?> ctor) {\n\t\tif (this.raiseExceptions) {\n\t\t\tthrow new UnsupportedOperationException(\"An advice method can never be a constructor\");\n\t\t}\n\t\telse {\n\t\t\t// we return null rather than throw an exception so that we behave well\n\t\t\t// in a chain-of-responsibility.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\n\tprivate void bindParameterName(int index, @Nullable String name) {\n\t\tthis.parameterNameBindings[index] = name;\n\t\tthis.numberOfRemainingUnboundArguments--;\n\t}\n\n\t/**\n\t * If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind \"thisJoinPoint\" as\n\t * parameter name and return true, else return false.\n\t */","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java#L272-L308","documentation":"Thrown by AspectJAdviceParameterNameDiscoverer.getParameterNames(Constructor) (line 288-291) when raiseExceptions is true and the discoverer is asked for the parameter names of a Constructor rather than a Method. Advice in Spring is always a Method, never a Constructor; the discoverer is only meaningful for methods, so it refuses the constructor call (returning null normally, throwing when raiseExceptions).","triggerScenarios":"A ParameterNameDiscoverer chain that includes AspectJAdviceParameterNameDiscoverer is invoked with a Constructor — e.g. DefaultParameterNameDiscoverer.getParameterNames(ctor) where the advice discoverer is in the chain and raiseExceptions was set to true. This is almost always a misconfiguration or misuse rather than a real aspect scenario.","commonSituations":"Reusing an AspectJAdviceParameterNameDiscoverer instance (or a DefaultParameterNameDiscoverer that has one added) for general constructor parameter-name discovery, while raiseExceptions=true. Should not occur in normal aspect processing.","solutions":["Do not use AspectJAdviceParameterNameDiscoverer for constructor parameter discovery — use a plain DefaultParameterNameDiscoverer or StandardReflectionParameterNameDiscoverer.","Keep raiseExceptions=false (default) if the discoverer participates in a general chain.","Separate the discoverer instance used for advice binding from the one used for general reflection."],"exampleFix":"// before — advice discoverer (raiseExceptions=true) reused for constructors\nDefaultParameterNameDiscoverer d = new DefaultParameterNameDiscoverer();\nAspectJAdviceParameterNameDiscoverer aj = new AspectJAdviceParameterNameDiscoverer(expr);\naj.setRaiseExceptions(true); d.addDiscoverer(aj);\nString[] names = d.getParameterNames(MyClass.class.getConstructors()[0]); // throws\n\n// after — separate discoverer for constructor reflection\nDefaultParameterNameDiscoverer ctorDiscoverer = new DefaultParameterNameDiscoverer();\nString[] names = ctorDiscoverer.getParameterNames(MyClass.class.getConstructors()[0]);","handlingStrategy":"validation","validationCode":"import java.lang.reflect.Constructor;\nimport org.springframework.aop.aspectj.AspectJAdviceParameterNameDiscoverer;\n\n// Do not route constructor queries through an advice discoverer with raiseExceptions=true.\nString[] safeGetCtorParamNames(Constructor<?> ctor,\n        AspectJAdviceParameterNameDiscoverer adviceDiscoverer) {\n    if (adviceDiscoverer != null) {\n        // use a separate plain discoverer for constructors\n        return new org.springframework.core.DefaultParameterNameDiscoverer().getParameterNames(ctor);\n    }\n    return adviceDiscoverer.getParameterNames(ctor);\n}","typeGuard":"boolean isMethodDiscoveryTarget(Class<?> member) {\n    return member == java.lang.reflect.Method.class;\n}","tryCatchPattern":"try {\n    String[] names = discoverer.getParameterNames(constructor);\n} catch (UnsupportedOperationException ex) {\n    if (ex.getMessage().contains(\"advice method can never be a constructor\")) {\n        // switch to a plain DefaultParameterNameDiscoverer for constructors\n    } else { throw ex; }\n}","preventionTips":["Use a dedicated DefaultParameterNameDiscoverer for general constructor reflection.","Keep the advice discoverer (raiseExceptions=true) scoped to advice binding only.","Never set raiseExceptions=true on a discoverer reused for non-advice purposes."],"tags":["spring-aop","aspectj","parameter-discovery","constructor","misconfiguration"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}