I implemented some rest web-services using spring.
I have some common code that I need to perform before every web service.
Crrently i explicitly call this common code at the start of each web service.
Is there a way to have spring "automatically" call this common code before calling web services?
You should using the spring aop intercept every web services,and execute the common code on it.Like following code:
<bean id="aspect" class="com.zhuyiren.Aspect"/>
<aop:config>
<aop:aspect ref="aspect">
<aop:before method="before" pointcut="execution(* com.zhuyiren.service..*.*(..))"/>
</aop:aspect>
</aop:config>
That above code means the invoke every method of some bean packaged at com.zhuyiren.service always execute the method common in com.zhuyiren.Aspect.You can write the common code in common method.Like:
public void common(){
System.out.println("execute some common code");
}
The test case:
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
UserService service = context.getBean(UserService.class);
System.out.println(service.getUser(3));
}
The print is :
execute some common code
UserInfo{id=3, name='zhuyiren', password='123456', testString=[]}
The answer depends on the code that you want to execute.
One technique is to use AOP (as described in the #dabaicai answer).
A different technique is to add a JEE filter to process the request before it lands in your handler method.
Here are some links:
JEE6 Tutorial Filtering Requests and Responses
Oracle's Essentials of filters
Mkyong's Spring JEE filters - Take this with a grain of salt, Mkyong is light on explanation.
Another option: google search for "spring jee filter"
Spring have notion of interceptors - piece of code which "wraps" incoming requests
The simplest is to implement HandlerInterceptor interface:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
public class LetterzInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration<String> headerNames = request.getHeaderNames();
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
Next step is to register the interceptor with Spring:
#Configuration
public class MyConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
Related
I migrate the code to springboot and our API works well. Only interceptor can't be triggerred.
I googled related solutions and modify the code to right format which still failed to trigger the interceptor.
In our project, we also have the filter which extends OncePerRequestFilter and works.
It makes me confused.
They should be no big difference.
Btw, AOP is used in the project.
It's my code.
JerseyConfig.class
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.xxx");
}
}
VaultAuthorizationInterceptor.class
#Component
public class VaultAuthorizationInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(VaultAuthorizationInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("test");
return true;
}
}
VaultAuthConfig.class
#Configuration
public class VaultAuthConfig implements WebMvcConfigurer {
#Bean
public VaultAuthorizationInterceptor getVaultInterceptor() {
return new VaultAuthorizationInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getVaultInterceptor()).addPathPatterns("/**");
}
}
When you are using the spring-boot-starter-jersey, you use jersey as your web stack. That means any requests will processed by jersey. So you have to register a jersey filter or interceptor. Take a look at the jersey documantation. There is described how to use filters and interceptors. I think you want to use a filter because interceptors in the jersey stack used to manipulate the input or output stream.
I have a Spring Boot v1.4.0 application configured with Jersey for delivery of RESTful services.
I have a working app, but I'm now trying to enhance it with a multi-tenancy SCHEMA awareness strategy. I was hoping to set a TenantContext based on client auth headers using a Spring's HandlerInterceptor framework...
BUT, there seems to be an issue with the Interceptors being fired with Jersey. I can hit the APIs fine, ( i.e. curl -i -H "Accept: application/json" -X GET http://localhost:8080/api/products ), but the interceptors just won't fire. If I wire up a more basic app without Jersey for resource management, they fire fine?
Here is the current application set-up:
#SpringBootApplication
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).run(args);
}
}
Registering the Interceptor
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
HandlerInterceptor tenantInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tenantInterceptor);
}
}
The Interceptor itself
#Component
public class TenantInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
// FIXME: Put in a Logger impl
System.out.println("++++++++++++=======+++++++++ TenantInterceptor.preHandle() Checking for Tenant Routing");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
TenantContext.clear();
// FIXME: Put in a Logger impl
System.out.println("++++++++++++=======+++++++++ TenantInterceptor.postHandle() Reset Tenant to " + TenantContext.getCurrentTenant());
}
}
The JerseyConfig
#Component
#ApplicationPath("api")
public class JerseyConfig extends ResourceConfig {
#PostConstruct
private void init() {
registerClasses(TenantsResource.class);
registerClasses(UsersResource.class);
registerClasses(ProductsResource.class);
}
}
I played around with the JerseyConfig #ApplicationPath("api") and the WebMvcConfig registry.addInterceptor(tenantInterceptor).addPathPatterns("patterns");. Tried the following one after the other, but no joy.
registry.addInterceptor(tenantInterceptor).addPathPatterns("/*");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/**");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/**");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/*");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/products");
registry.addInterceptor(tenantInterceptor).addPathPatterns("/api/products/");
Any help - much appreciated, or else I'll be resorting to hacking the Resource Controllers with smelly code :(.
Thanks - Derm
As mentioned by M.Deinum, HandlerInterceptor is not for Jersey, and it not some "underversal" interceptor. It is only for Spring MVC. For Jersey, you can use a ContainerRequestFilter. You would register it with you ResourceConfig.
See also:
Jersey docs for Filters and Interceptors
I would like to use the RepositoryEntityLinks class to get the link to a resource at various places in my code as per section 12.1 of the current Spring Data Rest manual
12.1. Programmatic Links Sometimes you need to add links to exported resources in your own custom built Spring MVC controllers. There are
three basic levels of linking available:
...
3 Using Spring Data REST’s implementation of RepositoryEntityLinks.
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#_programmatic_links
I note the docs refer explicitly to "...your own custom built Spring MVC controllers" and it would seem that is the only place it is available. I would like to use the configured instance in a Spring Security AuthenticationSuccessHandler however the application fails to start with the error:
No qualifying bean of type[org.springframework.data.rest.webmvc.support.RepositoryEntityLinks] found
I have been able to successfully inject it to a controller as expected.
Can I use the RepositoryEntityLinks class outside of a Spring MVC Controller?
public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
#Autowired
private RepositoryEntityLinks entityLinks;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException
{
//do something with entityLinks
}
}
Yes, You can. I have successfully used it in Assembler which generates links from HATEOAS model. Altough there may be some restrictions on where RepositoryEntityLinks class can be injected, for sure it can be used outside of Controllers.
Below you can see my working example. If anyone wnders this class extends ResourceAssemblerSupport which is part of spring-hateoas module. Maybe that's the thing that enables injection here.
#Component
public class UserAssembler extends ResourceAssemblerSupport<UserEntity, UserResource> {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
public UserAssembler() {
super(UserController.class, UserResource.class);
}
#Override
public UserResource toResource(UserEntity userEntity) {
Link userLink = repositoryEntityLinks.linkToSingleResource(UserEntity.class, userEntity.getId());
Link self = new Link(entryLink.getHref(), Link.REL_SELF);
return new UserResource(userEntity, self);
}
}
The following works for me:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RestApiIntegrationTests {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
#BeforeEach
public void initServletRequestAttributes() {
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
}
#Test
void test() {
System.out.println(repositoryEntityLinks.linkToCollectionResource(SomeClass.class));
}
}
The code is based on spring-data-rest-tests-core: AbstractControllerIntegrationTests, TestMvcClient.
I'm building a web application and I'm using the Guice Servlet extension to bootstrap everything.
Following the user guide and examples I found, my web.xml has a single listener which extends GuiceServletContextListener. In my listener, I create my injector as described here.
My app has a few components which need to be initialized and destroyed when the servlet context is initialized and destroyed respectively. Some examples are a cache manager, a client which fetches data from a 3rd party API, a client to access a Cassandra store, etc.
I'm trying to find the right place to init/destroy these components. Without Guice, I would probably do that directly in my context listener, but it seems that Guice doesn't promote that.
What I have seen is the use of a servlet filter for each service. By implementing init/destroy in each filter, I can start and stop each service. However, if I have no actual filtering to do, this seems like overkill just to hook into the servlet lifecycle:
#Singleton
public final class MyServiceFilter implements Filter {
private final MyService service;
#Inject
public MyServiceFilter(MyService service) {
this.service = service;
}
#Override
public void init(FilterConfig filterConfig) {
service.start();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
#Override
public void destroy() {
service.stop();
}
}
Are there any other options?
I would like to add web interface to my Java application, so that I can manipulate it's state using HTTP.
I have added to application context a Spring bean for some class that starts embedded Tomcat. This class of course has access to context that creates it. But I would like to store this context somehow in Tomcat class (org.apache.catalina.startup.Tomcat) so that later in can be retrieved in Servlets, so that I can do something like this:
public SomeClass extends extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext appContext = getContextStoredEarlierInTomcatClass();
SomeBeanFromContext sbfc = appContext.getBean("sbfc", ApplicationContext.class);
sbfc.setSomeProperty(newValue);
}
}
Any idea how I could achieve it?
Thanks!
Classes including Servlets do not require an ApplicationContext to obtain references to String beans. This is done using dependency injection
#Controller
#RequestMapping("/mypage")
public class SomeClass {
#Autowired
private SomeBeanFromContext sbfc;
#RequestMapping(value = "/individualRequest", method = RequestMethod.GET)
public String doIndividualRequest(HttpServletRequest request) {
sbfc.setSomeProperty(newValue);
...
}
}
Spring MVC offers a complete method of injecting beans into target web controller classes using #Controller annotated classes.