I'm working on a Spring MVC/Webflow Application (version 3.2) and trying to get exception handling working where I can output a custom exception message to a logfile and error.jsp. The problem I'm having is that the Exception Handler is not getting fired. I've created the following class and annotated it "#ControllerAdvice" and put it into the same package as my controller that is throwing the exception:
#ControllerAdvice
public class MyCustomExceptionController {
#ExceptionHandler(MyCustomException.class)
public ModelAndView handleMyException(MyCustomException ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/error/error");
modelAndView.addObject("errorId", ex.getErrorId());
modelAndView.addObject("message", ex.getErrorMessage());
return modelAndView;
}
}
and added the following to the mvc-config File:
<mvc:annotation-driven/>
And included the following in my app-config File:
<context:component-scan base-package="package containing my controllers and MyCustomExceptionController">
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
Any ideas why this isn't working?
The <mvc:annotation-driven/> element implicitly registers a ExceptionHandlerExceptionResolver bean. This class has a initExceptionHandlerAdviceCache() method which scans beans in the context to find those whose class type is annotated with #ControllerAdvice.
It does this by first calling ControllerAdviceBean.findAnnotatedBeans(ApplicationContext). Internally, this method uses ApplicationContext#getBeanDefinitionNames(). The javadoc of this method states
Does not consider any hierarchy this factory may participate
To clarify what this means. When you declare a ContextLoaderListener in your deployment descriptor, it loads what we call a root or application ApplicationContext and makes it available in the ServletContext. When you then declare a DispatcherServlet, it creates its own servlet ApplicationContext and uses any ApplicationContext it finds in the ServletContext attributes loaded by the ContextLoaderListener as a parent to that context. The hierarchy looks like so
Root ApplicationContext // loaded by the ContextLoaderListener
|
Servlet ApplicationContext // loaded by the DispatcherServlet
Every ApplicationContext has access to beans in parent contexts, but not the other way around.
The method above chooses not to use the beans in parent contexts and so only has access to beans in the current ApplicationContext (BeanFactory really).
As such, if your
<context:component-scan .../>
is declared in a root ApplicationContext as I'll assume from the name app-config, but the
<mvc:annotation-driven />
is declared in the servlet ApplicationContext, again assuming from mvc-config, then the ExceptionHandlerExceptionResolver looking for #ControllerAdvice beans will not find any. It is looking for beans in the servlet context but they aren't there, they are in the root context.
In case anyone else runs into a problem like this - I found an error I had.
I only had one RequestMapping (http://localhost:8080/myapp/verify/verify)
In an InterceptorController, in the PreHandle method, I explicitly threw an exception -> throw new MyCustomException("error","error.jsp") to test my #ControllerAdvice Exception handling.
When I went to http://localhost:8080/myapp/ I would see the interceptor controller get called, my custom exception get thrown, but the #ControllerAdvice class with my #ExceptionHandler(MyCustomException.class) was never called.
I added a #RequestMapping(value="/") and it resolved my issues. Since I was attempting to go to a URI that had no #RequestMapping associated with it, i was getting a 'NoHandlerFoundException' which was shorting out my Exception from bubbling up.
In short, make sure the URI you're attempting to invoke has a #RequestMapping associated with it, or have a method in your ExceptionHandler class to deal with the NoHandlerFoundException.
Hope this helps.
Related
For Beans, usually we can customise the id attribute but for ResourceBundleMessageSource class, if we dont specify id="messageSource" then a exception is thrown.
<bean id="myMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="message" />
</bean>
It throws the Exception
Exception in thread "main" org.springframework.context.NoSuchMessageException:
for code
new ClassPathXmlApplicationContext("springconfig.xml").getMessage("code");
Short answer - because that's the way they designed it.
As described in the reference guide:
When an ApplicationContext is loaded, it automatically searches for a MessageSource bean defined in the context. The bean must have the name messageSource. If such a bean is found, all calls to the preceding methods are delegated to the message source.... If the ApplicationContext cannot find any source for messages, an empty DelegatingMessageSource is instantiated in order to be able to accept calls to the methods defined above.
This is just a hard-coded value that is searched for. If you don't supply a bean with that name, you'll end up with a DelegatingMessageSource instance that won't resolve any of your messages, giving you a NoSuchMessageException.
Excerpt from the documentation:
When an ApplicationContext is loaded, it automatically searches for a MessageSource bean defined in the context. The bean must have the name messageSource. If such a bean is found, all calls to the preceding methods are delegated to the message source. If no message source is found, the ApplicationContext attempts to find a parent containing a bean with the same name. If it does, it uses that bean as the MessageSource. If the ApplicationContext cannot find any source for messages, an empty DelegatingMessageSource is instantiated in order to be able to accept calls to the methods defined above.
I came across below statement from Spring MVC documentation:
Upon initialization of a DispatcherServlet, Spring MVC looks for a
file named [servlet-name]-servlet.xml in the WEB-INF directory of
your web application and creates the beans defined there, overriding
the definitions of any beans defined with the same name in the global
scope.
Please help me in understanding this statement.
As global scope is used for portlet based applications, then why a developer wants to configure like this in normal Spring MVC applications?
I don't think the term "global scope" here means the global scope bean like singleton, prototype, request, session and global. I believe the global scope here means the scope of the bean context.
In Spring MVC, there are 2 scopes of bean can be defined. The first one is at servlet context level which I believe is what it meant by "global scope" in the above statement. The second one is at servlet level where the bean defined at this level will take priority when resolved by other bean in servlet level.
The beans in servlet level will be able to resolve beans in servlet context (global) level but not the other way round.
Spring MVC provides options to configure multiple Context based on number of DispacthServlet Configuration .
For example
Consider you have two modules with in your application and url pattern starts as module1/* and module2/* . And you want to keep two different context ( always declarations in *context.xml is private to the context ). So you would be creating two dispatchServlet and provide two different servletname and url pattern. Now you have two Spring context which has specific declartions and which is not visible to other. But still you may wanted to declare some application wide beans such as persistentManager / Logger instance and so. for those case you can keep seperate config as root-context.xml and keep the generic declartions in that root. Which is considered here as "GLOBAL SESSION" Now this Global Session beans scope can be different scope based on configuration. And the bean configured in Global Session ( root-context) can be overriden by specific-servlet for customization.
I like the answer provided here also Understanding contexts in Spring MVC
A typical Spring MVC application will have two contexts: the root context and the servlet context.
The root context is loaded by the ContextLoaderListener. The ContextLoaderListener loads the ApplicationContext and sets it as an attribute in the ServletContext. This makes it available to other Servlet, Filter, and XxxListener instances.
The servlet context is another ApplicationContext loaded by the DispatcherServlet when its init() method is called. Since the init() method of a Servlet is called after the contextInitialized() method of a ServletContextListener is called, the DispatcherServlet has access to the root context set as an attribute in the ContextLoaderListener and can therefore use the beans declared there.
[...] creates the beans defined there, overriding the definitions of any
beans defined with the same name in the global scope.
A bean definition is, for example,
<bean id="someBean" class="com.company.SomeBean">
<property name="someProp" value="some value" />
</bean>
Spring creates a BeanDefinition object for this and all other <bean> declarations (or #Bean annotation methods). When the ApplicationContext is refreshed, Spring generates actual instances from these bean definitions.
What the above quote from the documentation is saying is that if you have a <bean> declaration with the same name or id in both the root context and the servlet context, the servlet context one will overwrite the root context one.
I need to inject a object of a java class in spring controller through applicaionContext.xml. My controller will be ,
#Controller
public class SpringController{
private MyClass obj;
}
I know I can do it with #Autowired annotation.
Is this really good to create a object for a controller through applicaionContext.xml ? Also can I inject a object of a class in controller using the <property> tag inside a <bean> tag ?
Is this really possible ? or please forgive me if it is a stupid question.
I need to know the possible ways for how to inject a object of a class in Spring controller ?
You can of course use #Autowired annotation to autowire the relationships, which can reduce the need to define the properties and constructor arguments for the controller in your applicationContext.xml file. And also to add a dependency to a class, you don't need to modify the configuration files.
But it has some disadvantages too, like if you use #Autowired, there will not be any explicit documentation for the wiring details between Spring managed beans. And to know the relationships between the beans, you have to go through your managed beans. But, if you use configuration files to define the relationships, the relationship details can be found in one place.
You can inject an object of a class into your controller through your applicaionContext.xml as below:
Constructor based injection:
#Controller
public class SpringController{
private MyClass obj;
public SpringController(MyClass obj){
this.obj=obj;
}
}
<bean id="myClassImpl" class="x.y.z.MyClassImpl"></bean>
<bean id="springController" class="x.y.z.web.controllers.SpringController">
<constructor-arg ref="myClassImpl"></constructor-arg>
</bean>
Setter based injection:
#Controller
public class SpringController{
private MyClass obj;
public void setObj(MyClass obj){
this.obj=obj;
}
public MyClass getObj(){
return obj;
}
}
<bean id="myClassImpl" class="x.y.z.MyClassImpl"></bean>
<bean id="springController" class="x.y.z.web.controllers.SpringController">
<property name="obj" ref="myClassImpl"></property>
</bean>
If you want to inject an object in a controller and you particularly want to you use xml,then instead of component scanning of Controller you should create a bean of the controller class of singleton scope in the application context.
Your controller class need not be annotated with #Controller.
you then have to you extend some Controller also like AbstractCommandController, AbstractController, AbstractFormController, AbstractWizardFormController, BaseCommandController, CancellableFormController, MultiActionController SimpleFormController, UrlFilenameViewController
Now to inject a particular object you can use Either Constructor and Setter based injection.
or you can use Autowring by name or type to auto inject the object.
Make sure that you have also declared the bean of that object also in Application Context.
After a DispatcherServlet has received a request and has done its work to resolve locales, themes and suchlike, it then tries to resolve a Controller, using a HandlerMapping. When a Controller has been found to handle the request, the handleRequest method of the located Controller will be invoked; the located Controller is then responsible for handling the actual request and - if applicable - returning an appropriate ModelAndView.
Thats it.
Actually, injection with xml and annotation is same behind the scene. Xml is old fashion while annotations are newer.
Basically, there are 2 types of injection types.
byName
Autowiring by property name. Spring container looks at the properties
of the beans on which autowire attribute is set to byName in the XML
configuration file. It then tries to match and wire its properties
with the beans defined by the same names in the configuration file.
You can give explicit names to beans both with xml and annotation.
#Service("BeanName")
#Component("BeanName")
#Controller("BeanName")
<bean name="BeanName" class="someclass"></bean>
and inject beans by using #Qualifier annotation.
#Autowired
#Qualifier("BeanName")
and with xml
<bean id="MyBean2" class="MyBean2 class">
<property name="Property of MyBean2 which refers to injecting bean" ref="BeanName" />
</bean>
byType
Autowiring by property datatype. Spring container looks at the
properties of the beans on which autowire attribute is set to byType
in the XML configuration file. It then tries to match and wire a
property if its type matches with exactly one of the beans name in
configuration file. If more than one such beans exists, a fatal
exception is thrown.
Default auto wiring mode is byType, so spring will look for matching type in auto wiring. However, older versions of Spring has default behavior none on injection. If you want to inject byType using xml, you should tell spring contaioner explicitly.
For example MyBean2 has a reference to MyBean, by setting autowired attribute to byType it handles injection automatically.
<bean id="MyBean" class="MyBean class">
<property name="Property of MyBean2 which refers to injecting bean" ref="BeanName" />
</bean>
<bean id="MyBean2" class="MyBean2 class"
autowire="byType">
</bean>
It also depends on where the injection take place in your code. There are 2 types, setter getter injection and constructor injection.
Note : There is no difference in #Controller since they are already in spring context.
See also
Spring Beans Auto wiring
I ran into such problem. I was getting "Ambiguous mapping found". (I use xml configuration as well and i am injecting a bean into my controller)
Then looking at my console i realized that my controller was being instantiated twice.
In more detailed look i noticed that my annotation
#Controller(value = "aController")
(Note value = "aController")
was different from my xml configuration where i was instatiating the same controller with different bean id
<bean id="aControleRRRRR" class="package.ControllerClassName"
p:property-ref="beanToInject" />
(Note id="aControleRRRRR")
So in conclusion your #Controller name (value = "aController") needs to be exactly the same as the name you give in the XML configuration (id="aControleRRRRR"), so that Spring can manage to distinct that they refer to the same bean (instance)
Hope this helps
In an app I'm looking at I find this in the dispatcher xml:
<mvc:annotation-driven />
Am I correct that this is the Spring 3 way of defining handler mappings (url routes). One of the controllers in the app looks like this:
#Controller
#RequestMapping("/order")
#SessionAttributes(OrderController.ORDER)
public class OrderController
{
//...
I assume that the line
#RequestMapping("/order")
is the actual definition of the handler mapping for this url route.
Am I correct that the older way of defining this handler mapping would have been with one of:
BeanNameUrlHandlerMapping
SimpleUrlHandlerMapping
ControllerClassNameHandlerMapping
CommonsPathMapHandlerMapping
Yes. <mvc:annotation-driven /> is a convenience option for configuring Annotation-driven controllers. It configures special HandlerMappings and HandlerAdapters.
See the section in the Spring reference manual about <mvc:annotation-driven/> for a full list of what specifying this actually does.
As an alternative, you could always specify the DefaultAnnotationHandlerMapping, AnnotationMethodHandlerAdapter, etc. beans yourself manually.
I am using another application's service,since everything is already made and done.
My application is to use the interface class inside the application jar.
but something seem to be wrong when this code is called.
BeanFactory factory = new ClassPathXmlApplicationContext( "/Context-Controller.xml");
even if my Context-Controller.xml has this code
<context:component-scan base-package="com.package" />
My error.
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type
[com.package.ServiceIamUsing] is
defined: Unsatisfied dependency of
type [interface
com.package.ServiceIamUsing]: expected
at least 1 matching bean
this is how i autowired it on my applciation.
public class MyAppDao implements IMyAppDao {
#Autowired
#Qualifier("serviceIamUsing")
private ServiceIamUsing serviceIamUsing;
//More codes here
}
jay, try the following:
- eliminate the forward-slash "/"
- in your application context xml file, try to import the application context from your external jar file if it has any --> import resource="classpath*:/META-INF/spring/*.xml"
let me know if it does/doesn't work.
Is there, in fact, an instance of ServiceIamUsing in the jar that is also Spring-annotated (#Component, #Service, etc.)? It's possible this is the case, but I'd like to clarify.
If not, does the jar expose a Spring context file you can import, thus adding the additional beans to your context for autowiring?