I have been following the instructions here to implement a spring mvc handler interceptor using annotations and overriding DefaultAnnotationHandlerMapping.
However, my Interceptor is never called.
Can anyone see what I am doing wrong here?
I have implemented #Interceptors as in the blog post.
I've created one Interceptor:
#Component
public class InterceptorSpike extends HandlerInterceptorAdapter {
public InterceptorSpike() {
System.err.println("InterceptorSpike initialised");
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
throw new RuntimeException("Yay, intercepted!");
}
}
And a test controller:
#Controller
#Interceptors({InterceptorSpike.class})
public class TestController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void doSomething() {
System.err.println("I'm doing something, have I been intercepted??");
}
}
A sample of my handler (mostly the same as the blog post)
#Component
public class HandlerInterceptorAnnotationAwareHandlerMapping extends DefaultAnnotationHandlerMapping {
public HandlerInterceptorAnnotationAwareHandlerMapping() {
System.err.println("HandlerInterceptorAnnotationAwareHandlerMapping initialised");
}
...
[EDIT - Ignore, left for completeness. I have reverted these steps to use autowiring again]
I originally autowired this using #Component, but moved it into the app context as I've been attempting different fixes.
I added the order, and I'm not using <mvc:annotation-driven/>. These were suggested by some posts whilst I was looking for solutions.
[/EDIT]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">
<context:component-scan base-package="com.xxx"/>
<context:spring-configured/>
<!-- removed manual wiring -->
</beans>
And finally, here's my test :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/test-app-context.xml"})
public class ControllerInterceptorTest {
#Autowired
private ApplicationContext applicationContext;
#Autowired
private TestController controller;
#Test
public void shouldInterceptMethod() {
Map<String, DefaultAnnotationHandlerMapping> mappings = applicationContext.getBeansOfType(DefaultAnnotationHandlerMapping.class);
for (String key : mappings.keySet()) {
DefaultAnnotationHandlerMapping mapping = mappings.get(key);
System.out.println(String.format("key [%s], order [%s], value = %s", key, mapping.getOrder(), mapping.getClass().getCanonicalName()));
}
controller.doSomething();
fail("should have thrown exception");
}
}
I get this output:
HandlerInterceptorAnnotationAwareHandlerMapping initialised
InterceptorSpike initialised
key [handlerInterceptorAnnotationAwareHandlerMapping], order [2147483647], value = com.xxx.interceptors.HandlerInterceptorAnnotationAwareHandlerMapping
I'm doing something, have I been intercepted??
java.lang.AssertionError: should have thrown exception
at org.junit.Assert.fail(Assert.java:91)
...
getHandlerExecutionChain on my new DefaultAnnotationHandlerMapping is never called.
Thank you for reading this far - I know there is a lot here!
Can anyone see what I've missed or done wrong?
Thanks!
The issue might be in this line:
private TestController controller = new TestController();
You need to get the "controller handle" from the context and not initialize yourself.
Related
I have tennisCoach object created by Spring framework:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml") ;
Coach theCoach = context.getBean("tennisCoach", Coach.class);
Can't understand why I need #Autowired annotation in TennisCoach constructor in code below. It works fine with and without #Autowired annotation.
#Component
public class TennisCoach implements Coach {
private FortuneService fortuneService;
#Autowired
public TennisCoach(FortuneService theFortuneService) {
fortuneService = theFortuneService;
}
#Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
#Override
public String getDailyFortune() {
return fortuneService.getFortune();
}
}
UPD
Content of applicationContext.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.luv2code.springdemo"></context:component-scan>
</beans>
From #Autowired Javadoc:
If a class only declares a single constructor to begin with, it will always be used, even if not annotated.
Since Spring 4.3 you don’t need the #Autowired annotation as soon as you have the only constructor in your class.
Here #Autowired is used for constructor injection. TennisCoach has a dependency on FortuneService and it is injected through constructor. I'm not sure how you have configured beans in applicationContext.xml
TL/DR: The problem boils down to creating a custom Spring scope, injecting a prototype-like scoped bean into a singleton with proxyMode = ScopedProxyMode.TARGET_CLASS but still getting a singleton in the Java config version of the configuration (whereas it works fine with XML).
UPDATE: Problem solved, see answer.
I'm using jBehave to write BDD test scenarios for our Spring application. We recently thought that we need independence in executing test scenarios (meaning that test context has to be reset before each scenario) and found this article on the web that addresses exactly the issue we're dealing with.
The article advises creating a custom Spring Scenario scope, assigning it to the class that represents test context and injecting an AOP proxy instead of the context file.
I've coded everything in accordance with the article and it worked great, but the thing is we need it in terms of Java config, not XML, and when I converted all the changes to Java config, it stopped working - meaning the Map in StoryContext was not reset after each test scenario and contained values from the previous scenario.
My changes were as follows:
marked the ScenarioScope class with the #Component annotation:
#Component
public class ScenarioScope implements Scope {
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
#BeforeScenario
public void startScenario() {
cache.clear();
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return cache.putIfAbsent(name, objectFactory.getObject());
}
#Override
public Object remove(String name) {
return cache.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
#Override
public String getConversationId() {
return "scenario scope";
}
}
created a Spring configuration class to add the new scope:
#Configuration
public class SpringConfiguration {
#Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
annotated the StoryContext class with the #Component and #Scope annotations:
#Component
#Scope(value = "scenario", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class StoryContext {
private Map<String, Object> storyContext = new HashMap<>();
public void put(String key, Object value) {
storyContext.put(key,value);
}
public <T> T get(String key, Class<T> tClass) {
return (T) storyContext.get(key);
}
#PostConstruct
public void clearContext() {
storyContext.clear();
}
}
To my knowledge, the code above is analogous to the XML configuration, which was as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=" http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<context:component-scan base-package="foo"/>
<bean id="scenarioScope" class="foo.ScenarioScope"/>
<bean class="foo.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="scenario" value-ref="scenarioScope"/>
</map>
</property>
</bean>
<bean id="storyContext" class="foo.StoryContext" scope="scenario">
<aop:scoped-proxy/>
</bean>
</beans>
Can anyone please point me to why the Java config is not working as expected? I've spent some time researching stackoverflow but the majority of similar questions is solved by adding proxyMode = ScopedProxyMode.TARGET_CLASS to the #Scope annotation, which I did.
UPDATE: So I tried to gradually move from XML to Java config by commenting / decommenting corresponding lines in the files and figured out that the problem is in this part of the code:
<bean class="foo.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="scenario" value-ref="scenarioScope"/>
</map>
</property>
</bean>
When I replace it with
#Configuration
public class SpringConfiguration {
#Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
the StoryContext bean becomes a singleton. I tried doing it another way through registering a custom BeanFactoryPostProcessor and using the registerScope() method as described here, but it didn't work either.
I've managed to solve the problem, and the solution was trivial: the ScenarioScope instance in the SpringConfiguration class has to be managed by the Spring container rather than be created via the new() operator:
#Configuration
public class SpringConfiguration {
#Bean
public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", scenarioScope);
return configurer;
}
}
package com.mkyong.output;
IOutputGenerator.java
public interface IOutputGenerator
{
public void generateOutput();
}
package com.mkyong.output;
OutputHelper.java
#Component
public class OutputHelper {
#Autowired
IOutputGenerator outputGenerator;
public void generateOutput() {
outputGenerator.generateOutput();
}
/*//DI via setter method
public void setOutputGenerator(IOutputGenerator outputGenerator) {
this.outputGenerator = outputGenerator;
}*/
}
package com.mkyong.output.impl;
CsvOutputGenerator.java
#Component
public class CsvOutputGenerator implements IOutputGenerator {
public void generateOutput() {
System.out.println("This is Csv Output Generator");
}
}
SpringBeans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.mkyong" />
</beans>
i am getting this exception Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'OutputHelper' is defined
even though i have marked OutputHelper as component.
I have changed
OutputHelper output = (OutputHelper) context.getBean("OutputHelper");
to
OutputHelper output = (OutputHelper) context.getBean("outputHelper");
and it worked.
Hi i think you haven't added following in your Spring XML configuration
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
<mvc:annotation-driven/>
you need to see top exception and read the whole line.
i guess there have a exception is nested exception just like #Autowired xxxxxx,meas autowired fail.
i have notice this:
#Autowired
IOutputGenerator outputGenerator;
and
#Component
public class CsvOutputGenerator implements IOutputGenerator
so, in the default, class name is used to #Autowired,you can rewrite to
#Autowired
IOutputGenerator csvOutputGenerator;
notice:
"csvOutputGenerator" first letter is lowercase
the easier option would be to enable annotations in beans already registered in the application context, means that you can just use #Autowired instead of getting manually all beans with context.getBean()
just add this line to your SpringBeans.xml
<context:annotation-config>
if you really want to understand what you are doing reading this could help.
So, with Spring, I prefer xml over annotation. It's just a personal preference, I like having the xml docs unifying my spring config data.
Anyway, I'm working on a JUnit test case for database access. My first time using the spring-test library. I'm trying to use dependency injection to inject the StudentDao bean into this class below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration ({"classpath:/test-context.xml"})
public class StudentDaoTest extends TestCase {
private StudentDao studentDao;
public StudentDao getStudentDao() {
return studentDao;
}
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
#Test
#Transactional
public void test(){
//This ends up printing null, identifying the problem
if(studentDao == null){
System.out.println("Null");
}
Student student = new Student();
student.setFirstName("First");
studentDao.insertStudent(student);
}
}
The thing is, as you can guess from the comment, is this isn't working. The test-context.xml file starts up, and the other context file it imports also starts up, as I can see from the log, so it's not that the program can't find the file. Somehow the xml doc that I have is just not configuring the bean properly.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="data-context.xml"/>
<bean id="studentDaoTest" class="io.craigmiller160.schedule.persist.StudentDaoTest">
<property name="studentDao" ref="studentDao"/>
</bean>
I discovered that if I used the #Autowired annotation on studentDao it does work. The thing is, I don't use annotations anywhere else in the program, and I want to maintain consistency. Honestly, I would prefer to avoid using #ContextConfiguration too, but I don't think I'll be able to do that.
So, I'm looking for help making this injection work with just xml. I know, I'm being picky, but I like my consistency, as I said.
Thanks in advance.
PS. the full filepath of the files is:
StudentDaoTest: src/test/java/io/myname/schedule/persist/StudentDaoTest
test-context.xml: src/test/resources/test-context.xml
If you don't want to use Spring annotations why use them ?
public class MyTest {
private ConfigurableApplicationContext context;
#Before
public void initApplicationContext() {
context = new ClassPathXmlApplicationContext("...");
}
#After
public void closeApplicationContext() {
if (context != null) {
context.close();
context = null;
}
}
#Test
public void test() {
context.getBean(Object.class);
// ...
}
}
Inject application context and work with it as you like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration ({"classpath:/test-context.xml"})
public class StudentDaoTest extends TestCase {
#Autowired
private ApplicationContext ctx;
Actually the definition of the studentDaoTest bean has no effect on the test just because there is no such functional in the testing framework.
We have an application where we are trying to inject an empty java.util.HashSet into a member of type java.util.Set, in a class which itself is a #Component. Spring seems to inject a HashSet with one element of the containing type. Any idea why Spring doesn't just inject an empty set?
Set element class:
#Component
public class SetElement
{
private String value;
public String getValue()
{
return value;
}
}
Class that contains a Set as a member:
#Component
public class MyClassWithSet
{
#Autowired
private Set<SetElement> setOfElements;
protected void setStringSet(Set<SetElement> stringSet)
{
this.setOfElements = stringSet;
}
public Set<SetElement> getStringSet()
{
return Collections.unmodifiableSet(setOfElements);
}
}
Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<bean id="setOfElements" class="java.util.HashSet" />
<context:component-scan base-package="com.vikdor.db " />
</beans>
Sample test case to confirm the behavior
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations =
{ "classpath:META-INF/spring.xml" })
public class SpringSetTest
{
#Autowired
private MyClassWithSet myClassWithSet;
#Test
public void test()
{
assertNotNull(myClassWithSet);
assertNotNull(myClassWithSet.getStringSet());
assertTrue(myClassWithSet.getStringSet().isEmpty());
}
}
If you use #Autowired on a typed collection instance, then all beans in the application context that satisfy the type are injected:
It is also possible to provide all beans of a particular type from the
ApplicationContext by adding the annotation to a field or method that
expects an array of that type [...] The same applies for typed
collections:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
#Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-autowired-annotation
Thus, your single instance of SetElement is injected into the #Autowired Set<SetElement>. A possible solution would be to use a setter for the field. Alternatively, you could use the #Qualifier annotation or the #Resource annotation to refer to the bean by name.