初學java EE,雖然知道使用框架會使開發更加便捷高效,但是對於初學者來說,感到使用框架比較迷惑,尤其是各種jar包的引用、各種框架的配置、注解的使用等等。
最好的學習方法就是實踐,於是下載了一個現成的DEMO,通過簡單的修改先成功在自己電腦上跑起來,然後再逐個文件進行分析學習,最終才能從總體的高度上掌握框架的運行機制和配置方法,這樣才能在實際運用中靈活配置,不會再局限於示例框架中。
SSM框架Web程序的流程
上面鏈接總結的流程很好,但是該流程沒有加入DAO這一層,經過分析本項目源碼,流程應該是這樣的:
database–>Entity.java–>mapper.xml–>Mapper.java–>Dao.java–>DaoImpl.java–>Service.java–>ServiceImpl.java–>Controller.java–>login.jsp
根據上面所列,下面就按照這個流程進行設計。
使用MySQL數據庫:
CREATE DATABASE DB_TEST;
CREATE TABLE T_USER (
ID INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
USERNAME VARCHAR(16) NOT NULL,
PASSWORD VARCHAR(16) NOT NULL
);
INSERT INTO T_USER (USERNAME,PASSWORD) VALUES ("admin","123");
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int 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 interface UserMapper {
public User getUserByName(String username);
}
<mapper namespace="com.crm.mapper.UserMapper">
<resultMap type="com.crm.entity.User" id="userMap">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="password" column="password" />
</resultMap>
<!-- 此處select標簽的id值對應Mapper類中方法名 -->
<select id="getUserByName" parameterType="string" resultMap="userMap">
<!-- 此處寫sql語句,#{Mapper類傳入的參數} -->
select * from T_USER where username = #{username}
</select>
</mapper>
public interface UserDao {
//接口方法,通過用戶名得到User對象
public User findUserByName(String username);
}
@Repository對應數據訪問層Bean
@Repository(value=”userDao”)注解是告訴Spring,讓Spring創建一個名字叫“userDao”的UserDaoImpl實例。
當Service需要使用Spring創建的名為“userDao”的UserDaoImpl實例時, 就可以使用@Resource(name =”userDao”)注解告訴Spring,Spring把創建好的userDao注入給Service即可。
@Repository("userDao")
public class UserDaoImpl implements UserDao {
//注解引用Mapper類資源
@Resource(name = "userMapper")
private UserMapper userMapper;
/* 根據用戶名查找用戶對象 */
public User findUserByName(String username) {
//調用Mapper類從數據庫中得到User對象
return userMapper.getUserByName(username);
}
}
public interface UserService {
// 通過用戶名及密碼核查用戶登錄
public User checkLogin(String username, String password);
}
@Service對應的是業務層Bean
這樣當Action需要使用UserServiceImpl的的實例時,就可以由Spring創建好的”userService”注入給Action:在Action只需要聲明一個名字叫“userService”的變量來接收由Spring注入的”userService”即可
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
/* 登陸驗證 */
public User checkLogin(String username, String password) {
//根據用戶名實例化用戶對象
User user = userDao.findUserByName(username);
if (user != null && user.getPassword().equals(password)) {
return user;
}
return null;
}
}
@Controller對應表現層的Bean,也就是Action
注意:如果@Controller不指定其value【@Controller】,則默認的bean名字為這個類的類名首字母小寫,如果指定value【@Controller(value=”UserAction”)】或者【@Controller(“UserAction”)】,則使用value作為bean的名字。
@Scope(“prototype”)表示將Action的范圍聲明為原型
可以利用容器的scope=”prototype”來保證每一個請求有一個單獨的Action來處理,避免struts中Action的線程安全問題。
spring 默認scope 是單例模式(scope=”singleton”),這樣只會創建一個Action對象,每次訪問都是同一Action對象,數據不安全。
struts2 是要求每次次訪問都對應不同的Action,scope=”prototype” 可以保證當有請求的時候都創建一個Action對象
@RequestMapping(“/user”)
RequestMapping是一個用來處理請求地址映射的注解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。
本項目中,該Controller類RequestMapping(”user”) 該Controller類的login方法RequestMapping(”login”) 所以登錄頁面中用戶登錄的 action=”/ssm/user/login”
@Controller
@Scope(value="prototype")
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(User user,Model model) throws Exception {
user=userService.checkLogin(user.getUsername(), user.getPassword());
if(user!=null){
model.addAttribute(user);
return "welcome";// 路徑 WEB-INF/pages/welcome.jsp
}
return "fail";
}
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>用戶登錄</title>
</head>
<body>
<form action="/ssm/user/login" method="post" id="myform">
<input type="text" id="username" name="username"/>
<input type="password" id="password" name="password"/>
<input type="submit" value="提交" id="login" />
</form>
</body>
</html>
這個文件是Spring公共配置文件,下面分塊進行解析。
<bean id="property"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- classpath 即src根目錄 -->
<value>classpath:database.properties</value>
</list>
</property>
</bean>
數據庫連接池可以讓服務器預先與數據庫建立部分連接,存入內存中,以減少連接數據庫的耗時操作。 此處定義使用C3P0連接池的數據源。
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定連接數據庫的JDBC驅動 -->
<property name="driverClass">
<value>${mysql.driver_class}</value>
</property>
<!-- 連接數據庫所用的URL -->
<property name="jdbcUrl">
<value>${mysql.connection.url}</value>
</property>
<!-- 連接數據庫的用戶名 -->
<property name="user">
<value>${mysql.connection.username}</value>
</property>
<!-- 連接數據庫的密碼 -->
<property name="password">
<value>${mysql.connection.password}</value>
</property>
<!-- 設置數據庫連接池的最大連接數 -->
<property name="maxPoolSize">
<value>30</value>
</property>
<!-- 設置數據庫連接池的最小連接數 -->
<property name="minPoolSize">
<value>2</value>
</property>
<!-- 設置數據庫連接池的初始化連接數 -->
<property name="initialPoolSize">
<value>2</value>
</property>
<!-- 設置數據庫連接池的連接的最大空閒時間,單位為秒 -->
<property name="maxIdleTime">
<value>10</value>
</property>
</bean>
使用< context:component-scan>標簽後,spring可以自動去掃描base-pack下面或者子包下面的java文件,如果掃描到有@Component @Controller@Service等這些注解的類,則把這些類注冊為bean
<context:component-scan base-package="com.crm.*">
<!-- 不掃描注解為controller的類型 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
SqlSessionFactoryBean是一個工廠bean,它的作用就是解析配置(數據源、別名等)。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用上面已經配置好的數據庫連接池 -->
<property name="dataSource" ref="dataSource" />
<!-- Mybatis的配置文件路徑 -->
<property name="configLocation" value="classpath:mybatis.xml" />
<!-- mapper配置路徑 -->
<property name="mapperLocations">
<list>
<value>classpath:com/crm/mapper/*.xml</value>
</list>
</property>
</bean>
dao需要配置,如果數量大不適合一個個配置,需要使用mapper自動掃描來批量進行配置。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.crm.mapper" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
mysql.driver_class=com.mysql.jdbc.Driver
<!-- DB_TEST為新建的數據庫名 -->
mysql.connection.url=jdbc:mysql://localhost:3306/DB_TEST
<!-- 登錄數據庫的用戶名密碼 -->
mysql.connection.username=root
mysql.connection.password=root
此處省略,入門階段暫不考慮這麼高端的日志用法。
通過使用別名,可以縮短類名的長度,但是只能在配置文件中使用。
<configuration>
<!-- 別名 -->
<typeAliases>
<typeAlias type="com.crm.entity.User" alias="user" />
</typeAliases>
</configuration>
此配置文件注釋比較清楚,就不單獨解析了。
<display-name>crm</display-name>
<!--一個server下如果有多個項目,要對webAppRootKey進行配置,讓log能將日志寫到對應項目根目錄下-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>crm.root</param-value>
</context-param>
<!-- 由Sprng載入的Log4j配置文件位置 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<!-- Spring默認刷新Log4j配置文件的間隔,單位為millisecond -->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
<!-- Web 項目 Spring 加載 Log4j 的監聽 -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- 設置Spring容器加載配置文件路徑 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springContext/*Context.xml</param-value>
</context-param>
<!-- 加載Spring容器配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 防止內存洩漏的監聽器 -->
<listener>
<listener-class>
org.springframework.web.util.IntrospectorCleanupListener
</listener-class>
</listener>
<!-- 配置Springmvc核心控制器 -->
<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.xml</param-value>
</init-param>
<!-- 立馬啟動servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- servlet-mapping配置 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 解決工程編碼過濾器 -->
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 設置默認打開頁面列表 -->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
之前掃描專門忽略了Controller,在此處掃描。
例如在Controller類中常有類似
return welcome
這樣的語句其實完整的應該是
return WEB-INF/pages/welcome.jsp
<!-- 注解Controller掃描器 -->
<context:component-scan base-package="com.crm.controller"/>
<!-- 靜態資源訪問 -->
<mvc:resources location="/img/" mapping="/img/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/common/" mapping="/common/**"/>
<!-- 注解功能的默認配置,處理器和映射器 -->
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- 前後綴配置 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
至此基本內容已經全部完了,項目可以輕松跑起來了。
但是在學習過程中,發現這些框架真的是太強大了,不知道什麼時候才能掌握更多,目前僅僅是會用最基本的皮毛而已。
筆耕不綴,必有收獲;代碼不停,早晚能行。