1.1、基本了解

web相当于网页的意思,而web又分为静态web和动态web

  • 静态web:html、css等,给人看到的数据不会发生变化,所用用户看到的都是同一个页面;
  • 静态web存在的点击特效等等都是属于伪动态;
  • 它无法和数据库交互(数据不能持久化)
  • 动态web:提供给人看的数据会根据不同的人产生不同的数据,始终会发生变化;
  • 动态web通过服务器可以与数据库交互;
  • 动态web可能因为一些web资源出现问题,因此要维护代码,出现停机状态。

我们的javaWeb开发就属于动态web。

1.2、web应用程序基本了解

Web应用程序:是一种可以通过Web访问的应用程序,用户只需要有浏览器即可,不需要再安装其他软件。

web应用程序基本由(静态资源、JSP,Servlet、自定义类、工具类、配置文件)6个部分组成。这些资源会被统一整合放到同一个文件夹下。

在启动服务器的时候,通常要设置一个应用程序的环境路径(可以不设置),例如:它为/smbms,所有资源则必须要以/smbms为根目录摆放,在根目录下的资源可以直接下载或访问。

网上书城javaee报告 javaweb网上书城源码_服务器

但一个应用程序中有一些要保密的资源。而web中/WEB-INF目录则解决了这个问题。存放在这个目录中的资源不能被客户端直接访问,直接访问这个目录下的资源则会报出404错误,可以让程序来取得其中的资源。/WEB-INF中的资源有着一定的名称和结构,例如:

  • /WEB-INF/web.xml是部署描述文件(Servlet,过滤器,监听器等映射)
  • /WEB-INF/classes是用来放置自定义类和配置文件等等
  • /WEB-INF/lib用来放置web需要用到的Jar包(提醒:有时候web报错,可能是因为没有导入jar包)

所有我们能访问的资源,都是存在这个世界的某个计算机当中。

2.1、Tomcat服务器

Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以处理浏览器等Web客户端的请求并返回相应响应,也可以放置网站文件,让全世界浏览;可以放置数据文件,让全世界下载。

而我们这次Web的学习是使用Tomcat服务器。

2.1、Tomcat安装和配置

Tomcat官网:https://tomcat.apache.org/

在Tomcat官网上直接下载压缩包,解压成功后就可以在bin目录下找到并打开startup.bat文件

之后访问测试:https://localhost:8080/

Tomcat的默认端口号是8080,如果我们的端口号并占用可以修改它的端口号。在conif文件下找到server.xml中

<Connector port="8080" protocol="HTTP/1.1"               connectionTimeout="20000"               redirectPort="8443" />

修改port就可以了。

这里就测试完毕了。

这样我们可以先通过模仿来写我们第一个web网站

我们来到下面标红的路径可以发现,这里是我们测试路径localhost:8080网页的资源文件

网上书城javaee报告 javaweb网上书城源码_服务器_02

在前面我们说过启动服务器需要设置环境路径,这里ROOT是Tomcat的默认路径,这样我们可以通过模仿,在webapps文件夹下新创建一个根目录,复写ROOT文件中的资源。把index.jsp修改成

<h1>    HelloWorldh1>

之后启动服务器,通过环境路径就可以发布第一个web网站了

网上书城javaee报告 javaweb网上书城源码_客户端_03

这里还没有结束,我们要在编译软件上使用Tomcat还需要进行配置。大家可以自行百度。

这里建议大家再安装一个Maven,这样就可以方便写代码的时候导包。

3、Servlet

什么是Servlet呢?

Servlet是Sun公司提出的一项技术,使用Servlet可以将Http的请求和响应封装在标准的java类中实现各种web。Servlet是使用Java语言编写的服务器端程序,它能够接受客户端的请求并产生响应。

Servlet的产生和原理

由上图可以了解到Servlet的产生和原理,这里要注意一下,每一个Servlet只执行一次init()方法,之后的处理客户端的请求不会再调用此方法,直接调用service()方法。大家可以通过源码更加深入了解一下。

网上书城javaee报告 javaweb网上书城源码_网上书城javaee报告_04

Servlet的配置

当我们自定义Servlet类的时候,需要到WEB-INF文件中的web.xml配置Servlet

<servlet>        <servlet-name>helloservlet-name>        <servlet-class >com.feng.servlet.HelloServletservlet-class>    servlet>        <servlet-mapping>        <servlet-name>helloservlet-name>        <url-pattern>/hellourl-pattern>    servlet-mapping>

在以上代码不难看出,每个Servlet都需要在web.xml中注册,这样才能确保自定义的Servlet类和服务器有关联。可能有人会想和有什么关系。

网上书城javaee报告 javaweb网上书城源码_客户端_05

我们从下图可以看出这里使用访问顺序的,其中2和3的值是必须相同的。

而url-pattern标签中是我们在浏览器地址栏输入的url,两者相同则产生联系,然后2和3相同因为需要servlet-name映射到servlet-class才能访问到Servlet类。

这里再说一下,标签url-pattern中的/表示:https://localhost:端口号/ + 根目录(工程名)

从以上可以得出客户端的请求和自定义类能构成联系,主要的原因是servlet-name的关系。

在web.xml配置Servlet中,一个Servlet只可以指定一个映射路径,但是Servlet-mapping则可以指定多个映射路径。

ServletContext

ServletContext是Servlet上下文,Web容器启动的时候会为每个web程序创建一个对应的ServletContext对象,它代表了当前的web程序。这个对象是全局唯一的,程序中的所有Servlet都是共用这个对象。

ServletContext是一个域对象,不同的Servlet都可以拿到它的数据。

setAttribute(String name,Object value);

添加数据是以key-value形式添加

getAttribute(String );

用key读取数据

removeAttribute(String name);

用key删除数据

public class HelloServlet extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//        this.getInitParameter()   初始化参数//        this.getServletConfig()   servlet配置//        this.getServletContext()  servlet上下文//        这里的this.getServletContext()返回的ServletContex对象相当于一个全局变量,所有都可以用。        ServletContext context = this.getServletContext();        String usename = "Java";  //数据        context.setAttribute("usename", usename);        //这个可以理解成把这个键值对写入了web.xml中,        /*就像获取初始化参数这个道理            username        Java            */        //将一个数据保存在了ServletContext中:键为:usename   值为:usename    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        doGet(req, resp);    }}
public class Getservlet extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        ServletContext context = this.getServletContext();        String usename = (String) context.getAttribute("usename");        resp.setContentType("text/html");        resp.setCharacterEncoding("utf-8");        PrintWriter writer = resp.getWriter();        writer.print("名字"+usename);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        doGet(req, resp);    }}

上面代码可以看出在不同的两个Servlet中ServletContext可以共享数据。

在上述的代码中,大家大概可以看到这段代码。

<context-param>        <param-name>usernameparam-name>        <param-value>Javaparam-value>    context-param>

其实这段代码和this.getServletContext().setAttribute的本质是一样的,不同的是这里在web容器启动的时候已经初始化这个参数了。

@Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        String usename = this.getServletContext().getInitParameter("usename");    System.out.println(username);    }

用getInitParameter(String name)这个方法就能等到web.xml中的初始化参数了

HttpServletRequest和HttpServletResponse

web服务器接收到web客户端的请求,为了和请求联系上,分别创建了HttpServletRequest和HttpServletResponse,分别代表请求和响应。

HttpServletRequest代表客户端的请求,因为Http把所有信息都封装到这个这个对象中,所以我们在Servlet中可以通过HttpServletRequest获取客户端所有的信息。

这里我们说几个经常用到的方法

getContextPath();

获取web应用的根路径

getParameter(String name);

获取名为name的参数单个值

getParameterValues(String name);

获取名为name的参数的多个值

getAttribute(String name);

获取名为name的属性值

setAttribute(String name,String value);

设置名为name的属性值为value

getRequerstDispatcher(String path);

获取请求转发对象,转发到path地址中。所获得的对象需要forward()方法实现真正的跳转。

@Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        req.setCharacterEncoding("utf-8");        resp.setCharacterEncoding("utf-8");        String username = req.getParameter("username");        String password = req.getParameter("password");        String[] hobbys = req.getParameterValues("hobbys");              System.out.println(username);        System.out.println(password);              System.out.println(req.getContextPath());        //通过请求转发        //这里的/代表当前的web应用        req.getRequestDispatcher("/success.jsp").forward(req,resp);    }

HttpServletResponse代表响应,响应数据回客户端。

setContentType(String type)

设置响应的内容类型为type

setCharacterEncoding(String charset)

设置响应的编码字符集为charset。

有时候我们返回的类型不同,都需要用到serContentType(String type)来设置内容类型,例如设置Json之类的。而setCharacterEncoding(String charset)则是防止乱码,我们通常都设置成UTF-8字符集。

在HttpServletResponse负责发送数据的方法有:

HttpServletResponse.getOutputStream()

获取输出流对象,所获对象需要借助里面输出方法才能发送数据

HttpServletResponse.getWriter()

同上

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getOutStream().print("Hello world!");
    PrintWriter out = resp.getWriter();
    out.writer("Hello Servlet!");
}

响应中还有一个重要的功能,就是实现重定向。

重定向就是当A对B发送请求,B通知A你需要去访问C。

A-->B--(去访问C)->A-->C  相当于这个过程。

void sendRedirect(String url) throws IOException;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    /*重定向做了这两步
    resp.setHeader("Location", "/r/imag");
    resp.setStatus(302);
    */
    resp.sendRedirect("/r/imag"); //重定向
}

这时候我们来说下重定向和转发的一些区别和相同点。

相同点

  • 实现这个方法后,两者的页面都会发生跳转

不同点

  • 请求转发的时候,url地址栏不会产生变化,为原本页面的url
  • 重定向的时候,url地址栏会发生变化,为sendRedirect(String url)中的url

重定向和转发还需要注意一下相对路径和绝对路径的问题

相对路径response.sendRedirect("index.jsp")

http://localhost:8080/项目名/index.js

绝对路径response.sendRedirect("index.jsp")

http://localhost:8080/index.js

4、Cookie和Session

Cookie是存在于客户端(浏览器),是服务器发送回给浏览的一小片数据,浏览器可以把存储(cookie:一般会保存在本地的 用户目录下 appdata),等下一次请求的时候,会一起发送回服务器,一个浏览器可以创建多个cookie。cookie的作用通常是是用来判断请求是否来自同一个浏览器,就相当于通行证。

这个通行证就是session id,当服务器第一次接受到请求的时候,会创建一个Session对象,随着也会生成一个session id,并通过Response Header(响应头)中的Set-Cookie客户端发送要求设置cookie的响应,这样一来客户端中的cookie中的JSESSIONID就是session的id了。

因为cookie的默认周期是浏览器关闭,如果我们关闭了浏览器,cookie也随之关闭,因此cookie中的JSESSIONID发生了变化,就能判断出来session发生了变化,来自不同的浏览器。如果我们需要关闭浏览器后cookie还存在,我们可以设置cookie的有效期。

Cookie[] cookies = req.getCookies(); //获得Cookie
cookie.getName(); //获得cookie中的key
cookie.getValue(); //获得cookie中的vlaue
new Cookie("lastLoginTime", System.currentTimeMillis()+""); //新建一个cookie
cookie.setMaxAge(24*60*60); //设置cookie的有效期
resp.addCookie(cookie); //响应给客户端一个cookie

在cookie中我们也提到过session。Session的主要目的就是为了弥补Http的无状态特性。简单的说,就是服务器可以利用session存储客户端在同一个会话期间的一些操作记录;服务器用session id区分是否为同一个会话(cookie)。

  • 服务器第一次接收到客户端请求后,服务器就会为这次请求开辟一块内存空间,这个对象便是Session对象,存储结构为ConcurrentHashMap。
  • 一个Seesion独占一个浏览器,如果浏览器关闭了,重新打开浏览器,因为Session位于服务器,没有被删除,所以还存在着,但是因为Cookie发生了变化,所以这个原本的Session并不能作用于重新打开的浏览器上。
  • 这里就要说到一个Session劫持的问题,比如我们登录用户中,如果没有勾选保留七天之类的,直接关闭掉浏览器,下次打开浏览器的时候我们就要重新输入账号密码。但是如果我们使用某种手段改写浏览器发出的HTTP请求报头,把原来的session id发送到服务器,则再次打开浏览器仍然能够找到原来session。

session被删除的情况:

  • 程序调用HttoSession.invalidate();
  • 上一次客户端发送sessionid超过了session最大有效时间
  • 服务器停止
public class SessionDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //得到Session
        HttpSession session = req.getSession();
        //给Session中存东西
        session.setAttribute("name","随笔月记");
        //获取Session的ID
        String sessionId = session.getId();
        //判断Session是不是新创建
        if (session.isNew()){
            resp.getWriter().write("session创建成功,ID:"+sessionId);
        }else {
            resp.getWriter().write("session以及在服务器中存在了,ID:"+sessionId);
        }
        //移除Session中的属性
        session.removeAttribute("name");
		//手动注销Session
		session.invalidate();
        //Session创建的时候做了什么事情:这里就是给客户端中的Cookkie发送sessionID
//        Cookie cookie = new Cookie("JSESSIONID",sessionId);
//        resp.addCookie(cookie);
    }

在web.xml中设置Session的有效时间

15

5、JavaBean

JavaBean是Java的可重用组件技术,在我看来实际就是一个实体类。

JavaBean的固有写法:

  • 必须要有无参构造函数
  • 属性私有化
  • 属性必须有get()/set()方法,用作对属性的读写。

JavaBean经常用于更好的封装事务逻辑和数据库的字段映射,它主要目的是实现代码重用。

6、过滤器和监听器

Filter(过滤器)

如果设置了对应的过滤器,客户端访问相应的资源的时候会有对应的过滤器拦截。过滤器可以对请求内容做出改变或者重新发出请求。这里客户端和服务器都不需要知道过滤器的存在。

网上书城javaee报告 javaweb网上书城源码_javaweb网上书城源码_06

以上就是客户端访问有过滤器的客户端了。

Filter的使用要实现Filter接口,大家注意的是导包的时候,不要导错。

网上书城javaee报告 javaweb网上书城源码_客户端_07

下面是我在网上找的一个解决乱码的过滤器。实现Filter接口后,要实现三个方法,在下面的代码中交代的很清楚。

并且要在web.xml中配置过滤器。

public class demo01 implements Filter {
    //初始化:web服务器启动,就已经初始化了,随时等待过滤对象出现!
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("CharacterEncodingFilter初始化");
    }
    //Chain:链
    /*
    1.过滤中的所有代码,在过滤特定请求的时候都会执行
    2.必须要让过滤器继续通行
        filterChain.doFilter(request, response);
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");  //过来的请求
        response.setCharacterEncoding("utf-8"); //过去的请求
        response.setContentType("text/html;charset=UTF-8");
        System.out.println("过滤器执行前");
        filterChain.doFilter(request, response);//让我们的请求继续走,如果不写,程序到这里就被拦截停止了!
        System.out.println("过滤器执行后");
    }
    //销毁:web服务器关闭的时候,过滤会销毁
    public void destroy() {
        System.out.println("CharacterEncodingFilter销毁");
    }
}
CharacterEncodingFiltercom.filter.demo01CharacterEncodingFilter/servlet/*
监听器(了解即可)

监听器有许多种,它是根据不同类型来触发不同的触发器,就是说,你要用到监听器的时候,决于你的类型是什么。

例如:session监听,你就要实现HttpSessionListener接口

public class demo01 implements HttpSessionListener {
    //每当有一个Session创建,就会执行sessionCreated事件,常用于统计网站的在线人数
    public void sessionCreated(HttpSessionEvent se) {
    }
    //每当有一个Session销毁,就会执行sessionDestroyed事件。
    public void sessionDestroyed(HttpSessionEvent se) {
    }
}

监听器也需要在web.xml中注册

com.listener.demo01

7、JDBC

在javaWeb开发中,肯定少不了数据库的时候。这里我们就简单讲一下java如何连接数据库。

在数据库和Application有一个JDBC Driver Interface,这里可以成为统一驱动,因为有许多不同的数据库,所以会有许多不同的设置,因此设置了加多一层来统一连接方式。JDBC驱动程序是对JDBC规范完整的实现,它的存在在JAVA程序与数据库系统之间建立了一条通信的渠道。

网上书城javaee报告 javaweb网上书城源码_网上书城javaee报告_08

由上图可以看出不同的数据库用不同的驱动来连接Application。这里我们用MySQL举列子。

java连接MySQL数据库必须要用的jar包:

  • mysql-connector-java    (MySQL 驱动)
  • java.sql
  • javax.sql

连接数据库的方式基本是固定的,如下:

1加载驱动

2 建立连接,此处代表数据库

3 执行SQL语句

4 处理结果集

5 关闭数据库

public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //配置信息
    	//jdbc:mysql://localhost:3306/(数据库名字)?设置数据信息
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&&useSSL=true";
        String username = "root";
        String password = "123456";
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.连接数据库,代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);
        //3.得到执行SQL语句的对象
        Statement statement = connection.createStatement();
        //4.编写sql
        String sql = "select * from users;";
    	//5.执行SQL语句,并获得结果集
        ResultSet rs = statement.executeQuery(sql);
    	//6.处理结果集
        while (rs.next()) {
            System.out.println(rs.getInt("id"));
            System.out.println(rs.getString("name"));
            System.out.println(rs.getString("password"));
            System.out.println(rs.getString("email"));
            System.out.println(rs.getDate("birthday"));
        }
        //7.关闭连接,释放资源,最后开的最先关闭。
        rs.close();
        statement.close();
        connection.close();
    }
public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //配置信息
    	//jdbc:mysql://localhost:3306/(数据库名字)?设置数据信息
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&&useSSL=true";
        String username = "root";
        String password = "123456";
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.连接数据库,代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);
        //3.编写sql
        String sql = "select * from users where id = ?";
        //4.预编译的sql,在后面直接执行就可以了
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
    	//5.给sql语句中的占位符输入
    	//sql语句变成:select * from users where id = 1
    	preparedStatement.setObject(1, 1);
    	//6.获得结果集,因为已经预编译了sql语句,所以这里不用写sql的参数
        ResultSet rs = preparedStatement.executeQuery();
    	//7.处理结果集
        if (rs.next()) {
            System.out.println(rs.getInt("id"));
            System.out.println(rs.getString("name"));
            System.out.println(rs.getString("password"));
            System.out.println(rs.getString("email"));
            System.out.println(rs.getDate("birthday"));
        }
        //8.关闭连接,释放资源,最后开的最先关闭。
        rs.close();
        statement.close();
        connection.close();
    }

Staement和PreparedStatement两者执行SQL语句的区别在于,后者是通过预编译执行的,这样可以防止注入问题。

事务

数据库使用中,还有一个特别重要的ACID原则:原子性、一致性、隔离性、持久性。

在执行增删查改的时候,要开启事务,防止数据丢失等等。事务总的来说就是,执行语句的时候,要么都成功,要么都失败。不能只成功一半,而后半部分丢失的情况。

10、MVC三层架构

MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

每层的用处

Model:

  • 业务处理   ------Service层
  • 实现算法
  • 数据管理(CRUD)-----Mapper层

Controller

  • 接受请求,处理请求参数。
  • 视图跳转(转发,重定向)
  • 把业务交给业务层处理。

View

  • 界面设计
  • 与用户交互
  • 展示数据

网上书城javaee报告 javaweb网上书城源码_javaweb网上书城源码_09

从上图就可以很好的理解MVC架构

与MVC架构出现之前相比,View层和Controller层是在一起,这样的架构高耦合,不易操作,难维护。

MVC架构的优点有:低耦合、代码可重用性、可维护性高等;

这样一来每一层的分工明确,更好的实现功能。