本項目演示經典三層架構 (Controller-Service-Mapper) 整合,採用全註解驅動、Java Config 風格 XML、MyBatis 註解映射、聲明式事務、RESTful URL 設計,適合作為中小型項目脚手架參考。
src/ ├── main/ │ ├── java/com/mstf/ │ │ ├── controller/ # FormController, UserController │ │ ├── domain/ # User 實體 │ │ ├── mapper/ # UserMapper (MyBatis 註解) │ │ ├── service/ # UserService 接口 + UserServiceImpl │ │ └── config/ # (可選) Java Config 類 │ ├── resources/ │ │ ├── db.properties # 數據源參數 │ │ ├── log4j.properties # 日誌配置 │ │ └── mybatis-config.xml # MyBatis 全局配置 (可選) │ └── webapp/ │ ├── WEB-INF/ │ │ ├── jsp/ # loginForm, regeister, index, success │ │ ├── applicationContext.xml # 根容器:Service/Mapper/事務/數據源 │ │ ├── springmvc-config.xml # Web 容器:Controller/視圖解析/靜態資源 │ │ └── web.xml # 部署描述符 │ └── static/ # 靜態資源 (CSS/JS/圖片) └── test/
public class User implements Serializable {
private int id;
private String username, password, phone, email;
// 全參/無參構造器、getter/setter、toString
}
public interface UserMapper {
@Select("SELECT * FROM loginuser WHERE username=#{username} AND password=#{password}")
User findWithLoginnameAndPassword(@Param("username") String username, @Param("password") String password);
@Insert("INSERT INTO loginuser(username,password,phone,email) VALUES(#{username},#{password},#{phone},#{email})")
int regeist(@Param("username") String username, @Param("password") String password,
@Param("phone") String phone, @Param("email") String email);
}
public interface UserService {
User login(String username, String password);
int regeist(User user);
}
@Service("userService")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class UserServiceImpl implements UserService {
@Autowired private UserMapper userMapper;
@Transactional(readOnly = true)
@Override public User login(String username, String password) {
return userMapper.findWithLoginnameAndPassword(username, password);
}
@Override public int regeist(User user) {
return userMapper.regeist(user.getUsername(), user.getPassword(),
user.getPhone(), user.getEmail());
}
}
@Controller
public class FormController {
@RequestMapping(value = "/{formName}")
public String loginForm(@PathVariable String formName) {
return formName; // 動態跳轉 loginForm, regeister 等
}
}
@Controller
public class UserController {
@Autowired @Qualifier("userService") private UserService userService;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView login(String username, String password, ModelAndView mv, HttpSession session) {
User user = userService.login(username, password);
if (user != null) {
session.setAttribute("user", user);
mv.setView(new RedirectView("/Demo4/index")); // 重定向防重複提交
} else {
mv.addObject("message", "登錄名或密碼錯誤");
mv.setViewName("loginForm");
}
return mv;
}
@RequestMapping(value = "/regeist", method = RequestMethod.POST)
public String regeist(@ModelAttribute("user") User user) {
userService.regeist(user);
return "success";
}
}
<beans ... xmlns:mybatis="https://mybatis.org/schema/mybatis-spring"
xmlns:tx="https://www.springframework.org/schema/tx">
<mybatis:scan base-package="com.mstf.mapper" /> <!-- 掃描 Mapper 接口生成代理 -->
<context:component-scan base-package="com.mstf" /> <!-- 掃描 @Service/@Component -->
<context:property-override location="classpath:db.properties" /> <!-- 覆蓋屬性佔位符 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" /> <!-- 屬性來自 db.properties -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="dataSource" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
<tx:annotation-driven transaction-manager="txManager" /> <!-- 啟用 @Transactional -->
</beans>
<beans ... xmlns:mvc="https://www.springframework.org/schema/mvc">
<context:component-scan base-package="com.mstf.controller" /> <!-- 僅掃描 Controller -->
<mvc:annotation-driven /> <!-- 啟用 @RequestMapping/@RequestBody 等 -->
<mvc:default-servlet-handler /> <!-- 靜態資源交給容器默認 Servlet -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>
</filter>
<filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
<!-- loginForm.jsp -->
<form action="${pageContext.request.contextPath}/login" method="post">
<font color="red">${requestScope.message}</font>
登錄名: <input name="username" required maxlength="16">
密碼: <input type="password" name="password" required maxlength="16">
<input type="submit" value="登錄">
<a href="${pageContext.request.contextPath}/regeister">立即注冊</a>
</form>
<!-- index.jsp (登錄成功) -->
歡迎, ${sessionScope.user.username}!
RedirectView 到 /index此架構雖為 Spring 4 時代經典,但分層原則、註解驅動、雙容器隔離、事務邊界、RESTful 設計 至今仍是 Spring Boot 微服務架構的基因基石。理解透徹,遷移無障礙。