Reputation: 13
I'm a beginner with spring and I am having problem with basic context init and I can't find any easy tutorial. So what I want to do is basic dependency injection and I have seen I only have to mark classes as @Componant or @Service and inject them directly within target fiels with @Autowired. For this I need to set properly my spring context (I'll need to build a web service + use JPA so I want to build a webapp)
I'm using JDK 1.8, maven 3.2.1
I want to do a basic dependency injection. => I want to inject a storeImpl instance there but this lead to a null pointer exception => this work perfectly without injection ( private Store storeImpl = new storeImpl(); )
package com.mycompany.app.controller;
@Controller
public class Controller
{
@Autowired
private Store storeImpl;
public void use()
{
storeImpl.get();
}
}
package com.mycompany.app.store;
public interface Store {
boolean get();
}
package com.mycompany.app.store;
@Component
public class StoreImpl implements Store {
@Override
public boolean get()
{
return true;
}
}
=> my main (I test my app with this for now):
public class App
{
public static void main(String[] args)
{
Controller controller = new Controller();
controller.use();
}
}
=> my pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>my-app</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>4.0.4.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.12.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.5.Final</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
=> my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/META-INF/spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
=> my spring-context.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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--<context:component-scan base-package="com.mycompany.app"/>-->
<context:component-scan base-package="com.mycompany.app">
<!-- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />-->
</context:component-scan>
</beans>
=> my project architecture
my-app
src
main
java
com.mycompagny.app
controller
Controller.java
store
Store.java
StoreImpl.java
app.java
ressources
META-INF
spring-context.xml
webapp
WEB-INF
web.xml
test
java
pom.xml
=> stacktrace of main()
Exception in thread "main" java.lang.NullPointerException
at com.mycompany.app.controller.Controller.run(Controller.java:25)
at com.mycompany.app.App.main(App.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
So my problem is my injected field (storeImpl) is null. And I think I miss something with context init ? Thanks again for your help and yourr grats answer
Upvotes: 1
Views: 1815
Reputation: 125242
You have a couple of problems (the following are apparent from your post).
1. Mixing Jars of different versions of Spring
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
Your pom is littered with jars from different spring versions (3.0.5, 4.0.1, 4.0.3 and 4.0.3). If you want trouble then that is the way to go. Always stick to a single version of a framework and don't mix jars from different versions. This isn't only for Spring but goes for every (multi jar) framework out-there.
Use the power of maven to help you, specify a property to hold the version of Spring you want to use and use that property.
<properties>
<spring.version>4.0.4.RELEASE</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
You might need to add a couple more depending on which dependencies Spring Data JPA is pulling in, if that has different versions.
2. Wrong location of annotations
@Service
public interface Store {
boolean get();
}
Annotations aren't inherited from interfaces so putting the annotation on the interface is pretty much useless. Spring has some hacks in place for specific annotations like @Transactional
but not for this. Move the annotation to the concrete implementation and don't put it on the interface.
@Service
public class Controller {
@Autowired
private Store storeImpl;
public void use() {
storeImpl.get();
}
}
I don't see anything here no @Controller
or what so ever also no @RequestMapping
so I'm not sure how you think that Spring detects and handles this class.
3. Duplication in your applicationcontext.
<context:annotation-config />
remove it from your context as that is already implied by the use of <context:component-scan />
.
<mvc:annotation-driven />
should be only in the xml file loaded by the DispatcherServlet
in your case (if you have posted actual code) that should be the dispatcher-servlet.xml
.
You have the same <context:component-scan .. />
element in both your xml files, this will ead to bean duplication. The files loaded by the ContextLoaderListener
should load anything but @Controllers
and the DispatcherServlet
should load only @Controllers
and ignore anything else. To accomplish that use include/exclude filters when component scanning.
Use this for the ContextLoaderListener
.
<context:component-scan base-package="com.mycompany.app">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype. Controller" />
<context:component-scan>
Use this for the DispatcherServlet
.
<context:component-scan base-package="com.mycompany.app" use-default-filters="false >
<context:include-filter type="annotation" expression="org.springframework.stereotype. Controller" />
<context:component-scan>
Also don't import the configuration of the DispatcherServlet
into the ContextLoaderListener
as this will again lead to bean duplication.
4. Using versioned XSD files.
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
Your schema locations use versioned XSD files, it is recommended to use versionless xsd files. This will ensure that the xsd version belonging to your current spring version will be loaded.
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
5. Conflicting versioning in web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
What is it you want version 2.3 of 2.4? You including servlet-api 2.5 in your pom and I would expect that you want version 3.0. Change your header (and maven dependency) accordingly.
Change your web.xml
header to the following.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
Update your pom.xml to include the servlet 3.0 spec.
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
Upvotes: 3
Reputation: 12890
You have to define the bean in your Spring xml
<bean id="storeImpl" class="com.mycompany.app.store.StoreImpl">
<!-- collaborators and configuration for this bean go here -->
</bean>
So, the @Autowired
bean will be injected by Spring framework
Upvotes: 1