I'm going to implement a Spring MVC controller which uses dynamic view passed as a parameter to view:
#Controller
#RequestMapping("/page")
public class PageController {
#RequestMapping(value = "/{page}", method = {RequestMethod.GET})
public ModelAndView page(#PathVariable("page")String page) {
System.out.println("page = " + page);
return new ModelAndView(page);
}
}
Views are resolved by UrlBasedViewResolver:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="cache" value="false"/>
</bean>
Is there any way to return a default view when non-existing view requested? Maybe to do check if there's no view exists, then return new ModelAndView('requested_page_not_found')?
Not with UrlBasedViewResolver and friends. Once the chain has reached it, you're committed -- if the view doesn't exist, you're going to be directed to whatever 404 page your container has configured.
Note: When chaining ViewResolvers, a UrlBasedViewResolver always needs to be last, as it will attempt to resolve any view name, no matter whether the underlying resource actually exists.
Related
In the Spring docs, for NEVER propagation:
Execute non-transactionally, throw an exception if a transaction
exists.
I wanted to try like following:
#Transactional(propagation = Propagation.NEVER)
public void getDeps(long ID) {
System.out.println(databaseImp.getDepartmentByID(ID));
}
#Transactional(propagation = Propagation.REQUIRED)
public void allProcessOnDB_second(long ID) {
getDeps(ID);
operation(ID);
}
#Transactional
public void operation(long id){
System.out.println(databaseImp.getDepartmentByID(id));
}
Well, it is not important what code wants to do.
I use the #Transactional(propagation = Propagation.NEVER) and I use this method in any transactional method but it doesn't work. I mean it must throw an exception, but it doesn't.
My Spring meta configuration file (XML) contains the following:
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="database.transactionmanagement"/>
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="datasource2"/>
</bean>
<bean id="datasource2" class="org.apache.tomcat.dbcp.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/hr"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
Thanks for your answers.
#Transactional annotations only apply to the Spring proxy objects. For example, if you call allProcessOnDB_second() from some spring bean which injects your service like this
#Autowired
private MyService myService;
...
myService.allProcessOnDB_second();
then myService is Spring proxy, and its #Transactional(propagation = Propagation.REQUIRED) is applied. If you were to call myService.getDeps(id) then #Transactional(propagation = Propagation.NEVER) would be applied.
But when you call the first method, and then second method from it, then second method isn't called through Spring proxy but rather directly, so its transactional configuration is ignored.
Spring transactions are proxy-based. That exception would be thrown if a bean A called another bean B, because the transactional aspect would intercept the call and throw the exception. But here, you're calling another method in the same object, and the transactional proxy is thus out of the picture.
I am new to Spring, and have a simple problem.
I have written a simple controller with a simple constructor, annotated as shown below.
#Controller
public class LoginController
{
private LoginService loginService;
#Inject
public LoginController(LoginService loginService)
{
System.out.println("LoginController constructor - initializing login service");
this.loginService = loginService;
}
When I try to access my web app in a browser, I get the following error:
No default constructor found;
When I replace the #Inject annotation with the #Autowired annotation, it works. Can anyone explain why? Everything I've read says that have identical behavior. I'm using Tomcat 6.0.43.
Also, below is my spring dispatcher servlet xml:
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven/>
<context:component-scan base-package="com.rockwell_collins.webquery"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
You must be missing the JavaEE Inject API from your runtime classpath. Spring will only look for #Inject if the class is found in the classpath.
You can get it from Maven here and add it to your runtime classpath (manually or through dependencies).
I am attempting to setup a very simple base configuration for a spring web application that can handle the following cases:
map resource root requests to /index, eg / maps to /index, /resource/ maps to /resource/index
map /static/** to /static/ (this is a resources view - css,js,images)
handle some specific request paths with controller mappings
map all other requests to a url based view, eg /resource/page maps to /WEB-INF/views/resource/page.jsp
In my current configuration what I have is:
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:view-controller path="/" view-name="index"/>
which works for static resources and root requests (/) but not for resource based root requests (/resource/). When attempting to handle all other requests (**/) I break handling of the static resources.
<mvc:view-controller path="**/*" />
Is there a way to do all of these things at the same time? It doesn't need to be an xml only solution, I'm fine with a code configuration or hybrid solution.
I have managed to find a solution by implementing my own Controller and Config.
view-servlet.xml
<mvc:resources mapping="/static/**" location="/static/"/>
<context:component-scan base-package="com.example.web.view"/>
com.example.web.view.ViewConfig
#Configuration
public class ViewConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/views/");
vr.setSuffix(".jsp");
return vr;
}
#Bean RequestMappingHandlerMapping requestMappingHandlerMapping(){
return new RequestMappingHandlerMapping();
}
#Bean RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setOrder(2); //process after mvc:resources
return adapter;
}
}
com.example.web.view.ViewController
#Controller
public class ViewController {
#RequestMapping(value = "**/")
public String all(HttpServletRequest request) {
String path = request.getRequestURI().replace(request.getContextPath(),"");
return path.endsWith("/") ? path +"index" : path;
}
}
The magic was with setting the order value on the adaptor in ViewConfig. Spring creates a default mapping handler that all RequestMappings found by component-scan are added to. The reason why my initial setup failed was that the ordering of this handler is to match before the one registered by mvc:resources. Creating my own adaptor and setting its order to be processed after mvc:resources allowed both to work.
Information regarding this can found in the spring documentation 19.9.1 Setting up the dispatcher for annotation support.
Of course the ViewConfig can also be implemented purely in xml:
<mvc:resources mapping="/static/**" location="/static/"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="order" value="2"/>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<context:component-scan base-package="com.example.web.view"/>
Update: Changed to RequestMappingHandlerMapping and RequestMappingHandlerAdapter from DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter to reflect changes made in Spring 3.1.x and above.
I want to have the same request mapping but resolve to different view depending on the file extension. I have two JSPs one that renders HTML and another that renders XML. Depending on the file extension I should resolve to the corresponding jsp.
This is my controller:
#Controller
public class FileManagementController {
#RequestMapping(value="/filemanagements", method=RequestMethod.GET)
public ModelAndView list() {
//if file extension .xml return /filemanagement/listXml
//if no file extension present return /filemanagement/list
}
}
And I Have the following y Root of my WebApp:
/jsp/filemanagement/list.jsp
<%#page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
..
/jsp/filemanagement/listXml.jsp
<?xml version="1.0" encoding="UTF-8"?>
<%#page contentType="text/xml" pageEncoding="UTF-8"%>
....
This is how I have configured my ViewResolver in the servletContext.xml:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Have a look at ContentNegotiatingViewResolver. From the javadoc:
This view resolver uses the requested media type to select a suitable View for a request.
If the requested path has a file extension and if the setFavorPathExtension(boolean) property is true, the mediaTypes property is inspected for a matching media type.
There is also a section of the ref manual covering this resolver.
Following skaffman advice. I did solve this using ContentNegotiatingViewResolver.
servletContext.xml:
<bean id="resourceResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"/>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:annotation-driven/>
The resolvers are picked up automatically by ContentNegotiatingViewResolver.
Controller:
#Controller
#RequestMapping("/filemanagements")
public class FileManagementController {
#RequestMapping(method=RequestMethod.GET)
public ModelAndView list(#RequestParam(required=false) String contentType) {
return new ModelAndView(baseLogicView + "/list");
}
private static final String baseLogicView = "/filemanagement";
}
These jsps:
/WEB-INF/jsp/filemanagements/list.jsp
/WEB-INF/jsp/filemanagements/listXml.jsp
Resource bundle views.properties:
/filemanagement/list.(class)=org.springframework.web.servlet.view.JstlView
/filemanagement/list.url=/WEB-INF/jsp/filemanagement/listXml.jsp
/filemanagement/list.contentType=text/xml
It was necessary to specify the contentType there because if you don't it defaults to this "text/html;charset=ISO-8859-1" even if you set <%# page contentType="text/xml" %> in the JSP that renders the XML.
After that I could do the following requests:
http://localhost:8080/filemanagement-web/filemanagements (list.jsp)
http://localhost:8080/filemanagement-web/filemanagements.html (list.jsp)
http://localhost:8080/filemanagement-web/filemanagements.xml (listXml.jsp)
I'm trying to return content other than json in my Controller, but I can't seem to get it to work. Ideally, I'd like to return a rendered velocity template as plain text or html.
This is what I have in my controller:
#RequestMapping( value = "time", headers = "Accept=*/*", method = RequestMethod.GET )
public #ResponseBody
Date getCurrentTime( HttpServletRequest request, HttpServletResponse response ) {
response.setContentType( "text/plain" );
return new Date();
}
And this is in my springmvc-servlet.xml (I know this is not right...but I'm a bit lost here):
<context:component-scan base-package="com.paml.alerter.controller" />
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".vm" />
</bean>
<!-- This bean sets up the Velocity environment for us based on a root path
for templates. Optionally, a properties file can be specified for more control
over the Velocity environment, but the defaults are pretty sane for file
based template loading. -->
<bean id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/velocity/" />
</bean>
Does anyone know how to set this up right?
TIA
If your method is annotated with #ResponseBody, then the Spring MVC view layer will be bypassed entirely.
If you're not interested in JSON output, then #ResponseBody is inappropriate - just remove it, and your Velocity views will be used.
If you need to switch between JSON and some other View layer, then you should consider removing #ResponseBody and using ContentNegotiatingViewResolver instead. See the Spring docs for how to set this up.