Modifying @RequestMappings on startup

2019-07-26 23:44发布

问题:

Is it possible to change the @RequestMapping values on startup?

Basically what I want is to create an annotation @Api(Api.Version.V1) which would mean that the request mapping should be modified from /api/dogs to /api/v1/dogs. I want to do this both on class level (apply to all) and on method level (re-using the controller from an earlier version, and modifying it).

I could hardcode this in, but this leaves a lot of strings to take care of, and it's not as clean as I want it.

So is it possible (using a bpp or something similar) to change the request mappings during startup? after beans have been created, I don't want/need to modify them.

I have been also looking into RequestCondition but this seems to have a more dynamic nature, and I'm not sure it would help me in this case.

Another issue, I would like being able to annotation two classes with the same request mapping (and then let the annotation rewrite it), and I'm pretty sure this needs to be done on initial context load (so we don't get duplicate mappings etc).

Any pointers in the right direction would be appreciated.

Edit:

Got this almost working, I'm using a custom RequestMappingHandlerMapping and overriding the method getMappingForMethod, which allows me to get the annotations (on both type and method) and return a modified RequestMappingInfo with the paths added.

One issue remains, even if I remove all old mappings and only return /api/v1/dogs the old mapping to /api/dogs still works. Is it possible to also remove this mapping somehow?

The code is here, if anyone is interested.

@Component
public class CustomRequestMappingHandlerMapping
    extends RequestMappingHandlerMapping
{
    @Override
    protected RequestMappingInfo getMappingForMethod( Method method, Class<?> handlerType )
    {
        RequestMappingInfo info = super.getMappingForMethod( method, handlerType );

        ApiVersion methodApiVersion = AnnotationUtils.findAnnotation( method, ApiVersion.class );
        ApiVersion typeApiVersion = AnnotationUtils.findAnnotation( handlerType, ApiVersion.class );

        if ( typeApiVersion == null )
        {
            return info;
        }

        Set<String> oldPatterns = info.getPatternsCondition().getPatterns();
        Set<String> patterns = new HashSet<>();

        for ( String p : oldPatterns )
        {
            for ( int v = 0; v < typeApiVersion.value().length; v++ )
            {
                ApiVersion.Version version = typeApiVersion.value()[v];

                if ( !p.startsWith( version.getValue() ) )
                {
                    if ( p.startsWith( "/" ) ) patterns.add( "/" + version.getValue() + p );
                    else patterns.add( "/" + version.getValue() + "/" + p );
                }
                else
                {
                    patterns.add( p );
                }
            }
        }

        PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(
            patterns.toArray( new String[]{} ), null, null, true, true, null );

        RequestMappingInfo mappingInfo = new RequestMappingInfo(
            null, patternsRequestCondition, info.getMethodsCondition(), info.getParamsCondition(), info.getHeadersCondition(), info.getConsumesCondition(),
            info.getProducesCondition(), info.getCustomCondition()
        );

        return mappingInfo;
    }
}

回答1:

You can dynamically add requestMappings by either implementing HandlerMapping Interface or extending AbstractUrlHandlerMapping.

The idea is create your own Custom Handler Mapping instead of using default implementations such as SimpleUrlHandlerMapping,DefaultAnnotationHandlerMapping etc