低下头,看看自己有多么菜
瞎写的
这次小学期真的是让我身心俱疲。在睡了将近十二个小时后,我爬起来上了两节互联网协议,回到宿舍,看了看斗鱼时刻,终于下定决心,反思一下我这个菜鸡在这次小学期中是怎么把自己菜死的。
总结起来就两件事:过分高估自己,低估别人。
小学期代码已经全部传上github。https://github.com/iceboundx/Lumweb
开始
这次小学期我们专业还是做一个网站。这个网站使用的框架不限,老师推荐了一种上古的方法:jsp+servlet。。。。我看了看现在各种技术,选了个最傻逼的servlet做后端(如果要是当时不想着挑战自己选个spring boot早弄完了)
我有两个队友,都是很好的人,他们之前没接触过前后端,我就让他们去写前端的静态网页了,后端我实现restful之后前端用js拼接一下就行了(不要问我为什么不用模版引擎,因为我当时不知道有这种东西)。
数据库选了mysql,配合c3p0数据库连接池使用。
总之就这样,我们就开始干了。
结束
昨天刚刚验收完,得分不高,主要原因还是前端太丑了。
其他同学的网页基本上都是套的模板,前端的html模板配合jsp生成页面,难度非常低,但是一眼看出来的效果还不错。碰巧验收我们的老师似乎不太懂,我给他讲了我们的nginx+tomcat的服务器结构和前后端分离的restful api,他都没有什么反应,反而说我们数据库表太少。。。我们后一个组依靠着帅气的模板和一个视频成功俘获了老师的心,后来我还看到一个组后端套了个python机器学习的模块唬住老师。。。真是失败呀。。
前端
bootstrap
前端我们用了bootstrap的框架,大体上实现了部分页面的自适应和页面美化。
bootstrap最强的是它的栅格布局,其他控件没什么太多意思。栅格布局这里不多说,官网都有。另外他的navbar可以根据屏幕大小自动收缩也非常有意思。
jquery
jquery最常用的就是$(‘#id’)通过id选中某个元素,其他也都有文档,基本上html代码都能改。我这里ajax也是通过jquery实现的
function get_data() { $.ajax({ type: "GET", url: "product.do/list", dataType: "json", data:{ page:now_page+1, num:8 }, success: function(result){ now_page++; add_page(result,now_page,result.length); }, error: function(){ toastr.error("server error"); } }); }
然后因为拼接界面我没有用模板引擎,我用了jquery直接拼接。。这太蠢了。。
var now_page=0; var total_num=0; var proitem=['<div class="col-md-3">', '<div class="goods-item up-hover-shadow" onclick="todetail(this);" >', ' <div class="figure-img">', ' <img src="images/2.png" width="200" height="200" alt=""> ', ' </div> ', ' <h3 class="title">LumDogⅤ</h3> ', ' <p class="price">£17.98</p> ', ' <div class="flags"><span class="glyphicon glyphicon-fire" style="color:Red;font-size:20px;" aria-hidden="true"></span></div>', ' </div>', ' </div>'].join(""); var rowleft=['<div class="row" id="'].join(""); var rowright=['"></div>'].join(""); var proitemleft=['<div class="col-md-3">', '<div class="goods-item up-hover-shadow" onclick="todetail(this);" id="'].join(""); var proitemright=['"> ', '<div class="figure-img"> ', '<img src="images/loading.gif" width="200" height="200" alt=""> </div> ', '<h3 class="title">loading...</h3> ', '<p class="price">loading...</p>', '<div class="flags"></div></div></div>'].join(""); var hotitem=['<span class="glyphicon glyphicon-fire" style="color:Red;font-size:20px;" aria-hidden="true"></span>'].join(""); var nohotitem=['<span class="glyphicon glyphicon-leaf" style="color:White;font-size:20px;" aria-hidden="true"></span>'].join(""); function add_item(product,rownum){ var nowrow='#row'+rownum; var nowitem='#pro'+product.pid; $(nowrow).append(proitemleft+"pro"+product.pid+proitemright); $(nowitem).find('img').attr("src",'images/product/thum'+product.pid+'.jpg'); $(nowitem).find('.title').text(product.name); $(nowitem).find('.price').text('£'+product.price); if(product.state=="hot")$(nowitem).find('.flags').html(hotitem); else $(nowitem).find('.flags').html(nohotitem); } function add_page(product_array,page_num,tot){ var rn=Math.ceil(tot/4); var now_row=(page_num-1)*2; var now_item=0; for(i=1;i<=rn;i++){ add_row(now_row); for(j=1;now_item<tot&&j<=4;now_item++){ add_item(product_array[now_item],now_row); } now_row++; } endLoad(); }
以上代码建议大家不要学。。。。太蠢了,推荐大家使用模板引擎
这里说一个问题.val和.attr(‘value’,’xxx’)是不一样的。.val是实际的值,.attr(‘value’,’xxx)是更改的默认值。一开始这里被坑了。
html5
html5有很多新特性,这里用到一个表单检测输入是否合法的。
<input type="tel" required="required" pattern="[0-9]{11}" oninvalid="setCustomValidity('illegal input!')" oninput="setCustomValidity('')" placeholder="mobile phone number" name="u">
pattern里写正则,oninput写正在输入时的消息,oninvalid写错误信息。
后端
java
后端我分了四层和一个工具包
dao为持久层,负责与数据库通信,只有简单逻辑,其中dao.impl包为实现。
domain为数据模型层,其中的类均为javabean,定义了基本的数据模型。
service为服务层,该层可以调用多个dao层,负责复杂逻辑与服务处理。
web为网络层,根据请求不同选择不同的service层,负责网络通信。
utils为工具包,提供了各层公用的一些工具方法。
事实证明这样写一点卵用没有,我要是dao层出了bug,就可能要一路从底改到顶部(呲牙)。。。不过好在我代码水平还可以,dao层和service层基本没什么问题。
web层里都是httpservlet对象, utils里面我抄了别人的一个md5加密,序列化/反序列化用的fastjson。
数据库
数据库我本地用的mysql 8.0,服务器上用的MariaDB 10.2.11,这导致一个非常尴尬的事情,我本地的mysql workbench连上服务器上的MariaDB之后无法直接把数据导回本地,还要手动输入命令导出。
数据库连接池我直接使用了比较流行的c3p0,配置连接器的时候要注意一下,高版本的mysql需要考虑时区问题。这里我用了jdbc driver。配置要这么写
<property name="jdbcUrl">
jdbc:mysql://localhost:3306/lumweb&useSSL=false&serverTimezone=Asia/Shanghai
</property>
如果在中国,强烈建议时区写成上海,要不然时间会错乱。。
c3p0配置方面,注意设置maxIdleTime为3000或者5000之类的数字,要不然会出现连接过期,但是连接依然存在于连接池中的问题。
java代码方面,我用了org.apache.commons.dbutils这个包里的一部分方法,配合javabean真的非常非常好用。下面我详细写一下。
首先是建立一个工具类负责使用数据库连接池:
package utils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JdbcUtils { private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); public static DataSource getDataSource() { return dataSource; } public static Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } public static void release(Connection con, PreparedStatement par, ResultSet re) { if (re != null) { try { re.close(); } catch (SQLException e) { e.printStackTrace(); } } if (par != null) { try { par.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
然后这里是一段读取全部商品信息,并且保存到一个ArrayList的代码,还有一个添加商品信息的:
package dao.impl; import java.sql.SQLException; import java.util.ArrayList; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import dao.ProductDao; import domain.Product; import utils.JdbcUtils; public ArrayList<Product> getAll(int start, int end) { try { QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "select * from product order by state,pid limit ?,?"; return (ArrayList<Product>)runner.query(sql, new BeanListHandler<Product>(Product.class), start, end-start+1); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public boolean addPro(Product product) { if(existPro(product.getPid()))return false; try { QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource()); String sql = "insert into product values(?,?,?,?,?,?)"; Object params[] = {product.getPid(),product.getName(),product.getPrice(),product.getStock(),product.getShortdesc(),product.getState()}; runner.update(sql, params); return true; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } }
配合fastjson真的好用。。当然比不上spring boot
服务器配置
服务器我就用的是这台阿里云,9.79一个月,学生认证,注意每个月单独买比包年便宜几块钱。。
因为我一直懒得更新,服务器目前还是debian 9。java装的11.0.4,tomcat也是用的最新版本,nginx是1.12.1(懒得更新)。
为了挑战(zhuang)自己(B),我使用nginx访问静态资源,tomcat访问动态资源。
因为我的nginx上运行着这个博客,所以需要新建了一个vhost,里面配置文件如下:
server { listen 2333; #listen [::]:2333; server_name 60.205.183.171; index index.html index.htm; root /home/wwwroot/lumweb; error_page 404 /404.html; # Deny access to PHP files in specific directory #location ~ /(wp-content|uploads|wp-includes|images)/.*\.php$ { deny all; } location ~.*\.xml { deny all; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|html)$ { expires 10d; } location ~ .*\.(js|css)?$ { expires 12h; } location ~ /*.do { proxy_pass http://localhost:8080; } location ^~ /WEB-INF { deny all; } location ^~ /META-INF { deny all; } location ~ /\. { deny all; } access_log off; }
1.把这个文件放在conf/vhost/里即可。然后去修改tomcat的配置文件。因为我们这个server只运行这一个网站,所以我们需要去除tomcat自带的路径名。
2.此外,我这个网站支持上传图片到webroot文件夹中,所以我们要更改网站的根目录到 /home/wwwroot/lumweb中,并把tomcat的整个webroot文件夹复制到/home/wwwroot/lumweb(包括WEB-INF和META-INF)。
在conf/server.xml中末尾处修改Hostname里的配置为: <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="/" docBase="/home/wwwroot/lumweb" reloadable="false"></Context> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host>
3.这里还有另一个问题,新版tomcat上传的文件没有others的读取权限,这里也要改一下bin/catalina.sh中的unmask参数
# Set UMASK unless it has been overridden if [ -z "$UMASK" ]; then UMASK="0022" fi umask $UMASK
随后在bin目录中使用./startup.sh运行tomcat,并同时启动nginx即可。我这里选择的端口是2333端口,如果你使用的是阿里云服务器,一定记住在服务器安全组配置中打开2333端口。
反思
我这次被人干爆,主要原因是侧重点选错了。这小学期其实就是一个过家家游戏,游戏就要有游戏规则。我这次一开始就选定了自己撸前端,不使用现成的html模板的方式,由于我们没有美工,整个前端界面稀烂,使用体验还行,就是外观极差,老师看到都要呕。而后端虽然做了很多提高效率的方法,但由于没有并发,最后根本看不出来效果。还不如用jsp随便撸一个。
昨天npy和我在教三楼谈了很久,有几个观点我很认同。
第一个是我肯定是菜,因为如果我足够牛逼,首先这个网页根本没难度,其次我可能根本不需要跟那些jsp一起比,既然我已经和他们在同一个学校了,代表我已经和同专业其他人处于一个层次,我不能遗世独立,超脱世外(况且我还考不过他们),更不能轻视任何一个人,别人也不是傻子。
第二是我不能过分重视这个成绩。因为他没有办法反映参与者的真实水平,最后验收的时候,每个老师打分标准都不一样,导致成绩的随机性很高,电商法的Bindu老师根本什么都不懂,搞得很多同学很难受。
总而言之,这是我第一次接触这些框架。搞竞赛的时候特别瞧不起做web开发的,现在接触了django、spring boot,我改变了一些看法,很佩服搞出这些轮子的人,也不再认为web开发是个垃圾职业了。虽然我还是认为开发岗,尤其是前端岗,本身跟搬砖没什么区别。
仔細看了半天,纔從字縫裏看出字來,滿篇都寫着兩個字是「不值」!