研究生阶段主要用python做神经网络模型压缩,如今工作需要,从零开始学习Java web开发(实则只看了一周)。ide: idea + maven(快速配置依赖jar包) + ssh框架,部署到tomcat,使用mysql数据库。关于环境配置这里不进行描述。记录学习过程,很多语言不专业。
初次接触java web开发,第一个问题就是:为啥搞那么多东西?语言就有三个:java, jsp, sql。经过一周,实现并按需求修改了两个demo,对web开发有了粗略的认识。
用户访问web时,首先接收到服务器发送的页面(jsp编写),并与之交互。
交互过程中,服务器会接收到用户的各种操作(好像叫做请求…?),然后通过java编写的逻辑,进行服务(比如准备不同的页面内容)。
其中有些服务需要用到储存在服务器硬盘中的信息(比如用户登录需要知道服务器硬盘里是否储存了提交的用户和密码),此时就用到数据库。数据库当然可以用txt文本实现,但是当数据量大的时候,关系数据(sql)是个很好的选择。
maven: 就像python需要anaconda对包进行下载与管理,maven也是为这个目的而生的。maven管理的是.jar包,这些.jar包就是别人写的库。当然也可以手工配置,比较繁琐。
ssh: 代表了web项目中用到的三个.jar包:spring, struts, hibernate。因为这三者经常一起使用,因此简化为ssh。
tomcat: web项目编译好放入tomcat,就可以通过浏览器访问。
mysql: 关系数据库。
照着这个做
运行成功后,逐步分析其中的细节。
maven配置jar包的文件。每个下都是一个jar包。
ssh框架个人认为应该从web.xml看起,就像从main函数看起一样。不一样的是,web.xml是个配置文件。在web.xml一般会配置spring与strusts,配置方式是指定spring与strusts对应的.xml文件。hibernate一般在spring的.xml里配置
用来编写后端逻辑。可以看到source root文件夹java下有四个文件夹: action dao domain service。个人理解,domain里是定义实体的,与hibernate有关。
dao里是实体与数据库链接,与hibernate有关。
service里似乎只是加了一层封装,服务层,与spring有关。
action里进一步封装,与struts有关。
主要编写前端UI界面,其中会与后端的.java进行数据交换。
熟悉了小型Demo,现在看看一个网上商城如何实现。github地址是在eclipse上开发的,并且没有使用maven,因此需要做一定修改。以下零零散散记录一些ssh框架特点。
定义实体类是为了和数据库进行交互,也是后端与数据库交互的边界。随便看一个实体类Product.java:
package com.caozhihu.tmall.pojo;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
int id;
@ManyToOne
@JoinColumn(name = "cid")
private Category category;
@ManyToOne
@JoinColumn(name = "sid")
private Sellor sellor;
private String name;
private String subTitle;
private float originalPrice;
private int stock;
@Transient
private ProductImage firstProductImage; //@Transient表示这是一个瞬时字段,不会被保存到数据库中
@Transient
private List<ProductImage> productSingleImages;
@Transient
private List<ProductImage> productDetailImages;
@Transient
private int reviewCount;
@Transient
private int saleCount;
public void setSellor(Sellor sellor){
this.sellor = sellor;
}
public Sellor getSellor(){
return sellor;
}
...
other get and set
@Entity表示这是个和数据库挂钩的实体类
@Table(name = “product”)表示挂钩的表是product
@Id表示这个成员变量是主键
@GeneratedValue(strategy = GenerationType.IDENTITY)表示添加是主键自动增长
@Column(name=a_name)将这个成员变量与表的一列命a_name挂钩,默认则同名。
@ManyToOne表明是外键
@JoinColumn(name = “cid”)表的一列命cid挂钩
private Category category;另一个实体类对应的表就是外键关联的表,默认与Category 的主键关联。
@Transient表明与数据库无关。
本项目的数据链接非常简单
package com.caozhihu.tmall.dao.impl;
import org.springframework.orm.hibernate5.HibernateTemplate;
//定义了一个name = "dao" 的bean
public class DAOImpl extends HibernateTemplate {
}
其实,这时候就要看spring对应的xml了applicationContext.xml
<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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.caozhihu.tmall.*"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean name="ds"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/Tmall?characterEncoding=utf8&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123123"/>
bean>
<bean name="sf"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="ds"/>
<property name="packagesToScan">
<list>
<value>com.caozhihu.*value>
list>
property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=false
hibernate.hbm2ddl.auto=update
value>
property>
bean>
<bean id="dao" class="com.caozhihu.tmall.dao.impl.DAOImpl">
<property name="sessionFactory" ref="sf"/>
bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sf"/>
bean>
beans>
不得不说一句,bean就是指类的实例,只是些类有特殊的规范,所以他们的实例叫做bean。beans包裹下就是一些实例。
本质上说,有了DAO就可直接与前端进行交互了,进行Action的编写。但是这样会有很多冗余并且比较乱。所以这里会对每个实体类提供一个service类。
这里关键要看ServiceDelegateDAO.java,只有这个类与实例dao和sf进行了链接,其它service类都继承了ServiceDelegateDAO,也就继承了与实例dao和sf的链接。每个service实例化时会完成链接。
其实也可以在spring的xml中,进行service的实例化并于dao实例链接,但是这里的service太多,因此这么操作比较省力。
然后随便看一个ProductServiceImpl.java
现在,spring里应该有很多service类的实例(大家都叫做bean,可以但没必要)
项目里用了很多继承,其实是一个接着一个的,完全可以写在一个类里。读起来比较累,但是维护起来就很舒服。直接从Action4Pojo看起
网站默认进入index.jsp(欢迎页,web.xml里配置)。上来就是
response.sendRedirect("fore2home");
触发ForeAction里
@Action("fore2home")
public String home() {
categories = categoryService.list();
productService.fill(categories);//为每个category来设置setFirstProductImage()
productService.fillByRow(categories);//为每个product设置setFirstProductImage(),再为每个category来setProductsByRow(products)
return "home.jsp";
}
这里就出现了第一个交互!跳转。
home准备好资源后,显示home.jsp。注意,虽然返回home.jsp,其实这是要在Action4Result里通过@Result注册的(自己找找)。
这里就出现了第二个交互!后端送给前端categories,并且所有的service都连接上数据库了。
比如搜索栏
<form action="foresearch" method="post">
<div class="searchDiv">
<input name="keyword" type="text" value="" placeholder="时尚喵喵鞋 喵阳镜">
<button type="submit" class="searchButton">搜索</button>
</div>
</form>
这里的关键是keyword与foresearch。keyword是在Action4Parameter里定义的String(一定要有对应的get与se函数t),foresearch则是ForeAction中的。
@Action("foresearch")
public String search() {
products = productService.search(keyword, 0, 20);
productService.setSaleAndReviewNumber(products);
for (Product product : products) {
productImageService.setFirstProductImage(product);
}
return "searchResult.jsp";
}
可以看到,直接用了keyword。
同样是在home.jsp包含的categoryMenu.jsp(仔细找找)
<div class="categoryMenu">
<c:forEach items="${categories}" var="c">
<div cid="${c.id}" class="eachCategory">
<span class="glyphicon glyphicon-link"></span>
<a href="forecategory?category.id=${c.id}">${c.name}</a>
</div>
</c:forEach>
</div>
这里有个href="forecategory?category.id= c . i d " , 比 较 特 殊 的 是 后 面 有 个 ? c a t e g o r y . i d = {c.id}",比较特殊的是后面有个?category.id= c.id",比较特殊的是后面有个?category.id={c.id}(普通的href跳转自己找找)。
看到ForeAction
@Action("forecategory")
public String category() {
products = productService.fill(category);//category.setProducts(products);
//System.out.println(products.get(0).getSubTitle());
return "category.jsp";
}
这里的category.id就是${c.id}
这里有个奇怪的点:无论是前端送数据到后端,还是后端送数据到前端,都用了ForeAction中定义的成员变量。那么不会乱嘛?
(我记得)其实每个Action跑完,所有成员变量都会被初始化(清空)。看个栗子。
@Action("forelogin")
public String login() throws IOException {
User user_session = userService.get(user.getName(), user.getPassword());
if (user_session == null ) {
return "login.jsp";
}
ActionContext.getContext().getSession().put("user", user_session);
return "homePage";
}
这是登录界面跳转过来的,user.name和user.password在前端赋值传入。这里的逻辑是,根据name和password到数据库查找,找到User实例就给user_session。没找到user_session就是null,登录失败返回登录界面login.jsp。登录成功,则
ActionContext.getContext().getSession().put(“user”, user_session);并返回主页
这里就把user_session,也就登录的账户记录到session里,这样无论在哪里,都可以通过
(User) ActionContext.getContext().getSession().get(“user”)
得到这个User。没记录到session里的都会被清空。