During learning Spring Boot I noticed attributes to methods in Spring Boot controllers can be automatically injected.
To migrate my blog from Grails to Spring Boot I created an implementation to inject the current blog instance based on an session attribute.
The following code snippets show a basic implementation. Be aware there is no proper error handling (e.g. null checks) or security (check if user has access to the instance) implemented.
Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentBlogInstance {
}
Annotation Resolver
import javax.servlet.http.HttpSession;
import java.lang.annotation.Annotation;
@Component
public class BlogArgumentResolver implements HandlerMethodArgumentResolver {
private final BlogRepository blogRepository;
private final ObjectFactory<HttpSession> httpSessionFactory;
@Autowired
public BlogArgumentResolver(BlogRepository blogRepository,
ObjectFactory<HttpSession> httpSessionFactory) {
this.blogRepository = blogRepository;
this.httpSessionFactory = httpSessionFactory;
}
public boolean supportsParameter(MethodParameter parameter) {
return findMethodAnnotation(CurrentBlogInstance.class, parameter) != null;
}
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Object blogId = httpSessionFactory.getObject().getAttribute("blogId");
if (!(blogId instanceof Long)) {
return null;
}
Blog blogInstance = blogRepository.findById((Long) blogId).get();
if (!parameter.getParameterType().isAssignableFrom(blogInstance.getClass())) {
throw new ClassCastException(blogInstance + " is not assignable to "
+ parameter.getParameterType());
}
return blogInstance;
}
private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass,
MethodParameter parameter) {
T annotation = parameter.getParameterAnnotation(annotationClass);
if (annotation != null) {
return annotation;
}
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
for (Annotation toSearch : annotationsToSearch) {
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(),
annotationClass);
if (annotation != null) {
return annotation;
}
}
return null;
}
}
Configuration
import ch.kobelnet.blog.util.BlogArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
BlogArgumentResolver blogArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(blogArgumentResolver);
}
}
Usage
import ch.kobelnet.blog.domain.Article;
import ch.kobelnet.blog.domain.Blog;
import ch.kobelnet.blog.repo.ArticleRepository;
import ch.kobelnet.blog.util.CurrentBlogInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@Controller
public class ArticleController {
@Autowired
ArticleRepository articleRepository;
@GetMapping("/admin/article/list")
public String list(Model model, @CurrentBlogInstance Blog blog) {
List<Article> articles = articleRepository.findAllByBlog(blog);
model.addAttribute("articleList", articles);
return "admin/article/list";
}
}