Spring 3.0.7 で動作するカスタム引数リゾルバーSecurityRequestParametersArgumentResolver
がありますが、Spring 3.1.2 では失敗します。スタック トレースとテスト ドライバー コードを以下に示します。
スタック トレースを見ると、SecurityRequestParametersArgumentResolver.resolve( )
が呼び出されていないようです。代わりにHandlerMethodArgumentResolverComposite.resolve
、スタック トレースに表示されます。
提案?
使用する Java コードを更新しましたHandlerMethodArgumentResolver
(それはAnnotationMethodHandlerAdapter
3.0.7 からのものでした) が、ランタイム統合テストを実行すると、このスタック トレースが表示されます。
Caused by: java.lang.NoSuchMethodException: xyz.security.web.SecurityRequestParameters.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at org.springframework.beans.BeanUtils.BeanUtils.BeanUtils.instantiateClass(Class<T>) line: 104
ServletModelAttributeMethodProcessor(ModelAttributeMethodProcessor).createAttribute(String, MethodParameter, WebDataBinderFactory, NativeWebRequest) line: 132
ServletModelAttributeMethodProcessor.createAttribute(String, MethodParameter, WebDataBinderFactory, NativeWebRequest) line: 81
ServletModelAttributeMethodProcessor(ModelAttributeMethodProcessor).resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory) line: 103
HandlerMethodArgumentResolverComposite.resolveArgument(MethodParameter, ModelAndViewContainer, NativeWebRequest, WebDataBinderFactory) line: 75
ServletInvocableHandlerMethod(InvocableHandlerMethod).getMethodArgumentValues(NativeWebRequest, ModelAndViewContainer, Object...) line: 156
ServletInvocableHandlerMethod(InvocableHandlerMethod).invokeForRequest(NativeWebRequest, ModelAndViewContainer, Object...) line: 117
ServletInvocableHandlerMethod.invokeAndHandle(NativeWebRequest, ModelAndViewContainer, Object...) line: 96
RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 617
RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 578
RequestMappingHandlerAdapter(AbstractHandlerMethodAdapter).handle(HttpServletRequest, HttpServletResponse, Object) line: 80
SecuritySystemTestStartUp$1(DispatcherServlet).doDispatch(HttpServletRequest, HttpServletResponse) line: 923
SecuritySystemTestStartUp$1(DispatcherServlet).doService(HttpServletRequest, HttpServletResponse) line: 852
SecuritySystemTestStartUp$1(FrameworkServlet).processRequest(HttpServletRequest, HttpServletResponse) line: 882
SecuritySystemTestStartUp$1(FrameworkServlet).doPost(HttpServletRequest, HttpServletResponse) line: 789
SecuritySystemTestStartUp$1(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 637
SecuritySystemTestStartUp$1(HttpServlet).service(ServletRequest, ServletResponse) line: 717
SecurityExternalAPISystemTest.testEncrypt() line: 86
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available
Method.invoke(Object, Object...) line: not available
FrameworkMethod$1.runReflectiveCall() line: 44
FrameworkMethod$1(ReflectiveCallable).run() line: 15
FrameworkMethod.invokeExplosively(Object, Object...) line: 41
InvokeMethod.evaluate() line: 20
RunBeforeTestMethodCallbacks.evaluate() line: 74
RunAfterTestMethodCallbacks.evaluate() line: 83
SpringRepeat.evaluate() line: 72
SecuritySpringJUnitRunner(SpringJUnit4ClassRunner).runChild(FrameworkMethod, RunNotifier) line: 231
SecuritySpringJUnitRunner(BlockJUnit4ClassRunner).runChild(Object, RunNotifier) line: 49
ParentRunner$3.run() line: 193
ParentRunner$1.schedule(Runnable) line: 52
SecuritySpringJUnitRunner(ParentRunner<T>).runChildren(RunNotifier) line: 191
ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 42
ParentRunner$2.evaluate() line: 184
RunBefores.evaluate() line: 28
RunBeforeTestClassCallbacks.evaluate() line: 61
RunAfterTestClassCallbacks.evaluate() line: 71
SecuritySpringJUnitRunner(ParentRunner<T>).run(RunNotifier) line: 236
SecuritySpringJUnitRunner(SpringJUnit4ClassRunner).run(RunNotifier) line: 174
JUnit4TestClassReference(JUnit4TestReference).run(TestExecution) line: 50
TestExecution.run(ITestReference[]) line: 38
RemoteTestRunner.runTests(String[], String, TestExecution) line: 467
RemoteTestRunner.runTests(TestExecution) line: 683
RemoteTestRunner.run() line: 390
RemoteTestRunner.main(String[]) line: 197
SecuritySystemTestStartUp のテスト ドライバー コードには、@ContextConfiguration の注釈が付けられています。
@RunWith(SecuritySpringJUnitRunner.class)
@ContextConfiguration(loader = MockServletContextWebContextLoader.class,
locations = { "classpath:/spring-system-test.xml" })
public abstract class SecuritySystemTestStartUp
{
private static DispatcherServlet dispatcherServlet;
...
public static DispatcherServlet getDispatcherServlet()
{
try
{
if (dispatcherServlet == null)
{
final GenericWebApplicationContext context = new GenericWebApplicationContext();
context.setParent(MockServletContextWebContextLoader.getInstance());
context.refresh();
dispatcherServlet = new DispatcherServlet(context);
dispatcherServlet.init(new MockServletConfig());
}
}
catch (final Exception ex)
...
return dispatcherServlet;
}
}
postProcessAfterInitialization では、引数リゾルバー (Spring 3.0.7 と互換性があります) が使用されます。引数リゾルバーのコードの抜粋を以下に示します。
public class AnnotationMethodHandlerBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
@Autowired public AnnotationMethodHandlerBeanPostProcessor(final SecurityRequestParametersArgumentResolver resolver) { super(); _resolver = resolver; } @Override public Object postProcessAfterInitialization(final Object bean, final String beanName) { if (bean instanceof RequestMappingHandlerAdapter) { final RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean; final List<HttpMessageConverter<?>> converters = adapter.getMessageConverters(); converters.add(new BinaryMessageConverter()); List<HandlerMethodArgumentResolver> customArgumentResolvers = new ArrayList<HandlerMethodArgumentResolver>(); customArgumentResolvers.add(_resolver); adapter.setCustomArgumentResolvers(customArgumentResolvers); adapter.setMessageConverters(adapter.getMessageConverters()); } return super.postProcessAfterInitialization(bean, beanName); } ...
}
そして、Spring 構成で ... として指定されます。
<bean id="SecurityRequestParametersArgumentResolver" class="xyz.security.web.SecurityRequestParametersArgumentResolver"> <constructor-arg ref="CredentialsManager" /> <constructor-arg ref="TokenService" /> </bean> <!-- A bean post-processor that registers the SecurityRequestParametersArgumentResolver. --> <bean class="xyz.security.web.AnnotationMethodHandlerBeanPostProcessor"> <constructor-arg index="0" ref="SecurityRequestParametersArgumentResolver" /> </bean>
引数リゾルバの流れは次のとおりです。
@Component public class SecurityRequestParametersArgumentResolver implements HandlerMethodArgumentResolver { @Autowired // 暗黙的にオートワイヤーされる (xml にはありません) private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private final CredentialsManager _credentialsManager; private final TokenService _tokenService; private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null; /* For the component scan... */ SecurityRequestParametersArgumentResolver() { this(null, null); } public SecurityRequestParametersArgumentResolver(final CredentialsManager credentialsManager, final TokenService tokenService) { super(); _credentialsManager = credentialsManager; _tokenService = tokenService; } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { if (parameter != null && SecurityRequestParameters.class.equals(parameter.getParameterType())) { String productName = webRequest.getHeader(HttpHeader.PRODUCT_CONTAINER_HEADER.value()); ... return new SecurityRequestParameters(credentials, contentType, acceptType, dataEncoding, sessionIdentifier, remoteIpAddress, securityProductName); } return new Object(); } public boolean supportsParameter(MethodParameter parameter) { return getRequestResponseBodyMethodProcessor().supportsParameter(parameter); } private RequestResponseBodyMethodProcessor getRequestResponseBodyMethodProcessor() { if (requestResponseBodyMethodProcessor == null) { List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters(); requestResponseBodyMethodProcessor = new RequestResponseBodyMethodProcessor(messageConverters); } return requestResponseBodyMethodProcessor; }
}
そして最後に...これがSpring構成のアノテーション駆動型タグです
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.trgr.cobalt.security.web.SecurityRequestParametersArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>