第一步:先搭建maven环境,使用maven创建javaweb项目。Idea环境下创建具体步骤为:
第二步:配置相关文件
实现的功能:用户注册,用户登录,用户登出
**步骤分析:**注册用了ajax技术获取表单信息用向服务器发送请求,这里用到的新技术是用序列化获取表单的所有参数的值。
var params = $("#registerForm").serialize();
值得注意的一点是:这里的表单不能使用type=submit,因为表单提交是属于同步发送请求,所以前端页面的表单可以使用普通按钮绑定事件的方法来提交ajax异步发送请求。
web层获取到ajax的请求参数以后使用工具类BeanUtils进行封装,BeanUtils.populate(user,map);
参数传给Service层然后service层连接数据库,把封装好的javabean插入到数据库中,这里的密码使用工具类进行了加密:user.setPassword(Md5Util.getMD5(user.getPassword()));
在进行注册的时候对用户名、密码、邮箱、手机号码等信息使用jquery、ajax技术进行了合法性 校验,其中校验用户名需要发送ajax去数据库查询这个用户名是否存在,其他的信息则直接使用正则表达式进行校验即可。
用户登录的时候先是前端通过ajax传递参数:用户名和密码,验证码,这三个直接使用request.getparameter去获取请求参数即可,系统的验证码是工具类生成好的,绑定了点击事件和页面加载事件:
并且每次生成工具类都会把它放到session会话中,web层进行校验的时候直接进行调用即可。
这里在进行校验的时候除了要向前台发送是否校验通过,还要发送校验的信息,所以要返回一个javabean,这个javavean封装了返回是否成功的信息和返回失败的信息还有成功的信息。
public class ResultInfo {
private boolean flag; //表示服务器的执行结果
private String errorMsg; // 存放错误信息
private Object successData;
}
由于是javabean,ajax需要接收的返回值是json类型的数据所以web层调用service层进行查询后要对javabean进行封装,使用工具类:
String s = objectMapper.writeValueAsString(resultInfo);
response.getWriter().print(s);
由于验证码和用户名密码校验都需要返回resultinfo信息,所以把这段代码抽取出来直接放在resultinfo里面进行调用即可。
public String toJson(Object obj) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
String jsonStr = om.writeValueAsString(obj);
return jsonStr;
}
最后要把用户的信息放入到session作用域中方便回显读取,这里为了防止用户清除cookie导致读取不到session,所以要对cookie进行设置指定时间覆盖原来的绑定的cookie
Cookie cookie = new Cookie("JSESSIONID", session1.getId());
cookie.setMaxAge(30*60);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
接下来是用户信息的回显模块,每一次进行头页面加载的时候,都得调用ajax去web层查询session中是否存储 了用户信息,如果有的话就回显回页面。
最后一个是登出模块,在表单中进行请求,清除用户的session信息后跳转回到首页。
request.getSession().invalidate();
response.sendRedirect(request.getContextPath());
最后全部完成后进入代码优化阶段:
这里为了防止servlet的代码进行太多的方法判断对代码逻辑进行了抽取:提交过来的action方法必须和servet要调用的方法一致,新建一个BaseServlet类继承了HttpServlet,再让UserServlet去继承这个类,当请求过来的时候会发现UserServlet没有doget/dopost方法,就去找他的父类,由BaseServlet使用反射技术进行方法的调用。
try {
String action = req.getParameter("action");
Class clazz = this.getClass();
Method method = clazz.getMethod(action, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
这里的this指的是谁调用方法,this指的就是哪个对象。注:每次请求过来的时候UserServlet都是会创建一个对象的。
首先对代码进行优化,由于实际开发过程中web端和service端是分工的,web端要向service端传递接口,接口是一种规范,也就是方法名,返回值类型,参数。所以要开始面向接口编程。具体处理过程如下:
首先要创建一个BeanFactory工厂类+xml配置文件+接口+接口实现类,工厂类用来读取配置文件中的实现类的包名+类名,用接口去接收这个对象,从而实现解耦。
public class BeanFactory {
private static ResourceBundle bundle;
static {
bundle = ResourceBundle.getBundle("beans");
}
public static Object getbean(String id){
Object Object = null;
try {
// 获取需要创建的类的全限定名
String className = bundle.getString(id);
Object = Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return Object;
}
public static void main(String[] args) {
BeanFactory.getbean("User");
}
}
分页查询用户搜索线路:
首先在headel.html进行加载的时候就发送ajax请求导航条的信息,先去查询redis数据库如果没有这些缓存数据再去加载mysql数据库。
public class CategoryServiceImpl implements CategoryService {
@Override
public String findAllcategory() throws JsonProcessingException {
Jedis jedis = JedisUtil.getJedis();
String CategoryListJson = jedis.get("CategorylistJson");
if (CategoryListJson==null){
SqlSession sqlSession = MybatisUtils.getSqlSession();
CategoryMapper mapper = sqlSession.getMapper(CategoryMapper.class);
List<Category> category = mapper.findAllCategory();
ObjectMapper om = new ObjectMapper();
CategoryListJson = om.writeValueAsString(category);
jedis.set("CategorylistJson",CategoryListJson);
MybatisUtils.closeSession(sqlSession);
}
return CategoryListJson;
}
}
然后返回cid路线id的参数,点击的时候跳转到了路线列表页面,这个参数也带过去了,这里还有一个参数是搜索框的,怎么带过去处理如下,用的是方法跳转的形式
function searchRoute() {
// 获取浏览器地址栏中的cid的值
var cid = getParameter("cid");
// 在调用的函数中获取搜索框中的值,
var rname = $("#rname").val();
// 并向route_list.html页面上发送请求,并携带参数
location.href = `route_list.html?rname=${rname}&cid=${cid}`;
}
在进行页面加载的时候会带着这两个参数发送ajax请求去数据库里面分页查询数据信息,用的还是原来的方法:封装一个pageBean返回给浏览器。
这里的处理方法是把线路的绑定id带到线路详情页,线路详情页进行加载的时候发送ajax请求去数据库里面加载关于详情页面的所有信息:
这里所属分类和商家表和商品图片详情表都打包放到了路线类里面,然后前端在读取信息的时候再拆开进行贴图。
分页查询收藏排行榜:
其实思路和前面的技术差不多,就是用ajax去数据库按照收藏排行顺序查询线路的详情信息。然后用pageBean返回到前端页面。
遇到的Bug
收藏排行榜用到的技术点就是ajax加上分页查询技术。
进入到详情页的时候要先判断收藏的情况。
//发送ajax请求判断收藏情况
$(function () {
getButtonStatus();
})
function getButtonStatus(){
var rid = getParameter("rid");
var url = "findFavoriteRankByPage";
var params={
"action":"getButtonStatus",
"rid":rid
}
$.get(url,params,function (data) {
if (data==1){
$("#graybtn").hide();
}else{
$("#redbtn").hide()
}
},"text")
}
然后发送请求去带着rid去后台用session查看user是否登录了,如果登录成功了就去数据库修改状态数据,然后把状态和数据封装到一个对象里面返回给浏览器,这里还要更新一下收藏的次数。
function addFavorite(){
var rid = getParameter("rid")
var url = "findFavoriteRankByPage";
var params = {
"action":"addFavorite",
"rid":rid
}
$.get(url,params,function (data) {
if (data.flag){
$("#graybtn").show();
$("#redbtn").hide();
$("#countId").html(`已收藏${data.successData.count}`)
}else {
alert(data.errorMsg);
if(data.successData==1){
location.href = "login.html";
}
}
},"json")
}
```java
public static <T> T getBean(Class<T> daoInterface){
return (T)Proxy.newProxyInstance(
daoInterface.getClassLoader(),
new Class[]{daoInterface},
(proxy,method,args)->{
//得到会话
SqlSession sqlSession = MybatisUtils.getSqlSession();
//创建 DAO 代理类
T mapper = sqlSession.getMapper(daoInterface);
//调用 DAO 接口中的方法
Object obj = method.invoke(mapper, args);
//释放资源
MybatisUtils.closeSession(sqlSession);
//返回值
return obj;
}
);
}
动态代理主要使用类加载器和接口字节码,还有动态代理的接口对实现类的方法进行增强,让它们调用方法前后调用方法后都进行数据库连接对象的获取和释放资源:
原来的代码是这样的
SqlSession sqlSession = MybatisUtils.getSqlSession();
FavoriteMapper mapper = sqlSession.getMapper(FavoriteMapper.class);
MybatisUtils.closeSession(sqlSession);
使用加强类后的代码是这样的:
RouteMapper mapper = DaoFactory.getBean(RouteMapper.class);
主要有两个作用:反向代理和负载均衡的作用:所谓反向代理就是代理服务器,负载均衡是反向代理最典型的代表 隐藏真实服务器的所在位置,保证服务器的安全,外界不能直接访问tomcat服务器.
我们将tomcat服务器集群搭建在局域网中,tomcat集群跟Nginx服务器在同一个局域网中.
只有Nginx服务器才能被外界所访问,这样以来就保证了tomcat服务器的安全.
1.搭建tomcat集群
将来我们在公司中一台电脑上部署一个tomcat服务器端口都是8080的
我们现在没有那么多台电脑,我们在一台电脑上安装多个tomcat,修改tomcat的端口.
安装好多个tomcat后,需要修改tomcat的端口号:
修改3个地方
tomcat1:
8004
8070
8008
tomcat2
8005
8080
8009
tomcat3
8006
8090
8010
2.搭建nginx反向代理,实现负载均衡
2.1: 将集群交个nginx服务器管理,修改配置文件,告知nginx每一台tomcat服务器所在的位置
编写nginx.conf文件:
http{
# 配置tomcat集群(注意所在配置文件中的位置)
upstream tomcat_pool{
# 格式:
# server tomcat服务器所在的位置 weight=权重;
server localhost:8070 weight=5;
server localhost:8080 weight=3;
server localhost:8090 weight=2;
}
server{
location / {
# root html; # 静态资源所在目录
# proxy_pass 对应tomcat集群所在的位置
proxy_pass http://tomcat_pool;
index index.html; # 默认页面是什么
}
}
}
3.访问nginx实现反向代理,负载均衡
http://localhost:80
Linux版
启动和关闭:
启动:
cd /usr/local/nginx/sbin
./nginx (相对路径启动)
/usr/local/nginx/sbin/nginx
关闭:
cd /usr/local/nginx/sbin
./nginx -s stop
/usr/local/nginx/sbin/nginx -s stop
重启:
cd /usr/local/nginx/sbin
./nginx -s reload
// 公布端口
firewall-cmd --zone=public --add-port=80/tcp --permanent
// 展示防火墙中公布的所有端口
firewall-cmd --zone=public --list-ports
// 删除防火墙中公布的端口
firewall-cmd --zone=public --remove-port=80/tcp --permanent
// 重启防火墙
systemctl restart firewalld
主要用的就是同步技术和前端页面进行交互:
新技术点,就是利用js去上传图片文件:有三要素:
表单上必须要有 enctype=“multipart/form-data” 属性
1.请求方式必须是POST
文件上传项:
普通表单项:
这里会用工具类把图片名字区分开来,然后用标签获取图片的文件流保存到nginx静态位置中,记得要修改nginx默认访问的位置:
```java
Part part = request.getPart("rimage");
String fileName = part.getSubmittedFileName();
fileName = fileName.substring(0, fileName.length() - 4);
String fileurl = "E:/nginx/nginx-1.14.0-windows/nginx-1.14.0/imgs/";
String uuid = UuidUtils.getUuid();
fileName = uuid+fileName;
part.write(fileurl+fileName+".jpg");
part.delete();
Map<String, String[]> map = request.getParameterMap();
Route route = new Route();
route.setRimage("http://localhost:80/"+fileName+".jpg");
BeanUtils.populate(route,map);
RouteService service = (RouteService) BeanFactory.getbean("RouteService");
route.setIsThemeTour("0");
route.setCount(0);
route.setSid(1);
int flag = service.addRoute(route);
response.sendRedirect(request.getContextPath()+"/routeServlet?action=findRouteByPage&pageSize=6&pageNumber=1");