@RequestMapping和@FeginClient注解不能同时使用的问题

这篇具有很好参考价值的文章主要介绍了@RequestMapping和@FeginClient注解不能同时使用的问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、问题

在新版本SpringCloud中,增加了契约验证,当一个类上同时使用@RequestMapping 和 @FeignClient 注解时,会抛出此异常信息:java.lang.IllegalArgumentException: @RequestMapping annotation not allowed on @FeignClient interfaces

Caused by: java.lang.IllegalArgumentException: @RequestMapping annotation not allowed on @FeignClient interfaces
    at org.springframework.cloud.openfeign.support.SpringMvcContract.processAnnotationOnClass(SpringMvcContract.java:180)
    at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:91)
    at org.springframework.cloud.openfeign.support.SpringMvcContract.parseAndValidateMetadata(SpringMvcContract.java:187)
    at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:62)
    at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:151)
    at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:49)
    at feign.Feign$Builder.target(Feign.java:269)
    at org.springframework.cloud.openfeign.DefaultTargeter.target(DefaultTargeter.java:30)
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.loadBalance(FeignClientFactoryBean.java:373)
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:421)
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:396)
    at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:235)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    ... 28 common frames omitted

二、解决方案

方案一

将类上的@RequestMapping注解删掉,将路径更改到每个方法的路径上即可,然后使用@FeignClient自己的path属性指定路径

方案二

通过分析源码,替换源码相关约定,进行兼容处理

2.1 根据异常提示,定位错误提示位置:

1. 新版本实现:
// org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationOnClass

    @Override
    protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
        RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
        if (classAnnotation != null) {
            LOG.error("Cannot process class: " + clz.getName()
                    + ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");
            throw new IllegalArgumentException("@RequestMapping annotation not allowed on @FeignClient interfaces");
        }
    }
2. 旧版本实现:
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
        if (clz.getInterfaces().length == 0) {
            RequestMapping classAnnotation = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(clz, RequestMapping.class);
            if (classAnnotation != null && classAnnotation.value().length > 0) {
                String pathValue = Util.emptyToNull(classAnnotation.value()[0]);
                pathValue = this.resolve(pathValue);
                if (!pathValue.startsWith("/")) {
                    pathValue = "/" + pathValue;
                }

                data.template().insert(0, pathValue);
            }
        }

    }
3. Contract契约接口:

@RequestMapping和@FeginClient注解不能同时使用的问题

4. processAnnotationOnClass接口方法实现:

@RequestMapping和@FeginClient注解不能同时使用的问题

public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
5. 解决方案:覆盖SpringMvcContract的processAnnotationOnClass接口方法实现
5.1 确定SpringMvcContract加载点

@ConditionalOnMissingBean 源码中的这个注解很关键,意思是我们可以替换Contract 的实现。所以目标很明确,就是要用自己的契约机制替换新版本中的契约

#org.springframework.cloud.openfeign.FeignClientsConfiguration#feignContract

	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash();
		return new SpringMvcContract(this.parameterProcessors, feignConversionService, decodeSlash);
	}
5.2 编写自己的SvcSpringContract契约

主要是更改processAnnotationOnClass方法的实现为老版本的实现方式(微调一下即可,具体看下面的代码)。文章来源地址https://www.toymoban.com/news/detail-437555.html

package xxxx.basic.config;

import feign.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.CollectionFormat;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.annotation.*;
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.*;

import static feign.Util.checkState;
import static feign.Util.emptyToNull;
import static java.util.Optional.ofNullable;
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;

/**
 * 重写契约
 *
 * @author qiyu
 * @version 1.0.0
 */
@ConditionalOnBean
public class SvcSpringContract extends Contract.BaseContract implements ResourceLoaderAware {
    private static final Log LOG = LogFactory.getLog(SvcSpringContract.class);

    private static final String ACCEPT = "Accept";

    private static final String CONTENT_TYPE = "Content-Type";

    private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);

    private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Iterable.class);

    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

    private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;

    private final Map<String, Method> processedMethods = new HashMap<>();

    private final ConversionService conversionService;

    private final ConvertingExpanderFactory convertingExpanderFactory;

    private ResourceLoader resourceLoader = new DefaultResourceLoader();

    private boolean decodeSlash;

    public SvcSpringContract() {
        this(Collections.emptyList());
    }

    public SvcSpringContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
        this(annotatedParameterProcessors, new DefaultConversionService());
    }

    public SvcSpringContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
                             ConversionService conversionService) {
        this(annotatedParameterProcessors, conversionService, true);
    }

    public SvcSpringContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
                             ConversionService conversionService, boolean decodeSlash) {
        Assert.notNull(annotatedParameterProcessors, "Parameter processors can not be null.");
        Assert.notNull(conversionService, "ConversionService can not be null.");

        List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors();
        processors.addAll(annotatedParameterProcessors);

        annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
        this.conversionService = conversionService;
        convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
        this.decodeSlash = decodeSlash;
    }


    @Override
    public void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
        if (data != null && clz.getInterfaces().length == 0) {
            RequestMapping classAnnotation = AnnotatedElementUtils.findMergedAnnotation(clz, RequestMapping.class);
            if (classAnnotation != null && classAnnotation.value().length > 0) {
                String pathValue = Util.emptyToNull(classAnnotation.value()[0]);
                pathValue = this.resolve(pathValue);
                if (!pathValue.startsWith("/")) {
                    pathValue = "/" + pathValue;
                }
                data.template().uri(pathValue);
            }
        }
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
        processedMethods.put(Feign.configKey(targetType, method), method);
        return super.parseAndValidateMetadata(targetType, method);
    }

    @Override
    public void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
        if (CollectionFormat.class.isInstance(methodAnnotation)) {
            CollectionFormat collectionFormat = findMergedAnnotation(method, CollectionFormat.class);
            data.template().collectionFormat(collectionFormat.value());
        }

        if (!RequestMapping.class.isInstance(methodAnnotation)
                && !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
            return;
        }

        RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
        // HTTP Method
        RequestMethod[] methods = methodMapping.method();
        if (methods.length == 0) {
            methods = new RequestMethod[]{RequestMethod.GET};
        }
        checkOne(method, methods, "method");
        data.template().method(Request.HttpMethod.valueOf(methods[0].name()));

        // path
        checkAtMostOne(method, methodMapping.value(), "value");
        if (methodMapping.value().length > 0) {
            String pathValue = emptyToNull(methodMapping.value()[0]);
            if (pathValue != null) {
                pathValue = resolve(pathValue);
                // Append path from @RequestMapping if value is present on method
                if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
                    pathValue = "/" + pathValue;
                }
                data.template().uri(pathValue, true);
                if (data.template().decodeSlash() != decodeSlash) {
                    data.template().decodeSlash(decodeSlash);
                }
            }
        }

        // produces
        parseProduces(data, method, methodMapping);

        // consumes
        parseConsumes(data, method, methodMapping);

        // headers
        parseHeaders(data, method, methodMapping);

        data.indexToExpander(new LinkedHashMap<>());
    }

    @Override
    public boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
        boolean isHttpAnnotation = false;

        AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data,
                paramIndex);
        Method method = processedMethods.get(data.configKey());
        for (Annotation parameterAnnotation : annotations) {
            AnnotatedParameterProcessor processor = annotatedArgumentProcessors
                    .get(parameterAnnotation.annotationType());
            if (processor != null) {
                Annotation processParameterAnnotation;
                // synthesize, handling @AliasFor, while falling back to parameter name on
                // missing String #value():
                processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation,
                        method, paramIndex);
                isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
            }
        }

        if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
            TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
            if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
                Param.Expander expander = convertingExpanderFactory.getExpander(typeDescriptor);
                if (expander != null) {
                    data.indexToExpander().put(paramIndex, expander);
                }
            }
        }
        return isHttpAnnotation;
    }

    private String resolve(String value) {
        if (StringUtils.hasText(value) && resourceLoader instanceof ConfigurableApplicationContext) {
            return ((ConfigurableApplicationContext) resourceLoader).getEnvironment().resolvePlaceholders(value);
        }
        return value;
    }

    private void checkOne(Method method, Object[] values, String fieldName) {
        checkState(values != null && values.length == 1, "Method %s can only contain 1 %s field. Found: %s",
                method.getName(), fieldName, values == null ? null : Arrays.asList(values));
    }

    private void checkAtMostOne(Method method, Object[] values, String fieldName) {
        checkState(values != null && (values.length == 0 || values.length == 1),
                "Method %s can only contain at most 1 %s field. Found: %s", method.getName(), fieldName,
                values == null ? null : Arrays.asList(values));
    }

    private void parseProduces(MethodMetadata md, Method method, RequestMapping annotation) {
        String[] serverProduces = annotation.produces();
        String clientAccepts = serverProduces.length == 0 ? null : emptyToNull(serverProduces[0]);
        if (clientAccepts != null) {
            md.template().header(ACCEPT, clientAccepts);
        }
    }

    private void parseConsumes(MethodMetadata md, Method method, RequestMapping annotation) {
        String[] serverConsumes = annotation.consumes();
        String clientProduces = serverConsumes.length == 0 ? null : emptyToNull(serverConsumes[0]);
        if (clientProduces != null) {
            md.template().header(CONTENT_TYPE, clientProduces);
        }
    }

    private void parseHeaders(MethodMetadata md, Method method, RequestMapping annotation) {
        if (annotation.headers() != null && annotation.headers().length > 0) {
            for (String header : annotation.headers()) {
                int index = header.indexOf('=');
                if (!header.contains("!=") && index >= 0) {
                    md.template().header(resolve(header.substring(0, index)),
                            resolve(header.substring(index + 1).trim()));
                }
            }
        }
    }

    private Annotation synthesizeWithMethodParameterNameAsFallbackValue(Annotation parameterAnnotation, Method method,
                                                                        int parameterIndex) {
        Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(parameterAnnotation);
        Object defaultValue = AnnotationUtils.getDefaultValue(parameterAnnotation);
        if (defaultValue instanceof String && defaultValue.equals(annotationAttributes.get(AnnotationUtils.VALUE))) {
            Type[] parameterTypes = method.getGenericParameterTypes();
            String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
            if (shouldAddParameterName(parameterIndex, parameterTypes, parameterNames)) {
                annotationAttributes.put(AnnotationUtils.VALUE, parameterNames[parameterIndex]);
            }
        }
        return AnnotationUtils.synthesizeAnnotation(annotationAttributes, parameterAnnotation.annotationType(), null);
    }

    private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, String[] parameterNames) {
        // has a parameter name
        return parameterNames != null && parameterNames.length > parameterIndex
                // has a type
                && parameterTypes != null && parameterTypes.length > parameterIndex;
    }

    private boolean isMultipartFormData(MethodMetadata data) {
        Collection<String> contentTypes = data.template().headers().get(HttpEncoding.CONTENT_TYPE);

        if (contentTypes != null && !contentTypes.isEmpty()) {
            String type = contentTypes.iterator().next();
            try {
                return Objects.equals(MediaType.valueOf(type), MediaType.MULTIPART_FORM_DATA);
            } catch (InvalidMediaTypeException ignored) {
                return false;
            }
        }

        return false;
    }

    private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() {

        List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();

        annotatedArgumentResolvers.add(new MatrixVariableParameterProcessor());
        annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
        annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
        annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
        annotatedArgumentResolvers.add(new QueryMapParameterProcessor());
        annotatedArgumentResolvers.add(new RequestPartParameterProcessor());

        return annotatedArgumentResolvers;
    }

    private Map<Class<? extends Annotation>, AnnotatedParameterProcessor> toAnnotatedArgumentProcessorMap(
            List<AnnotatedParameterProcessor> processors) {
        Map<Class<? extends Annotation>, AnnotatedParameterProcessor> result = new HashMap<>();
        for (AnnotatedParameterProcessor processor : processors) {
            result.put(processor.getAnnotationType(), processor);
        }
        return result;
    }

    private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
        Parameter parameter = method.getParameters()[paramIndex];
        MethodParameter methodParameter = MethodParameter.forParameter(parameter);
        TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter);

        // Feign applies the Param.Expander to each element of an Iterable, so in those
        // cases we need to provide a TypeDescriptor of the element.
        if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
            TypeDescriptor elementTypeDescriptor = getElementTypeDescriptor(typeDescriptor);

            checkState(elementTypeDescriptor != null,
                    "Could not resolve element type of Iterable type %s. Not declared?", typeDescriptor);

            typeDescriptor = elementTypeDescriptor;
        }
        return typeDescriptor;
    }

    private static TypeDescriptor getElementTypeDescriptor(TypeDescriptor typeDescriptor) {
        TypeDescriptor elementTypeDescriptor = typeDescriptor.getElementTypeDescriptor();
        // that means it's not a collection but it is iterable, gh-135
        if (elementTypeDescriptor == null && Iterable.class.isAssignableFrom(typeDescriptor.getType())) {
            ResolvableType type = typeDescriptor.getResolvableType().as(Iterable.class).getGeneric(0);
            if (type.resolve() == null) {
                return null;
            }
            return new TypeDescriptor(type, null, typeDescriptor.getAnnotations());
        }
        return elementTypeDescriptor;
    }


    private static class ConvertingExpanderFactory {

        private final ConversionService conversionService;

        ConvertingExpanderFactory(ConversionService conversionService) {
            this.conversionService = conversionService;
        }

        Param.Expander getExpander(TypeDescriptor typeDescriptor) {
            return value -> {
                Object converted = conversionService.convert(value, typeDescriptor, STRING_TYPE_DESCRIPTOR);
                return (String) converted;
            };
        }

    }

    private class SimpleAnnotatedParameterContext implements AnnotatedParameterProcessor.AnnotatedParameterContext {

        private final MethodMetadata methodMetadata;

        private final int parameterIndex;

        SimpleAnnotatedParameterContext(MethodMetadata methodMetadata, int parameterIndex) {
            this.methodMetadata = methodMetadata;
            this.parameterIndex = parameterIndex;
        }

        @Override
        public MethodMetadata getMethodMetadata() {
            return methodMetadata;
        }

        @Override
        public int getParameterIndex() {
            return parameterIndex;
        }

        @Override
        public void setParameterName(String name) {
            nameParam(methodMetadata, name, parameterIndex);
        }

        @Override
        public Collection<String> setTemplateParameter(String name, Collection<String> rest) {
            return addTemplateParameter(rest, name);
        }

        Collection<String> addTemplateParameter(Collection<String> possiblyNull, String paramName) {
            Collection<String> params = ofNullable(possiblyNull).map(ArrayList::new).orElse(new ArrayList<>());
            params.add(String.format("{%s}", paramName));
            return params;
        }
    }
}

5.3 增加自动配置:
package xxxxx.basic.config;

import feign.Contract;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.support.DefaultConversionService;

import java.util.ArrayList;
import java.util.List;

/**
 * 契约协议配置
 *
 * @author qiyu
 */
@Configuration
@ConditionalOnClass(SpringMvcContract.class)
@Order(Integer.MIN_VALUE)
public class SvcSpringContractConfig {

    @Autowired(required = false)
    private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();

    @Bean
    public Contract feignContract() {
        return new SvcSpringContract(parameterProcessors, new DefaultConversionService(), true);
    }
}

到了这里,关于@RequestMapping和@FeginClient注解不能同时使用的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 解决SpringBoot中@RequestBody不能和Multipart同时传递的问题

    今天在做文件上传的时候,遇到了这么一个错误日志: Resolved[org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘multipart/form-data;boundary=--------------------------771899451541318130280588;charset=UTF-8’ not supported] 从日志中可以发现,好像是因为@RequestBody不能和MultipartFile一起使用。

    2024年01月25日
    浏览(31)
  • 【SpringMVC】二、RequestMapping注解

    ReqeustMapping实现的是将请求与处理请求的控制器方法进行关联,建立映射,实现当请求传进来的时候,调用对应方法的情况。 注意RequestMapping的value值不可重复,即每个请求只能映射一个Controller类的方法。 若@RequestMapping定义在类上,表示映射请求的初始信息 @RequestMapping定义在

    2024年02月04日
    浏览(28)
  • 【SpringMVC】@RequestMapping注解(详解)

    SpringMVC汇总: SpringMVC笔记汇总 从注解名称上我们可以看到, @RequestMapping 注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。 SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。 @RequestMapping 标识一个类:设置映射请

    2024年02月10日
    浏览(28)
  • SpringMVC之@RequestMapping注解

    @RequestMapping注解 (1)@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。 (2)SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。 (3)在SpringMVC中如何被使用:浏览器发送请求,若请求地址符合前端控

    2024年02月12日
    浏览(40)
  • 【Java EE】@RequestMapping注解

    在Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射 ,也就是浏览器连接程序的作⽤ 我们先来看看代码怎么写 创建⼀个 TalkController 类,实现⽤⼾通过浏览器和程序的交互,具体实现代码如下: 这时候我们使用浏览器访问 http://127.0.0.1:8080/hi 就可访问 @RequestMapping 是Spring Web M

    2024年04月09日
    浏览(30)
  • 详解 SpringMVC 的 @RequestMapping 注解

    @RequestMapping是一个用于映射HTTP请求到处理方法的注解,在Spring框架中使用。它可以用于控制器类和处理方法上,用来指定处理不同URL路径的请求,并定义请求的方法(GET、POST等)、请求参数、请求头等。 @RequestMapping注解的常用属性包括 : value:用于指定URL路径,可以是单个

    2024年02月11日
    浏览(27)
  • 3、SpringMVC之RequestMapping注解

    创建名为spring_mvc_demo的新module,过程参考2.1节 细节请参考2.6节 从注解名称上可以看出,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系; SpringMVC 的前端控制器(DispatcherServlet)接收到请求后,就会在映射关系中找到对应的控制器方法来处

    2024年02月08日
    浏览(26)
  • Spring MVC-@RequestMapping注解详解

    目录 1、@RequestMapping注解的作用 2、@RequestMapping注解的位置 3、value属性 (1)基础用法 (3)路径中的占位符(重点) 4、method属性 5、params属性(了解) 6、headers属性(了解) @RequestMapping注解的作用,就是 将请求和处理请求的控制器方法关联起来 ,建立映射关系。 SpringMVC接

    2023年04月24日
    浏览(34)
  • Spring MVC 的RequestMapping注解

    作用:用于建立请求URL和处理请求方法之间的对应关系。 出现位置: 类上: 请求 URL的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。它出现的目的是为了使我们的 URL 可以按照模块化管理,例如: 账户模块: /account /add /account /update /account

    2024年02月02日
    浏览(30)
  • Spring MVC学习之——RequestMapping注解

    用于建立请求URL和处理请求方法之间的对应关系。 value:指定请求的实际地址,可以是一个字符串或者一个字符串列表。 value可以不写,直接在括号中写,默认就是value值 @RequestMapping(value=“/hello”) public String hello(){ return…} method:指定请求的方式,可以是GET、POST、PUT、DELE

    2024年01月18日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包