Spring4.x快速構建應用入門(二)

主要的內容

  • 構建工具:Maven
  • 例子:用戶登陸校驗
  • 持久層:使用SpringJDBC和MyBatis
  • 業務層是設計Spring的聲明式事務
  • 展現層:SpringMvc
  • 開發工具:IDEA
  • 實例使用

一.包類的規劃

將項目劃分爲按照業務劃分可以劃分爲三層,分別時持久層,業務層,展現層,但是所有的層使用到的對象可能會共用,所以將所有的對象放在領域對象的包內,使得這些公共的類都存放一個包裏面。

單元測試的類包和程序的類包對應,但是方式再不同的的文件夾下,這樣的目的是方便程序的打包和發佈,因爲測試是在開發的時候使用,無需部署到包中。

包類結構圖如下:

應用的結果如下

二.配置文件規劃

集成Spring的系統可以將所有的配置信息統一到一個文件中,也可以放置到多個文件中,對於簡單的應用來說,由於配置信息少,使用一個配置文件足夠應付,但是隨着配置信息增加,一個配置文件就顯得捉襟見肘,對於團隊的協作開發也是很不利的。這裏我們的配置信息很少,所以一個配置文件足夠應付

配置文件

配置內容: flexible-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:p="http://www.springframework.org/schema/p"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	   http://www.springframework.org/schema/context
	   http://www.springframework.org/schema/context/spring-context-4.0.xsd
	   http://www.springframework.org/schema/tx
	   http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	   http://www.springframework.org/schema/aop
	   http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

	<!--掃描類,使得使用了註解的類自動生成bean,同時完成Bean的注入-->
	<context:component-scan base-package="com.flexible.dao"></context:component-scan>
	<context:component-scan base-package="com.flexible.web"></context:component-scan>
	<context:component-scan base-package="com.flexible.service"></context:component-scan>
	<!--使用dbcp作爲數據庫連接池-->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		  destroy-method="close"
		  p:driverClassName="com.mysql.jdbc.Driver"
		  p:url="jdbc:mysql://localhost:3306/springdb"
		  p:username="root"
		  p:password="root@123"
	></bean>
	<!--配置JDBC模板-->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
		  p:dataSource-ref="dataSource"
	></bean>
	<!--配置事務管理器-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		  p:dataSource-ref="dataSource"
	></bean>
	<!--通過AOP配置提供事務增強,讓service包下的Bean的所有方法都擁有事務-->
	<aop:config proxy-target-class="true">
		<aop:pointcut id="serviceMethod"
					  expression="(execution(* com.flexible.service..*(..))) and (@annotation(org.springframework.transaction.annotation.Transactional))"/>
		<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
	</aop:config>
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*"/>
		</tx:attributes>
	</tx:advice>
</beans>

flexible-servlet.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:p="http://www.springframework.org/schema/p"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	   http://www.springframework.org/schema/context
	   http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!--掃描web包,應用Sprig的註解-->
	<context:component-scan base-package="com.flexible.web"/>
	<!--配置試圖解析器,將ModelAndView及字符串解析爲具體的頁面-->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		  p:viewClass="org.springframework.web.servlet.view.JstlView"
		  p:prefix="/WEB-INF/pages/"
		  p:suffix=".jsp"></bean>
</beans>

web.xml

三.持久層

創建領域對象

public class User implements Serializable {
/**
 * 用戶id
 */
private Integer userId;
/**
 * 用戶名稱
 */
private String userName;
/**
 *用戶密碼
 */
private String password;
/**
 *簽到分數
 */
private Integer credits;
/**
 * 最新Ip
 */
private String lastIp;
/**
 * 最後訪問日期
 */
private Date lastVisit;
....
}

public class LoginLog {
/**
 * 登陸日誌id
 */
private Integer loginLogId;
/**
 * 用戶id
 */
private Integer userId;
/**
 * ip
 */
private String ip;
/**
 * 登陸日期
 */
private Date loginDate;
...
}

知識點總結: 領域對象可以細分爲四種: PO(Persistent Object):持久化對象,表示持久層的的數據結果 DTO(Data Transfer Object):數據傳輸對象,以前是EJB分佈式應用的粗粒度的數據事提,現在泛指展現層於服務之間的數據傳輸對象,可以堪稱和DO等效 DO等等於DTO VO(View Object):試圖對象,展示對象。

一.使用DAO 在DAO實現類中不需要我們手動得去鏈接和釋放鏈接,jdbcTemplate將這些都已經抽取出來了,我們需要做得就是需要在配置文件裏面配置 然後將jdbcTemplate使用註解@Autowired註解注入jdbcTemplate變量,所以我們必須先在配置文件聲明數據源和定義jdbcTemplate的bean

flexible-context.xml

	<!--使用dbcp作爲數據庫連接池-->
	<bean id="dataSource"
	//使用了DBCP數據源
	class="org.apache.commons.dbcp.BasicDataSource"
		  destroy-method="close"
		  p:driverClassName="com.mysql.jdbc.Driver"
		  p:url="jdbc:mysql://localhost:3306/springdb"
		  p:username="root"
		  p:password="root@123"
	></bean>
	<!--配置JDBC模板-->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
		  p:dataSource-ref="dataSource"
	></bean>

Java掉代碼 UserDao.java

[@Repository](https://my.oschina.net/u/3055569)
public class UserDao {
private JdbcTemplate jdbcTemplate;
private final static String MATCH_COUNT_SQL = " SELECT count(*) " +
        "FROM t_user  " +
        "WHERE user_name =? " +
        "AND password=? ";
private final static String UPDATE_LOGIN_INFO_SQL = " UPDATE t_user " +
        "SET " +
        "last_visit=?," +
        "last_ip=?," +
        "credits=?"+
        " WHERE user_id =?";
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}
/**
 * 根據用戶名查詢
 *
 * @param userName
 * @return
 */
public User getUserByUserName(String userName) {
    String searchSql = "SELECT user_id,user_name,credits " +
            "FROM t_user " +
            "WHERE user_name=?";
    final User user = new User();
    jdbcTemplate.query(searchSql, new Object[]{userName}, (resultSet) -> {
        user.setUserId(resultSet.getInt("user_id"));
        user.setUserName(userName);
        user.setCredits(resultSet.getInt("credits"));
    });
    return user == null ? new User() : user;
}
/**
 * 更新用戶登陸日誌
 *
 * @param user
 */
public Boolean updateLoginInfo(User user) {
    System.out.println(user.toString());
     Integer flag = jdbcTemplate.update(UPDATE_LOGIN_INFO_SQL, new Object[] { user.getLastVisit(),
             user.getLastIp(),user.getCredits(),user.getUserId()});
    return flag > 0 ? true : false;
}
/**
 * 根據賬號和密碼查找匹配的數量
 *
 * @param userName
 * @param password
 * @return
 */
public int getMatchCount(String userName, String password) {
    return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[]{userName, password}, Integer.class);
}
}

LoginLogDao.java

@Repository
public class LoginLogDao {
private JdbcTemplate jdbcTemplate;
//保存登陸日誌SQL
private final static String INSERT_LOGIN_LOG_SQL= "INSERT INTO t_login_log(user_id,ip,login_datetime) VALUES(?,?,?)";
public void insertLoginLog(LoginLog loginLog) {
    Object[] args = { loginLog.getUserId(), loginLog.getIp(),
            loginLog.getLoginDate() };
    jdbcTemplate.update(INSERT_LOGIN_LOG_SQL, args);
}
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}
}

二.使用Mapper

業務層

事務管理代碼沒有出現在程序的代碼中,但是我們需要以某種方式告訴Spring哪些業務需要工作在事務環境下以及事務的規則等內容,以便Spring根據這些信息自動爲目標業務添加事務管理的能力 flexible-context.xml

	<!--掃描事務層的代碼-->
	<context:component-scan base-package="com.flexible.service">	</context:component-scan>
	<!--通過AOP配置提供事務增強,讓service包下的Bean的所有方法都擁有事務-->
	<aop:config proxy-target-class="true">
		<aop:pointcut id="serviceMethod"
					  expression="(execution(* com.flexible.service..*(..))) and (@annotation(org.springframework.transaction.annotation.Transactional))"/>
		<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
	</aop:config>
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*"/>
		</tx:attributes>
	</tx:advice>

在Java代碼裏面我們需要做的就是將方法上面聲明一個註解 @Transactional,例如

@Transactional
public void loginSuccess(User user) {
    user.setCredits(5 + user.getCredits());
    LoginLog loginLog = new LoginLog();
    loginLog.setUserId(user.getUserId());
    loginLog.setIp(user.getLastIp());
    loginLog.setLoginDate(user.getLastVisit());
    userDao.updateLoginInfo(user);
    loginLogDao.insertLoginLog(loginLog);
}

單元測試

TestNG和JUnit相比有較大的改進,需要將TestNG依賴添加進pom.xml.Spring4.x的測試框架很好的整合了TestNG單元測試框架,通過繼承AbstractTransactionalTestNGSpringContextTests類來啓動測試運行器,@ContextConfiguration用於知道Spring的配置文件。

       <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>

單元測試代碼如下:

@ContextConfiguration("classpath*:/flexible-context.xml")
public class UserServiceTest extends AbstractTransactionalTestNGSpringContextTests {
	@Autowired
	UserService userService;
	@Test
	public void testHasMatchUser(){
		boolean b1 = userService.hasMatchUser("admin", "123456");
		boolean b2 = userService.hasMatchUser("admin", "1111");
		assertTrue(b1);
		assertTrue(!b2);
	}
}

展現層

Spring3.0提供了REST風格的MVC,是SpringMvc變得更加的輕便,易用。Spring4.x對mvc金行了全面的升級,支持了@CrossOrigi配置,Groovy Web集成,Gson,Jackson,Protobuf的HttpMessageConverter消息轉換器,使得SpringMvc的功能更加豐富,強大。

@RestController
public class LoginController {

	private UserService userService;

	@Autowired
	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	@RequestMapping(value = "/index.html")
	public String loginPage() {
		return "login";
	}

	@RequestMapping(value = "/loginCheck.html")
	public ModelAndView loginCheck(HttpServletRequest request, LoginCommand loginCommand) {
		boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(),
				loginCommand.getPassword());
		System.out.println("the request parameter is username {},password {}" + loginCommand.getUserName() + "," + loginCommand.getPassword());
		if (!isValidUser) {
			return new ModelAndView("login", "error", "用戶名或密碼錯誤。");
		} else {
			User user = userService.findUserByUserName(loginCommand
					.getUserName());
			user.setLastIp(request.getLocalAddr());
			user.setLastVisit(new Date());
			userService.loginSuccess(user);
			request.getSession().setAttribute("user", user);
			return new ModelAndView("main");
		}
	}
}

ModelAndView對象既包括試圖信息,又包括渲染試圖需要的模型信息,解析試圖的配置在flexible-servlet.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:p="http://www.springframework.org/schema/p"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	   http://www.springframework.org/schema/context
	   http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!--掃描web包,應用Sprig的註解-->
	<context:component-scan base-package="com.flexible.web"/>
	<!--配置試圖解析器,將ModelAndView及字符串解析爲具體的頁面-->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		  p:viewClass="org.springframework.web.servlet.view.JstlView"
		  p:prefix="/WEB-INF/pages/"
		  p:suffix=".jsp"></bean>
</beans>

項目地址: https://github.com/chenanddom/SpringSection1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章