基于SSM框架的Web网站项目

您所在的位置:网站首页 网站框架怎么写 基于SSM框架的Web网站项目

基于SSM框架的Web网站项目

2024-01-06 14:05| 来源: 网络整理| 查看: 265

我将手把手教大家完成一个基于SSM框架的web项目,实现注册、登录功能 注意:该项目需要的基础知识为Java/H5/CSS3/JS/JSON/AJAX/Servlet/JSP/SSM 一、 搭建开发环境(准备阶段) 安装JDK(JDK安装过程自行百度),目前我用的JDK版本是1.8 安装Eclipse(用MyEclipse也是可以的,或者IDEA),只要能装Tomcat插件即可安装Tomcat 8.5,下载地址,顺便啰嗦一句,7.0以上版本就已经不需要配置环境变量了,当然你也可以为tomcat配置环境变量。安装mysql数据库(我的数据库版本是5.5) 二、需求分析

这里我们的需求分析比较简单,我们想要做一个网站的注册登录功能,这时候我们就会去想数据库需要哪些表呢?表里需要哪些字段呢?这里我们设计的表略简单,目的就是为了方便新手入门理解。注册登录一个表足以,我们创建一个名为web的数据库,然后新建一个用户user表,表里面的字段和内容如下:

usernamepasswordxxxf123456xxxxf123456方大大123456测试员123456

这里,我也给出创建数据库和表的SQL语句:

CREATE DATABASE web; USE web; CREATE TABLE USER( username VARCHAR(20) NOT NULL PRIMARY KEY COMMENT '账号', PASSWORD VARCHAR(20) NOT NULL COMMENT '密码' )ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO USER(username,PASSWORD) VALUES ('xxxf','123456'); INSERT INTO USER(username,PASSWORD) VALUES ('xxxxf','123456'); INSERT INTO USER(username,PASSWORD) VALUES ('方大大','123456'); INSERT INTO USER(username,PASSWORD) VALUES ('测试员','123456');

这里讲一下我们的业务逻辑,注册时会检查数据库有无此账号名,若有注册失败,若无则注册成功;登录时,检查数据库账号密码是否正确,账号或密码错误都会登录失败,并且前端会给返回提示。需求分析就这么多,还是蛮easy吧!

三、创建Web项目 打开Eclipse,选择File ->New->Dynamic Web Project ,新建名为PersonalWebSite的项目,一直选择next,最后把web.xml勾选上。 这里写图片描述 这里写图片描述

包结构 这里写图片描述 这里写图片描述 这里简单解释一下src下每个包的作用: (1) POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。在POJO层我们创建的类,属性用private修饰,目的是为了封装属性,然后用getter&setter方法去获得或修改属性值。

(2) DAO(Data Access Object)是一个数据访问接口,数据访问:顾名思义就是与数据库打交道。夹在业务逻辑与数据库资源中间。dao我们放了很多方法接口,在mapper层使用xml配置实现SQL语句的填充,这是使用mybatis的一贯做法。

(3)Service层(biz):业务层 控制业务

Service层主要负责业务模块的逻辑应用设计。和DAO层一样都是先设计接口,再创建要实现的类,然后在配置文件中进行配置其实现的关联。接下来就可以在service层调用接口进行业务逻辑应用的处理。 封装Service层的业务逻辑有利于业务逻辑的独立性和重复利用性。

(4)ServiceImpl:业务层的实现层 service层只写了接口方法,并没有去真正去实现方法,在impl层我们会把具体实现方法写出来

(5)Controller层: 控制层 控制业务逻辑

Controller层负责具体的业务模块流程的控制,controller层主要调用Service层里面的接口控制具体的业务流程,控制的配置也需要在配置文件中进行。

(6)config层:配置层 一般来说,配置层在xml中实现也可,这里我用了java代码来实现SSM初始化配置,有三个类需要我们去关注,WebAPPInitialzer.java(配置类),RootConfig.java(Spring IoC上下文配置)、WebConfig.java(配置DispatchServlet上下文)

(7)mybatis层:使用xml实现mybatis配置

四、SSM框架环境搭建

这里我使用的是全注解方式完成项目

首先我们需要做的是,把项目所需的jar包导入到你项目的WebContent/WEB-INF/lib或者是WebRoot/WEB-INF/lib下,这里我们的所依赖的JAR包有很多个,所以为了方便大家找到所需要的资源我将项目放到了github上,你只要访问我的Git仓库(戳这里),在WebContent/WEB-INF/lib下复制所有的jar包到你本地代码同样的目录下,再buildpath一下就可以使用啦!顺便提一下,这里面有很多jar包该项目并没有用到,为什么不删除呢?答案是:我比较懒,不想花时间去挑选JAR包。如果你用maven去构建项目,只需要在pom.xml把所需要的jar包信息配置上也可以达到相同的效果。

1.通过继承AbstractAnnotationConfigDispatcherServletInitializer(这个类名字巨长,反正我是记不住)去配置其他内容,因此首先来配置WebAPPInitialzer,如代码所示:

package com.xxxxf.config; import javax.servlet.MultipartConfigElement; import javax.servlet.ServletRegistration.Dynamic; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; /** *WebAPPInitialzer.java配置类 */ public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { // Spring IoC环境配置 @Override protected Class[] getRootConfigClasses() { // 配置Spring IoC资源 return new Class[] { RootConfig.class }; } // DispatcherServlet环境配置 @Override protected Class[] getServletConfigClasses() { // 加载Java配置类 return new Class[] { WebConfig.class }; } // DispatchServlet拦截请求配置 @Override protected String[] getServletMappings() { return new String[] { "*.do" };//拦截所有以.do结尾的请求 } /** * @param dynamic * Servlet上传文件配置. */ @Override protected void customizeRegistration(Dynamic dynamic) { // 配置上传文件路径 String filepath = "e:/mvc/uploads"; // 5MB Long singleMax = (long) (5 * Math.pow(2, 20)); // 10MB Long totalMax = (long) (10 * Math.pow(2, 20)); // 设置上传文件配置 dynamic.setMultipartConfig(new MultipartConfigElement(filepath, singleMax, totalMax, 0)); } }

2.上面代码重写的三个方法就可以配置Web工程中的Spring IoC资源和DispatchServlet的配置内容,然后是配置Spring IoC容器,配置类RootConfig,代码如下:

package com.xxxxf.config; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; import redis.clients.jedis.JedisPoolConfig; @Configuration //定义Spring 扫描的包 @ComponentScan(value= "com.*", includeFilters= {@Filter(type = FilterType.ANNOTATION, value ={Service.class})}) //使用事务驱动管理器 @EnableTransactionManagement //实现接口TransactionManagementConfigurer,这样可以配置注解驱动事务 public class RootConfig implements TransactionManagementConfigurer { private DataSource dataSource = null; /** * 配置数据库. * @return 数据连接池 */ @Bean(name = "dataSource") public DataSource initDataSource() { if (dataSource != null) { return dataSource; } Properties props = new Properties(); props.setProperty("driverClassName", "com.mysql.jdbc.Driver"); props.setProperty("url", "jdbc:mysql://localhost:3306/web?useUnicode=true&characterEncoding=UTF-8"); props.setProperty("username", "root"); props.setProperty("password", "123456"); props.setProperty("maxActive", "200"); props.setProperty("maxIdle", "20"); props.setProperty("maxWait", "30000"); try { dataSource = BasicDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } System.out.println("连接成功----------"); return dataSource; } /*** * 配置SqlSessionFactoryBean * @return SqlSessionFactoryBean */ @Bean(name="sqlSessionFactory") public SqlSessionFactoryBean initSqlSessionFactory() { SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); sqlSessionFactory.setDataSource(initDataSource()); //配置MyBatis配置文件 Resource resource = new ClassPathResource("mybatis/mybatis-config.xml"); sqlSessionFactory.setConfigLocation(resource); return sqlSessionFactory; } /*** * 通过自动扫描,发现MyBatis Mapper接口 * @return Mapper扫描器 */ @Bean public MapperScannerConfigurer initMapperScannerConfigurer() { MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.*"); msc.setSqlSessionFactoryBeanName("sqlSessionFactory"); msc.setAnnotationClass(Repository.class); return msc; } /** * 实现接口方法,注册注解事务,当@Transactional 使用的时候产生数据库事务 */ @Override @Bean(name="annotationDrivenTransactionManager") public PlatformTransactionManager annotationDrivenTransactionManager() { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(initDataSource()); return transactionManager; } }

3.有了Spring IoC容器后,还需要配置DispatchServlet上下文,完成这个任务的便是WebConfig这个类,代码如下:

package com.xxxxf.config; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration //定义Spring MVC扫描的包 @ComponentScan(value="com.*", includeFilters= {@Filter(type = FilterType.ANNOTATION, value = Controller.class)}) //启动Spring MVC配置 @EnableWebMvc public class WebConfig extends AsyncConfigurerSupport { /*** * 通过注解 @Bean 初始化视图解析器 * @return ViewResolver 视图解析器 */ @Bean(name="internalResourceViewResolver") public ViewResolver initViewResolver() { InternalResourceViewResolver viewResolver =new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } /** * 初始化RequestMappingHandlerAdapter,并加载Http的Json转换器 * @return RequestMappingHandlerAdapter 对象 */ @Bean(name="requestMappingHandlerAdapter") public HandlerAdapter initRequestMappingHandlerAdapter() { //创建RequestMappingHandlerAdapter适配器 RequestMappingHandlerAdapter rmhd = new RequestMappingHandlerAdapter(); //HTTP JSON转换器 MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); //MappingJackson2HttpMessageConverter接收JSON类型消息的转换 MediaType mediaType = MediaType.APPLICATION_JSON_UTF8; List mediaTypes = new ArrayList(); mediaTypes.add(mediaType); //加入转换器的支持类型 jsonConverter.setSupportedMediaTypes(mediaTypes); //往适配器加入json转换器 rmhd.getMessageConverters().add(jsonConverter); return rmhd; } /** * 异步任务处理 * */ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(5); taskExecutor.setMaxPoolSize(10); taskExecutor.setQueueCapacity(200); taskExecutor.initialize(); return taskExecutor; } }

4.通过上面三个类就搭建好了Spring MVC和Spring开发环境,但是没有对MyBatis进行配置,使用/mybatis/mabatis-config.xml进行配置,源码也很简单:

DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> 到此我们的SSM环境也就搭建好了,现在就要去实现业务的代码了 五、实现需求代码

1.pojo层的User类,属性对应数据库表的每个字段,并且我们使用驼峰规则命名属性(这里很重要,字段userName和UserName在封装时差别很大,会映射不到mapper层的xml上),我们一贯的做法会让pojo序列化,这里虽然没有并发的情况,但是这是一种良好的做法。代码如下:

package com.xxxxf.pojo; import java.io.Serializable; /** *@author xxxxf *2018年7月10日 */ public class User implements Serializable{ private static final long serialVersionUID = 89448952; private String userName; private String passWord; /** * getter && setter方法 */ 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; } /** * toString方法 */ @Override public String toString() { return "user [userName=" + userName + ", passWord=" + passWord + "]"; } }

2.dao层中的UserDao类,这里实现我们业务和数据库直接交互的很多接口,@Repository的注解目的是持久化数据层。代码如下:

package com.xxxxf.dao; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import com.xxxxf.pojo.User; /** *@author xxxxf *2018年7月10日 */ @Repository public interface UserDao { /** * 查询用户信息 */ public String getUser(@Param("userName") String userName,@Param("passWord") String passWord); /** * 增加用户 */ public int addUser(User user); /** * 按主键查询用户 */ public String getUserByUserName(String userName); }

编写完dao层以后我们需要在mapper层编写User.xml实现DAO层接口方法,它相当于Dao的实现层(你可以这么理解,但是本质上有很大区别的),在User.xml我们是写了很多SQL语句与MySQL数据库进行交互,至于怎么去写SQL,举个例子: select语句的返回值是不确定的,insert,update语句的返回值总是int(自己去思考是为什么?),所以Mybatis封装了这两种SQL语句的resultType,程序员就不用去写,这几种SQL语句参数类型也是根据方法里面的参数而决定的,两个或两个以上参数就需要用到@Param注解,MyBatis的语法我就不多赘述,有地方不理解或不清楚的自行百度,user.xml具体实现代码如下:

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> select username as userName,password as passWord from user where username=#{userName} and password=#{passWord} select username as userName from user where username=#{userName} insert into user(username,password) value(#{userName},#{passWord})

3.service层中的UserService类存放几个和用户相关的接口,这是是对用户而言的业务,这里有一种思想就是:用户要干嘛?我们程序员要去帮助用户实现什么接口?代码如下:

package com.xxxxf.service; import com.xxxxf.pojo.User; /** *@author xxxxf *2018年7月10日 */ public interface UserService { /** * 用户注册 */ public void regist(User user); /** * 用户登录 */ public String login(String userName,String passWord); /** * 检测用户是否存在 */ public String checkUser(String userName); }

4.serviceImpl层中UserServiceImpl是UserService的实现类,@Service注解告诉Spring这是个业务层,UserService中只声明了几个接口,我们并没有去真正去实现,但是有人想为什么不直接写个实现类,反而要加一个Service层呢?这个问题,这要归结到代码复用性和维护性问题,如果你直接写一个实现,当你增加新的功能时就要在一大堆代码中是添加修改,这是非常耗时的操作,所以接口和实现分离有利于维护,代码如下:

package com.xxxxf.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.xxxxf.dao.UserDao; import com.xxxxf.pojo.User; import com.xxxxf.service.UserService; /** *@author xxxxf *2018年7月10日 */ @Service public class UserServiceImpl implements UserService{ //依赖注入 @Autowired private UserDao userDao=null; @Override //读写提交 @Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public void regist(User user) { // TODO Auto-generated method stub userDao.addUser(user); } @Override //读写提交 @Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public String login(String userName, String passWord) { // TODO Auto-generated method stub //userDao.getUser(userName, passWord); return userDao.getUser(userName, passWord); } @Override //读写提交 @Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) public String checkUser(String userName) { // TODO Auto-generated method stub return userDao.getUserByUserName(userName); } }

5.控制层Controller 使用@Controller注解,然后使用@AutoWired导入service层,因为service中的方法是我们使用到的,controller通过接收前端传过来的参数进行业务操作,在返回一个指定的路径或者数据表。RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需要注意的呢,在使用此注解之后不会再走试图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。上面讲的这些都是Spring MVC知识,如果你看不懂请百度Spring MVC流程控制,基础知识我就不多废话。这里我们的控制器类叫UserController.java,里面有两个方法,一个叫注册,另一个叫登录,具体代码如下:

package com.xxxxf.controller; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.codehaus.jackson.map.util.JSONPObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.portlet.bind.annotation.ResourceMapping; import org.springframework.web.servlet.ModelAndView; import com.sun.java.swing.plaf.windows.resources.windows; import com.xxxxf.dao.UserDao; import com.xxxxf.pojo.User; import com.xxxxf.service.UserService; /** *@author xxxxf *2018年7月10日 */ @Controller public class UserController { //依赖注入 @Autowired private UserService userService=null; /** * 注册功能实现 * */ @RequestMapping(value="/register.do") @ResponseBody public ModelAndView regist(User user,Model model,HttpServletRequest request,HttpServletResponse response) throws IOException { request.setCharacterEncoding("utf-8");//必须写在第一位,因为采用这种方式去读取数据,否则数据会出错。 //设置这样方式去读。这样中文就能够读取出来了,但是需要注意。表单的发送方式必须是method='post' response.setContentType("text/html;charset=utf-8");//设置传过去的页面显示的编码 System.out.println("#################################################"); System.out.println("用户注册:"+"账号:"+user.getUserName()+" "+"密码:"+user.getPassWord()); String userName=user.getUserName(); String result=userService.checkUser(userName); if (result==null) { userService.regist(user); PrintWriter out = response.getWriter(); out.print("alert('注册成功!前往登录页面');window.location.href='/PersonWebSite/login.jsp'"); //model.addAttribute("msg","注册成功"); //return new ModelAndView("redirect:/login.jsp"); }else { PrintWriter out = response.getWriter(); out.print("alert('注册失败!账号已存在!');window.location.href='/PersonWebSite/register.jsp'"); //model.addAttribute("msg","账号存在,请重新注册"); //return new ModelAndView("redirect:/register.jsp"); } return null; } /** * 登录功能实现 * */ @RequestMapping(value="/login.do") @ResponseBody public ModelAndView login( @RequestBody User user,HttpServletRequest request,HttpServletResponse response) { /*Model model,HttpServletRequest request,HttpServletResponse response*/ System.out.println("***********************************"); String username=user.getUserName(); //request.getParameter(username); String password=user.getPassWord(); System.out.println("用户登录:"+"账号:"+username+" "+"密码:"+password); String result=userService.login(username, password); System.out.println("从数据查询结果:"+result);//查询账号密码都正确,登录成功;账号或密码错误,result=null; if (result != null) { try { response.getWriter().write("true"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else { try { response.getWriter().write("false"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //model.addAttribute("msg", "登陆成功"); return null; } }

在Controller里面有一些业务流程控制,业务思想是需要你去仔细思考,比如:

String userName=user.getUserName(); String result=userService.checkUser(userName); if (result==null) { userService.regist(user); PrintWriter out = response.getWriter(); out.print("alert('注册成功!前往登录页面');window.location.href='/PersonWebSite/login.jsp'"); }else { PrintWriter out = response.getWriter(); out.print("alert('注册失败!账号已存在!');window.location.href='/PersonWebSite/register.jsp'"); }

这里为什么对result这个变量进行空判断,那你就得看看result是什么?奥,原来啊,reslut这个变量调用了checkUser(String userName)这个方法,这个方法去数据库查询是否有这个username是否存在,如果不存在就写进数据库里面,注册成功,如果有则前端就会提示注册失败。登录方法也用到了类似的思想,需要读者自己去揣摩思考。

至此我们的后台代码就告一段落了 六、前台代码

对于前端页面和功能的实现,我是借助于layui和jQuery这个前台框架,实际上我是个后台人员前端并不是我的强项。小伙伴在跟着这篇博客学习的时候,我会把我的整个项目的源码放到github上,源码戳这里,项目的WebContent目录下几个页面就是我们项目所需的页面(PS:如果写得不好请不要吐槽我,尽力了),当然前端页面与后台数据交互用到了JSON,还实现了AJAX技术,在页面代码里需要大家去揣摩,我就不多BB了。网页中文乱码想必是家常便饭,包括请求数据到后台乱码,前台页面展示乱码等等,这里我也解决好了,你自己去看我的项目源码,我是怎么去解决的,如果是你你会怎么去解决呢?

七、总结

写完一个项目,一定要去总结,这个项目什么地方我花费了很多时间,怎么去解决如何去解决,怎么样解决又快又好?我个人觉得思考问题本身比代码设计阶段更重要,如果没有思维过程,自己写得东西就像是套一个代码模板,下次别人换一个需求你可能就写不出来了,这也是很多刚入门的新手需要注意的地方,也是从一个码农到一个合格程序员必经之路!当然,新手刚开始只需要去多看别人写的,多去查一些资料,自己再去仿写出来就行了,久而久之你一定会有很多收获。

希望就该博客有什么问题或疑惑向我提供意见和建议,谢谢


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3