在实战一中,我们已经完成了实体层和Dao层的代码编写,这篇文章中,我们将会完成剩余的代码编写工作。
新建一个UserService类,作为我们的业务类。UserService负责将UserDao与LoginDao组织起来,完成用户的登录验证,以及登录日志的记录等操作。
@Service // 将UserService标注为一个服务层的Bean
public class UserService {
private UserDao userDao;
private LoginLogDao loginLogDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Autowired
public void setLoginLogDao(LoginLogDao loginLogDao) {
this.loginLogDao = loginLogDao;
}
public boolean hasMatchUser(String userName, String password) {
int matchCount = userDao.getmatchCount(userName, password);
return matchCount > 0;
}
public User findUserByUserName(String userName) {
return userDao.findUserByUserName(userName);
}
@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);
}
}
以上代码也十分简单,首先注解@Service将UserService类声明为一个Bean,@Autowired则自动装配UserDao 和LoginLogDao 两个Bean,注解@Transactional标注了该方法需要在事务环境中执行,而我们需要做的仅仅是在smart-context.xml中配置事务管理器。
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource" />
<!-- 以AOP的方式,让com.smart.service包下所有类标注了@Transactional的所有方法,都工作在事务环境中 -->
<aop:config proxy-target-class="true">
<aop:pointcut id="serviceMethod"
expression=" (execution(* com.smart.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>
完成以上工作后,其实我们代码已经可以运行了,只是缺少展现层,但是这没关系,我们可以先通过单元测试来验证我们的代码功能是否正确。
本次单元测试采用Junit,而对于Junit的依赖引用,已经在实战一的pom.xml中贴出来了。为了区别于正常功能的代码,Maven已经帮我们的工程结构做了很好的规划了,单元测试的代码,我们可以在src/test/java中编写。
新建一个UserServiceTest类,package为com.smart.service,这样与正常功能代码保持一致。
@RunWith(SpringJUnit4ClassRunner.class) // @RunWith就是一个运行器,使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。
@ContextConfiguration("classpath*:smart-context.xml")
public class UserServiceTest {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@Test
public void testHashMatchUser() {
// System.out.println("Test...hashMatchUser!");
boolean b1 = userService.hasMatchUser("admin", "123456");
boolean b2 = userService.hasMatchUser("admin", "4321");
assertTrue(b1);
assertTrue(!b2);
}
@Test
public void testFindUserByUserName() {
User user = userService.findUserByUserName("admin");
assertEquals(user.getUserName(), "admin");
}
@Test
public void testAddLoginLog() {
User user = userService.findUserByUserName("admin");
userService.loginSuccess(user);
}
}
OK。那么接下来就只要右键这个类,Run as->Juint Test执行一下,就能看到结果了。
以上这些工作做完之后,就该到了去实现展现层功能的时候了。废话不多说,马上来配置Spring MVC框架。
<!-- ①从类路径下加载Spring配置文件,classpath关键字特指类路径下加载 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:smart-context.xml</param-value>
</context-param>
<!-- 负责启动Spring容器的监听器,它将引用①处的上下文参数获得Spring配置文件的地址 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring MVC的主控Servlet -->
<servlet>
<servlet-name>smart</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Spring MVC处理的URL -->
<servlet-mapping>
<servlet-name>smart</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
在web.xml中,我们声明了一个smart的Servlet,则必须提供一个smart-servlet.xml的配置文件,但这个配置文件的路径我们无需声明,因为Spring会自动的将这些配置文件进行拼装。若是smart-servlet.xml的文件名不是采用Servlet名字-servlet的形式的话,则需要我们自己去主动声明路径。
在smart中,我们让.html结尾的URL请求都被截获,进而转由Spring MVC框架进行处理。请求被截获之后,会根据请求的URL查找到目标的处理控制器。
@Controller // 标注成为一个Spring MVC的Controller
public class LoginController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
// 负责处理/index.html的请求
@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());
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");
}
}
}
public class LoginCommand {
private String userName;
private String password;
//此处省略get() set()方法
}
上面说到配置了名为smart的Servlet之后,还需要配置smart-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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
">
<context:component-scan
base-package="com.smart.web" />
<!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:viewClass="org.springframework.web.servlet.view.JstlView"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="100" />
<!--<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/html/" p:suffix=".htm" p:order="1" /> -->
<!-- 静态资源访问处理 -->
<!-- 为了让正常的@RequestMapping不会被影响 -->
<mvc:annotation-driven/>
<!-- <mvc:default-servlet-handler />-->
<mvc:resources mapping="/html/**"
location="/html/" cache-period="31556926" />eans>
我们需要在smart-servlet中声明LoginController 控制器,以及配置视图解析器。完成MVC配置和控制器之后,就只剩JSP视图界面了,那么我们只能上代码。
login.jsp
<body>
<c:if test="${!empty error} }">//使用JSTLL标签对登录错误返回的信息进行处理
<font color="red"><c:out value="${error}" /></font>
</c:if>
<form action="<c:url value="/loginCheck.html" />" method="post">
用户名: <input type="text" name="userName"> <br> 密码: <input
type="password" name="password"> <br> <input
type="submit" value="登录"> <input type="reset" value="重置">
</form>
</body>
main.jsp
<body>${user.userName},欢迎您,您当前积分${user.credits};
</body>
OK。至此,我们已经完成了本次Spring MVC实战的所有代码工作了。剩下的就是运行此Web应用,我们采用在pom.xml文件配置Jetty插件的形式来运行。配置如下:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!-- Jetty插件 -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.5.v20170502</version>
<!-- 配置说明 -->
<configuration>
<httpConnector>
<port>8099</port>
</httpConnector>
<!-- 默认值是 0。大于 0 的数值表示开启,0 表示关闭,单位为秒。以配置数值为一个周期,自动的扫描文件检查其内容是否有变化,如果发现文件的内容被改变,则自动重新部署运用。 -->
<!-- <scanIntervalSeconds>2</scanIntervalSeconds> -->
<!-- 默认值为 automatic,它与大于 0 的 scanIntervalSeconds 节点一起作用,实现自动热部署的工作。设为
manual 的好处是,当你改变文件 内容并保存时,不会马上触发自动扫描和重部署的动作,你还可以继续的修改,直至你在 Console 或命令行中敲回车键(Enter)的时候才触发重新加载的动作。这样可以更加的方便调试修改。
命令行的方式是:mvn -Djetty.reload=manual jetty:run 。 -->
<reload>manual</reload>
<dumpOnStart>true</dumpOnStart>
<webApp>
<contextPath>/${project.artifactId}</contextPath>
<descriptor>${project.basedir}/src/main/webapp/WEB-INF/web.xml</descriptor>
</webApp>
<requestLog
implementation="org.eclipse.jetty.server.NCSARequestLog">
<filename>target/access-yyyy_mm_dd.log</filename>
<filenameDateFormat>yyyy_MM_dd</filenameDateFormat>
<logDateFormat>yyyy-MM-dd HH:mm:ss</logDateFormat>
<logTimeZone>GMT+8:00</logTimeZone>
<append>true</append>
<logServer>true</logServer>
<retainDays>120</retainDays>
<logCookies>true</logCookies>
</requestLog>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
<configuration>
<parallel>methods</parallel>
<threadCount>10</threadCount>
</configuration>
</plugin>
</plugins>
</build>
接下来,我们就对项目右键run as->maven build吧。
Goals处填写jetty:run。
至此,我们已经完成了这次Demo的编写了,如有不足的地方,希望大家多多指教~~~