1.基于spring完成的简单项目的项目结构
2.关于配置文件
a.Spring配置文件smart-config.xml
扫描dao层(可以单独使用一个配置文件配置dao层配置,与mybatis整合时将mybatis配置写入spring-dao配置,mybatis配置文件保留(内容为空),可能需要使用<context:property-placeholder>读取properties文件中的配置)
扫描service层(可以使用<context:exclude-filter>标签定制扫描规则)(可单独使用一个配置文件配置service层配置)
配置jdbc数据源(在与mybatis整合的时候,可以配置成c3p0数据源)
配置jdbc模板bean->jdbcTemplate(在与mybatis整合时,可以配置成mybatis的SqlSessionFactory)
配置事务管理(事务管理与事务增强及其切面配置可单独写成一个spring-tx配置)
配置事务增强
配置切面
(与mybatis整合时,需要配置mybatis扫描器(org.mybatis.spring.mapper.MapperScannerConfigurer),用于扫描dao层接口)
b.SpringMVC配置文件smart-servlet.xml
开启注解
扫描Controller(只扫描控制层,业务层以及持久层交给spring处理)
配置视图解析器(org.springframework.web.servlet.view.InternalResourceViewResolver)
可能需要:支持json的对象转换器(<mvc:message-converters>中配置StringHttpMessageConverter和MappingJackson2HttpMessageConverter);静态资源映射(js,css等)
c.Web项目配置文件web.xml
ContextLoaderListener监听器--spring的启动
spring配置文件路径配置
springMVC前端控制器(org.springframework.web.servlet.DispatcherServlet)
①Web项目的配置文件内容(web.xml):
- spring配置文件加载
<!--①从类路径下加载spring配置文件,classpath指类路径下加载-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:smart-config.xml</param-value>
</context-param>
<!--②负责启动spring容器的监听器,他将获得①的上下文参数获得spring的配置文件的地址-->
<!--该监听器在web容器启动时自动运行,并启动spring容器-->
<!--需要将log4j.properties放在类路径下,以便日志引擎生效-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- SpringMVC的配置
<!--配置springMVC--> <!--spring MVC的主控servlet--> <servlet><!--此处声明了一个servlet,springMVC也有一个配置文件,该配置文件名和此处定义的servlet名有一个契约,即采用(<servlet名-servlet.xml>)的形式--> <!--WEB-INF目录下必须提供一个smart-servlet.xml的配置文件,但这个文件无需在web.xml中使用contextConfigLocation声明--> <!--因为spring的servlet会自动将smart-servlet.xml文件和spring的其他配置文件进行拼装--> <servlet-name>smart</servlet-name> <!--此处的servlet-name指向smart-servlet.xml--> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--在加载spring容器的时候同时启动--> <load-on-startup>1</load-on-startup> </servlet> <!--SpringMVC通过一个servlet配置来处理来截取URL请求--> <!--springMVC处理的url--> <servlet-mapping> <servlet-name>smart</servlet-name> <!--真正的静态页面使用htm区分--> <url-pattern>*.html</url-pattern> </servlet-mapping>
②SpringMVC配置文件的配置文件(smart-servlet.xml)
- 扫描Controller
<!--扫描Controller类包-->
<context:component-scan base-package="com.smart.web"/>
- 配置视图解析器,这里用的解析器是IntervalResourceViewResolver
<!--配置视图解析器,将ModelAndView及字符串解析成具体的页面-->
<!--ModelAndView("login","error","用户名或密码错误!")
第一个参数是视图的逻辑名,
第二第三个参数是数据模型名称和数据模型对象,
存放在request域-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<!--通过prefix在视图前添加前缀,通过suffix在视图后添加后缀-->
<property name="prefix" value="/WEB-INF/jsps/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
③Spring配置文件(smart-config.xml)
- Dao层类的自动注入
- Service层类的自动注入
<!--1.扫描类包,将标注spring注解的类自动转化成Bean,使该包下的类的spring中的注解生效,同时完成bean的注入-->
<context:component-scan base-package="com.smart.dao"/>
<!--扫描service类包,应用spring的注解(@Service)配置-->
<context:component-scan base-package="com.smart.service"/>
- Jdbc数据源
<!--2.定义一个使用dbcp实现的数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url"
value="jdbc:mysql://localhost:3306/sampledb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC">
</property>
<property name="username" value="root">
</property>
<property name="password" value="123456"></property>
</bean>
- Jdbc模板bean
<!--3.定义jdbc模板bean,将2声明的数据源注入jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 配置事务管理
<!--4.配置事务管理器-->
<!--a.配置事务管理 DataSourceTransactionManager基于数据源的事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--dataSource链接上面声明的数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
- 配置事务增强
<!--b.配置事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
- 配置切面,通过aop配置事务增强,让com.smart.service包下的所有方法拥有事务
<!--c.配置切面,通过aop配置事务增强,让service层下的所有方法拥有事务-->
<aop:config proxy-target-class="true">
<!--切入点-->
<aop:pointcut id="serviceMethod" expression="(execution(* com.smart.service..*(..)))and(@annotation(org.springframework.transaction.annotation.Transactional))"></aop:pointcut>
<!--切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"></aop:advisor>
</aop:config>
- 配置事务注解
<!--配置事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
- 注入userService,在类中需要注入的成员变量上边添加注释@AutoWired
<!--注入userService-->
<bean id="userService" class="com.smart.service.UserService"></bean>
注解
1.@Autowired:可以使用 @Autowired 注解通过setter方法,构造函数或字段自动装配Bean。此外,它可以在一个特定的bean属性自动装配。
2.@Repository它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean
3.@Service:通常作用在业务层,但是目前该功能与 @Component 相同
4.@Transactional:声明事务
5.@Controller:通常作用在控制层,但是目前该功能与 @Component 相同
6.@Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。
3.代码
实体类
package com.smart.domain;
import java.util.Date;
//每次登录以后,记录一条登录日志
public class LoginLogger {
private int loginLogId;
private int userId;
private String ip;
private Date loginDate;
public void setLoginLogId(int loginLogId) {
this.loginLogId = loginLogId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
}
public int getLoginLogId() {
return loginLogId;
}
public int getUserId() {
return userId;
}
public String getIp() {
return ip;
}
public Date getLoginDate() {
return loginDate;
}
}
User.java
package com.smart.domain;
import java.util.Date;
import java.io.Serializable;
//领域对象一般要实现Serializable接口
public class User implements Serializable {
private int userId;
private String username;
private String password;
private int credits;
private String lastIp;
private Date lastVisit;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
", credits=" + credits +
", lastIp='" + lastIp + '\'' +
", lastVisit=" + lastVisit +
'}';
}
public void setPassword(String password) {
this.password = password;
}
public void setCredits(int credits) {
this.credits = credits;
}
public void setLastIp(String lastIp) {
this.lastIp = lastIp;
}
public void setLastVisit(Date lastVisit) {
this.lastVisit = lastVisit;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public int getCredits() {
return credits;
}
public String getLastIp() {
return lastIp;
}
public Date getLastVisit() {
return lastVisit;
}
}
Dao层
UserDao.java
package com.smart.dao;
import com.smart.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
@Repository//通过spring注解定义一个Dao
public class UserDao {
private JdbcTemplate jdbcTemplate;
private final static String MATCH_COUNT_SQL = "select count(*) from t_user where username=? and upassword=?";
private final static String UPDATE_LOGIN_LOG_SQL = "update t_user set last_visit=?,last_ip=?,credits=? where user_id=?";
private final static String FIND_USERNAME_SQL = "select *from t_user where username=?";
@Autowired//自动注入JdbcTemplate的bean
public void setJdbcTemp(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int getMacthCount(String username, String password) {
//queryForInt在spring3.2以后取消了
return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[]{username, password},Integer.class);
}
public User findUserByName(final String username) {
final User user = new User();
/*
* query三个参数:
* sql语句,占位符数组
* 查询结果的回调函数接口
* */
jdbcTemplate.query(FIND_USERNAME_SQL, new Object[]{username}, new RowCallbackHandler() {
//匿名内部类实现的回调函数
public void processRow(ResultSet rs) throws SQLException {
user.setUsername(username);
user.setUserId(rs.getInt("user_id"));
user.setCredits(rs.getInt("credits"));
}
});
return user;
}
public void updateLogInfo(User user) {
jdbcTemplate.update(UPDATE_LOGIN_LOG_SQL,new Object[]{user.getLastVisit(),user.getLastIp(),user.getCredits(),user.getUserId()});
}
}
LoginLogDao.java
package com.smart.dao;
import com.smart.domain.LoginLogger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class LoginLogDao {
private JdbcTemplate jdbcTemplate;
//保存登录日志SQL
private final static String INSERT_LOG_SQL="insert into t_login_log (user_id,ip,login_datetime)values(?,?,?)";
public void insertLoginLog(LoginLogger loginLogger){
Object[] args = {loginLogger.getUserId(),loginLogger.getIp(),loginLogger.getLoginDate()};
jdbcTemplate.update(INSERT_LOG_SQL,args);
}
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
}
service层
UserService.java
package com.smart.service;
import com.smart.dao.LoginLogDao;
import com.smart.dao.UserDao;
import com.smart.domain.LoginLogger;
import com.smart.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service//注解标注成一个服务层Bean
public class UserService {
private UserDao userDao;
private LoginLogDao loginLogDao;
/*判断用户名密码是否正确*/
public boolean hasMatchUser(String username,String password){
return userDao.getMacthCount(username,password)>0;
}
/*判断用户名是否存在*/
public User findUserByName(String username){
return userDao.findUserByName(username);
}
/**
* 登录成功后的操作
* */
@Transactional
public void loginSuccess(User user){
user.setCredits(5+ user.getCredits());//加5积分
LoginLogger loginLogger = new LoginLogger();
loginLogger.setUserId(user.getUserId());
loginLogger.setIp(user.getLastIp());
loginLogger.setLoginDate(user.getLastVisit());
userDao.updateLogInfo(user);
loginLogDao.insertLoginLog(loginLogger);
}
public UserDao getUserDao() {
return userDao;
}
@Autowired//注入DAO层的Bean
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public LoginLogDao getLoginLogDao() {
return loginLogDao;
}
@Autowired//注入DAO层的Bean
public void setLoginLogDao(LoginLogDao loginLogDao) {
this.loginLogDao = loginLogDao;
}
}
controller层
LoginController.java
package com.smart.web;
import com.smart.domain.User;
import com.smart.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Controller//标注成Spring MVC控制器
public class LoginController {
@Autowired
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
//处理/index.html的请求
@RequestMapping(value="/index.html")
public String login(){
return "login";
}
//处理/loginCheck.html的请求
@RequestMapping(value = "/loginCheck.html")
public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){
// System.out.print(loginCommand);
boolean isValidUser = userService.hasMatchUser(loginCommand.getUsername(),loginCommand.getPassword());
if(!isValidUser){
//若无匹配用户
return new ModelAndView("login","error","用户名或密码错误!");
}else{
User u = userService.findUserByName(loginCommand.getUsername());
u.setLastIp(request.getLocalAddr());
u.setLastVisit(new Date());
userService.loginSuccess(u);
request.getSession().setAttribute("user",u);
return new ModelAndView("main");
}
}
}
LoginCommand.java//登陆表单
package com.smart.web;
public class LoginCommand {
private String username;
private String password;
@Override
public String toString() {
return "LoginCommand{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
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;
}
}
页面
login.jsp
<body>
<c:if test="${!empty error}">
<font color="red"><c:out value="${error}"/></font>
</c:if>
<form action="<c:url value="/loginCheck.html"/>" method="post">
username:<input name="username" type="text"/><br/>
password:<input name="password" type="text"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
main.jsp
<body>
hello,${user.username},You are success!This is main page.Your credits is ${user.credits}.
</body>