@PathVariable을 사용한 Spring MVC 주석 컨트롤러 인터페이스
컨트롤러를 인터페이스로 매핑하지 않는 이유가 있습니까?
주변 컨트롤러의 예와 질문에는 모두 구체적인 클래스입니다.이유가 있나요?요청 매핑을 구현에서 분리하고 싶습니다.벽에 부딪혔는데...@PathVariable제 콘크리트 수업의 매개 변수입니다.
컨트롤러 인터페이스는 다음과 같습니다.
@Controller
@RequestMapping("/services/goal/")
public interface GoalService {
@RequestMapping("options/")
@ResponseBody
Map<String, Long> getGoals();
@RequestMapping(value = "{id}/", method = RequestMethod.DELETE)
@ResponseBody
void removeGoal(@PathVariable String id);
}
그리고 구현 클래스:
@Component
public class GoalServiceImpl implements GoalService {
/* init code */
public Map<String, Long> getGoals() {
/* method code */
return map;
}
public void removeGoal(String id) {
Goal goal = goalDao.findByPrimaryKey(Long.parseLong(id));
goalDao.remove(goal);
}
}
그getGoals()이 방법은 훌륭하게 기능합니다.removeGoal(String id)예외를 발생시키다
ExceptionHandlerExceptionResolver - Resolving exception from handler [public void
todo.webapp.controllers.services.GoalServiceImpl.removeGoal(java.lang.String)]:
org.springframework.web.bind.MissingServletRequestParameterException: Required
String parameter 'id' is not present
를 추가하면@PathVariable콘크리트 클래스에 대한 주석 모든 것이 예상대로 작동하는데, 왜 내가 콘크리트 클래스에서 이것을 다시 설명해야 합니까?이 문제는 어떤 것이든 간에 처리되어야 하는 것 아닌가요?@Controller주석?
분명히 요구 패턴이 Method에 매핑되어 있습니다.@RequestMapping주석은 구체적인 방법 구현에 매핑됩니다.따라서 선언과 일치하는 요청이 호출됩니다.GoalServiceImpl.removeGoal()처음에 선언한 방법보다 직접적@RequestMapping즉,GoalService.removeGoal().
인터페이스, 인터페이스 방식 또는 인터페이스 방식 파라미터에 대한 주석이 구현으로 이어지지 않기 때문에 Spring MVC가 이를 인터페이스, 인터페이스 방식 또는 인터페이스 방식 파라미터로 인식할 수 있는 방법은 없습니다.@PathVariable구현 클래스가 명시적으로 선언하지 않는 한.이것이 없으면 AOP에 대한 조언은@PathVariable파라미터는 실행되지 않습니다.
인터페이스상의 모든 바인딩을 정의하는 기능은 최근 Spring 5.1.5에서 실장되었습니다.
다음 호를 참조해 주세요.https://github.com/spring-projects/spring-framework/issues/15682 - 그것은 힘든 일이었습니다. : )
이제 실제로 다음을 수행할 수 있습니다.
@RequestMapping("/random")
public interface RandomDataController {
@RequestMapping(value = "/{type}", method = RequestMethod.GET)
@ResponseBody
RandomData getRandomData(
@PathVariable(value = "type") RandomDataType type, @RequestParam(value = "size", required = false, defaultValue = "10") int size);
}
@Controller
public class RandomDataImpl implements RandomDataController {
@Autowired
private RandomGenerator randomGenerator;
@Override
public RandomData getPathParamRandomData(RandomDataType type, int size) {
return randomGenerator.generateRandomData(type, size);
}
}
다음 라이브러리도 사용할 수 있습니다.https://github.com/ggeorgovassilis/spring-rest-invoker
RestEasys 클라이언트 프레임워크가 JAX-RS에서 작동하는 방식과 마찬가지로 해당 인터페이스를 기반으로 클라이언트 프록시를 얻는 것입니다.
봄의 새로운 버전에서 작동합니다.
import org.springframework.web.bind.annotation.RequestMapping;
public interface TestApi {
@RequestMapping("/test")
public String test();
}
컨트롤러에 인터페이스를 실장하다
@RestController
@Slf4j
public class TestApiController implements TestApi {
@Override
public String test() {
log.info("In Test");
return "Value";
}
}
Rest client로 사용할 수 있습니다.
최근에 나도 같은 문제를 겪었어.다음과 같은 것이 도움이 되었습니다.
public class GoalServiceImpl implements GoalService {
...
public void removeGoal(@PathVariableString id) {
}
}
나는 이 문제를 해결했다.
클라이언트 측:
저는 이 라이브러리 https://github.com/ggeorgovassilis/spring-rest-invoker/를 사용하고 있습니다.이 라이브러리는 spring rest 서비스를 호출하는 프록시를 인터페이스에서 생성합니다.
이 라이브러리를 확장했습니다.
주석과 공장 클라이언트 클래스를 만들었습니다.
스프링 레스트 서비스 식별
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpringRestService {
String baseUri();
}
이 클래스는 인터페이스에서 클라이언트레스트를 생성합니다.
public class RestFactory implements BeanFactoryPostProcessor,EmbeddedValueResolverAware {
StringValueResolver resolver;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
private String basePackage = "com";
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
createBeanProxy(beanFactory,SpringRestService.class);
createBeanProxy(beanFactory,JaxrsRestService.class);
}
private void createBeanProxy(ConfigurableListableBeanFactory beanFactory,Class<? extends Annotation> annotation) {
List<Class<Object>> classes;
try {
classes = AnnotationUtils.findAnnotatedClasses(basePackage, annotation);
} catch (Exception e) {
throw new BeanInstantiationException(annotation, e.getMessage(), e);
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
for (Class<Object> classType : classes) {
Annotation typeService = classType.getAnnotation(annotation);
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(getQueryServiceFactory(classType, typeService));
ConstructorArgumentValues cav = new ConstructorArgumentValues();
cav.addIndexedArgumentValue(0, classType);
cav.addIndexedArgumentValue(1, baseUri(classType,typeService));
beanDef.setConstructorArgumentValues(cav);
registry.registerBeanDefinition(classType.getName() + "Proxy", beanDef);
}
}
private String baseUri(Class<Object> c,Annotation typeService){
String baseUri = null;
if(typeService instanceof SpringRestService){
baseUri = ((SpringRestService)typeService).baseUri();
}else if(typeService instanceof JaxrsRestService){
baseUri = ((JaxrsRestService)typeService).baseUri();
}
if(baseUri!=null && !baseUri.isEmpty()){
return baseUri = resolver.resolveStringValue(baseUri);
}else{
throw new IllegalStateException("Impossibile individuare una baseUri per l'interface :"+c);
}
}
private static Class<? extends FactoryBean<?>> getQueryServiceFactory(Class<Object> c,Annotation typeService){
if(typeService instanceof SpringRestService){
return it.eng.rete2i.springjsonmapper.spring.SpringRestInvokerProxyFactoryBean.class;
}else if(typeService instanceof JaxrsRestService){
return it.eng.rete2i.springjsonmapper.jaxrs.JaxRsInvokerProxyFactoryBean.class;
}
throw new IllegalStateException("Impossibile individuare una classe per l'interface :"+c);
}
}
공장 설정:
<bean class="it.eng.rete2i.springjsonmapper.factory.RestFactory">
<property name="basePackage" value="it.giancarlo.rest.services" />
</bean>
정지 시 서비스 서명
인터페이스의 예를 다음에 나타냅니다.
package it.giancarlo.rest.services.spring;
import ...
@SpringRestService(baseUri="${bookservice.url}")
public interface BookService{
@Override
@RequestMapping("/volumes")
QueryResult findBooksByTitle(@RequestParam("q") String q);
@Override
@RequestMapping("/volumes/{id}")
Item findBookById(@PathVariable("id") String id);
}
온레스트 서비스 구현
서비스 구현
@RestController
@RequestMapping("bookService")
public class BookServiceImpl implements BookService {
@Override
public QueryResult findBooksByTitle(String q) {
// TODO Auto-generated method stub
return null;
}
@Override
public Item findBookById(String id) {
// TODO Auto-generated method stub
return null;
}
}
파라미터에 대한 주석을 해결하려면 @SpringRestService에서 주석이 달린 모든 인터페이스를 표시하는 커스텀 RequestMappingHandlerMapping을 만듭니다.
public class RestServiceRequestMappingHandlerMapping extends RequestMappingHandlerMapping{
public HandlerMethod testCreateHandlerMethod(Object handler, Method method){
return createHandlerMethod(handler, method);
}
@Override
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
HandlerMethod handlerMethod;
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new RestServiceHandlerMethod(beanName,getApplicationContext().getAutowireCapableBeanFactory(), method);
}
else {
handlerMethod = new RestServiceHandlerMethod(handler, method);
}
return handlerMethod;
}
public static class RestServiceHandlerMethod extends HandlerMethod{
private Method interfaceMethod;
public RestServiceHandlerMethod(Object bean, Method method) {
super(bean,method);
changeType();
}
public RestServiceHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
super(bean,methodName,parameterTypes);
changeType();
}
public RestServiceHandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
super(beanName,beanFactory,method);
changeType();
}
private void changeType(){
for(Class<?> clazz : getMethod().getDeclaringClass().getInterfaces()){
if(clazz.isAnnotationPresent(SpringRestService.class)){
try{
interfaceMethod = clazz.getMethod(getMethod().getName(), getMethod().getParameterTypes());
break;
}catch(NoSuchMethodException e){
}
}
}
MethodParameter[] params = super.getMethodParameters();
for(int i=0;i<params.length;i++){
params[i] = new RestServiceMethodParameter(params[i]);
}
}
private class RestServiceMethodParameter extends MethodParameter{
private volatile Annotation[] parameterAnnotations;
public RestServiceMethodParameter(MethodParameter methodParameter){
super(methodParameter);
}
@Override
public Annotation[] getParameterAnnotations() {
if (this.parameterAnnotations == null){
if(RestServiceHandlerMethod.this.interfaceMethod!=null) {
Annotation[][] annotationArray = RestServiceHandlerMethod.this.interfaceMethod.getParameterAnnotations();
if (this.getParameterIndex() >= 0 && this.getParameterIndex() < annotationArray.length) {
this.parameterAnnotations = annotationArray[this.getParameterIndex()];
}
else {
this.parameterAnnotations = new Annotation[0];
}
}else{
this.parameterAnnotations = super.getParameterAnnotations();
}
}
return this.parameterAnnotations;
}
}
}
}
구성 클래스를 만들었습니다.
@Configuration
public class WebConfig extends WebMvcConfigurationSupport{
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RestServiceRequestMappingHandlerMapping handlerMapping = new RestServiceRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.isUseSuffixPatternMatch() != null) {
handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
}
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
}
if (configurer.isUseTrailingSlashMatch() != null) {
handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
if (configurer.getPathMatcher() != null) {
handlerMapping.setPathMatcher(configurer.getPathMatcher());
}
if (configurer.getUrlPathHelper() != null) {
handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
}
return handlerMapping;
}
}
설정했습니다.
<bean class="....WebConfig" />
언급URL : https://stackoverflow.com/questions/8002514/spring-mvc-annotated-controller-interface-with-pathvariable
'source' 카테고리의 다른 글
| React에서 중첩된 모양에 대한 기본 소품을 제공하려면 어떻게 해야 합니까? (0) | 2023.02.12 |
|---|---|
| JSON 세트를 시리얼화하는 방법 (0) | 2023.02.12 |
| WooCommerce 쿠폰은 데이터베이스에 어떻게 저장됩니까? (0) | 2023.02.12 |
| 각도 각도 조절기 - E2E 테스트 후 브라우저를 열어 둡니다. (0) | 2023.02.08 |
| Wordpress RSS2 피드에 CORS 지원을 추가하는 방법 (0) | 2023.02.08 |