I pass arguments to get data from database by JPA.
There are two arguments.
The first argument is ServiceType ; to switch database from Aspect (AOP)
The second argument is used to make query.
However there is org.springframework.data.jpa.repository.query.ParameterBinder.bind error
in conclusion, how to ignore the first(ServiceType) argument to make JPA query.
The code is like that.
#Repository
public interface BlogRepository extends JpaRepository<Blog, Integer> {
List<Blog> findByName(ServiceType serviceType, String name, Pageable pageable);
}
#Aspect
#Order(1)
#Component
public class MDBDecisionAspect {
private static ThreadLocal<ServiceType> localService = new ThreadLocal<>();
#Pointcut("execution(public * com.test.jpa.mdb..*.*(com.test.enums.ServiceType, ..))")
public void repositoryAspectTarget() {
}
#Around("repositoryAspectTarget()")
public Object initDaoService(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
if (args[0] != null && ServiceType.class.equals(args[0].getClass())) {
setServiceType(((ServiceType) args[0]));
}
return joinPoint.proceed();
}
#AfterReturning("repositoryAspectTarget()")
public void afterInitDaoServiceReturningTargetMethod() {
setServiceType(null);
}
public static ServiceType getServiceType() {
return localService.get();
}
public static void setServiceType(ServiceType serviceType) {
localService.set(serviceType);
}
}
create a service class and call repository in it. So you can use serviceType inside service class.
#Repository
public interface BlogRepository extends JpaRepository<Blog, Integer> {
List<Blog> findByName(String name, Pageable pageable);
}
class BlogService{
public List<Blog> getListOfBlog(ServiceType st, String name){
blogRepository. findByName(name);
}
}
change aspect pointcut to check service class.
Related
This is small sample from bigger project, trying to replicate issue that we see.
Implementation:
#Component
public class TestService {
public void test(String target, String key, Object message) {
System.out.println("Wrong method");
}
public void test(String target, Object message, SomeProcessor processor) {
System.out.println("Correct method");
}
}
Wrapper:
#Aspect
#Component
public class TestServiceAspect {
#Around(value = "execution(* com.example.TestService.test(..))" +
" && args(target, key, message)",
argNames = "pjp,target,key,message"))
public Object traceTest(ProceedingJoinPoint pjp,
String target, String key, Object message) throws Throwable {
System.out.println("Wrong wrapper");
return pjp.proceed();
}
#Around(value = "execution(* com.example.TestService.test(..))" +
" && args(target, message, processor)",
argNames = "pjp,target,message,processor")
public Object traceTest(ProceedingJoinPoint pjp,
String target, Object message, SomeProcessor processor) throws Throwable {
System.out.println("Correct wrapper");
return pjp.proceed();
}
}
Calling code:
#RestController
public class Tester {
private final TestService tst;
public Tester(TestService tst) {
this.tst = tst;
}
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void testHandler() {
System.out.println("START");
tst.test("foo", (Object)"hello", new SomeProcessorImpl());
System.out.println("END");
}
}
We get following output:
START
Wrong wrapper
Correct wrapper
Correct method
END
Why is it OK for second pointcut ("Wrong wrapper") to match (is it correct at all)? I know why it happens, because they internally check if type can be coerced into expected type. Based on runtime information, framework will detect that passed object ("hello") can be cast into String and SomeProcessor can be converted to Object. But does it still make right thing to do?
Just make your pointcuts more precise, not just the bound parameter types. Binding parameters is only necessary, if you actually want to use them in the advice methods.
Here is an MCVE for native AspectJ, which should work the same way in Spring AOP. I was just lazy to set up a Spring project with #Component and #Service stuff.
package de.scrum_master.app;
public interface SomeProcessor {}
package de.scrum_master.app;
public class SomeProcessorImpl implements SomeProcessor {}
package de.scrum_master.app;
public class TestService {
public void test(String target, String key, Object message) {
System.out.println("Wrong method");
}
public void test(String target, Object message, SomeProcessor processor) {
System.out.println("Correct method");
}
}
package de.scrum_master.app;
public class Tester {
private final TestService tst;
public Tester(TestService tst) {
this.tst = tst;
}
public void testHandler() {
System.out.println("START");
tst.test("foo", (Object) "hello", new SomeProcessorImpl());
// tst.test("foo", "hello", (Object) new SomeProcessorImpl());
System.out.println("END");
}
public static void main(String[] args) {
new Tester(new TestService()).testHandler();
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.SomeProcessor;
#Aspect
public class TestServiceAspect {
#Around("execution(* de.scrum_master.app.TestService.test(String, String, Object)) && args(target, key, message)")
public Object traceTest(ProceedingJoinPoint pjp, String target, String key, Object message) throws Throwable {
System.out.println(pjp + " -> wrong wrapper");
return pjp.proceed();
}
#Around("execution(* de.scrum_master.app.TestService.test(String, Object, SomeProcessor)) && args(target, message, processor)")
public Object traceTest(ProceedingJoinPoint pjp, String target, Object message, SomeProcessor processor)
throws Throwable {
System.out.println(pjp + " -> correct wrapper");
return pjp.proceed();
}
}
Console output when running the driver application:
START
execution(void de.scrum_master.app.TestService.test(String, Object, SomeProcessor)) -> correct wrapper
Correct method
END
After looking around, I couldn't find any good solution to this.
My autowired didn't work as expected where it returns null. I've autowired this particular class in other classes and it works so it only doesn't work in constraintvalidator classes.
UserService class
#Service
public class UserService {
#Autowired
private UserRepository userRep;
public void addUser(User user) {
userRep.save(user);
}
public void deleteUser(long userId) {
userRep.deleteById(userId);
}
public List<User> retrieveAllUsers(){
Iterable<User>temp =userRep.findAll();
List<User>allUsers = null;
temp.forEach(allUsers::add);
return allUsers;
}
public boolean searchByEmail(String email) {
return userRep.findByEmail(email);
}
public void updateUser(User user) {
userRep.save(user);
}
}
Annotation interface class
#Target(ElementType.FIELD)
//When will the annotation be processed compilation, runtime etc
#Retention(RetentionPolicy.RUNTIME)
//Where is the logic
#Constraint(validatedBy = EmailValidator.class)
#Documented
public #interface ValidEmail {
//Error message
String message() default "Invalid email";
//Required for annotation
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Annotation logic class. The autowired here returns null
public class EmailValidator implements ConstraintValidator<ValidEmail, String> {
#Autowired
private UserService service;
//Actual place to place the logic to check if the data is valid or not
#Override
public boolean isValid(String email, ConstraintValidatorContext context) {
if (email == null) {
return false;
}
List<User> users = service.retrieveAllUsers();
if (users.size() > 0) {
return Pattern.matches("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", email)
&& service.searchByEmail(email);
}
else {
return Pattern.matches("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", email);
}
}
#Override
public void initialize(ValidEmail validEmail) {
validEmail.message();
}
}
Main
#SpringBootApplication
#ComponentScan(basePackages = {
"com.Alex.Mains", "com.Alex.UserPackage", "com.Alex.Flights", "com.Alex.Security"
})
#EntityScan( basePackages = {"com.Alex.UserPackage", "com.Alex.Flights"})
#EnableJpaRepositories({"com.Alex.UserPackage", "com.Alex.Flights"})
public class JpaApplication {
public static void main(String[] args) {
SpringApplication.run(JpaApplication.class, args);
}
// #Bean
// public Validator validator(final AutowireCapableBeanFactory beanFactory) {
//
// ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
// .configure()
// .constraintValidatorFactory(new SpringConstraintValidatorFactory(beanFactory))
// .buildValidatorFactory();
//
// return validatorFactory.getValidator();
// }
}
Edit: Tried #Componenet
Fixed with adding the following to application.properties. No idea why but it works
spring.jpa.properties.javax.persistence.validation.mode=none
EDIT: My Suggestion
Instead of a custom validator, use the existing #EMail and a unique constraint:
#Entity
public class User {
// ...your properties
#Email
#Column(unique = true)
private String email.
// Rest of class...
}
OLD:
So, first off:
List<User> users = service.retrieveAllUsers();
if (users.size() > 0) {
You are fetching all the Users from the database, just to check whether any users exists? This is very, very inefficient. If you are already using Spring Data, you can just do
#Query("SELECT COUNT(*) > 0 FROM Users")
boolean anyExists();
Furthermore, your Service does not get injected, because EmailValidator is a POJO (plain old java object) and not a Spring managed component. If you annotate it with #Component or #Service Spring will take care of injection.
But I would not recommend that. I'm not sure what your exact use case is, but validators are often used on Entities and as such, they get called when the entity is created or updated. You don't want to issue additional queries in those cases.
Like I said, I don't know what exactly you are trying to achieve, but you could use the existing #Email validator (you can even provide a custom regular expression with the regexp attribute).
I upgraded from Spring Boot v.1.5.8 to v.2.1.5. When I try to start the application I get the following error:
IllegalArgumentException: Fragment implementation .OtmUniParentRepository2019GeneratedImpl$$EnhancerBySpringCGLIB$$cdf9e294 does not implement x.OtmUniParentRepository2019Generated!
Why I can't start it anymore?
The files:
OtoUniChildRepository2019
#RepositoryRestResource(collectionResourceRel = "OtoUniChild2019", path = "OtoUniChild2019")
public interface OtoUniChildRepository2019 extends OtoUniChildRepository2019Generated {
}
#Transactional
class OtoUniChildRepository2019Impl extends HeartcoreRepositoryImpl<OtoUniChild> {
#PostConstruct
private void setIni() {
super.setIni(OtoUniChild.TABLENAME, OtoUniChild.getColumnName(), OtoUniChild.class, "AGRIDB2019");
}
}
OtoUniChildRepository2019Generated
public interface OtoUniChildRepository2019Generated extends HeartcoreRepository<OtoUniChild> {
OtoUniChild findByIdAndOtoUniParentIsNotNull(#Param("id") String id);
OtoUniChild findByOtoUniParentId(#Param("id") String id);
}
#Transactional
class OtoUniChildRepository2019GeneratedImpl extends HeartcoreRepositoryImpl<OtoUniChild> {
#PostConstruct
private void setIni() {
super.setIni(OtoUniChild.TABLENAME, OtoUniChild.getColumnName(), OtoUniChild.class, "AGRIDB2019");
}
}
HeartcoreRepository
#NoRepositoryBean
public interface HeartcoreRepository<T extends Heartcore> extends RevisionRepository<T, String, Integer>, PagingAndSortingRepository<T, String>, HeartcoreCustomRepository<T> {
#Override
T findOne(String id);
boolean existsById(String id);
#Override
Collection<T> findAll();
List<T> findAllByKanton(#Param("kanton") String kanton);
}
HeartcoreCustomRepository
public interface HeartcoreCustomRepository<T extends Heartcore> {
List<T> findCustom(String sqlQuery);
List<T> findCustom(String select, String where);
Class<T> getType();
T findOne(String id);
Collection<T> findAll();
String getSequence(String sequenceName);
}
HeartcoreCustomRepositoryImpl
#Transactional
public class HeartcoreRepositoryImpl<T extends Heartcore> implements HeartcoreCustomRepository<T> {
#PersistenceContext
protected EntityManager entityManager;
// irrelevant code
public void setIni(String tablename, List<String> columns, Class<T> type, String schema) {
this.tablename = tablename;
this.columns = columns;
this.type = type;
this.schema = schema;
MultitenantDataSource multitenantDataSource = (MultitenantDataSource) entityManager.getEntityManagerFactory().getProperties().get("hibernate.connection.datasource");
DataSource dataSource = (DataSource) multitenantDataSource.determineTargetDataSource();
try {
this.dbDriver = dataSource.getConnection().getMetaData().getDriverName();
}
catch (SQLException e) {
e.printStackTrace();
}
}
// irrelevant code
With 1.5.8 it works fine and I couldn't find infos about breaking changes.
EDIT: Is something wrong with this inheritance structure of the repositories? I tried some different approaches but none worked. Is there an other way to implement some basic functionality for repositories?
I'd like to create a generic security crud repository and annotate each method with preauthorize, however I cannot get how to retrieve entity class name
#NoRepositoryBean
public interface AbstractEntityRepository<T extends AbstractEntity> extends CrudRepository<T, String> {
#Override
#PreAuthorize("hasPermission(null, #entityName, 'find');
Iterable<T> findAll();
}
public interface UserRepository extends AbstractEntityRepository<User> {}
in this case, entityName is always null.
#Component
#Log
public class CustomPermissionEvaluator implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
log.fine("type " + targetType); // <- null
return true;
}
...
Any idea how to get either "User" or "UserRepository" ?
#Component
public class ClassTypeResolver {
public static String resolve(Object object) {
if (AopUtils.isJdkDynamicProxy(object)) {
try {
return ((SimpleJpaRepository) ((Advised)object).getTargetSource().getTarget()).getDomainClass().getCanonicalName();
} catch (Exception e) {
return null;
}
} else {
return ((SimpleJpaRepository) object).getDomainClass().getCanonicalName();
}
}
}
Is there any reason not to map Controllers as interfaces?
In all the examples and questions I see surrounding controllers, all are concrete classes. Is there a reason for this? I would like to separate the request mappings from the implementation. I hit a wall though when I tried to get a #PathVariable as a parameter in my concrete class.
My Controller interface looks like this:
#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);
}
And the implementing class:
#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);
}
}
The getGoals() method works great; the removeGoal(String id) throws an exception
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
If I add the #PathVariable annotation to the concrete class everything works as expected, but why should i have to re-declare this in the concrete class? Shouldn't it be handled by whatever has the #Controller annotation?
Apparently, when a request pattern is mapped to a method via the #RequestMapping annotation, it is mapped to to the concrete method implementation. So a request that matches the declaration will invoke GoalServiceImpl.removeGoal() directly rather than the method that originally declared the #RequestMapping ie GoalService.removeGoal().
Since an annotation on an interface, interface method, or interface method parameter does not carry over to the implementation there is no way for Spring MVC to recognize this as a #PathVariable unless the implementing class declares it explicitly. Without it, any AOP advice that targets #PathVariable parameters will not be executed.
The feature of defining all bindings on interface actually got implement recently in Spring 5.1.5.
Please see this issue: https://github.com/spring-projects/spring-framework/issues/15682 - it was a struggle :)
Now you can actually do:
#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);
}
}
You can even use this library: https://github.com/ggeorgovassilis/spring-rest-invoker
To get a client-proxy based on that interface, similarly to how RestEasys client framework works in the JAX-RS land.
It works in newer version of Spring.
import org.springframework.web.bind.annotation.RequestMapping;
public interface TestApi {
#RequestMapping("/test")
public String test();
}
Implement the interface in the Controller
#RestController
#Slf4j
public class TestApiController implements TestApi {
#Override
public String test() {
log.info("In Test");
return "Value";
}
}
It can be used as:
Rest client
Recently I had the same problem. Following has worked for me:
public class GoalServiceImpl implements GoalService {
...
public void removeGoal(#PathVariableString id) {
}
}
i resolved this problem.
ON CLIENT SIDE:
I'm using this library https://github.com/ggeorgovassilis/spring-rest-invoker/. This library generate a proxy from interface to invoke spring rest service.
I extended this library:
I created an annotations and a factory client class:
Identify a Spring Rest Service
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface SpringRestService {
String baseUri();
}
This class generates a client rest from interfaces
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);
}
}
I configure my factory:
<bean class="it.eng.rete2i.springjsonmapper.factory.RestFactory">
<property name="basePackage" value="it.giancarlo.rest.services" />
</bean>
ON REST SERVICE SIGNATURE
this is an example interface:
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);
}
ON REST SERVICE IMPLEMENTATION
Service implementation
#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;
}
}
To resolve annotation on parameters I create a custom RequestMappingHandlerMapping that looks all interfaces annotated with #SpringRestService
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;
}
}
}
}
I created a configuration class
#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;
}
}
and I configurated it
<bean class="....WebConfig" />