前言
看到群博上大家积极的更新博客,惭愧的自己,康康啊康康,怎么能堕落呢。赶紧写博客啊。
老早就知道Spring-SpringMVC-Mybatis的大名了,甚至在小伙伴面试时也了解了一下session/cookie的相关知识,但是一直没有机会自己动手实践一下,感谢母校给了我这样一个宝贵的机会,让我在毕业前真正体验了一把Java web开发的感觉。
作为一个C++后台,一直对这些东西都没什么理解。
师兄说:“想的通,写不通,还是空。”对我来说,如果想都想不通,那不如先写写,写着写着,可能也就想通了。
不得不说,真正写了一周项目后,我的感受就一个。
真香!
至于原因嘛
先立个FLAG,我先去学车,放假了补上。
(科三过了ohohoh)
我觉得最大的两个好处1.方便开发,大大的提升了开发效率2.架构思路清晰,可以说遵循框架的架构设计,让整个项目的架构简洁,美观,可扩展性非常强。
本文的目的
网上SSM的资料超级多,而我这篇文章的目的并不是一篇正统的SSM科普文,想要系统学习Java Web的同学可以点击x了。
这只是自己从一个Java web 0基础小白的总结文,其中概念叙述并不是很正确(并没有认真看书学习)。如果你也是需要快速实现一个小项目,或者带着好奇的心态想看看web开发是啥(当然你需要有一定的其他开发的基础),这篇文章可以让你快速入门。
正文
开发环境
便于操作,开发环境为Linux + IDEA。
IDEA可以说是新手的必备了,一个很重要的tip:IDEA没自动补全的,估计就是你写错了。而Linux则是因为这是我更加熟悉的开发环境。
而IDEA,java环境啥的,请自己准备吧。同时,为了减少学习知识,我采用了手动导jar包,而并没有使用Maven(怎么简单怎么来,Yeah)。
总体架构
我的项目采用的是前后端分离架构,所以总结的是纯后端,并不会操心前端的事。要做的只是接受参数,处理业务->操作数据库,然后将结果以JSON的格式返回。
而到代码层面,则分为Entity,DAO,Service,Crontroller四层。
Entity,实体类,每个实体对应数据库里的一张表。
DAO,这个类都是抽象类,表示一个实体对数据库的操作。
Service,感觉是对DAO层的一次封装,提供服务给更上层的Crontroller
Crontroller,实现业务逻辑的地方,调用Service提供的服务。
开发流程
1.设计数据库
这就是按照业务去设计数据库,建表。
以一个简单的用户表User为例,包含三个字段id(自增主键),name(姓名),passwd(密码)。
2.编写Entity类
每张表对应一个实体类,把表中的字段写成类的属性,然后自动生成getter-setter方法。
对应User表的User类
public class User {
private int id;
private String name;
private String passwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
3.编写DAO层
还是以User为例,分为一个抽象类和一个xml文件。抽象类定义接口,xml按照mybatis的语(zi)法(dong)编(bu)写(quan)。
public interface UserDAO {
public User queryUserByName(String name);
public int insertUser(User user);
public List<User> display();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDAO">
<select id="queryUserByName" resultType="entity.User">
select * from User where name=#{name};
</select>
<insert id="insertUser" keyProperty="id" useGeneratedKeys="true" parameterType="entity.User">
insert into User(name, passwd) values(#{name}, #{passwd});
</insert>
<select id="display" resultType="entity.User">
select * from User;
</select>
</mapper>
简单说,MyBatis就是将SQL语句转换为成了一个接口,这样我们就不用再像原来那样使用比较原始的方式操作数据库了。
上面的三个接口可以说非常有代表性了。
1.#{}指定参数,很神奇的是MyBatis可以“智能”地找到需要的参数。
2.插入时指定了自增主键
3.返回List<User>,但resultType依然是User。
当然,DAO涉及到数据库,我们还需要一些更多的配置。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 加载SQL映射文件 -->
<mappers>
<mapper resource="dao/UserDAO.xml"/>
</mappers>
</configuration>
还有applicationContext.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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置DB 1.加载驱动文件(这个文件就是将数据库的配置,比如用户密码,也可以直接写死) -->
<context:property-placeholder location="WEB-INF/db.properties"/>
<!-- 配置DB 2.配置数据源 -->
<bean id="jdbcDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="${url}"></property>
<property name="username" value="xxx"></property>
<property name="password" value="xxx"></property>
</bean>
<!--配置mybatis的会话工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="jdbcDataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
<!--配置组件扫描 -->
<context:component-scan base-package="controller,dao,entity,Service"></context:component-scan>
<!--配置mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer ">
<property name="basePackage" value="dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!--下面就和数据库没啥关系了 -->
<!--配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--开始mvc注解扫描 -->
<mvc:annotation-driven/>
<!--映射静态资源 -->
<mvc:resources location="/" mapping="*.html"></mvc:resources>
<mvc:resources location="/fonts/" mapping="/fonts/**"></mvc:resources>
<mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
<mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
</beans>
可以看到我们上面引入了一个db.properties文件,其中有一个属性就是url
那么在db.properties文件就要定义url
url=jdbc:mysql://47.102.199.xxx:3306/kangkang?useUnicode=true&characterEncoding=GBK
我这里使用的是云上的mysql,直接写的是ip+port,注意mysql还要加上指定的database,涉及到中文还要指定编码,不然都是乱码。
4.编写Service层
DAO写好后,就是编写其上一层的Service层,我的理解就是Service可能是实现一些具体的操作?而Controller则是业务逻辑,抽象程度更高
这里依然是User类的例子,我的Service就很简单(逻辑都写Controller里了)
@Service
public class UserService {
@Autowired
private UserDAO userDAO;
public User queryUser(String name) {
User user = userDAO.queryUserByName(name);
return user;
}
public int insertUser(User user) {
return userDAO.insertUser(user);
}
public List<User> display() {
return userDAO.display();
}
}
Service注解->不解释
Autowired注解->同不解释,写就vans。
这里的UserDAO就是DAO层的抽象类,可以看到在Service层我们直接调用接口就行类,非常方便。
5.编写Controller层
以用户注册为例,直接看代码
@Controller //不解释
@RequestMapping("/user") //表示整个类的根路径
@ResponseBody //整个类内的方法都带这个注解,就实现类HTTP那种回复体
public class UserController {
@Autowired
private UserService userService; //对应Service层,直接调用接口
@RequestMapping("/register") //这个方法的路径就是/user/register
public String registerUser(@RequestParam("user") String name, @RequestParam("password") String passwd) {
//因为我们前端请求的Content-Type是application/x-www-form-urlencoded格式(我们前端也low,就没用JSON)。
//所以这里就用@RequestParam将前端的参数对应到你函数的形参
//业务逻辑就是简单的,查询不存在就插入,然后结果以JSON格式返回
String result = null;
JSONObject resultObject = new JSONObject();
if (userService.queryUser(name) != null) { //检查是否已经存在用户名
resultObject.put("result", false);
resultObject.put("detail", "name is dup");
} else {
User newUser = new User();
newUser.setName(name);
newUser.setPasswd(passwd);
userService.insertUser(newUser);
resultObject.put("result",true);
}
result = resultObject.toJSONString();
return result;
}
}
OK到这里你的函数就编写完了,前端就可以测试了。
通过在浏览器里输入URL,
比如localhost:8080/user/register?user=kang&passwd=kkk
就可以看返回的结果了。
6.Cookie和Session的那些事
因为登录要维持一个会话,而这里就是用到Cookie和Session了。
简单的说Session,就是会话成功建立后(比如登录成功),维持一个状态,保存了一定的数据,这个状态维持也不用你操心,底层都做好了。
以登录函数为例
@RequestMapping("/login")
public String loginUser(HttpSession session, @RequestParam("password") String passwd, @RequestParam("user") String name) {
String result = null;
JSONObject resultObject = new JSONObject();
User user = userService.queryUser(name);
if (user == null || !passwd.equals(user.getPasswd())) {
resultObject.put("result", false);
} else {
resultObject.put("id", user.getId());
resultObject.put("result", true);
//将这个会话的用户信息存下,以后想知道这是哪个会话看名字就行了
session.setAttribute("userName",name);
session.setAttribute("id", user.getId());
}
result = resultObject.toJSONString();
return result;
}
我们的需求是网站有些内容是登录后才能查看的,那么如何检测登录呢?
这里可以通过过滤器或者拦截器,我们用的是过滤器,大概意思就是所有指定的调用都会经过过滤器,那么没有建立会话(没登录的)我们拦住它就行。
创建类时选择Filter就行
//指定你需要检测的路径
@WebFilter(filterName = "LoginFilter",urlPatterns = {"/job/createJob","/job/updateJob"})
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//查看是否保存了userName,也就是是否登录了
String userName = (String) ((HttpServletRequest) req).getSession().getAttribute("userName");
if(userName == null){
//没登录,返回一个错误信息,这里用的是Servlet,enmmm这我也不太懂是啥
HttpServletResponse response = (HttpServletResponse)resp;
response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器
response.setHeader("content-type", "text/html;charset=UTF-8");//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码 }else {
PrintWriter out = response.getWriter();//获取PrintWriter输出流
out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
out.write("please login!!!!");
}else {
//登录了,放行
chain.doFilter(req, resp);
}
}
public void init(FilterConfig config) throws ServletException {
}
}
最后
项目完整地址
https://github.com/kangyijie5473/JobInfoPlatform(请不要点星星,这个项目太low)
感谢小组同学dela,sillduck,hepangda,fujie的帮助,快速上手一个技术还是有人手把手教最快了,不然只能在无尽的bug中连helloworld都整不明白。。