First step: Create an Maven Application
Create Maven web application in Eclipse using the maven-archetype-webapp archetype and do the following:
1. Change web.xml to the following:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" id="springmvc">
<display-name>Spring MVC Example</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list
>
</web-app>
This change basically says that our project requires an application server that supports the Servlet 3.0 Specification.
2. Add the following to pom.xml (right after <finalName>):
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
This change says that the project requires JDK 1.7 (or above) to compile, and the compiled class files are compatible with JVM 1.7.
3. Due to some bug in Eclipse Kepler Release, Eclipse can no longer change project facets based on changes to web.xml and pom.xml, so we have to do it manually:
- Close Eclipse.
- Use a text editor to open the file .setting/org.eclipse.wst.common.project.facet.core.xml under the project folder (i.e. <workspace>/<project>), and change the version of java and jst.web to 1.7 and 3.0 respectively:
<installed facet="java" version="1.7" /> <installed facet="jst.web" version="3.0 />
- Start Eclipse, Right click on the project and select Maven -> Update Project...
4. Remove the junit dependency (we'll use TestNG instead), the add the following dependencies to pom.xml:
- javax.servlet:javax.servlet-api:3.0.1 (set the scope of the dependency to provided)
- javax.servlet:jstl:1.2
-
Maven code for including these dependencies
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1%lt;/version> <scope>provided%lt;/scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
5. Add a folder src/main/java. This is where the Java code would go.
6. Right click on the project and select Maven -> Update Project...
Step 2: Convert this project to Spring web MVC
Make the following changes to the Maven web application we created in the previous step:
1. Add the dependency org.springframework:spring-webmvc:3.2.3.RELEASE
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.2.3.RELEASE</version> <!-- Current spring version 4.0.3 is not currently compatible with spring security so we use spring 3.2.3 --> </dependency>
At the time of writing the latest Spring Framework release is 4.0.3. The reason we use 3.2.3 instead is that later we'll add Spring Security which depends on Spring 3.2.3. If you don't plan to use Spring Security, feel free to use the latest Spring Framework; otherwise be aware that Spring Security usually lags a little behind the main framework release.
2. Add the following to web.xml:
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
The DispatcherServlet is loaded when the web application is started (notice the <load-on-startup>), and it is responsible for initializing the Spring servlet context, i.e. creating all the beans specified in a bean configuration file. The default name for the bean configuration file is /WEB-INF/<dispatcher-servlet-name>-servlet.xml. In our case, the name of the DispatcherServlet is springmvc (notice the <servlet-name>) so the bean configuration file is /WEB-INF/springmvc-servlet.xml. During operation DispatcherServlet also serves as the front controller that dispatches requests to the proper controller based on controller URL mapping.
3. Create the Spring bean configuration file /WEB-INF/springmvc-servlet.xml 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:mvc="http://www.springframework.org/schema/mvc"
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.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="springmvc.web" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
<mvc:annotation-driven/> enables request dispatching to @Controllers among some other things. <context:component-scan> finds all the annotated beans in springmvc.web and its sub-packages. The InternalResourceViewResolver maps a view name to a JSP file. It has two properties: prefix and suffix, so a view name like "home" would be mapped to a JSP file /WEB-INF/jsp/home.jsp.
And this is pretty much it - your web application is now a Spring MVC application. You can create a controller and a view like the following:
src/main/java/springmvc/web/controller/HomeController.java
package springmvc.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
@RequestMapping("/home.html")
public String index(){
return "home";
}
}
src/main/webapp/WEB-INF/jsp/home.jsp
<html>
<head><title>Spring MVC Example</title></head>
<body>
Welcome to Spring MVC.
</body>
</html>
And after that you can run the project and check if it works at the URL http://localhost:8080/springmvc/home.html .
Step 3: Add Hibernate
HibernateThere are many different ways to configure Hibernate with Spring - first of all you can use either JPA or Hibernate Session, and then you have various choices like using hibernate.cfg.xml or not using one, specifying database information in Spring or in Hibernate, creating a data source in Spring or getting one from JNDI, and so on. Here I'll describe the way that I think is the best and the simplest.
Make the following changes to the Spring MVC application we created in the previous step:
1. Add the following dependencies:
- org.hibernate:hibernate-entitymanager:4.2.11.Final
- org.springframework:spring-orm
- org.apache.tomcat:tomcat-jdbc
- org.postgresql:postgresql
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.2.11.Final</version> </dependency>Likewise include all dependencies.
tomcat-jdbc is for database connection pooling under Tomcat. postgresql is the database driver.
Note that if we don't specify the version of a dependency, it means the latest release version is used (i.e. not alpha, beta, RC etc.), with the exception of Spring related dependencies, for which you should choose the same version as spring-webmvc used in the previous step.
2. Add a file src/main/resources/META-INF/persistence.xml as follows:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="springmvc">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
</properties>
</persistence-unit>
</persistence>
This file provides the information about the JPA Entity Manager provider.3. Add the following to web.xml:
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<filter> <filter-name>jpaFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> </filter>
<filter-mapping> <filter-name>jpaFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
The ContextLoaderListener will load another Spring bean configuration file, and by default the file is /WEB-INF/applicationContext.xml. This bean configuration file is not absolutely necessary for this step - we could put all the Hibernate/database beans in springmvc-servlet.xml, but this file is required later when we add security to the application, and it is customary to put beans that are not related to the web tier in applicationContext.xml as oppose to <dispatcher-servlet-name>-servlet.xml, which is sometimes referred to as the servlet context.
The jpaFilter keeps the entity manager open until after the view is rendered; without it Hibernate won't be able to load lazy collections in JSP.
4. Create a file /WEB-INF/applicationContext.xml as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/springmvc" />
<property name="username" value="cysun" />
<property name="password" value="abcd" />
<property name="initialSize" value="1" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="springmvc" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<context:annotation-config />
<tx:annotation-driven />
<context:component-scan base-package="springmvc.model" />
</beans>
It adds four beans: a data source, an entity manager factory, a transaction manager, and an exception translator that translates various SQL/database exceptions into a Spring exception. <context:annotation-config> enables annotations like @Autowired and @PersistenceContext (which is like an @Autowired specifically for entity manager), <tx:annotation-driven> enables annotations like @Transactional, and <context:component-scan> looks for beans under the package springmvc.model and its sub-packages (presumably that's where your DAO classes are located).
And that is it. Now the application is a web application with both Spring and Hibernate. Here is some code you can use for testing:
src/main/scripts/springmvc-create.sql
create table users (
id integer primary key,
username varchar(255) unique not null,
password varchar(255) not null,
enabled boolean not null default 't'
);
insert into users values (1, 'admin', '1234', 't');
insert into users values (2, 'cysun', 'abcd', 't');
create table authorities (
username varchar(255) not null references users(username),
authority varchar(255)
);
insert into authorities values('admin', 'ROLE_ADMIN');
insert into authorities values('cysun', 'ROLE_USER');
src/main/scripts/springmvc-drop.sql
drop table authorities; drop table users;
src/main/java/springmvc/model/User.java
package springmvc.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue
private Integer id;
private String username;
private String password;
private boolean enabled = true;
public User(){
}
public Integer getId(){
return id;
}
public void setId( Integer id ){
this.id = id;
}
public String getUsername(){
return username;
}
public void setUsername( String username ){
this.username = username;
}
public String getPassword(){
return password;
}
public void setPassword( String password ){
this.password = password;
}
public boolean isEnabled(){
return enabled;
}
public void setEnabled( boolean enabled ){
this.enabled = enabled;
}
}
src/main/java/springmvc/model/dao/UserDao.java
package springmvc.model.dao;
import java.util.List;
import springmvc.model.User;
public interface UserDao {
User getUser( Integer id );
List<User> getUsers();
}
src/main/java/springmvc/model/dao/jpa/UserDaoImpl.java
package springmvc.model.dao.jpa;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import springmvc.model.User;
import springmvc.model.dao.UserDao;
@Repository
public class UserDaoImpl implements UserDao {
@PersistenceContext
private EntityManager entityManager;
@Override
public User getUser( Integer id ){
return entityManager.find( User.class, id );
}
@Override
public List<User> getUsers(){
return entityManager.createQuery( "from User order by id", User.class ).getResultList();
}
}
src/main/java/springmvc/web/controller/UserController.java
package springmvc.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import springmvc.model.dao.UserDao;
@Controller
public class UserController {
@Autowired
private UserDao userDao;
@RequestMapping("/users.html")
public String users( ModelMap models ){
models.put( "users", userDao.getUsers() );
return "users";
}
}
src/main/webapp/WEB-INF/jsp/users.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>\
<html>
<head><title>Users</title></head>
<body>
<table>
<tr><th>ID</th><th>Username</th><th>Enabled</th></tr>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.enabled}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
Step 4: Testing
Testing
There are two popular Java testing frameworks: JUnit and TestNG. There was a time when TestNG was considered technically superior, but these days these two are pretty much the same. In this section we will describe how to use TestNG to test database access.
1. Add the following dependencies:
- org.testng:testng
- org.springframework:spring-test
2. Create the following folders:
- src/test
- src/test/java
- src/test/resources
3. Copy src/main/webapp/WEB-INF/applicationContext.xml to src/test/resources. This bean configuration file will be used by the testing code to initialize the Spring framework and create beans for data sources, entity manager, and so on.
4. Create a test class as follows and place it under src/test/java/springmvc/model/dao:
package springmvc.model.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.Test;
@Test(groups = "UserDaoTest")
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class UserDaoTest extends AbstractTransactionalTestNGSpringContextTests {
@Autowired
UserDao userDao;
@Test
public void getUser()
{
assert userDao.getUser( 1 ).getUsername().equalsIgnoreCase( "admin" );
}
@Test
public void getUsers()
{
assert userDao.getUsers().size() == 2;
}
}
In Eclipse, right click the class and select Run As -> TestNG Test. If you have created the database using springmvc-create.sql as described in the previous step, both tests should pass.Step 5: Spring Security
Spring SecurityMake the following changes to the Spring and Hibernate application we created in the previous step:
1. Add the following dependencies to pom.xml:
- org.springframework.security:spring-security-taglibs
- org.springframework.security:spring-security-config
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
The DelegatingFilterProxy is the entry point of Spring Security, which utilizes a number of handler interceptors (Spring's equivalent of servlet filters) and method interceptors configured in applicationContext.xml to implement security. Note that the URL pattern /* ensures that all requests will pass through Spring Security.
3. Add the security namespace to applicationContext.xml so the beginning of the file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
And then add the following to applicationContext.xml:
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource" />
</security:authentication-provider>
</security:authentication-manager>
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/users.html" access="hasRole('ROLE_ADMIN')" />
</security:http>
Authentication is done using the users and authorities table we created in the Hibernate step. Here we simply let <jdbc-user-service> reference the data source and Spring Security will take care of the rest. The <http> element sets up a number of Spring Security filters for a web application environment, and <intercept-url> can be used to control access to certain URL patterns based on the roles of a user. And this is it. Your web application now have some basic security measures in place.
No comments:
Post a Comment