javaWeb笔记

这篇博客详细介绍了JavaWeb开发中的关键知识点,包括JDBC的使用,如JDBC核心组件、数据库连接、SQL语句执行、事务处理和优化,以及三层架构(DAO、Service、View)。此外,还涵盖了Tomcat、XML配置、Servlet的生命周期、请求响应、状态管理(Cookie、Session、ServletContext)、JSP的三大指令、九大内置对象、EL表达式、JSTL和分页、上传下载等内容,全面讲解了JavaWeb开发的基础与实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVAWEB知识点

——黄铭

1 JDBC

1.1 JDBC核心组件

DriverManager: 此类管理数据库驱动程序列表。使用通信协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配。

Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象。

​ **Connection:**该接口具有用于连接数据库的所有方法。连接对象表示通信上下文,数据库的所有通信仅通过连接对象。

Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。

​ **ResultSet:**在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据。

​ **SQLException:**此类处理数据库应用程序中发生的任何异常。

1.2 JDBC初始

  • **导入JDBC驱动包:**需要下载包含数据库编程所需的JDBC的jar包

  • 注册JDBC驱动程序: Class.forName(“com.mysql.jdbc.Driver”);

​ Driver myDriver = new com.mysql.jdbc.Driver();DriverManager.registerDriver( myDriver );

  • **创建连接:**需要使用*DriverManager.getConnection()*方法创建一个Connection对象,该对象表示与数据库的物理连接。

  • **执行查询:**需要使用类型为Statement的对象来构建和提交SQL语句到数据库。

  • **从结果集中提取数据:**需要使用相应的*ResultSet.getXXX()*方法从结果集中检索数据。

  • **释放资源:**需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。

1.3 SQL语句执行

  • boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。使用此方法执行SQL DDL语句或需要使用真正的动态SQL时。
  • int executeUpdate(String SQL):返回受SQL语句执行影响的行数。使用此方法执行预期会影响多个行的SQL语句,例如INSERT,UPDATE或DELETE语句。
  • ResultSet executeQuery(String SQL):返回一个ResultSet对象。当您希望获得结果集时,请使用此方法,就像使用SELECT语句一样。

1.4 案例演示

DML操作

public class JdbcDemo01 {
    public static void main(String[] args) throws Exception{
        ArrayList<Student> list = new ArrayList<>();
        Student student = null;

        // 驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 连接
        String url = "jdbc:mysql://localhost:3306/gp";
        Connection conn = DriverManager.getConnection(url, "root", "1111");

        if (conn != null){
            System.out.println("连接成功!");
        }

        // 操作
        String sql = "select * from student";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);

        // 结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()){
            int sno = resultSet.getInt("sno");
            String name = resultSet.getString("name");
            int age = resultSet.getInt("age");
            String gender = resultSet.getString("gender");
            Date birth = resultSet.getDate("birth");
            String adddress = resultSet.getString("address");
            student = new Student(sno,name,age,gender,birth,adddress);
            list.add(student);
        }

        // 关闭资源
        resultSet.close();
        preparedStatement.clearParameters();
        conn.close();

        // 打印
        for (Student student1 : list) {
            System.out.println(student1);
        }

    }
}

DQL操作

public class JdbcDemo02 {
    public static void main(String[] args) throws Exception {

        Class.forName("com.mysql.jdbc.Driver");

        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/gp", "root", "1111");

        Statement stat = conn.createStatement();


        //int insert = stat.executeUpdate("insert into student values(112,'xf',17,'女','1999-08-15','八百弓')",Statement.RETURN_GENERATED_KEYS);
        int delete = stat.executeUpdate("delete from student where sno = '112';");

//        ResultSet generatedKeys = stat.getGeneratedKeys();
//        if (generatedKeys.next()){
//            String sno = generatedKeys.getString(1);
//            System.out.println(sno);
//        }
        //System.out.println(insert);
        System.out.println(delete);

        stat.close();
        conn.close();
    }
}

1.5 SQL注入

​ 就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。

​ 使用statment的子类preparedStatment可以防止SQL注入,该对象采用预编译,其sql语句的每个?都是一个占位符,用来赋值。

public class JdbcDemo01 {
    public static void main(String[] args) throws Exception{

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = scanner.nextLine();
        System.out.println("请输入密码:");
        String password = scanner.nextLine();

        Class.forName("com.mysql.jdbc.Driver");

        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/gp", "root", "1111");

//        Statement stat = conn.createStatement();
//
//        ResultSet resultSet = stat.executeQuery("select * from user where username ='" + username + "' and password = '" + password + "';");

        PreparedStatement preparedStatement =  conn.prepareStatement("select * from user where username = ? and password = ?");
        preparedStatement.setString(1, username);
        preparedStatement.setString(2, password);

        ResultSet resultSet = preparedStatement.executeQuery();

        if (resultSet.next()){
            System.out.println("登录成功");
        }else {
            System.out.println("登陆失败");
        }
    }
}

1.6 三层架构

1.6.1 DAO(Date Access Object)

​ 此包中的类和接口的作用是,对数据库的表进行操作并返回结果。

1.6.2 service

​ 此包主要处理业务逻辑,即把DAO中得到的数据进行处理。也是开启事务的层,提交,回滚的层。

1.6.3 view

​ 此包是对进行了业务逻辑处理后的数据展示出来,

1.7 事务

​ 事务是一个原子操作,在同一个事务中,要么都执行成功,要么都执行失败。开启事务需要设置setAutoCommit(false)。并且在一个事务中需要连接同一个Connection,所以需要一个ThreadLocal类来绑定Connection。当一个失误中当升错误是,需要执行回滚操作。

 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); 

//(2)获取连接
    public static Connection getConnection() {
        try {
            //1判断线程有没有连接,第一次执行一定是null
            Connection conn = threadLocal.get();
            if (conn == null) {

                conn = DriverManager.getConnection(url, user, password);
                //2绑定到线程
                threadLocal.set(conn);
                System.out.println("绑定了一个连接:" + conn.hashCode());
            }
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

//与事务相关的四个方法
//1开启事务
public static void beginTransaction() throws SQLException {
    Connection connection = getConnection();
    if (connection != null) {
        connection.setAutoCommit(false);
    }
}

//2提交事务
public static void commit() throws SQLException {
    Connection connection = getConnection();
    if (connection != null) {
        connection.commit();
    }
}

//3回滚事务
public static void rollback() throws SQLException {
    Connection connection = getConnection();
    if (connection != null) {
        connection.rollback();
    }
}

//4关闭连接
public static void close() throws SQLException {
    Connection connection = getConnection();
    if (connection != null) {
        threadLocal.remove();//解除绑定
        connection.close();
        //System.out.println("close解除绑定,并关闭连接"+ connection.hashCode());
    }
}

1.8 优化DbUtils

​ 在DAO层中,对于增删改查有许多冗余代码,所以可以在DbUtils工具类中封装增删改,和查的方法。

1.8.1 增删改

public static int executeUpdate(String sql, Object... params) throws SQLException { //sql ?
    Connection conn = null;
    PreparedStatement pstat = null;
    try {
        conn = getConnection();
        pstat = conn.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                pstat.setObject(i + 1, params[i]);
            }
        }
        return pstat.executeUpdate();
    } finally {
        closeAll(null, pstat, conn);
    }
}

1.8.2 通过RowMapper封装查询方法

RowMapper是一个借口,此接扣的方法是T getRow(Result rs)(),每一个实现类都是一个查询对象的操作。

public static <T> List<T> executeQuery(RowMapper rowMapper, String sql, Object... params) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;

    try {
        connection = getConnection();
        preparedStatement = connection.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                preparedStatement.setObject(i + 1, params[i]);
            }
        }
        resultSet = preparedStatement.executeQuery();
        List<T> list = new ArrayList<>();
        while (resultSet.next()) {
            T t = (T) rowMapper.getRow(resultSet);
            list.add(t);
        }
    } catch (Exception e) {
        throw new BookException("查询失败", e);
    } finally {
        closeAll(resultSet, preparedStatement, connection);
    }
    return null;
}
public interface RowMapper<T> {

    T getRow(ResultSet rs);
}
public class BookRowMapper implements RowMapper<Book> {
    @Override
    public Book getRow(ResultSet rs) {
        try {
            int id = rs.getInt("id");
            String _title = rs.getString("title");
            String author = rs.getString("author");
            Date publicDate = rs.getTimestamp("publicDate");
            String publisher = rs.getString("publisher");
            String isbn = rs.getString("isbn");
            BigDecimal price = rs.getBigDecimal("price");
            String picture = rs.getString("picture");
            int cid = rs.getInt("cid");
            Book book = new Book(id, _title, author, publicDate, publisher, isbn, price, picture, cid);
            return book;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}

1.8.3 通过反射来封装查询操作

public static <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try {
        connection = getConnection();
        preparedStatement = connection.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                preparedStatement.setObject(i + 1, params[i]);
            }
        }
        resultSet = preparedStatement.executeQuery();
        // 获得标题
        ResultSetMetaData metaData = resultSet.getMetaData();
        System.out.println("标题个数:" + metaData.getColumnCount());
        List<T> list = new ArrayList<>();

        // 遍历meta
        while (resultSet.next()) {
            // 创建对象
            T t = clazz.newInstance();

            // 给每列赋值
            for (int i = 0; i < metaData.getColumnCount(); i++) {
                // 得到字段的数据
                String columnLabel = metaData.getColumnLabel(i + 1);
                try {
                    // 赋值
                    PropertyDescriptor pd = new PropertyDescriptor(columnLabel, clazz);
                    if (pd != null) {
                        Method writeMethod = pd.getWriteMethod();
                        writeMethod.invoke(t, resultSet.getObject(columnLabel));
                    }
                } catch (Exception e) {
                    continue;
                }
                list.add(t);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException("查询失败", e);
    } finally {
        closeAll(resultSet, preparedStatement, connection);
    }
    return null;
}

1.9 Druid连接池

连接池的作用

数据库连接频繁的创建和关闭会影响性能,数据库连接池的作用就是负责分配,管理和释放连接。

Druid连接池中的部分数据

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/数据库名

username=root

password=1111

initialSize=10 初始连接数

maxActive=最大连接数

minIdle=最小空闲连接

maxWait=5000 超时等待时间

static {
    try {
        Properties properties = new Properties();
        InputStream is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        properties.load(is);
        is.close();
        dataSource = DruidDataSourceFactory.createDataSource(properties);
    } catch (Exception e) {
        System.out.println("连接池初始失败...");
    }
}

1.10 Commons DbUtils

概念:
●Commons DbUtils是Apache组织提供的-一个对JDBC进行简单封装的开源工具类库。
●使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
作用:
●小巧、简单、实用。
●对于数据表的查询操作,可以把结果转换为List、Array. Set等集合。
●对于数据表的DML操作,只需要写SQL语句。

public class EmpDaoImpl implements EmpDao {
    private QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
    //查询所有
    @Override
    public List<Emp> getAllEmp() {
        try {
            List<Emp> list = queryRunner.query("select * from emp;", new BeanListHandler<Emp>(Emp.class));
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    //查询单个
    @Override
    public Emp getEmpByNo(int empNo) {
        try {
            Emp emp = queryRunner.query("select * from emp where empno = ?", new BeanHandler<Emp>(Emp.class),empNo);
            return emp;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    //添加
    @Override
    public int add(Emp emp) {
        Object[] params = {emp.getEmpno(),emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHiredate(),emp.getSal(),emp.getComm(),emp.getDeptno()};
        try {
            int update = queryRunner.update("insert into emp values(?,?,?,?,?,?,?,?)", params);
            return update;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    //修改
    @Override
    public int update(Emp emp) {
        Object[] params = {emp.getEname(),emp.getJob(),emp.getMgr(),emp.getHiredate(),emp.getSal(),emp.getComm(),emp.getDeptno(),emp.getEmpno()};
        try {
            int update = queryRunner.update("update  emp set ename=?,job=?,mgr=?,hiredate=?,sal=?,comm=?,deptno=? where empno = ?", params);
            return update;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    //删除一个员工
    @Override
    public int delete(int empno) {
        try {
            int update = queryRunner.update("delete from emp where empno=?",empno);
            return update;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    //查询员工总数
    @Override
    public long getCount() {
        try {
            long count =  queryRunner.query("select count(*) from emp;", new ScalarHandler<>());
            return count;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }
    //查询所有员工姓名
    @Override
    public List<String> getAllEmpName() {
        try {
            List<String> query = queryRunner.query("select ename from emp;", new ColumnListHandler<String>());
            return query;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    //查询单行,封装数组
    @Override
    public Object[] get() {
        try {
            Object[] objs  = queryRunner.query("select * from emp;", new ArrayHandler());
            return objs;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    //查询多行,封装数组,存放集合
    @Override
    public List<Object[]> getAll() {
        try {
            List<Object[]> query = queryRunner.query("select * from emp;", new ArrayListHandler());
            return query;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }


}

1.11 综合案例

2 Tomcat 和 Xml配置文件

2.1 Tomcat目录介绍

1、bin:该目录下存放的是二进制可执行文件,如果是安装版,那么这个目录下会有两个exe文件:tomcat9.exe、tomcat9w.exe,前者是在控制台下启动Tomcat,后者是弹出UGI窗口启动Tomcat;如果是解压版,那么会有startup.bat和shutdown.bat文件,startup.bat用来启动Tomcat,但需要JDK的配置,shutdawn.bat用来停止Tomcat;

2、conf:这是一个非常非常重要的目录,这个目录下有四个最为重要的文件:

server.xml:配置整个服务器信息。例如修改端口号,添加虚拟主机等;

tomcatusers.xml:存储tomcat用户的文件,这里保存的是tomcat的用户名及密码,以及用户的角色信息。可以按着该文件中的注释信息添加tomcat用户,然后就可以在Tomcat主页中进入Tomcat Manager页面了;

web.xml:部署描述符文件,这个文件中注册了很多MIME类型,即文档类型。这些MIME类型是客户端与服务器之间说明文档类型的,如用户请求一个html网页,那么服务器还会告诉客户端浏览器响应的文档是text/html类型的,这就是一个MIME类型。客户端浏览器通过这个MIME类型就知道如何处理它了。当然是在浏览器中显示这个html文件了。但如果服务器响应的是一个exe文件,那么浏览器就不可能显示它,而是应该弹出下载窗口才对。MIME就是用来说明文档的内容是什么类型的!

context.xml:对所有应用的统一配置,通常我们不会去配置它。

3、lib:Tomcat的类库,里面是一大堆jar文件。如果需要添加Tomcat依赖的jar文件,可以把它放到这个目录中,当然也可以把应用依赖的jar文件放到这个目录中,这个目录中的jar所有项目都可以共享之,但这样你的应用放到其他Tomcat下时就不能再共享这个目录下的Jar包了,所以建议只把Tomcat需要的Jar包放到这个目录下;

4、logs:这个目录中都是日志文件,记录了Tomcat启动和关闭的信息,如果启动Tomcat时有错误,那么异常也会记录在日志文件中。

5、temp:存放Tomcat的临时文件,这个目录下的东西可以在停止Tomcat后删除!

6、webapps:存放web项目的目录,其中每个文件夹都是一个项目;如果这个目录下已经存在了目录,那么都是tomcat自带的项目。其中ROOT是一个特殊的项目,在地址栏中没有给出项目目录时,对应的就是ROOT项目。http://localhost:8080/examples,进入示例项目。其中examples就是项目名,即文件夹的名字。

7、work:运行时生成的文件,最终运行的文件都在这里。通过webapps中的项目生成的!可以把这个目录下的内容删除,再次运行时会生再次生成work目录。当客户端用户访问一个JSP文件时,Tomcat会通过JSP生成Java文件,然后再编译Java文件生成class文件,生成的java和class文件都会存放到这个目录下。

8、LICENSE:许可证。

9、NOTICE:说明文件。

2.2 一些乱码问题

response.setContentType("text/html");
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
解决servlet打印乱码问题


help 	--> 	Edit Custom Vm option 	添加	 -Dfile.encoding=utf-8
在toncat中的Vm option	 添加 	  -Dfile.encoding=utf-8
解决控制台service乱码问题


设置tomcat中conf-->server.xml
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
解决request.getRequestDispatcher乱码

2.3 web.xml

1.Web 应用名称

<display-name>firstweb</display-name>

2.Web 应用描述:给出于此相关的说明性文本

<desciption>Tomcat Example servlets and JSP pages.</desciption>

3.映射,servlet初始值

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>cn.qf.servlet.HelloServlet</servlet-class>
    
    <init-param>
            <param-name>username</param-name>
            <param-value>huangming</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>111111</param-value>
        </init-param>
    
</servlet>

<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hm</url-pattern>
</servlet-mapping>

4.项目首页配置

 <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

5.配置网站错误页面

 <error-page>
    <error-code>404</error-code>
    <location>/error/404.html</location>
  </error-page>
  <error-page>
    <error-code>500</error-code>
    <location>/error/500.html</location>
  </error-page>
  <error-page>
    <location>/error/error.html</location>
</error-page>

3 servlet

3.1 servlet的使用

生命周期

Servlet接口
在ServletAPI中最重要的是Servlet接口,所有Servlet都会直接或间接的与该接口发生联系,或是直接实现该接口,或间接继承自实现了该接口的类。
该接口包括以下五个方法:

init(ServletConfig config)
ServletConfig getServletConfig()
service(ServletRequest req,ServletResponse res)
String getServletInfo()
destroy()

处理方式:

(1)第一次访问Servlet时,服务器会创建Servlet对象,并调用init方法,再调用service方法
(2)第二次再访问时,Servlet对象已经存在,不再创建,也不再初始化,执行service方法
(3)当服务器停止,会释放Servlet,调用destroy方法

servlet的两种创建方式:

​ 继承HttpServlet或者GenericServlet方法

​ 实现Servlet接口

使用注解方式:

注解类 WebServlet

value: 配置url路径

urlPatterns:配置url路径 ,和value作用一样,不能同时使用

loadOnStartup:配置Servlet的创建的时机, 如果是0或者正数 启动程序时,则创建,如果是负数,则访问时创建。

// 获取初始值
ServletConfig servletConfig = getServletConfig();
String initName = servletConfig.getInitParameter("name");
String initPassword = servletConfig.getInitParameter("password");
容器在进行url-pattern配置的时候是遵循一定的匹配原则的
url-pattern定义匹配规则,取值说明:
精确匹配     /具体的名称		只有url路径是具体的名称的时候才会触发Servlet
后缀匹配     *.xxx			只要是以xxx结尾的就匹配触发Servlet
通配符匹配   /* 			    匹配所有请求,包含服务器的所有资源
通配符匹配   /                匹配所有请求,包含服务器的所有资源,不包括.jsp

load-on-startup 
1元素标记容器是否应该在web应用程序启动的时候就加载这个servlet。
2它的值必须是一个整数,表示servlet被加载的先后顺序。
3如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
4如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

3.2 request

获取请求参数

​ html页面或者jsp页面通过post或者get方法到servlet的数据可以通过request来获取

html页面

<form action="HelloServlet">
<label>姓名:</label><input name="name"><br/>
<label>年龄:</label><input  name="age"><br/>
<input type="submit" value="提交">

servlet页面

//post方法:
		//获取表单提交的姓名
		String name=request.getParameter("name");
		//获取年龄
		String age=request.getParameter("age");
		//服务端输出打印
		System.out.println(request.getRemoteAddr()+"发来信息:姓名:"+name+"---->年龄:"+age);
	}

请求方式

默认的请求方式是get

  • get请求
GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连
GET提交的数据大小有限制(因为浏览器对URL的长度有限制)
GET方式提交数据,会带来安全问题
效率高
  • post请求
POST方法是把提交的数据放在HTTP包的Body中
POST方法提交的数据没有限制
POST提交的数据相对安全
效率相对没有GET高

3.3 respons

​ 用于响应客户端请求并向客户端输出信息

​ 包含的响应内容的所有信息

主要方法:

方法名说明
setHeader(name,value)设置响应头信息
getContentType(String)设置响应文件类型,响应式得到编码格式
setCharecterEncoding(String)设置服务器编码格式
getWrite()获取字符输出流

3.4 页面跳转

跳转分两种,一种是转发,另一种是重定向

重定向

重定向就是通过各种方法将网络请求重新定个方向转到其它位置。

	  客户浏览器发送http请求
----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器
----》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址
----》服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。
特点:
1,重定向是客户端行为。
2,重定向是浏览器做了至少两次的访问请求。
3,重定向浏览器地址改变。
4,重定向两次跳转之间传输的信息会丢失(request范围)。
5,重定向可以指向任何的资源,包括当前应用程序中的其他资源,同一个站点上的其他应用程序中的资源,其他站点的资源。注意:传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录

方法:

response.sendRedirect("/index.html");
response.sendRedirect(request.ContextPath()+"/index.jsp");

转发

原理

    客户浏览器发送http请求
    ----》web服务器接受此请求
    --》调用内部的一个方法在容器内部完成请求处理和转发动作
    ----》将目标资源发送给客户。在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。

特点

1,转发是服务器行为
2,转发是浏览器只做了一次访问请求
3,转发浏览器地址不变
4,转发两次跳转之间传输的信息不会丢失,所以可以通过request进行数据的传递
5,转发只能将请求转发给同一个WEB应用中的组件
注意:如果创建RequestDispatcher 对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。

方法:

request.getRequestDispatcher("/login.html").forward(request, response);

4 状态管理

  • 现有问题:
    • HTTP协议是无状态的,不能保存每次提交的信息。
    • 客户端发送一个新的请求,服务器无法知道它是否与上次的请求有联系。
    • 对于需要多次提交数据才能完成的Web操作,比如登录来说,较为麻烦
  • 概念:
    • 将浏览器与Web服务器之间多次交互当作-个整体来处理。
    • 将多次交互所涉及的数据(即状态)保存下来。
  • 分类:
    • 客户端状态管理技术:将状态保存在客户端,代表性的是Cookie技术。
    • 服务器状态管理技术:将状态保存在服务器端,代表性的是Session技术。

1 cookie

●概念:
●Web服务器在HTTP响应消息头中附带传送给浏览器的一小段数据。
●当浏览器保存了Cookie,之后每次访问该服务器时,会通过请求头回传该Cookie数据。
●Cookie主要由标识该信息的名称(name )和值(value) 组成。

// 创建cookie对象
Cookie cookie = new Cookie("useranme", "huangming");
// 设置cookie可使用路径
cookie.setPath("/");
// 设置cookie生命周期
// <0 默认值   =0 session失效   >0 cookie存活时间
cookie.setMaxAge(60*60*24);

cookie.setHttpOnly(true);
// 响应给客户端
response.addCookie(cookie);
// 获取cookie并返回
Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName() + "..." + cookie.getValue());
        }
// 修改cookie
// 只需要保证cookie的名字和路径一致即可修改
Cookie cookie = new Cookie("useranme", "huangming");
cookie.setPath("/");
cookie.setMaxAge(60*60*24*7);
response.addCookie(cookie);
// cookie存储中文和取中文
Cookie cookie2 = new Cookie(URLEncoder.encode("姓名", "utf-8"), URLEncoder.encode("张三", "utf-8"));
Cookie cookie2 = new Cookie(URLDecoder.encode(cookie.getName(), "utf-8"), URLDecoder.encode(cookie.getValue(), "utf-8"));

cookie禁用方案

URL重写
访问服务器上的某个地址时,对地址做重写( 即在原来的地址后面加上了sessionid)。
response.encodeRedirectURL (String url)生成重写的URL。

cookie总结

  1. Cookie的优点:
    1. 可配置到期规则。
    2. 简单性: Cookie是一 种基于文本的轻量结构,包含简单的键值对。
    3. 数据持久性: Cookie默认在过期之前是可以一 直存在客户端浏览器.上的。
  2. Cookie的缺点:
    1. 大小受到限制:
      大多数浏览器对Cookie的大小有4K、8K字节的限制。
    2. 用户配置为禁用:
      有些用户禁用了浏览器或客户端设备接收Cookie的能力,因此限制了这一功能。
    3. 潜在的安全风险:
      Cookie可能会被篡改。会对安全性造成潜在风险或者导致依赖于Cookie的应用程序失败。

2 session

●概念:
Session用于记录用户的状态。
Session指的是在一段时间内, 单个客户端与Web服务器的一-连串相关的交互过程。
在一个Session中,客户可能会多次请求访问同一个资源或不同的服务器资源。

session作用域

​ 作用域:拥有存储数据的空间,作用范围是一次会话有效。
​ 一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话。
​ 可以将数据存入Session作用域,在一次会话的任意位置进行获取。
​ 可传递任何数据(基本数据类型、对象、集合、数组)。

session的一些方法

// 获取session
HttpSession session = request.getSession();
// session保存数据
session.setAttribute("username", username);
// session获取数据
session.getAttribute("username");
// session移除数据
session.removeAttribute("username");

session的生命周期

开始:

​ 第一次使用session的请求产生,则创建session

结束:

​ 浏览器关闭

​ session超时,则失效

​ session.setMaxInactiveInterval(seconds); //设置session存活时间

​ 手工销毁,则失效

​ session,invalidate();

session与request的区别

​ request是一次请求有效,请求改变,则request改变

​ session是一次会话有效,浏览器改变,则session改变

3 servletContext(application)

概念

全局对象,拥有作用域,对应一个Tomcat中的Web应用。
当Web服务器启动时,会为每一个Web应用程 序创建一块共享 的存储区域。
ServletContext在Web服务器启动时创建,服务器关闭时销毁。

获取servletContext

GenericServlet提供了getServletContext()方法。
HttpServletRequest提供了getServletContext()方法。.
HttpSession提供了getServletContext()方法。

方法

// 获取项目真实路径
String realpath=servletContext.getRealPath("/");
// 获取应用上下文路径
servletContext.getContextPath()// 全局容器
servletContext.setAttribute("name" ,value);
servletContext.getAttribute("name");
servletContext.removeAttribute("name");

例子:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html");
    response.setCharacterEncoding("utf-8");
    PrintWriter out = response.getWriter();

    ServletContext application = getServletContext();
    Integer count = null;
    synchronized (this) {
        count = (Integer) application.getAttribute("count");
        if (count == null) {
            count = 1;
        } else {
            count++;
        }
        application.setAttribute("count", count);
    }
    out.write("访问次数:" + count);
}

SercletvConText读取web应用下的文件

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1 类加载读取java项目的bin或者web下的classes
    //2 SercletvConText读取web应用下的文件

    ServletContext servletContext = getServletContext();
    String path = servletContext.getRealPath("/img/2.jpg");
    FileInputStream fis = new FileInputStream(path);

    byte[] bytes = new byte[1024];
    int len = 0;

    // 保存文件而不是打开
    response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("2.jpg", "utf-8"));
    ServletOutputStream os = response.getOutputStream();
    while ((len = fis.read(bytes)) != -1){
        os.write(bytes,0,len);
    }

    fis.close();
}

5 JSP

5.1 jsp的基本使用方法

<%  java代码 %>
<% 定义成员变量,方法 %>
<%=输出变量的值或者调用方法 %>
<%!
    private String email = "123@qq.com";
    private int age = 21;

    private String getEmail(){
        return  email;
    }
%>
<%=age %>
<%=getEmail() %>

5.2 三大指令

page指令

​ page指令没有必须属性,都是可选属性,每个jsp页面可以重复page指令。

<%@page %>
<%@ page language=”java”%>
<%@ page import=”java.util.*”%>
<%@ page pageEncoding=”utf-8”%>

page指令的pageEncoding和contentType

​ pageEncoding指定当前JSP页面的编码!这个编码是给服务器看的,服务器需要知道当前JSP使用的编码,不然服务器无法正确把JSP编译成java文件。所以这个编码只需要与真实的页面编码一致即可!

​ contentType属性与response.setContentType()方法的作用相同!它会完成两项工作,一是设置响应字符流的编码,二是设置content-type响应头。例如:<%@ page contentType=”text/html;charset=utf-8”%>,它会使“真身”中出现response.setContentType(“text/html;charset=utf-8”)。

** 当pageEncoding和contentType只出现一个时,那么另一个的值与出现的值相同。如果两个都不出现,那么两个属性的值都是ISO-8859-1。所以通过我们至少设置它们两个其中一个!**

page指令的errorPage和isErrorPage

​ 在一个JSP页面出错后,Tomcat会响应给用户错误信息(500页面)!如果你不希望Tomcat给用户输出错误信息,那么可以使用page指令的errorPage来指定错误页!也就是自定义错误页面,例如:<%@page errorPage=”xxx.jsp”%>。这时,在当前JSP页面出现错误时,会请求转发到xxx.jsp页面。浏览器地址栏是不会变化的。并且转发是留消息头而不留消息体即当前页面输出的内容是不会在错误页面出现的。

a.jsp

<%@ page import="java.util.*" pageEncoding="UTF-8"%>
<%@ page  errorPage="b.jsp" %>
    <%
        if(true)
            throw new Exception("哈哈~");
    %>

b.jsp

<%@ page pageEncoding="UTF-8"%>
<html>  
    <body>   
        <h1>出错啦!</h1>  
    </body> 
</html>

在上面代码中,a.jsp抛出异常后,会请求转发到b.jsp。在浏览器的地址栏中还是a.jsp,因为是请求转发!
而且客户端浏览器收到的响应码为200,表示请求成功!如果希望客户端得到500,那么需要指定b.jsp为错误页面(page添加属性为:isErrorPage=”t rue”)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 
<%@ page  isErrorPage="true"  %> 
<html>  
    <body>   
        <h1>出错啦!</h1>    
        <%=exception.getMessage() %> 
    </body><!-- 设置b.jsp页面为错误页,在错误页中就可以使用exception隐藏对象了。--><!-- 注意:一旦转发到错误页,那么Tomcat会把状态码设置为500,而不在是200了。-->
</html>

include指令

​ JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。
Include指令的语法格式如下:<%@ include file=”文件相对 url 地址” %>

<%@include file="header.jsp" %>

taglib指令

​ 用于在当前jsp页面导入第三方标签库。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

prefix:指定标签前缀,这个东西可以随意起名

uri:指定第三方标签库的uri(唯一标识)

当然,需要先把第三方标签库所需jar包放到类路径中。

5.3 四大域

作用范围

page范围:只在一个页面保留数据(javax.servlet.jsp.PageContext(抽象类))

request范围:只在一个请求中保存数据(javax.servlet.httpServletRequest)

Session范围:在一次会话中保存数据,仅供单个用户使用(javax.servlet.http.HttpSession)

Application范围:在整个服务器中保存数据,全部用户共享(javax.servlet.ServletContext)

5.4 七大动作

jsp:include动作元素

jsp:include动作元素用来包含静态和动态的文件。该动作把指定文件插入正在生成的页面。改动作元素与<%@ include file=”文件相对 url 地址” %>效果相似,区别在于此动作会把包含进来的jsp页面也转变为java文件并加载成字节码文件。

属性描述
page包含在页面中的相对URL地址。
flush布尔属性,定义在包含资源前是否刷新缓存区。
<jsp:include page="date.jsp" flush="true" />

jsp:useBean动作元素

jsp:useBean 动作用来加载一个将在JSP页面中使用的JavaBean。

这个功能非常有用,因为它使得我们可以发挥 Java 组件复用的优势。

<jsp:useBean id="name" class="package.class" />

在类载入后,我们既可以通过 jsp:setProperty 和 jsp:getProperty 动作来修改和检索bean的属性。

jsp:setProperty动作元素

jsp:setProperty用来设置已经实例化的Bean对象的属性,有两种用法。首先,你可以在jsp:useBean元素的外面(后面)使用jsp:setProperty.

<jsp:useBean id="myName" ... />
...
<jsp:setProperty name="myName" property="someProperty" .../>

此时,不管jsp:useBean是找到了一个现有的Bean,还是新创建了一个Bean实例,jsp:setProperty都会执行。第二种用法是把jsp:setProperty放入jsp:useBean元素的内部.

<jsp:useBean id="myName" ... >
...
   <jsp:setProperty name="myName" property="someProperty" .../>
</jsp:useBean>

此时,jsp:setProperty只有在新建Bean实例时才会执行,如果是使用现有实例则不执行jsp:setProperty。

属性描述
namename属性是必需的。它表示要设置属性的是哪个Bean。
propertyproperty属性是必需的。它表示要设置哪个属性。有一个特殊用法:如果property的值是"*",表示所有名字和Bean属性名字匹配的请求参数都将被传递给相应的属性set方法。
valuevalue 属性是可选的。该属性用来指定Bean属性的值。字符串数据会在目标类中通过标准的valueOf方法自动转换成数字、boolean、Boolean、 byte、Byte、char、Character。例如,boolean和Boolean类型的属性值(比如"true")通过 Boolean.valueOf转换,int和Integer类型的属性值(比如"42")通过Integer.valueOf转换。   value和param不能同时使用,但可以使用其中任意一个。
paramparam 是可选的。它指定用哪个请求参数作为Bean属性的值。如果当前请求没有参数,则什么事情也不做,系统不会把null传递给Bean属性的set方法。因此,你可以让Bean自己提供默认属性值,只有当请求参数明确指定了新值时才修改默认属性值。

jsp:getProperty动作元素

jsp:getProperty动作提取指定Bean属性的值,转换成字符串,然后输出。

<jsp:useBean id="myName" ... />
...
<jsp:getProperty name="myName" property="someProperty" .../>
属性描述
name要检索的Bean属性名称。Bean必须已定义
property表示要取得bean的值

jsp:forward 动作元素

jsp:forward动作把请求转到另外的页面。jsp:forward标记只有一个属性page。

<jsp:forward page="相对 URL 地址" />

5.5 九大内置对象

out输出流对象

隐藏对象out是javax.servlet.jsp.JspWriter类的实例服务器向客户输出的字符内容可以通过out对象输出。获取方法: PrintWriter out = response.getWriter();out对象常用的方法如下:
1   void clear()  清除缓冲区的内容
2   void clearBuffer()  清除缓冲区的当前内容
3   void flush()  将缓冲内容flush到客户端浏览器
4   int getBufferSize()  返回缓冲大小,单位KB
5   int getRemaining()  返回缓冲剩余大小,单位KB
6   isAutoFlush()  返回缓冲区满时,是自动清空还是抛出异常
7   void close()   关闭输出流

request请求对象

​ 隐藏对象request是javax.servlet.ServletRequest类的实例,代表客户端的请求。request包含客户端的信息以及请求的信息,如请求那个文件,附带的地址参数等。每次客户端的请求都会产生一个request实例。

object getAttribute(String name)  返回指定属性的属性值

String getCharacterEncoding()  返回字符编码方式

String getParameter(String name)  返回name指定参数的参数值

void setAttribute(String key Object obj)  设置属性的属性值

String getRealPath(String path)  返回一虚拟路径的真实路径

void setCharacterEncoding(“gb2312”)  设置接受参数的字符集

response响应对象

​ 隐藏对象response是javax.servlet.ServletResponse类的实例,代表客户端的响应。服务器端的任何输出都通过response对象发送到客户端浏览器。每次服务器端都会响应一个response实例。

1   String getCharacterEncoding()   返回响应用的是何种字符编码
2   ServletOutputStream getOutputStream()  返回响应的一个二进制输出流
3   PrintWriter getWriter()  返回可以向客户端输出字符的一个对象
4    void setContentLength(int len)  设置响应头长度
5   void setContentType(String type)  设置响应的MIME类型
6   sendRedirect(java.lang.String location)  重新定向客户端的请求
7   void setCharacterEncoding(“gb2312”)  设置响应头的字符集

session会话对象

​ 隐藏对象session是javax.servlet.http.HttpSession类的实例。session与cookie是记录客户访问信息的两种机制,session是用于服务器端保存用户信息,cookie用于在客户端保存用户信息。Servlet中通过request.getSession()来获取session对象,而JSP中可以直接使用。如果JSP中配置了<%@page session=”false”%>,则隐藏对象session不可用。每个用户对应一个session对象。

1   long getCreationTime()  返回Session创建时间
2   public String getId()  返回Session创建时JSP引擎为它设的唯一ID号
3   long getLastAccessedTime()  返回此Session里客户端最近一次请求时间
4   int getMaxInactiveInterval()   返回两次请求间隔多长时间此Session被取消(ms)
5   String[] getValueNames()  返回一个包含此Session中所有可用属性的数组
6   void invalidate() 取消Session,使Session不可用
7   boolean isNew()  返回服务器创建的一个Session,客户端是否已经加入
8   void removeValue(String name)  删除Session中指定的属性
9   void setAttribute(String key,Object obj)  设置Session的属性
10 Object getAttribute(String name)   返回session中属性名为name的对象

application应用程序对象

​ 隐藏对象application是javax.servlet.ServletContext类的对象。application封装JSP所在Web应用程序的信息,例如web.xml中国配置的全局的初始化信息。Servlet中application对象需要通过ServletConfig.getServletContext()来获取。整个Web应用程序对应一个application对象。

Object getAttribute(String name)  返回application中属性为name的对象

void setAttribute(String name,Object value)  设置application属性

void removeAttribute(String name)  移除application属性

String getRealPath(String relativePath)  返回Web应用程序内相对网址对应的绝对路径

page页面对象

隐藏对象page是javax.servlet.jsp.HttpJspPage类的实例。page对象代表当前JSP页面,是当前JSP编译后的Servlet类的对象。page想当于Java类中的关键字this

pageContext页面上下文对象

​ 隐藏对象pageContext为javax.servlet.jsp.PageContext类的实例。pageContext对象代表当前JSP页面编译后的内容。通过pageContext能够获取到JSP中的资源。

1    JspWriter getOut()  返回out对象
2    HttpSession getSession()   返回Session对象(session)
3    Object getPage()  返回page对象
4    ServletRequest getRequest()   返回request对象
5    ServletResponse getResponse()   返回response对象

6    void setAttribute(String name,Object attribute)   设置属性及属性值 ,在page范围内有效
7    void setAttribute(String name,Object obj,int scope)   在指定范围内设置属性及属性值 ,int1=page,2=request,3=session,4=application
8   public Object getAttribute(String name)  取属性的值
9   Object getAttribute(String name,int scope)  在指定范围内取属性的值
10   public Object findAttribute(String name)  寻找一属性,返回起属性值或NULL
11   void removeAttribute(String name)  删除某属性
12   void removeAttribute(String name,int scope)   在指定范围删除某属性
13   int getAttributeScope(String name)   返回某属性的作用范围
14  Enumeration getAttributeNamesInScope(int scope)  返回指定范围内可用的属性名枚举
15   void release()  释放pageContext所占用的资源
16  void forward(String relativeUrlPath)   使当前页面重导到另一页面
17   void include(String relativeUrlPath)   在当前位置包含另一文件

Eexception异常对象

隐藏对象exception为java.lang.Exception类的对象。exception封装了JSP中抛出的异常信息。要使用exception隐藏对象,需要设置<%@page isErrorPage”true”%>。隐藏对象exception通常被用来处理错误页面,

config配置对象

隐藏对象config是javax.servlet.ServletConfig类的实例,ServletConfig封装了配置在web.xml中初始化JSP的参数。JSP中通过config获取这些参数。每个JSP文件中共有一个config对象。config对象的常用方法如表:
1  String getInitParameter(String name)  返回配置在web.xml中初始化参数
2  Enumeration getInitParameterNames()  返回所有的初始化参数名称
3  ServletContext getServletContext()  返回ServletContext对象
4  String getServletName  返回Servlet对象

5.6 EL表达式

从四个域中通过key找到简单数据并显示出来

${name}      <!-- 类比于<%=pageContext.findAttribute("name") %> -->

四个域的寻找顺序是 page,request,session,application。用EL表达式还有个好处,若找不到键值为name的属性值,不会显示null,会显示空字符串。若是确定键值是在request域中,则可以用如下EL表达式代码

${requestScope.name}
<%
    User user = new User("黄铭", 21, "男");
    pageContext.setAttribute("user", user);

    List<String> list = new ArrayList<>();
    list.add("西瓜");
    list.add("苹果");
    list.add("香蕉");
    list.add("榴莲");
    list.add("橘子");
    pageContext.setAttribute("list", list);

    Map<String,String> map = new HashMap<>();
    map.put("湖南", "长沙");
    map.put("湖北", "武汉");
    map.put("广东", "广州");
    map.put("四川", "成都");
    pageContext.setAttribute("map", map);
%>
${user}<br>
${user.name}&nbsp;
${user.age}&nbsp;
${user.gender}<br>
<hr width="400" align="left" color="blue">

${list}<br>
${list.get(0)}&nbsp;
${list.get(1)}&nbsp;
${list.get(2)}&nbsp;
${list.get(3)}&nbsp;
${list.get(4)}<br>
<hr width="400" align="left" color="blue">

${map}<br>
${map['湖南']}

运算符

1)语法:${运算表达式}

2)常见运算符:==(eq) !=(ne) <(lt) >(gt) <=(le) >=(ge) &&(and) ||(or) !(not)

3)判断是否为空:${empty name }

4)三目运算符:${name == null?“null”:name }

隐式对象

对象名用法等价JSP代码或作用
param${param.name}request.getParameter(name)
paramValues${paramValues.name}request.getParameterValues(name)//返回一个字符串数组
header${header.name}request.getHeader(name)
headerValues${headerValues.name}request.getHeaderValues(name)
cookie${cookie.name.value}request.getCookie()
initParam${initParam.name}ServletContext.getInitparameter(name)
pageContext${pageContext.request.contextPath}获取应用上下文
pageContext${pageContext.request.sessionid}获取sessionId
pageContext${pageContext.request.remoteAddr}获取主机名

5.7 JSTL

​ JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

​ JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。

<!-- 核心标签库导入语法 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:out> 标签

​ <c:out>标签用来显示一个表达式的结果,与<%= %>作用相似,它们的区别就是<c:out>标签可以直接通过"."操作符来访问属性。举例来说,如果想要访问customer.address.street,只需要这样写:<c:out value=“customer.address.street”>。

​ <c:out>标签会自动忽略XML标记字符,所以它们不会被当做标签来处理。

<!-- 语法格式-->
<c:out value="<string>" default="<string>" escapeXml="<true|false>"/>
属性描述是否必要默认值
value要输出的内容
default输出的默认值主体中的内容
escapeXml是否忽略XML特殊字符true

<c:set> 标签

​ <c:set>标签用于设置变量值和对象属性。

​ <c:set>标签就是jsp:setProperty行为标签的孪生兄弟。

​ 这个标签之所以很有用呢,是因为它会计算表达式的值,然后使用计算结果来设置 JavaBean 对象或 java.util.Map 对象的值。

<!-- 语法格式 -->
<c:set   var="<string>"   value="<string>"   target="<string>"   property="<string>"   scope="<string>"/>
属性描述是否必要默认值
value要存储的值主体的内容
target要修改的属性所属的对象
property要修改的属性
var存储信息的变量
scopevar属性的作用域Page

<c:remove> 标签

​ <c:remove>标签用于移除一个变量,可以指定这个变量的作用域,若未指定,则默认为变量第一次出现的作用域。这个标签不是特别有用,不过可以用来确保JSP完成清理工作。

<!-- 语言格式 -->
<c:remove var="<string>" scope="<string>"/>
属性描述是否必要默认值
var要移除的变量名称
scope变量所属的作用域所有作用域

<c:if>标签

​ <c:if>标签判断表达式的值,如果表达式的值为真则执行其主体内容。

<!-- 语法格式 -->
<c:if test="<boolean>" var="<string>" scope="<string>">   正确后执行的部分 </c:if>
属性描述是否必要默认值
test条件
var用于存储条件结果的变量
scopevar属性的作用域page

<c:choose>, <c:when>, <c:otherwise> 标签

​ <c:choose>标签与Java switch语句的功能一样,用于在众多选项中做出选择。switch语句中有case,而<c:choose>标签中对应有<c:when>,switch语句中有default,而<c:choose>标签中有<c:otherwise>。

<c:forEach>, <c:forTokens> 标签

​ 这些标签封装了Java中的for,while,do-while循环。相比而言,<c:forEach>标签是更加通用的标签,因为它迭代一个集合中的对象。<c:forTokens>标签将字符串分隔为一个子串数组然后迭代它们。

forEach 语法格式

<c:forEach
    items="<object>"
    begin="<int>"
    end="<int>"
    step="<int>"
    var="<string>"
    varStatus="<string>">

    ...

forTokens 语法格式

<c:forTokens
    items="<string>"
    delims="<string>"
    begin="<int>"
    end="<int>"
    step="<int>"
    var="<string>"
    varStatus="<string>">

<c:forEach>标签有如下属性:

属性描述是否必要默认值
items要被循环的信息
begin开始的元素(0=第一个元素,1=第二个元素)0
end最后一个元素(0=第一个元素,1=第二个元素)Last element
step每一次迭代的步长1
var代表当前条目的变量名称
varStatus代表循环状态的变量名称

<c:forTokens>标签与<c:forEach>标签有相似的属性,不过<c:forTokens>还有另一个属性:

属性描述是否必要默认值
delims分隔符

<c:param> 标签

<c:param>标签用于在<c:url>标签中指定参数,而且与URL编码相关。

在<c:param>标签内,name属性表明参数的名称,value属性表明参数的值。

语法格式

<c:param name="<string>" value="<string>"/>

<c:param>标签有如下属性:

属性描述是否必要默认值
nameURL中要设置的参数的名称
value参数的值Body

<c:url> 标签

<c:url>标签将URL格式化为一个字符串,然后存储在一个变量中。

这个标签在需要的时候会自动重写URL。

var属性用于存储格式化后的URL。

<c:url>标签只是用于调用response.encodeURL()方法的一种可选的方法。它真正的优势在于提供了合适的URL编码,包括<c:param>中指定的参数。

语法格式

<c:url
  var="<string>"
  scope="<string>"
  value="<string>"
  context="<string>"/>

<c:url>标签有如下属性:

属性描述是否必要默认值
value基础URL
context本地网络应用程序的名称当前应用程序
var代表URL的变量名Print to page
scopevar属性的作用域Page

6 分页

第一步,pageBean类

​ 需要创建一个page类,此类中需有一下5个成员变量:pageNum(当前页数),pageCount(总页数),pageSize(一页展示的数据数),totalSize(从数据库搜索到的数据得总条数),List (把从数据库搜索到的数据临时存储在一个集合里,因为不知道查询的类,所以此处用泛型)。在构造方法中,pageCount由pageSize和totalSize计算得到。以下是pageBean类的构造方法。

public PageBean(int pageNum, int pageSize, long totalSize, List<T> data) {
    this.pageNum = pageNum;
    this.pageSize = pageSize;
    this.totalSize = totalSize;
    this.data = data;

    //计算pageCount
    pageCount= (int) (totalSize%pageSize==0?totalSize/pageSize:totalSize/pageSize+1);
}

第二步,servlet

​ 创建一个servlet,从request请求中获取pageNum和pageSize,然后通过这两个参数的值决定page_num和page_size的值。再通过调用service的方法获取到pageBean,把pageBean存到request域中后转发到jsp页面显示。

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("utf-8");

    String pageNum = request.getParameter("pageNum");
    String pageSize = request.getParameter("pageSize");

    //条件查询添加代码1处
    
    int page_num = 1;
    int page_size = 5;

    if (!StringUtils.isEmpty(pageNum)) {
        page_num = Integer.parseInt(pageNum);
        if (page_num <= 0) {
            page_num = 1;
        }
    }

    if (!StringUtils.isEmpty(pageSize)) {
        page_size = Integer.parseInt(pageSize);
    }

    BookService bookService = new BookServiceImpl();

    try {
        // 条件查询添加代码2处
        PageBean<Book> bookPageBean = bookService.queryByPage(page_num, page_size);
        request.setAttribute("pageBean", bookPageBean);
        request.getRequestDispatcher("/booklist.jsp").forward(request, response);
    } catch (Exception e) {
        e.printStackTrace();
        request.setAttribute("msg", "查询失败"+e.getMessage());
        request.getRequestDispatcher("/message.jsp").forward(request,response);
    }
}

第三步,jsp页面

​ 创建jsp页面显示数据,用JSTL的<c:foreach>来遍历pageBean.list,其中日期用fmt:formatDate。其中页面间的跳转代码如下,跳转时需要把跳转对象的pageNum和pageSize一同传过去。

<div>
    <a href="${pageContext.request.contextPath}/booklist?pageNum=1&pageSize=${pageBean.pageSize}">首页</a>
    <a href="${pageContext.request.contextPath}/booklist?pageNum=${pageBean.pageNum-1}&pageSize=${pageBean.pageSize}">上一页</a>
    <c:forEach var="page" begin="1" end="${pageBean.pageCount}" step="1">
        <a href="${pageContext.request.contextPath}/booklist?pageNum=${page}&pageSize=${pageBean.pageSize}">${page}</a>
    </c:forEach>
    <c:if test="${pageBean.pageNum>=pageBean.pageCount}">
        <a href="${pageContext.request.contextPath}/booklist?pageNum=${pageBean.pageCount}&pageSize=${pageBean.pageSize}">下一页</a>
    </c:if>
    <c:if test="${pageBean.pageNum<pageBean.pageCount}">
        <a href="${pageContext.request.contextPath}/booklist?pageNum=${pageBean.pageNum+1}&pageSize=${pageBean.pageSize}">下一页</a>
    </c:if>
    <a href="${pageContext.request.contextPath}/booklist?pageNum=${pageBean.pageCount}&pageSize=${pageBean.pageSize}">尾页</a>
</div>

​ 如果要加手动输入数字进行页面跳转,则代码如下:

共【${pageBean.pageCount}/${pageBean.pageNum}】页 <input id="page" type="number" name="page" min="1" max="${pageBean.pageCount}"><input type="button" value="跳转" οnclick="judgePage()">

<script type="text/javascript">
        function judgePage() {
            var n=document.getElementById("page").value;
            window.location="${pageContext.request.contextPath}/booklist?pageNum="+n+"&pageSize=${pageBean.pageSize}";
        }
    </script>

第四步,小功能添加

​ 如果要通过表单条件查询信息,则jsp中,页面跳转的a标签需要加上${search}目的是跳转时显示的信息还是之前条件查询查到的数据。且jsp另需要添加如下代码:

<form action="${pageContext.request.contextPath}/booklist" method="post">
    <input type="text" name="search" value="${search}">
    <input type="submit" value="搜索">
</form>

​ servlet需要request获得到search的值,以此来决定sql语句需要添加的条件查询的拼接部分。在第一步的 //条件查询添加代码1处添加如下代码

String search = request.getParameter("search");
String author = request.getParameter("author");

String condition="where";
if(!StringUtils.isEmpty(search)){
    condition+=" title like '%"+search+"%'";
}
if(!StringUtils.isEmpty(author)){
    if(condition.equals("where")){
        condition+=" author like '%"+author+"%'";
    }else{
        condition+=" and author like '%"+author+"%'";
    }
}

​ 在//条件查询添加代码2处添加如下代码:

if(condition.equals("where")){
    condition="";
}
request.setAttribute("search", search);

//并更改下一句为:
    PageBean<Book> pageBean=bookService.queryByPage(page_num, page_size,condition);

​ 当pageCount过多是,页面值显示当前页面前后10页的数字。则要在pageBean类中添加startPage和endPage两个值,用来决定显示页面数字跳转的范围。pageBean类需要更改构造方法如下:

//第一种情况,正常情况
startPage=pageNum-4;
endPage=pageNum+5;

//第二种情况,页码小于5
if(pageNum<5){
    startPage=1;
    endPage=10;
}
//第三种请求,页码大于
if(pageNum>pageCount-5){
    startPage=pageCount-9;
    endPage=pageCount;
}
//第四种情况,总页数小于10
if(pageCount<10) {
    startPage = 1;
    endPage = pageCount;
}

并且在显示jsp中,<c:foreach>的begin变为 p a g e B e a n . s t a r t P a g e ∗ ∗ , ∗ ∗ e n d 变 为 {pageBean.startPage}**,**end变为 pageBean.startPage,end{pageBean.endPage}.

7 上传,下载

一 、上传

1 UploadUtils工具类

​ 上传文件时以免相同名文件上传时覆盖之前上传的文件,此时需要编写一个方法通过UUID拼接上传文件时文件的真是文件名来获取一个新的文件名。

public static String makeNewFileName(String fileName) {
    String uuid = UUID.randomUUID().toString().replace("-", "");
    return uuid + "_" + fileName;
}

​ 通过创建多级目录来存放上传的文件,

public static String makeNewFilePath(String path, String fileName) {
    int hashCode = fileName.hashCode();
    int path1 = hashCode & 0xf;
    int path2 = (hashCode >> 4) & 0xf;
    String newPath = path + File.separator + path1 + File.separator + path2;
    File file = new File(newPath);
    if (!file.exists()) {
        file.mkdirs();
    }
    return newPath;
}

​ 通过Flie和Map集合把查到的所有文件的文件名存储到集合中,集合的key因为唯一,所以存储文件的带UUID的名字,value因为可以重复,所以value存储去UUID的文件名。

public static void listFile(File dir, HashMap<String, String> map) {
    File[] files = dir.listFiles();
    if (files != null && files.length > 0) {
        for (File file : files) {
            if (file.isDirectory()) {
                listFile(file, map);
            } else {
                String uuidFilename = file.getName();
                String filename = uuidFilename.substring(uuidFilename.indexOf("_") + 1);
                map.put(uuidFilename, filename);
            }
        }
    }
}

2 UploadServlet

​ 此类中主要对上传的文件进行处理,其中包括:获取存储文件的文件夹的真实路径,定义文件可上传的后缀名集合并判断此文件是否符合上传要求获取上传文件并判断上传的内容是普通字段还是文件字段,最后把文件存储到真实路径下。

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html");
    response.setCharacterEncoding("utf-8");

    // 获取文件夹的真实路径
    String realPath = request.getServletContext().getRealPath("/upload");

    // 判断文件是否存在
    File file = new File(realPath);
    if (!file.exists()) {
        file.mkdirs();
    }

    // 文件后缀可包含的类型集合
    List<String> allowExts = new ArrayList<String>();
    allowExts.add("jpg");
    allowExts.add("png");
    allowExts.add("gif");


    // 用于获取使用multipart/form-data格式传递的http请求的请求体,通常用于获取上传文件。
    Collection<Part> parts = request.getParts();
    PrintWriter out = response.getWriter();

    if (parts != null && parts.size() > 0) {
        for (Part part : parts) {
            // 获取提交的文件名
            String submittedFileName = part.getSubmittedFileName();

            if (submittedFileName == null) {// 普通字段
                String name = part.getName();
                String value = request.getParameter(name);
                System.out.println(name + "..." + value);
            } else { // 文件字段

                // 判断文件是否为空
                if (submittedFileName == "") {
                    continue;
                }

                //System.out.println(submittedFileName);
                // 从请求头获取文件名
                String dis = part.getHeader("content-disposition");
                System.out.println(dis);
                String filename = dis.substring(dis.lastIndexOf("filename=") + 10, dis.length() - 1);
                filename = filename.substring(filename.lastIndexOf("//") + 1);
                System.out.println(filename);

                // 获取文件后缀名
                String ext = filename.substring(filename.lastIndexOf(".") + 1);
                if (!allowExts.contains(ext)) {
                    out.println(filename + "不符合上传文件类型要求...");
                    continue;
                }

                // 1 创建新的文件名
                String newFileName = UploadUtils.makeNewFileName(filename);
                // 2 创建新的路径
                String newFilePath = UploadUtils.makeNewFilePath(realPath, filename);
                // 3 把文件保存
                part.write(newFilePath + File.separator + newFileName);
                // 4 清空缓part冲区
                part.delete();
                out.println("上传成功" + filename);
            }
        }
    }

}

二、下载

1 ListFileServlet

​ 在jsp页面显示之前,需要调用一个servlet来把map设置在域中转发到jsp页面。以便于在jsp页面能够下载图片。其中map集合存储的是真是路径下能够下载的文件。

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html");
    response.setCharacterEncoding("utf-8");

    // 读取可被下载的文件
    String realPath = request.getServletContext().getRealPath("/upload");
    HashMap<String, String> map = new HashMap<>();
    UploadUtils.listFile(new File(realPath), map);

    // 放入域中
    request.setAttribute("map", map);

    // 转发
    request.getRequestDispatcher("/list.jsp").forward(request, response);
}

2 DownLoadServlet

​ 通过list.jsp页面上传的key值,得到带UUID的文件名,再通国去UUID的文件名和存放文件的根路径获得存放文件的真实路径后。通过获取下载请求头和输出流下载文件。

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 乱码
    request.setCharacterEncoding("utf-8");
    // 获取文件名
    String uuidFilename = request.getParameter("filename");
    
    // 去uuid的文件名
    String filename = uuidFilename.substring(uuidFilename.indexOf("_") + 1);
    // 存放文件的根路径
    String realPath = request.getServletContext().getRealPath("/upload");
    // 获取真正的目录
    String path = UploadUtils.makeNewFilePath(realPath,filename);
    // 文件绝对路径
    File file = new File(path + File.separator + uuidFilename);

    if (file.exists()){
        // 下载文件的请求头
        response.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(filename,"utf-8"));
        // 输出流,下载的方式
        ServletOutputStream os = response.getOutputStream();
        FileInputStream fis = new FileInputStream(file);
        byte[] bytes = new byte[1024 * 4];
        int len = 0;
        while ((len = fis.read(bytes)) != -1){
            os.write(bytes,0,len);
        }
        os.close();
        fis.close();
    }else {
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("文件不存在");
    }
}

三、图片显示在网页上

<img src="${pageContext.request.contextPath}/picture?pic=${book.picture}" width="50" height="50">
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String pic = request.getParameter("pic");
    if(StringUtils.isEmpty(pic)){
        return;
    }
    String realPath = request.getServletContext().getRealPath("/WEB-INF/pictures/" + pic);
    File file=new File(realPath);
    if(file.exists()){
        ServletOutputStream os = response.getOutputStream();
        FileInputStream fis=new FileInputStream(file);
        byte[] buf=new byte[1024*4];
        int len=0;
        while((len=fis.read(buf))!=-1) {
            os.write(buf, 0, len);
        }
    }

}

8 filter,listener

Java Web开发涉及多个方面,从基础的Java语言掌握到Web应用的构建与部署,再到框架的使用和高级功能的实现。以下是一个系统化的学习路径,结合了Java Web开发的核心技术点和学习资源建议。 ### 1. Java Web开发基础 在开始学习Java Web开发之前,确保对Java基础语法、面向对象编程、异常处理、集合框架和IO流等有扎实的掌握。这些是构建Web应用的基础,也是后续学习的必要前提。 ### 2. 理解Web开发基本概念 了解Web应用的基本工作原理,包括HTTP协议、Servlet、JSP、Tomcat服务器等。Java Web应用通常基于Servlet API和JSP技术构建,Tomcat是常用的Servlet容器,适合初学者使用和学习。 ### 3. 掌握Servlet和JSP - **Servlet**:用于处理HTTP请求和响应,可以动态生成网页内容。需要掌握Servlet生命周期、请求和响应对象的使用、会话管理(如Cookie和Session)等内容。 - **JSP**:Java Server Pages技术允许在HTML页面中嵌入Java代码,用于生成动态内容。学习JSP时需要理解JSP生命周期、EL表达式和JSTL标签库的使用。 ### 4. MVC三层架构 Java Web开发通常采用MVC(Model-View-Controller)架构,将业务逻辑、数据和用户界面分离。Model负责数据处理,View负责显示数据,Controller负责接收用户输入并协调Model和View[^1]。 ### 5. 使用Maven进行项目管理 Maven是一个强大的项目管理工具,能够简化项目的构建、依赖管理和项目信息管理。学习Maven可以帮助开发者更高效地管理Java Web项目。 ### 6. 学习框架的使用 随着Web开发的复杂度增加,使用框架可以提高开发效率和代码质量。常见的Java Web开发框架包括: - **Spring**:一个全面的编程和配置模型,用于构建企业级应用。 - **Spring MVC**:基于Spring框架的Web模块,提供灵活的Web层解决方案。 - **MyBatis**:一个持久层框架,简化了数据库操作。 ### 7. 掌握数据库操作 Java Web应用通常需要与数据库进行交互。学习JDBC(Java Database Connectivity)是连接和操作数据库的基础,同时了解ORM(对象关系映射)框架如Hibernate或MyBatis可以提高开发效率。 ### 8. 安全性和性能优化 学习如何保护Web应用免受常见攻击(如SQL注入、XSS攻击等)是必要的。此外,了解Web应用的性能优化策略,如缓存机制、数据库优化等,也是高级Java Web开发者必备的技能。 ### 9. 学习部署和维护 掌握如何将Java Web应用部署到生产环境,包括服务器配置、应用部署、日志管理和监控等。学习使用工具如Docker可以帮助简化部署过程。 ### 10. 持续学习和实践 技术不断进步,持续学习最新的框架和技术是保持竞争力的关键。参与开源项目、阅读官方文档、参加技术社区活动都是提升技能的好方法。 ### 学习资源推荐 - **书籍**:《Java Web程序设计任务教程》是学习Java Web开发的经典教材,适合初学者和有一定基础的开发者[^1]。 - **在线课程**:参考“狂神说Java笔记”中的Java后端开发工程师学习笔记,涵盖从基础到高级的全面内容[^2]。 - **实践项目**:通过实际项目练习,如开发一个完整的博客系统或电商网站,可以加深对Java Web开发的理解。 ### 示例代码 以下是一个简单的Servlet示例,展示了如何处理HTTP GET请求: ```java import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorldServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<h1>Hello, World!</h1>"); } } ``` ### 示例JSP页面 以下是一个简单的JSP页面示例,展示了如何在HTML中嵌入Java代码: ```jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Simple JSP Page</title> </head> <body> <h1>Welcome to JSP!</h1> <p>Current time: <%= new java.util.Date() %></p> </body> </html> ``` ### 相关问题 1. 如何在Java Web应用中实现用户登录功能? 2. Java Web开发中常用的框架有哪些,各自的特点是什么? 3. 如何使用Maven管理Java Web项目的依赖? 4. Java Web应用的安全性如何保障? 5. 如何优化Java Web应用的性能?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值