Test Environment
Tomcat 9
Java 17
Workspace C:\www
C:\www>mvn archetype:generate -Dfilter=maven-archetype-webapp Choose archetype: 1: remote -> com.haoxuer.maven.archetype:maven-archetype-webapp 2: remote -> com.lodsve:lodsve-maven-archetype-webapp 3: remote -> org.apache.maven.archetypes:maven-archetype-webapp 4: remote -> org.bytesizebook.com.guide.boot:maven-archetype-webapp Choose a number or apply filter: : 3 Choose org.apache.maven.archetypes:maven-archetype-webapp version: 1: 1.0-alpha-1 2: 1.0-alpha-2 3: 1.0-alpha-3 4: 1.0-alpha-4 5: 1.0 6: 1.3 7: 1.4 Choose a number: 7: ↵ Define value for property 'groupId': net.java_school Define value for property 'artifactId': mybatisspring Define value for property 'version' 1.0-SNAPSHOT: : ↵ Define value for property 'package' net.java_school: : ↵ Confirm properties configuration: groupId: net.java_school artifactId: mybatisspring version: 1.0-SNAPSHOT package: net.java_school Y: : ↵
Maven creates the mybatisspring folder in C:\www through the build process. C:\www\mybatisspring is the root directory of your project. The src/main/webapp folder, just above WEB-INF, is the document base.
Spring MVC Test
Modify pom.xml like below.
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.java_school</groupId> <artifactId>mybatisspring</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>mybatisspring Maven Webapp</name> <url>http://localhhost:8080</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <spring.version>5.3.33</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Servlet JSP --> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>mybatisspring</finalName> <pluginManagement> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> <configuration> <filesets> <fileset> <directory>src/main/webapp/WEB-INF/classes</directory> </fileset> <fileset> <directory>src/main/webapp/WEB-INF/lib</directory> </fileset> </filesets> </configuration> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
src/main/webapp/WEB-INF/web.xml
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <display-name>MyBatis Spring</display-name> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>mybatisspring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mybatisspring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
src/main/webapp/WEB-INF/app-config.xml
app-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:resources mapping="/resources/**" location="/resources/" /> <mvc:annotation-driven /> <bean id="internalResourceViewResolver" 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> </beans>
Homepage
src/main/webapp/resources/css/styles.css
styles.css
html, body { margin: 0; padding: 0; background-color: #FFF; font-family: "Liberation Sans", Helvetica, sans-serif; } #wordcard { margin: 7px auto; padding: 1em; border: 3px solid grey; width: 600px; text-align: center; }
src/main/webapp/WEB-INF/views/index.jsp
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Spring MVC Test</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="Keywords" content="Spring MVC Test" /> <meta name="Description" content="This is web app for Spring MVC Test" /> <link href="./resources/css/styles.css" rel="stylesheet" /> </head> <body> <div id="wordcard"> <h1>Vocabulary</h1> <p> total number of words which (with rules for combining them) make up a language </p> </div> </body> </html>
The screen shows words and their meanings.
When the design is complete, it is common to start with database design, then implement JavaBeans, DAO, service, and controller in that order.
To experience Spring MVC more quickly, let's omit most of these and create an example that works only with controllers and views.
Controller
src/main/java/net/java_school/english/HomeController.java
package net.java_school.english; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class HomeController { @GetMapping("/") public String homepage() { return "index"; } }
For the homepage ("/") to be served through the controller, you should not declare any other servlets mapped to the homepage. However, the archetype has the index.jsp in the src/main/webapp folder. This file will respond to homepage requests. Delete the JSP file created by Maven when creating the archetype.
Register the controller
<bean id="homeController" class="net.java_school.english.HomeController" />
ROOT Application
Create a ROOT.xml file in the CATALINA_HOME/conf/Catalina/localhost folder.
CATALINA_HOME: Represents the root of your Tomcat installation, for example C:\apache-tomcat-9.0.87.
ROOT.xml
<?xml version="1.0" encoding="UTF-8"?> <Context docBase="C:/www/mybatisspring/src/main/webapp" reloadable="true"> </Context>
Build and deploy
Execute the following in the maven root directory:
mvn compile war:inplace
The war:inplace option creates bytecode in src/main/WEB-INF/classes and dependent libraries in src/main/WEB-INF/lib.
Test
Start Tomcat.
Visit http://localhost:8080
We figured out how Spring MVC works.
Next, practice how the controller delivers data to a view.
We won't use a database to keep the example simple.
Java Beans
src/main/java/net/java_school/english/WordCard.java
WordCard.java
package net.java_school.english; public class WordCard { private String word; private String definitions; public WordCard() {} public WordCard(String word, String definitions) { this.word = word; this.definitions = definitions; } public String getWord() { return word; } public void setWord(String word) { this.word = word; } public String getDefinitions() { return definitions; } public void setDefinitions(String definitions) { this.definitions = definitions; } }
DAO
src/main/java/net/java_school/english/WordCardDao.java
WordCardDao.java
package net.java_school.english; public class WordCardDao { private final String[][] words = { {"illegible", "difficult or impossible to read"}, {"vulnerable", "that is liable to be damaged; not protected against attack"}, {"abate", "(of winds, storms, floods, pain, etc) make or become less"}, {"clandestine", "secret; done secretly; kept secret"}, {"sojourn", "(make a) stay (with sb, at or in) for a time"}, {"defiance", "open disobedience or resistance; refusal to recognize authority; defyling"}, {"infraction", "breaking of a rule, law, etc; instance of this"} }; public WordCard selectOne() { int no = (int)(Math.random() * 7) + 1; WordCard wordCard = new WordCard(); wordCard.setWord(words[no - 1][0]); wordCard.setDefinitions(words[no - 1][1]); return wordCard; } }
<bean id="wordCardDao" class="net.java_school.english.WordCardDao" />
Service
src/main/java/net/java_school/english/WordCardService.java
WordCardService.java
package net.java_school.english; public class WordCardService { private WordCardDao wordCardDao; public WordCardService(WordCardDao wordCardDao) { this.wordCardDao = wordCardDao; } public WordCard getWordCard() { return wordCardDao.selectOne(); } }
<bean id="wordCardService" class="net.java_school.english.WordCardService"> <constructor-arg ref="wordCardDao" /> </bean>
Controller
package net.java_school.english; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; public class HomeController { private WordCardService wordCardService; public HomeController(WordCardService wordCardService) { this.wordCardService = wordCardService; } @GetMapping("/") public String homepage(Model model) { WordCard wordCard = wordCardService.getWordCard(); model.addAttribute("wordCard", wordCard); return "index"; } }
<bean id="homeController" class="net.java_school.english.HomeController"> <constructor-arg ref="wordCardService" /> </bean>
View
src/main/webapp/WEB-INF/views/index.jsp
Modify the content of the body element as below.
<div id="wordcard"> <h1>${wordCard.word }</h1> <p> ${wordCard.definitions } </p> </div>
Test
mvn clean compile war:inplace
http://localhost:8080
Using Database
Let's modify our app to use MyBatis-Spring.
MyBatis-Spring is an interlocking module for conveniently using MyBatis in Spring.
MyBatis is a SQL mapping persistence framework.
See https://mybatis.org/spring/getting-started.html
Modify Homepage
Add the following form after #wordcard in the index.jsp.
src/main/webapp/WEB-INF/views/index.jsp
<form id="new-form" method="post"> <input type="text" name="word" /> <input type="text" name="definitions" /> <input type="submit" value="Add" style="color: grey;" /> </form>
Add the following styles to your stylesheet.
src/main/webapp/resources/css/styles.css
#new-form { margin: 7px auto; padding-left: 2em; width: 600px; text-align: right; font-size: 0.8em; }
Connect to your SCOTT account and create the following table.
create table wordcard ( word varchar2(45), definitions varchar2(4000), constraint pk_wordcard primary key(word) );
Add Spring JDBC, Oracle JDBC driver, Apache DBCP, and MyBatis with mybatis-spring.
pom.xml
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6 --> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.4</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.11</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.1.0</version> </dependency>
commons-dbcp is a database connection pool provided by Apache.
Since ojdbc6.jar is a JDBC 4 driver, we need the DBCP 1.4 version.
See https://dlcdn.apache.org//commons/dbcp/
Add datasource and SqlSessionFactoryBean to the spring configuration.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" /> <property name="username" value="scott" /> <property name="password" value="tiger" /> <property name="maxActive" value="100" /> <property name="maxWait" value="1000" /> <property name="poolPreparedStatements" value="true" /> <property name="defaultAutoCommit" value="true" /> <property name="validationQuery" value=" SELECT 1 FROM DUAL" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean>
If you are using commons-dbcp2, you need to change the maxActive and maxWait parameters to maxTotal and maxWaitMillis.
Java Beans
src/main/java/net/java_school/english/WordCard.java
WordCard.java
package net.java_school.english; public class WordCard { private String word; private String definitions; public WordCard() {} public WordCard(String word, String definitions) { this.word = word; this.definitions = definitions; } public int getWord() { return word; } public void setWord(String word) { this.word = word; } public String getDefinitions() { return definitions; } public void setDefinitions(String definitions) { this.definitions = definitions; } }
Mapper Interface
It is better to put the mapper interface in a separate package from other Spring components.
src/main/java/net/java_school/mybatis/WordMapper.java
WordMapper.java
package net.java_school.mybatis; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; public interface WordMapper { @Insert("INSERT INTO wordcard VALUES (#{word}, #{definitions})") public void insert(@Param("word") String word, @Param("definitions") String definitions); @Select("select * from (select * from wordcard order by dbms_random.value) where rownum = 1") public WordCard selectOne(); }
Add the mapper interface to the spring configuration using MapperFactoryBean.
<bean id="wordMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="net.java_school.mybatis.WordMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
Service
Modify the WordCardService class to an interface.
src/main/java/net/java_school/english/WordCardService.java
WordCardService.java
package net.java_school.english; public interface WordCardService { public void add(String word, String definitions); public WordCard getWordCard(); }
src/main/java/net/java_school/english/WordCardServiceImpl.java
WordCardServiceImpl.java
package net.java_school.english; import net.java_school.mybatis.WordMapper; import org.springframework.stereotype.Service; @Service public class WordCardServiceImpl implements WordCardService { private WordMapper wordMapper; public WordCardServiceImpl(WordMapper wordMapper) { this.wordMapper = wordMapper; } @Override public void add(String content) { wordMapper.insert(content); } @Override public WordCard getWordCard() { return wordMapper.selectOne(); }
<bean id="wordCardService" class="net.java_school.english.WordCardServiceImpl"> <constructor-arg ref="wordMapper" /> </bean>
Controller
src/main/java/net/java_school/english/HomeController.java
HomeController.java
package net.java_school.english; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @Controller public class HomeController { private WordCardService wordCardService; public HomeController(WordCardService wordCardService) { this.wordCardService = wordCardService; } @GetMapping("/") public String homepage(Model model) { WordCard wordCard = wordCardService.getWordCard(); model.addAttribute("wordCard", wordCard); return "index"; } @PostMapping("/") public String add(String word, String definitions) { wordCardService.add(word, definitions); return "redirect:/"; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:resources mapping="/resources/**" location="/resources/" /> <mvc:annotation-driven /> <bean id="internalResourceViewResolver" 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> <!-- <bean id="wordCardDao" class="net.java_school.english.WordCardDao" /> --> <bean id="wordMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="net.java_school.mybatis.WordMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> <bean id="wordCardService" class="net.java_school.english.WordCardServiceImpl"> <constructor-arg ref="wordMapper" /> </bean> <bean id="homeController" class="net.java_school.english.HomeController"> <constructor-arg ref="wordCardService" /> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" /> <property name="username" value="scott" /> <property name="password" value="tiger" /> <property name="maxActive" value="100" /> <property name="maxWait" value="1000" /> <property name="poolPreparedStatements" value="true" /> <property name="defaultAutoCommit" value="true" /> <property name="validationQuery" value=" SELECT 1 FROM DUAL" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
Build and Deploy
mvn clean compile war:inplace
Test
http://localhost:8080
Logging
See https://mybatis.org/mybatis-3/logging.html
Add the apache commons-logging and log4j2 libraries to dependencies in pom.xml.
<!-- Logging --> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.19.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.19.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-jcl --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jcl</artifactId> <version>2.19.0</version> </dependency>
Create a log4j2 configuration file with the name log4j2.xml in the src/main/resources/ folder.
src/main/resources/log4j2.xml
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration> <Configuration> <Appenders> <File name="WordCard" fileName="WordCard.log" append="false"> <PatternLayout pattern="%t %-5p %c{2} - %m%n" /> </File> <Console name="STDOUT" target="SYSTEM_OUT"> <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" /> </Console> </Appenders> <Loggers> <Logger name="net.java_school" level="DEBUG"> <AppenderRef ref="WordCard" /> </Logger> <Root level="INFO"> <AppenderRef ref="STDOUT" /> </Root> </Loggers> </Configuration>
Run mvn compile war:inplace to compile and check the log message.
Mapper XML Files
MyBatis provides a way to separate queries into XML files. This file is the Mapper XML file.
So far, we have not created a MyBatis configuration file.
If you put the Mapper XML file and the mapper interface on the same classpath, you don't need a MyBatis configuration file.
See https://mybatis.org/spring/mappers.html#register
Create a Mapper XML file named WordMapper.xml in the src/main/resources/net/java_school/mybatis folder.
src/main/resources/net/java_school/mybatis/WordMapper.xml
WordMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.java_school.mybatis.WordMapper"> <insert id="insert"> insert into wordcard values (#{word}, #{definitions}) </insert> <select id="selectOne" resultType="net.java_school.english.WordCard"> select * from (select * from wordcard order by dbms_random.value) where rownum = 1 </select> </mapper>
Using the typeAlias element of the MyBatis configuration file can shorten resultType="net.java_school.english.WordCard" to resultType="WordCard" in the Mapper XML file.
See https://mybatis.org/mybatis-3/sqlmap-xml.html
<-- MyBatis XML Configuration --> <typeAlias type="net.java_school.english.WordCard" alias="WordCard"/>
Proceed without creating a MyBatis configuration file since only one Java Bean.
Remove queries from the WordMapper interface.
WordMapper.java
package net.java_school.mybatis; import net.java_school.english.WordCard; import org.apache.ibatis.annotations.Param; public interface WordMapper { public void insert(@Param("word") String word, @Param("definitions") String definitions); public WordCard selectOne(); }
Spring Auto Scan
You can use Spring's auto-scan to reduce configuration file content.
<context:component-scan /> scans Spring's components like Controllers, services, and Repositories.
<mybatis:scan /> scans mapper instances.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"> <mvc:resources mapping="/resources/**" location="/resources/" /> <mvc:annotation-driven /> <context:component-scan base-package="net.java_school.english" /> <mybatis:scan base-package="net.java_school.mybatis" /> <bean id="internalResourceViewResolver" 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> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" /> <property name="username" value="java" /> <property name="password" value="school" /> <property name="maxActive" value="100" /> <property name="maxWait" value="1000" /> <property name="poolPreparedStatements" value="true" /> <property name="defaultAutoCommit" value="true" /> <property name="validationQuery" value=" SELECT 1 FROM DUAL" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
<context:component-scan /> lets you to use @Autowired annotation.
HomeController.java
import org.springframework.beans.factory.annotation.Autowired; @Controller public class HomeController { @Autowired private WordCardService wordCardService; /* remove constructors public HomeController(WordCardService wordCardService) { this.wordCardSerivce = wordCardService; } */ //..omit.. }
WordCardServiceImpl.java
import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Autowired; @Service public class WordCardServiceImpl implements WordCardService { @Autowired private WordMapper wordMapper; /* reomve constructors public WordCardServiceImpl(WordMapper wordMapper) { this.wordMapper = wordMapper; } */ //..omit.. }
Java Based Configuration
Create the following java file.
AppConfig.java
package net.java_school.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class AppConfig implements WebMvcConfigurer { //TODO }
Complete the AppConfig.java file by referring to the following.
<mvc:annotation-driven />
⇩
@Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer {
<mvc:resources mapping="/resources/**" location="/resources/" cache-period="31556926" />
⇩
@Configuration @EnableWebMvc public class AppConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/resources/") .setCacheControl(CacheControl.maxAge(Duration.ofDays(365))); } }
<context:component-scan base-package="net.java_school.english" />
⇩
@Configuration @EnableWebMvc @ComponentScan("net.java_school.english") public class AppConfig implements WebMvcConfigurer {
<mybatis:scan base-package="net.java_school.mybatis" />
⇩
@Configuration @EnableWebMvc @ComponentScan("net.java_school.english") @MapperScan("net.java_school.mybatis") public class AppConfig implements WebMvcConfigurer {
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> </bean>
⇩
@Bean public ViewResolver configureViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); viewResolver.setViewClass(JstlView.class); return viewResolver; }
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" /> <property name="username" value="scott" /> <property name="password" value="tiger" /> <property name="maxActive" value="100" /> <property name="maxWait" value="1000" /> <property name="poolPreparedStatements" value="true" /> <property name="defaultAutoCommit" value="true" /> <property name="validationQuery" value=" SELECT 1 FROM DUAL" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean>
⇩
@Bean(destroyMethod = "close") public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:XE"); dataSource.setUsername("scott"); dataSource.setPassword("tiger"); dataSource.setMaxActive(100); dataSource.setMaxWait(1000); dataSource.setPoolPreparedStatements(true); dataSource.setDefaultAutoCommit(true); dataSource.setValidationQuery("SELECT 1 FROM DUAL"); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource()); return sessionFactory.getObject(); }
AppConfig.java
package net.java_school.config; import java.time.Duration; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.http.CacheControl; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration @EnableWebMvc @ComponentScan("net.java_school.english") @MapperScan("net.java_school.mybatis") public class AppConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/resources/") .setCacheControl(CacheControl.maxAge(Duration.ofDays(365))); } @Bean public ViewResolver configureViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); viewResolver.setViewClass(JstlView.class); return viewResolver; } @Bean(destroyMethod = "close") public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@localhost:1521:XE"); dataSource.setUsername("scott"); dataSource.setPassword("tiger"); dataSource.setMaxActive(100); dataSource.setMaxWait(1000); dataSource.setPoolPreparedStatements(true); dataSource.setDefaultAutoCommit(true); dataSource.setValidationQuery("SELECT 1 FROM DUAL"); return dataSource; } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource()); return sessionFactory.getObject(); } }
Create a Java-based configuration file to replace web.xml.
src/main/java/net/java_school/config/MyWebAppInitializer.java
MyWebAppInitializer.java
package net.java_school.config; import javax.servlet.Filter; import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { AppConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } @Override protected Filter[] getServletFilters() { return new Filter[] { new HiddenHttpMethodFilter() }; } }
You can also set it as below.
See https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet-context-hierarchy
package net.java_school.config; import javax.servlet.Filter; import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { AppConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } @Override protected Filter[] getServletFilters() { return new Filter[] { new HiddenHttpMethodFilter() }; } }
Because Java-based configuration cannot map error pages, you better leave a minimal web.xml file for later error page mapping.
See https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-customer-servlet-container-error-page
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> </web-app>References