zoukankan      html  css  js  c++  java
  • Java Web

    JDBC:

    1. 概念:Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
      JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类使用统一的Java 代码操作所有的关系型数据库!!!

    2. 快速入门:
      步骤:
        1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
          1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
          2.右键-->Add As Library
        2. 注册驱动
        3. 获取数据库连接对象 Connection
        4. 定义sql
        5. 获取执行sql语句的对象 Statement
        6. 执行sql,接受返回结果
        7. 处理结果
        8. 释放资源

    package cn.kaoyan;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java_db", "root", "123456");
            String sql = "select * from user;";
    
            Statement statement = conn.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
    
            while (resultSet.next()){
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                System.out.println(id+","+name+","+age);
            }
            
            // 释放资源
            statement.close();
            conn.close();
    
        }
    
    
    }
    View Code
    package cn.kaoyan;
    
    import java.sql.*;
    
    public class Test {
        public static void main(String[] args)  {
    
            Connection conn = null;
            Statement statement = null;
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java_db", "root", "123456");
                String sql = "select * from user;";
    
                statement = conn.createStatement();
                ResultSet resultSet = statement.executeQuery(sql);
                while (resultSet.next()){
                    int id = resultSet.getInt("id");
                    String name = resultSet.getString("name");
                    int age = resultSet.getInt("age");
                    System.out.println(id+","+name+","+age);
                }
            }catch (ClassNotFoundException e) {
                e.printStackTrace();
    //        }catch (SQLException e){
    //            e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 释放资源
                if(statement != null){
                    try {
                        statement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if(conn != null){
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
        }
    }
    带有异常处理的 数据库连接程序

    JDBC 重要的类 和 接口:

    1,DriverManager 类: 驱动管理对象 , 注册驱动:告诉程序该使用哪一个数据库驱动jar ,获取数据库连接

      static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
      写代码使用: Class.forName("com.mysql.jdbc.Driver");
      通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
        static {
          try {
            java.sql.DriverManager.registerDriver(new Driver());
          } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
          }
        }

        注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。可以省略Class.forName!

    2. Connection 接口:数据库连接对象
      功能:
        1. 获取执行sql 的对象
          Statement createStatement()
          PreparedStatement prepareStatement(String sql)
        2. 管理事务:
          开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
          提交事务:commit()
          回滚事务:rollback()

    3, Statement执行sql的对象

       执行sql
        1. boolean execute(String sql) :可以执行任意的sql 了解
        2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
          返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
        3. ResultSet executeQuery(String sql) :执行DQL(select)语句

    4,    ResultSet 

      结果集对象,封装查询结果
        boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
        getXxx(参数):获取数据
        Xxx:代表数据类型 如: int getInt() , String getString()
          参数:
            1. int:代表列的编号,从1开始 如: getString(1)
            2. String:代表列名称。 如: getDouble("balance")

          注意:
            使用步骤:
              1. 游标向下移动一行
              2. 判断是否有数据
              3. 获取数据

              //while 循环

    package cn.kaoyan;
    
    import java.sql.*;
    
    public class Test {
        public static void main(String[] args)  {
    
            Connection conn = null;
            Statement statement = null;
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java_db", "root", "123456");
                String sql = "select * from user;";
    
                statement = conn.createStatement();
                ResultSet resultSet = statement.executeQuery(sql);
                while (resultSet.next()){
                    int id = resultSet.getInt("id");
                    String name = resultSet.getString("name");
                    int age = resultSet.getInt("age");
                    System.out.println(id+","+name+","+age);
                }
            }catch (ClassNotFoundException e) {
                e.printStackTrace();
    //        }catch (SQLException e){
    //            e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 释放资源
                if(statement != null){
                    try {
                        statement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if(conn != null){
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
        }
    }
    View Code

    5,    PreparedStatement

      解决sql注入问题:使用PreparedStatement对象来解决
        预编译的SQL:参数使用?作为占位符

    package cn.kaoyan;
    
    import java.sql.*;
    
    public class Test {
        public static void main(String[] args)  {
    
            Connection conn = null;
            PreparedStatement statement = null;
            try {
                Class.forName("com.mysql.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/java_db", "root", "123456");
                String sql = "select * from user where name = ?;";
    
    //            statement = conn.createStatement();
                statement = conn.prepareStatement(sql);
                statement.setString(1,"tom"); // 给第一个问号 赋值
    
    //            ResultSet resultSet = statement.executeQuery(sql);
                ResultSet resultSet = statement.executeQuery(); // 不需要传递 sql
    
                while (resultSet.next()){
                    int id = resultSet.getInt("id");
                    String name = resultSet.getString("name");
                    int age = resultSet.getInt("age");
                    System.out.println(id+","+name+","+age);
                }
            }catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 释放资源
                if(statement != null){
                    try {
                        statement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if(conn != null){
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
        }
    }
    View Code

    注意:后期都会使用PreparedStatement来完成增删改查的所有操作
      1. 可以防止SQL注入
      2. 效率更高

    抽取JDBC 工具类:

    降低代码的重复度,

    1,抽取获取连接的代码(通过配置文件(使用Properties))

    2,抽取释放资源的代码

    package cn.itcast.util;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.net.URL;
    import java.sql.*;
    import java.util.Properties;
    
    /**
     * JDBC工具类
     */
    public class JDBCUtils {
        private static String url;
        private static String user;
        private static String password;
        private static String driver;
        /**
         * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
         */
        static{
            //读取资源文件,获取值。
    
            try {
                //1. 创建Properties集合类。
                Properties pro = new Properties();
    
                //获取src路径下的文件的方式--->ClassLoader 类加载器
                ClassLoader classLoader = JDBCUtils.class.getClassLoader();
                URL res  = classLoader.getResource("jdbc.properties");
                String path = res.getPath();
               // System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties
                //2. 加载文件
               // pro.load(new FileReader("D:\IdeaProjects\itcast\day04_jdbc\src\jdbc.properties"));
                pro.load(new FileReader(path));
    
                //3. 获取数据,赋值
                url = pro.getProperty("url");
                user = pro.getProperty("user");
                password = pro.getProperty("password");
                driver = pro.getProperty("driver");
                //4. 注册驱动
                Class.forName(driver);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        /**
         * 获取连接
         * @return 连接对象
         */
        public static Connection getConnection() throws SQLException {
    
            return DriverManager.getConnection(url, user, password);
        }
    
        /**
         * 释放资源
         * @param stmt
         * @param conn
         */
        public static void close(Statement stmt,Connection conn){
            if( stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if( conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        /**
         * 释放资源
         * @param stmt
         * @param conn
         */
        public static void close(ResultSet rs,Statement stmt, Connection conn){
            if( rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if( stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if( conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    View Code

    JDBC 处理事务:

    使用Connection对象来管理事务
      开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
        在执行sql之前开启事务
      提交事务:commit()   
        当所有sql都执行完提交事务
      回滚事务:rollback()
        在catch中回滚事务

    JDBC 连接池:

    概念:其实就是一个容器(集合),存放数据库连接的容器。
      当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

    好处:
      1. 节约资源
      2. 用户访问高效

    DataSource 接口:
    方法:
      获取连接:getConnection()
      归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接

    一般我们不去实现它,有现成的可以使用:
      C3P0:数据库连接池技术

      Druid:数据库连接池实现技术,由阿里巴巴提供的

    C3P0 的使用:

      1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar (依赖包),

        mysql 驱动不要忘记导入,
      2. 定义配置文件:(会自动加载配置文件)
        配置文件名称: c3p0.properties 或者 c3p0-config.xml (必须这两个名字)
        文件路径:直接将文件放在src目录下即可。

      3. 创建核心对象 数据库连接池对象 ComboPooledDataSource
      4. 获取连接: getConnection

    package cn.kaoyan;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class Test {
        public static void main(String[] args) throws SQLException {
    
            // 1创建数据库连接池对象
            DataSource ds = new ComboPooledDataSource();
            // 2获取连接对象
            Connection conn = ds.getConnection();
            System.out.println(conn);
    
        }
    }
    c3p0 基本使用

    <c3p0-config>
      <!-- 使用默认的配置读取连接池对象 -->
      <default-config>
          <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/java_db</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        
        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">10</property>
        <property name="checkoutTimeout">3000</property>
      </default-config>
    
      <named-config name="otherc3p0"> 
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/java_db</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        
        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">8</property>
        <property name="checkoutTimeout">1000</property>
      </named-config>
    </c3p0-config>
    c3p0-config.xml

    Druid 的使用:

    步骤:
      1. 导入jar包 druid-1.0.9.jar
      2. 定义配置文件:
        它是properties形式的
        它可以叫任意名称,可以放在任意目录下
      3. 加载配置文件(要手动加载)。Properties
      4. 获取数据库连接池对象:通过工厂类来获取 DruidDataSourceFactory
      5. 获取连接:getConnection

    package cn.kaoyan;
    
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    public class Test {
        public static void main(String[] args) throws Exception {
    
            // 1,加载配置文件 (手动)
            Properties pro = new Properties();
    
            InputStream is  = Test.class.getClassLoader().getResourceAsStream("druid.properties");
            pro.load(is);
    
            // 2,获取连接池对象
            DataSource ds = DruidDataSourceFactory.createDataSource(pro);
    
            // 3,获取连接对象
            Connection conn = ds.getConnection();
            System.out.println(conn);
        }
    }
    Druid 的基本使用 

    定义工具类
      1. 定义一个类 JDBCUtils
      2. 提供静态代码块加载配置文件,初始化连接池对象
      3. 提供方法
        1. 获取连接方法:通过数据库连接池获取连接
        2. 释放资源
        3. 获取连接池的方法

    package cn.itcast.utils;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    /**
     * Druid连接池的工具类
     */
    public class JDBCUtils {
    
        //1.定义成员变量 DataSource
        private static DataSource ds ;
    
        static{
            try {
                //1.加载配置文件
                Properties pro = new Properties();
                pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
                //2.获取DataSource
                ds = DruidDataSourceFactory.createDataSource(pro);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取连接
         */
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        }
    
        /**
         * 释放资源
         */
        public static void close(Statement stmt,Connection conn){
           /* if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if(conn != null){
                try {
                    conn.close();//归还连接
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }*/
    
           close(null,stmt,conn);
        }
    
    
        public static void close(ResultSet rs , Statement stmt, Connection conn){
    
    
            if(rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
    
            if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if(conn != null){
                try {
                    conn.close();//归还连接
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 获取连接池方法
         */
    
        public static DataSource getDataSource(){
            return  ds;
        }
    
    }
    View Code

    使用连接池的工具类:

    package cn.itcast.datasource.druid;
    
    import cn.itcast.utils.JDBCUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /**
     * 使用新的工具类
     */
    public class DruidDemo2 {
    
        public static void main(String[] args) {
            /*
             * 完成添加操作:给account表添加一条记录
             */
            Connection conn = null;
            PreparedStatement pstmt = null;
            try {
                //1.获取连接
                conn = JDBCUtils.getConnection();
                //2.定义sql
                String sql = "insert into account values(null,?,?)";
                //3.获取pstmt对象
                pstmt = conn.prepareStatement(sql);
                //4.给?赋值
                pstmt.setString(1,"王五");
                pstmt.setDouble(2,3000);
                //5.执行sql
                int count = pstmt.executeUpdate();
                System.out.println(count);
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                //6. 释放资源
                JDBCUtils.close(pstmt,conn);
            }
        }
    
    }
    View Code

    JDBC Template:

    它是  Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发

    使用步骤:
      1. 导入jar包
      2. 创建JdbcTemplate对象。依赖于数据源DataSource(数据库连接池)
        JdbcTemplate template = new JdbcTemplate(ds);

      3. 调用JdbcTemplate的方法来完成CRUD的操作
        update():执行DML语句。增、删、改语句
        queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合
          注意:这个方法查询的结果集长度只能是1
        queryForList():查询结果将结果集封装为list集合
          注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
        query():查询结果,将结果封装为JavaBean对象
          query的参数:RowMapper
            一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
              new BeanPropertyRowMapper<类型>(类型.class)
        queryForObject:查询结果,将结果封装为对象
          一般用于聚合函数的查询

    package cn.itcast.jdbctemplate;
    
    import cn.itcast.utils.JDBCUtils;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    /**
     * JdbcTemplate入门
     */
    public class JdbcTemplateDemo1 {
    
        public static void main(String[] args) {
            //1.导入jar包
            //2.创建JDBCTemplate对象
            JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
            //3.调用方法
            String sql = "update account set balance = 5000 where id = ?";
            int count = template.update(sql, 3);
            System.out.println(count);
        }
    }
    会自动释放资源
    package cn.itcast.jdbctemplate;
    
    import cn.itcast.domain.Emp;
    import cn.itcast.utils.JDBCUtils;
    import org.junit.Test;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;
    
    import java.sql.Date;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    import java.util.Map;
    
    public class JdbcTemplateDemo2 {
    
        //Junit单元测试,可以让方法独立执行
    
    
        //1. 获取JDBCTemplate对象
        private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
        /**
         * 1. 修改1号数据的 salary 为 10000
         */
        @Test
        public void test1(){
    
            //2. 定义sql
            String sql = "update emp set salary = 10000 where id = 1001";
            //3. 执行sql
            int count = template.update(sql);
            System.out.println(count);
        }
    
        /**
         * 2. 添加一条记录
         */
        @Test
        public void test2(){
            String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
            int count = template.update(sql, 1015, "郭靖", 10);
            System.out.println(count);
    
        }
    
        /**
         * 3.删除刚才添加的记录
         */
        @Test
        public void test3(){
            String sql = "delete from emp where id = ?";
            int count = template.update(sql, 1015);
            System.out.println(count);
        }
    
        /**
         * 4.查询id为1001的记录,将其封装为Map集合
         * 注意:这个方法查询的结果集长度只能是1
         */
        @Test
        public void test4(){
            String sql = "select * from emp where id = ? or id = ?";
            Map<String, Object> map = template.queryForMap(sql, 1001,1002);
            System.out.println(map);
            //{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
    
        }
    
        /**
         * 5. 查询所有记录,将其封装为List
         */
        @Test
        public void test5(){
            String sql = "select * from emp";
            List<Map<String, Object>> list = template.queryForList(sql);
    
            for (Map<String, Object> stringObjectMap : list) {
                System.out.println(stringObjectMap);
            }
        }
    
        /**
         * 6. 查询所有记录,将其封装为Emp对象的List集合
         */
    
        @Test
        public void test6(){
            String sql = "select * from emp";
            List<Emp> list = template.query(sql, new RowMapper<Emp>() {
    
                @Override
                public Emp mapRow(ResultSet rs, int i) throws SQLException {
                    Emp emp = new Emp();
                    int id = rs.getInt("id");
                    String ename = rs.getString("ename");
                    int job_id = rs.getInt("job_id");
                    int mgr = rs.getInt("mgr");
                    Date joindate = rs.getDate("joindate");
                    double salary = rs.getDouble("salary");
                    double bonus = rs.getDouble("bonus");
                    int dept_id = rs.getInt("dept_id");
    
                    emp.setId(id);
                    emp.setEname(ename);
                    emp.setJob_id(job_id);
                    emp.setMgr(mgr);
                    emp.setJoindate(joindate);
                    emp.setSalary(salary);
                    emp.setBonus(bonus);
                    emp.setDept_id(dept_id);
    
                    return emp;
                }
            });
    
    
            for (Emp emp : list) {
                System.out.println(emp);
            }
        }
    
        /**
         * 6. 查询所有记录,将其封装为Emp对象的List集合
         */
    
        @Test
        public void test6_2(){
            String sql = "select * from emp";
            List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
            for (Emp emp : list) {
                System.out.println(emp);
            }
        }
    
        /**
         * 7. 查询总记录数
         */
    
        @Test
        public void test7(){
            String sql = "select count(id) from emp";
            Long total = template.queryForObject(sql, Long.class);
            System.out.println(total);
        }
    
    }
    基本使用

    注: 定义结果集类的时候,可能会产生 null 复制给基本数据类型产生的错误,所以,要把int 换成 Integer 等...

    XML:

    w3c:

    World Wide Web Consortium

    万维网联盟

    xml的概述:

    1. 概念:Extensible Markup Language 可扩展标记语言
      可扩展:标签都是自定义的。 <user> <student>

    2,功能
      存储数据
        1,配置文件
        2,在网络中传输
    3 xml与html的区别
      1. xml标签都是自定义的,html标签是预定义。
      2. xml的语法严格,html语法松散
      3. xml是存储数据的,html是展示数据

    基本语法:

    1. xml文档的后缀名 .xml
    2. xml第一行必须定义为文档声明
    3. xml文档中有且仅有一个根标签
    4. 属性值必须使用引号  (单双都可)  引起来
    5. 标签必须正确关闭
    6. xml标签名称区分大小写

                <?xml version='1.0' ?>
                <users>
                    <user id='1'>
                        <name>zhangsan</name>
                        <age>23</age>
                        <gender>male</gender>
                        <br/>
                    </user>
                    
                    <user id='2'>
                        <name>lisi</name>
                        <age>24</age>
                        <gender>female</gender>
                    </user>
                </users>
    View Code

    Tomcat :

    java 常见的web 服务器软件:

    常见的java相关的web服务器软件:
      webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
      webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
      JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
      Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。

    tomcat 的安装 与 部署项目:

    1. 下载:http://tomcat.apache.org/
    2. 安装:解压压缩包即可。
    	注意:安装目录建议不要有中文和空格
    3. 卸载:删除目录就行了
    4. 启动:
    	bin/startup.bat ,双击运行该文件即可
    	访问:浏览器输入:http://localhost:8080 回车访问自己
    					  http://别人的ip:8080 访问别人
    	
    	可能遇到的问题:
    		1. 黑窗口一闪而过:
    			原因: 没有正确配置JAVA_HOME环境变量
    			解决方案:正确配置JAVA_HOME环境变量
    
    		2. 启动报错:
    			1. 暴力:找到占用的端口号,并且找到对应的进程,杀死该进程
    			2. 温柔:修改自身的端口号,要改好多端口~ 
    			
    5. 关闭:
    	1. 正常关闭:
    		bin/shutdown.bat
    		ctrl+c
    	2. 强制关闭:
    		点击启动窗口的×
    6. 配置:
    	部署项目的方式:
    		1. 直接将项目放到webapps目录下即可。
    			/hello:项目的访问路径-->虚拟目录
    			简化部署:将项目打成一个war包,再将war包放置到webapps目录下。
    				war包会自动解压缩
    
    		2. 配置conf/server.xml文件
    			在<Host>标签体中配置
    			<Context docBase="D:hello" path="/hehe" />
    			docBase:项目存放的路径
    			path:虚拟目录
    
    		3. 在confCatalinalocalhost创建任意名称的xml文件。在文件中编写
    			<Context docBase="D:hello" />
    			虚拟目录:xml文件的名称
    	
    	静态项目和动态项目:
    		目录结构
    			java动态项目的目录结构:
    				-- 项目的根目录
    					-- WEB-INF目录:
    						-- web.xml:web项目的核心配置文件
    						-- classes目录:放置字节码文件的目录
    						-- lib目录:放置依赖的jar包
    
    
    	将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。
    View Code

    Servlet 

    servlet 概述:

    Servlet: server applet
    概念:运行在服务器端的程序
      本质上,Servlet就是一个接口(javaee api),定义了Java类被浏览器访问到(tomcat识别)的规则。
      将来我们自定义一个类,实现Servlet接口,复写方法。

    servlet 快速入门:

    1. 创建JavaEE项目
    2. 定义一个类,实现Servlet接口
      public class ServletDemo1 implements Servlet
    3. 实现接口中的抽象方法
    4. 配置Servlet
      在web.xml中配置:
      <!--配置Servlet -->
      <servlet>
        <servlet-name>demo1</servlet-name> <!--名字 和 对应的类全路径 -->
        <servlet-class>cn.itcast.web.servlet.ServletDemo1</servlet-class>
      </servlet>

      <servlet-mapping>
        <servlet-name>demo1</servlet-name> <!--名字 和 访问路径 -->
        <url-pattern>/demo1</url-pattern>
      </servlet-mapping>

    执行原理:   

      1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
      2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
      3. 如果有,则在找到对应的<servlet-class>全类名
      4. tomcat会将字节码文件加载进内存,并且创建其对象
      5. 调用其方法

    Servlet中的生命周期方法:

    Servlet中的生命周期方法:
    	1.被创建:执行init方法,只执行一次
    		Servlet什么时候被创建?
    			默认情况下,第一次被访问时,Servlet被创建
    			可以配置执行Servlet的创建时机,在<servlet>标签下配置
    				1. 第一次被访问时,创建
    					<load-on-startup>的值为负数
    				2. 在服务器启动时,创建
    					<load-on-startup>的值为0或正整数
    
    		Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
    			多个用户同时访问时,可能存在线程安全问题。
    			解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
    
    	2. 提供服务:执行service方法,执行多次
    		每次访问Servlet时,Service方法都会被调用一次。
    	3. 被销毁:执行destroy方法,只执行一次
    		Servlet被销毁时执行。服务器关闭时,Servlet被销毁,只有服务器正常关闭时,才会执行destroy方法。
    		destroy方法在Servlet被销毁之前执行,一般用于释放资源
    Servlet中的生命周期方法
    package com.kaoyan.servlet;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class ServletDemo01 implements Servlet {
    
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
    
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return null;
        }
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("hello world");
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
    
        }
    }
    View Code

    另外两个方法:getServletConfig,getServletInfo  了解,

    Servlet 注解配置:

    Servlet3.0 注解配置:
    	好处:
    		支持注解配置。可以不需要web.xml了。
    
    	步骤:
    		1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
    		2. 定义一个类,实现Servlet接口
    		3. 复写方法
    		4. 在类上使用@WebServlet注解,进行配置
    			@WebServlet("资源路径")
    
    
    			@Target({ElementType.TYPE})
    			@Retention(RetentionPolicy.RUNTIME)
    			@Documented
    			public @interface WebServlet {
    				String name() default "";//相当于<Servlet-name>
    			
    				String[] value() default {};//代表urlPatterns()属性配置
    			
    				String[] urlPatterns() default {};//相当于<url-pattern>
    			
    				int loadOnStartup() default -1;//相当于<load-on-startup>
    			
    				WebInitParam[] initParams() default {};
    			
    				boolean asyncSupported() default false;
    			
    				String smallIcon() default "";
    			
    				String largeIcon() default "";
    			
    				String description() default "";
    			
    				String displayName() default "";
    			}
    View Code

    Servlet 继承体系:

    Servlet的继承体系	
    	Servlet -- 接口 <-  GenericServlet -- 抽象类 <-  HttpServlet  -- 抽象类
    
    	GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象
    		将来定义Servlet类时,可以继承GenericServlet,只实现一个service()方法即可
    
    	HttpServlet:对http协议的一种封装,简化请求响应相关操作
    		1. 定义类继承HttpServlet
    		2. 复写doGet/doPost方法
    View Code

    Servlet 路径配置:

    Servlet路径配置
    	1. urlpartten:Servlet访问路径
    		1. 一个Servlet可以定义多个访问路径 : @WebServlet({"/d4","/dd4","/ddd4"})
    		2. 路径定义规则:
    			1. /xxx:路径匹配
    			2. /xxx/xxx:多层路径,目录结构
    			3. *.xxx:扩展名匹配  (注:一定不要加/ )
    View Code

    ServletRequest对象继承体系结构:

    ServletRequest 接口 <- HttpServletRequest  接口 <- org.apache.catalina.connector.RequestFacade 类 (tomcat)

    ServletRequest功能 和 ServletContext 的使用:

    request功能:
    	a. 获取请求消息数据
    		1. 获取请求行数据
    			GET /day14/demo1?name=zhangsan HTTP/1.1 
    			方法:
    				1. 获取请求方式 :GET
    					String getMethod()  
    				2. 获取虚拟目录:/day14        
    					String getContextPath()
    				3. 获取Servlet路径: /demo1
    					String getServletPath()
    				4. 获取get方式请求参数:name=zhangsan
    					String getQueryString()
    				5. 获取请求URI:/day14/demo1
    					String getRequestURI():		/day14/demo1
    					StringBuffer getRequestURL()  :http://localhost/day14/demo1
    
    					URL:统一资源定位符 : http://localhost/day14/demo1	中华人民共和国
    					URI:统一资源标识符 : /day14/demo1					共和国
    				
    				6. 获取协议及版本:HTTP/1.1
    					String getProtocol()
    
    				7. 获取客户机的IP地址:
    					String getRemoteAddr()
    				
    		2. 获取请求头数据
    			方法:
    				String getHeader(String name):通过请求头的名称获取请求头的值
    				Enumeration<String> getHeaderNames():获取所有的请求头名称
    			
    		3. 获取请求体数据:
    			请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
    			步骤:
    				1. 获取流对象
    					BufferedReader getReader():获取字符输入流,只能操作字符数据
    					ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据 (文件上传)
    						
    				2. 再从流对象中拿数据
    				
    				
    	b. 其他功能:
    		1. 获取请求参数通用方式: 不论get还是post请求方式都可以使用下列方法来获取请求参数
    			1. (常用)String getParameter(String name):根据参数名称获取参数值    username=zs&password=123
    			2. String[] getParameterValues(String name):根据参数名称获取参数值的数组  hobby=xx&hobby=game
    			3. Enumeration<String> getParameterNames():获取所有请求的参数名称
    			4. (常用)Map<String,String[]> getParameterMap():获取所有参数的map集合
    
    			中文乱码问题:
    				get方式:不会乱码, tomcat8 已经将get方式乱码问题解决了
    				post方式:中文会乱码
    					解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");
    		
    				
    		2. 请求转发:                        
    			1. 步骤:  
    				1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
    				2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response) 
    
    			2. 特点:
    				1. 浏览器地址栏路径不发生变化
    				2. 只能转发到当前服务器内部资源中。
    				3. 转发只是一次请求
    
    		3. 共享数据:
    			域对象:一个有作用范围的对象,可以在范围内共享数据
    			request域:代表一次请求的范围,一般用于请求转发(一次请求)的多个资源中共享数据
    			方法:
    				1. void setAttribute(String name,Object obj):存储数据
    				2. Object getAttitude(String name):通过键获取值
    				3. void  (String name):通过键移除键值对
    
    		4. 获取ServletContext:
    			ServletContext getServletContext()
    			
    			# 关于ServletContext
    			1. 概念:代表整个web应用,可以和servlet的容器来通信
    			2. 获取(两种方式):
    				1. 通过request对象获取
    					request.getServletContext();
    				2. 通过HttpServlet获取
    					this.getServletContext();
    			3. 功能:
    				a. 获取MIME类型:
    					MIME类型:在互联网通信过程中定义的一种文件数据类型
    						格式: 大范围的类型/细分的类型   text/html		image/jpeg
    						方法:String getMimeType(String file)  然后把MIME类型发送给浏览器(放在Content-type)
    						它的原理是根据文件名称的后缀名,从tomcat 的配置文件去读取匹配的MIME类型! 
    	
    						
    				b. 域对象:共享数据
    					1. setAttribute(String name,Object value)
    					2. getAttribute(String name)
    					3. removeAttribute(String name)
    					ServletContext对象范围:所有用户所有请求的数据 (大范围,生命周期长,会占用内存,使用要谨慎)
    					
    				c. 获取文件的真实(tomcat)路径(不是工作空间的路径)
    					例如:配置文件可以放在 web 下,可以放在web/WEB-INF/ 下,可以放在 src 下
    					1. 方法:String getRealPath(String path)  
    						String b = context.getRealPath("/b.txt");//web目录下资源访问
    						System.out.println(b);
    				
    						String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
    						System.out.println(c);
    				
    						String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问 
    						System.out.println(a);
    View Code

    响应消息:

    响应消息:服务器端发送给客户端的数据
    	数据格式:
    		a. 响应行
    			1. 组成:协议/版本 响应状态码 状态码描述
    			2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
    				1. 状态码都是3位数字 
    				2. 分类:
    					1. 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
    					2. 2xx:成功。代表:200
    					3. 3xx:重定向。代表:302(重定向),304(访问缓存)
    					4. 4xx:客户端错误。
    						代表:
    							404(请求路径没有对应的资源) 
    							405:请求方式没有对应的doXxx方法
    					5. 5xx:服务器端错误。代表:500(服务器内部出现异常)
    						
    				
    		b. 响应头:
    			1. 格式:头名称: 值
    			2. 常见的响应头:
    				1. Content-Type:服务器告诉客户端本次响应体数据格式  以及  编码格式
    				2. Content-Disposition:服务器告诉客户端以什么格式打开响应体数据
    					取值:
    						in-line:默认值,在当前页面内打开
    						attachment;filename=xxx:以附件形式打开响应体。文件下载
    		c. 响应空行
    		d. 响应体:传输的数据
    View Code

    ServletResponse功能:

    功能:设置响应消息
    	1. 设置响应行
    		1. 格式:HTTP/1.1 200 ok
    		2. 设置状态码:setStatus(int sc) 
    	2. 设置响应头:setHeader(String name, String value) 
    		
    	3. 设置响应体:
    		使用步骤:
    			1. 获取输出流
    				字符输出流:PrintWriter getWriter()
    				字节输出流:ServletOutputStream getOutputStream()
    			2. 使用输出流,将数据输出到客户端浏览器
    View Code

    响应对象之重定向:

    重定向:资源跳转的方式
    代码实现:
    	//1. 设置状态码为302
    	response.setStatus(302);
    	//2.设置响应头location
    	response.setHeader("location","/day15/responseDemo2");
    
    
    	//简单的重定向方法
    	response.sendRedirect("/day15/responseDemo2");
    
    重定向的特点:redirect
    	1. 地址栏发生变化
    	2. 重定向可以访问其他站点(服务器)的资源
    	3. 重定向是两次请求。不能使用request对象来共享数据
    
    路径写法:
    	路径分类
    		a. 相对路径:通过相对路径不可以确定唯一资源
    			如:./index.html
    			不以/开头,以.开头路径
    
    		b. 绝对路径:通过绝对路径可以确定唯一资源
    			如:http://localhost/day15/responseDemo2		/day15/responseDemo2
    			以/开头的路径
    
    			规则:判断定义的路径是给谁用的?
    				a,给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
    					建议虚拟目录动态获取:request.getContextPath()
    					<a> , <form> 重定向...
    				b,给服务器使用:不需要加虚拟目录
    					当转发的时候,就是服务器内部使用~
    View Code

    响应对象之输出字符数据:

    1. 获取字符输出流 PrintWriter pw = response.getWriter();
    2. 输出数据 pw.write("hello world");
    
    注意:
    	乱码问题:
    		1. PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1
    		2. 设置该流的默认编码
    		3. 告诉浏览器响应体使用的编码
    
    		//简单的形式,设置编码,是在获取流之前设置
    		response.setContentType("text/html;charset=utf-8");
    View Code

    响应对象之输出字节数据:

    response.setContentType("text/html;charset=utf-8");
    1. 获取字节输出流 ServletOutputStream sos = response.getOutputStream();
    2. 输出数据 sos.write("hello world".getBytes());
    一般字节流都是用来图片等
    View Code

    验证码:

    原理,在内存中动态生成一个图片,

    @WebServlet("/checkCodeServlet")
    public class CheckCodeServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
            int width = 100;
            int height = 50;
    
            //1.创建一对象,在内存中图片(验证码图片对象)
            BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    
    
            //2.美化图片
            //2.1 填充背景色
            Graphics g = image.getGraphics();//画笔对象
            g.setColor(Color.PINK);//设置画笔颜色
            g.fillRect(0,0,width,height);
    
            //2.2画边框
            g.setColor(Color.BLUE);
            g.drawRect(0,0,width - 1,height - 1);
    
            String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
            //生成随机角标
            Random ran = new Random();
    
            for (int i = 1; i <= 4; i++) {
                int index = ran.nextInt(str.length());
                //获取字符
                char ch = str.charAt(index);//随机字符
                //2.3写验证码
                g.drawString(ch+"",width/5*i,height/2);
            }
    
    
            //2.4画干扰线
            g.setColor(Color.GREEN);
            //随机生成坐标点
            for (int i = 0; i < 10; i++) {
                int x1 = ran.nextInt(width);
                int x2 = ran.nextInt(width);
    
                int y1 = ran.nextInt(height);
                int y2 = ran.nextInt(height);
                g.drawLine(x1,y1,x2,y2);
            }
    
    
            //3.将图片输出到页面展示   把image 写到 response outputstream 中
            ImageIO.write(image,"jpg",response.getOutputStream());
    
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    View Code

    文件下载:

    文件下载需求:
    	1. 页面显示超链接
    	2. 点击超链接后弹出下载提示框
    	3. 完成图片文件下载
    
    
    分析:
    	1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
    	3. 使用响应头设置资源的打开方式:
    		Content-Disposition:attachment;filename=xxx
    
    
    步骤:
    	1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
    	2. 定义Servlet
    		1. 获取文件名称
    		2. 使用字节输入流加载文件进内存
    		3. 指定response的响应头: Content-Disposition:attachment;filename=xxx
    		4. 将数据写出到response输出流
    View Code
    package cn.itcast.web.download;
    
    import cn.itcast.web.utils.DownLoadUtils;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    @WebServlet("/downloadServlet")
    public class DownloadServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1.获取请求参数,文件名称
            String filename = request.getParameter("filename");
            //2.使用字节输入流加载文件进内存
            //2.1找到文件服务器路径
            ServletContext servletContext = this.getServletContext();
            String realPath = servletContext.getRealPath("/img/" + filename);
            //2.2用字节流关联
            FileInputStream fis = new FileInputStream(realPath);
    
            //3.设置response的响应头
            //3.1设置响应头类型:content-type
            String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
            response.setHeader("content-type",mimeType);
            //3.2设置响应头打开方式:content-disposition
    
            //解决中文文件名问题
            //1.获取user-agent请求头、
            String agent = request.getHeader("user-agent");
            //2.使用工具类方法编码文件名即可
            filename = DownLoadUtils.getFileName(agent, filename);
    
            response.setHeader("content-disposition","attachment;filename="+filename);
            //4.将输入流的数据写出到输出流中
            ServletOutputStream sos = response.getOutputStream();
            byte[] buff = new byte[1024];
            int len = 0;
            while((len = fis.read(buff)) != -1){
                sos.write(buff,0,len);
            }
            fis.close();
    
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    View Code
    package cn.itcast.web.utils;
    
    import sun.misc.BASE64Encoder;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    
    
    public class DownLoadUtils {
    
        public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
            if (agent.contains("MSIE")) {
                // IE浏览器
                filename = URLEncoder.encode(filename, "utf-8");
                filename = filename.replace("+", " ");
            } else if (agent.contains("Firefox")) {
                // 火狐浏览器
                BASE64Encoder base64Encoder = new BASE64Encoder();
                filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
            } else {
                // 其它浏览器
                filename = URLEncoder.encode(filename, "utf-8");
            }
            return filename;
        }
    }
    工具类

    IDEA与tomcat的相关配置:

      1. IDEA会为每一个tomcat部署的项目单独建立一份配置文件
        查看控制台的log 输出:Using CATALINA_BASE: "C:Usersfqy.IntelliJIdea2018.1system omcat\_itcast"

      2. 有两个存储位置 工作空间项目 和 tomcat部署的web项目
        tomcat真正访问的是“tomcat部署的web项目”,"tomcat部署的web项目"对应着"工作空间项目" 的web目录下的所有资源
        WEB-INF目录下的资源不能被浏览器直接访问。

    Cookie 和 Session :

    会话技术:

    会话技术
    	1. 会话:一次会话中包含多次请求和响应。
    			一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
    	2. 功能:在一次会话的范围内的多次请求间,共享数据
    	3. 方式:
    		1. 客户端会话技术:Cookie
    		2. 服务器端会话技术:Session
    View Code

    Cookie :

    快速入门:

    Cookie:
    	1. 概念:客户端会话技术,将数据保存到客户端
    
    	2. 快速入门:
    		使用步骤:
    			a. 创建Cookie对象,绑定数据
    				new Cookie(String name, String value) 
    			b. 发送Cookie对象
    				response.addCookie(Cookie cookie) 
    			c. 新的请求,获取Cookie,拿到数据
    				Cookie[]  request.getCookies()  
    View Code

    cookie 实现原理:

    基于响应头set-cookie和请求头cookie实现

    cookie的细节:

    cookie的细节
    	a. 一次可不可以发送多个cookie?
    		可以
    		可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。
    	b. cookie在浏览器中保存多长时间?
    		1. 默认情况下,当浏览器关闭后,Cookie数据被销毁
    		2. 持久化存储:
    			setMaxAge(int seconds)
    				1. 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效
    				2. 负数:默认值,浏览器关闭后,Cookie数据被销毁  
    				3. 零:删除cookie信息
    	c. cookie能不能存中文?
    		在tomcat 8 之前 cookie中不能直接存储中文数据。
    			需要将中文数据转码---一般采用URL编码(%E3)
    		在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
    	d. cookie共享问题?
    		1. 假设在一个tomcat服务器(同一个端口)中,部署了多个web项目(多个虚拟目录),那么在这些web项目中cookie能不能共享?
    			默认情况下cookie不能共享
    			setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录
    				如果要共享,则可以将path设置为"/",当前服务器的根目录下都可以共享cookie  
    		
    		2. 不同的tomcat服务器间cookie共享问题?
    			setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享
    				setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享
    View Code

    Cookie的特点和作用:

    Cookie的特点和作用
    	1. cookie存储数据在客户端浏览器
    	2. 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
    
    	作用:
    		1. cookie一般用于存储少量的不太敏感的数据
    		2. 在不登录的情况下,完成服务器对客户端的身份识别
    View Code

    案例:

    案例:记住上一次访问时间
    	1. 需求:
    		1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
    		2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
    
    	2. 分析:
    		1. 可以采用Cookie来完成
    		2. 在服务器中的Servlet判断是否有一个名为lastTime的cookie
    			1. 有:不是第一次访问
    				1. 响应数据:欢迎回来,您上次访问时间为:2018年6月10日11:50:20
    				2. 写回Cookie:lastTime=2018年6月10日11:50:01
    			2. 没有:是第一次访问
    				1. 响应数据:您好,欢迎您首次访问
    				2. 写回Cookie:lastTime=2018年6月10日11:50:01
    View Code
            @WebServlet("/cookieTest")
            public class CookieTest extends HttpServlet {
                protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                    //设置响应的消息体的数据格式以及编码
                    response.setContentType("text/html;charset=utf-8");
            
                    //1.获取所有Cookie
                    Cookie[] cookies = request.getCookies();
                    boolean flag = false;//没有cookie为lastTime
                    //2.遍历cookie数组
                    if(cookies != null && cookies.length > 0){
                        for (Cookie cookie : cookies) {
                            //3.获取cookie的名称
                            String name = cookie.getName();
                            //4.判断名称是否是:lastTime
                            if("lastTime".equals(name)){
                                //有该Cookie,不是第一次访问
            
                                flag = true;//有lastTime的cookie
            
                                //设置Cookie的value
                                //获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
                                Date date  = new Date();
                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
                                String str_date = sdf.format(date);
                                System.out.println("编码前:"+str_date);
                                //URL编码
                                str_date = URLEncoder.encode(str_date,"utf-8");
                                System.out.println("编码后:"+str_date);
                                cookie.setValue(str_date);
                                //设置cookie的存活时间
                                cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
                                response.addCookie(cookie);
            
            
                                //响应数据
                                //获取Cookie的value,时间
                                String value = cookie.getValue();
                                System.out.println("解码前:"+value);
                                //URL解码:
                                value = URLDecoder.decode(value,"utf-8");
                                System.out.println("解码后:"+value);
                                response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");
            
                                break;
            
                            }
                        }
                    }
            
            
                    if(cookies == null || cookies.length == 0 || flag == false){
                        //没有,第一次访问
            
                        //设置Cookie的value
                        //获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
                        Date date  = new Date();
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
                        String str_date = sdf.format(date);
                        System.out.println("编码前:"+str_date);
                        //URL编码
                        str_date = URLEncoder.encode(str_date,"utf-8");
                        System.out.println("编码后:"+str_date);
            
                        Cookie cookie = new Cookie("lastTime",str_date);
                        //设置cookie的存活时间
                        cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
                        response.addCookie(cookie);
            
                        response.getWriter().write("<h1>您好,欢迎您首次访问</h1>");
                    }
            
            
                }
            
                protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                    this.doPost(request, response);
                }
            }
    View Code

    JSP:

    Java Server Pages:  java服务器端页面

    JSP:入门学习
    	1. 概念:
    		Java Server Pages: java服务器端页面
    			可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码
    			用于简化书写!!!
    
    
    	2. 原理
    		JSP本质上就是一个Servlet,相应的类实现了Servlet接口 
    
    	3. JSP的脚本:JSP定义Java代码的方式
    		1. <%  代码 %>:定义的java代码,在service方法中。service方法中可以定义什么,该脚本中就可以定义什么。System.out.println("hello world");
    		2. <%! 代码 %>:定义的java代码,在jsp转换后的java类的成员位置。 eg:int i = 3; // 用的很少~
    		3. <%= 代码 %>:定义的java代码,会输出到页面上。输出语句中可以定义什么,该脚本中就可以定义什么。
    		
    
    	4. JSP的内置对象:
    		在jsp页面中不需要获取和创建,可以直接使用的对象
    		jsp一共有9个内置对象。
    			变量名					真实类型						作用
    		pageContext				PageContext					当前页面共享数据,还可以获取其他八个内置对象
    		request					HttpServletRequest			一次请求访问的多个资源(转发)
    		session					HttpSession					一次会话的多个请求间
    		application				ServletContext				所有用户间共享数据
    		response					HttpServletResponse			响应对象
    		page						Object						当前页面(Servlet)的对象  this
    		out						JspWriter					字符输出流对象。可以将数据输出到页面上。和response.getWriter()类似
    		config					ServletConfig				Servlet的配置对象
    		exception					Throwable					异常对象
    		
    		// 注:
    		response.getWriter()和out.write()的区别:
    			在tomcat服务器真正给客户端做出响应之前,会先找response缓冲区数据,再找out缓冲区数据。
    			response.getWriter()数据输出永远在out.write()之前
    View Code

    JSP 指令 和 注释:

    1. 指令
    	作用:用于配置JSP页面,导入资源文件
    	格式:
    		<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %>
    	分类:
    		a. page		: 配置JSP页面的
    			contentType:等同于response.setContentType()
    				1. 设置响应体的mime类型以及字符集
    				2. 设置当前jsp页面的编码(只能是高级的IDE才能生效,如果使用低级工具,则需要设置  pageEncoding  属性设置当前页面的字符集)
    			import:导包
    			errorPage:当前页面发生异常后,会自动跳转到指定的错误页面
    			isErrorPage:标识当前页面是否是错误页面。
    				true:是,可以使用内置对象exception
    				false:否。默认值。不可以使用内置对象exception
    
    
    		b. include	: 页面包含的。导入页面的资源文件
    			<%@ include file="top.jsp"%>
    		c. taglib	: 导入资源
    			<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    				prefix:前缀,自定义的
    2. 注释:
    	1. html注释:
    		<!-- -->:只能注释html代码片段
    	2. jsp注释:推荐使用
    		<%-- --%>:可以注释所有
    View Code

    Session :

    Session:
    	1. 概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
    	2. 快速入门:
    		1. 获取HttpSession对象:
    			HttpSession session = request.getSession();
    		2. 使用HttpSession对象:
    			Object getAttribute(String name)  
    			void setAttribute(String name, Object value)
    			void removeAttribute(String name)  
    	 
    	3. 原理
    		Session的实现是依赖于Cookie的。sessionId 存在cookie中
    
    	
    	4. 细节:
    		a. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
    			默认情况下。不是。
    			如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
    				 Cookie c = new Cookie("JSESSIONID",session.getId());
    		         c.setMaxAge(60*60);
    		         response.addCookie(c);
    
    		b. 客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
    			不是同一个,但是要确保数据不丢失。
    			
    			tomcat自动完成以下工作
    				session的钝化:
    					在服务器正常关闭之前,将session对象序列化到硬盘上
    				session的活化:
    					在服务器启动后,将session文件转化为内存中的session对象即可。(反序列化)  
    
    			
    		c. session什么时候被销毁?
    			1. 服务器关闭
    			2. session对象调用invalidate() 。
    			3. session默认失效时间 30分钟
    				选择性配置修改	
    				<session-config>
    			        <session-timeout>30</session-timeout>
    			    </session-config>
    
    	5. session的特点
    		1. session用于存储一次会话的多次请求的数据,存在服务器端
    		2. session可以存储任意类型,任意大小的数据
    
    		session与Cookie的区别:
    			1. session存储数据在服务器端,Cookie在客户端
    			2. session没有数据大小限制,Cookie有
    			3. session数据安全,Cookie相对于不安全
    View Code

    MVC:开发模式

    MVC:开发模式	
    	1. jsp演变历史
    		1. 早期只有servlet,只能使用response输出标签数据,非常麻烦
    		2. 后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中既写大量的java代码,有写html表,造成难于维护,难于分工协作
    		3. 再后来,java的web开发,借鉴mvc开发模式,使得程序的设计更加合理性
    
    	2. MVC:
    		1. M:Model,模型。JavaBean
    			完成具体的业务操作,如:查询数据库,封装对象
    		2. V:View,视图。JSP
    			展示数据
    		3. C:Controller,控制器。Servlet
    			获取用户的输入
    			调用模型
    			将数据交给视图进行展示
    
    
    		优缺点:
    			1. 优点:
    				1. 耦合性低,方便维护,可以利于分工协作
    				2. 重用性高
    
    			2. 缺点:
    				1. 使得项目架构变得复杂,对开发人员要求高
    MVC:开发模式

    EL 和 JSTL标签:

    EL表达式
    	1. 概念:Expression Language 表达式语言
    	2. 作用:替换和简化jsp页面中java代码的编写
    	3. 语法:${表达式}
    	4. 注意:
    		jsp默认支持el表达式的。如果要忽略el表达式
    			1. 设置jsp中page指令中:isELIgnored="true" 忽略当前jsp页面中所有的el表达式(全局)
    			2.  ${表达式} :忽略当前这个el表达式
    
    
    	5. 使用:
    		a. 运算:
    			运算符:
    				1. 算数运算符: + - *        /(div)          %(mod)
    				2. 比较运算符: > < >= <= == !=
    				3. 逻辑运算符: &&(and) ||(or) !(not)
    				4. 空运算符: empty
    					功能:用于判断字符串、集合、数组对象是否为null或者长度是否为0
    					${empty list}:判断字符串、集合、数组对象是否为null或者长度为0
    					${not empty str}:表示判断字符串、集合、数组对象是否不为null 并且 长度>0
    		b. 获取值
    			1. el表达式只能从域对象中获取值
    			2. 语法:
    				1. ${域名称.键名}:从指定域中获取指定键的值
    					域名称:
    						1. pageScope		--> pageContext
    						2. requestScope 	--> request
    						3. sessionScope 	--> session
    						4. applicationScope --> application(ServletContext)
    					举例:在request域中存储了name=张三
    					获取:${requestScope.name}
    
    				2. ${键名}:表示依次从最小的域中查找是否有该键对应的值,直到找到为止。
    
    				
    				
    				3. 获取对象、List集合、Map集合的值
    					1. 对象:${域名称.键名.属性名}
    						本质上会去调用对象的getter方法
    
    					2. List集合:${域名称.键名[索引]}
    
    					3. Map集合:
    						${域名称.键名.key名称}
    						${域名称.键名["key名称"]}
    
    
    		c. 隐式对象:
    			el表达式中有11个隐式对象
    			pageContext:
    				获取jsp其他八个jsp的内置对象
    					${pageContext.request.contextPath}:动态获取虚拟目录
    					
    					
    					
    	
    JSTL
    	1. 概念:JavaServer Pages Tag Library  JSP标准   标签库
    		是由Apache组织提供的开源的免费的jsp标签		<标签>
    
    	2. 作用:用于简化和替换jsp页面上的java代码		
    
    	3. 使用步骤:
    		1. 导入jstl相关jar包
    		2. 引入标签库:taglib指令:  <%@ taglib %>
    		3. 使用标签
    	
    	4. 常用的JSTL标签
    		1. if:相当于java代码的if语句
    			1. 属性:
    	            test 必须属性,接受boolean表达式
    	                如果表达式为true,则显示if标签体内容,如果为false,则不显示标签体内容
    	                一般情况下,test属性值会结合el表达式一起使用
           		 2. 注意:
    	       		 c:if标签没有else情况,想要else情况,则可以在定义一个c:if标签
    		2. choose:相当于java代码的switch语句
    			1. 使用choose标签声明         			相当于switch声明
                2. 使用when标签做判断         			相当于case
                3. 使用otherwise标签做其他情况的声明    	相当于default
    
    		3. foreach:相当于java代码的for语句
    			
    
    	5. 练习:
    		需求:在request域中有一个存有User对象的List集合。需要使用jstl+el将list集合数据展示到jsp页面的表格table中
    View Code

    软件三层架构:

    三层架构:软件设计架构
    	1. 界面层(表示层):用户看的得界面。用户可以通过界面上的组件和服务器进行交互
    	2. 业务逻辑层:处理业务逻辑的。
    	3. 数据访问层:操作数据存储文件。
    View Code

    Filter 过滤器:

    三大组件 servlet 和 filter 和 listener

    Filter:过滤器
    	1. 概念:
    		生活中的过滤器:净水器,空气净化器,
    		web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
    		过滤器的作用:
    			一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤...
    
    	2. 快速入门:
    		1. 步骤:
    			1. 定义一个类,实现接口Filter
    			2. 复写方法
    			3. 配置拦截路径
    				1. web.xml
    				2. 注解
    		2. 代码:
    			@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
    			public class FilterDemo1 implements Filter {
    			    @Override
    			    public void init(FilterConfig filterConfig) throws ServletException {
    			
    			    }
    			
    			    @Override
    			    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    			        System.out.println("filterDemo1被执行了....");
    			
    			
    			        //放行
    			        filterChain.doFilter(servletRequest,servletResponse);
    			    }
    			
    			    @Override
    			    public void destroy() {
    			
    			    }
    			}
    
    
    	3. 过滤器细节:
    		a. web.xml配置	
    			<filter>
    		        <filter-name>demo1</filter-name>
    		        <filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>
    		    </filter>
    		    <filter-mapping>
    		        <filter-name>demo1</filter-name>
    				<!-- 拦截路径 -->
    		        <url-pattern>/*</url-pattern>
    		    </filter-mapping>
    		b. 过滤器执行流程
    			1. 执行过滤器
    			2. 执行放行后的资源
    			3. 回来执行过滤器放行代码下边的代码
    		c. 过滤器生命周期方法
    			1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
    			2. doFilter:每一次请求被拦截资源时,会执行。执行多次
    			3. destroy:服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源
    		d. 过滤器配置详解
    			拦截路径配置:
    				1. 具体资源路径: /index.jsp   只有访问index.jsp资源时,过滤器才会被执行
    				2. 拦截目录: /user/*	访问/user下的所有资源时,过滤器都会被执行
    				3. 后缀名拦截: *.jsp		访问所有后缀名为jsp资源时,过滤器都会被执行
    				4. 拦截所有资源:/*		访问所有资源时,过滤器都会被执行
    			拦截方式配置:资源被访问的方式
    				注解配置:
    					设置dispatcherTypes属性
    						1. REQUEST:默认值。浏览器直接请求资源
    						2. FORWARD:转发访问资源
    						3. INCLUDE:包含访问资源
    						4. ERROR:错误跳转资源
    						5. ASYNC:异步访问资源
    				web.xml配置
    					设置<dispatcher></dispatcher>标签即可
    				
    		e. 过滤器链(配置多个过滤器)
    			执行顺序:如果有两个过滤器:过滤器1和过滤器2  洋葱模型~  
    				1. 过滤器1
    				2. 过滤器2
    				3. 资源执行
    				4. 过滤器2
    				5. 过滤器1 
    
    			过滤器先后顺序问题:
    				1. 注解配置:按照类名的字符串比较规则比较,值小的先执行
    					如: AFilter 和 BFilter,AFilter就先执行了。
    				2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行
    View Code

    增强对象的功能:

    增强对象的功能:
    	设计模式:一些通用的解决固定问题的方式
    	1. 装饰模式
    	2. 代理模式
    		概念:
    			1. 真实对象:被代理的对象
    			2. 代理对象:
    			3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
    		实现方式:
    			a. 静态代理:有一个类文件描述代理模式
    			b. 动态代理:在内存中形成代理类
    				实现步骤:
    					1. 代理对象和真实对象实现相同的接口
    					2. 代理对象 = Proxy.newProxyInstance();
    					3. 使用代理对象调用方法。
    					4. 增强方法
    
    				增强方式:(就是进一步处理数据)
    					I. 增强参数列表
    					II. 增强返回值类型
    					III. 增强方法体执行逻辑	
    View Code
    package cn.itcast.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyTest {
    
        public static void main(String[] args) {
            //1.创建真实对象
            Lenovo lenovo = new Lenovo();
            
            //2.动态代理增强lenovo对象
            /*
                三个参数:
                    1. 类加载器:真实对象.getClass().getClassLoader()
                    2. 接口数组:真实对象.getClass().getInterfaces()
                    3. 处理器:new InvocationHandler()
             */
            SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
    
                /*
                    代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
                        参数:
                            1. proxy:代理对象
                            2. method:代理对象调用的方法,被封装为的对象
                            3. args:代理对象调用的方法时,传递的实际参数
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    /*System.out.println("该方法执行了....");
                    System.out.println(method.getName());
                    System.out.println(args[0]);
    
    
    
    
    */
                    //判断是否是sale方法
                    if(method.getName().equals("sale")){
                        //1.增强参数
                        double money = (double) args[0];
                        money = money * 0.85;
                        System.out.println("专车接你....");
                        //使用真实对象调用该方法
                        String obj = (String) method.invoke(lenovo, money);
                        System.out.println("免费送货...");
                        //2.增强返回值
                        return obj+"_鼠标垫";
                    }else{
                        Object obj = method.invoke(lenovo, args);
                        return obj;
                    }
    
    
    
                }
            });
    
            //3.调用方法
    
           /* String computer = proxy_lenovo.sale(8000);
            System.out.println(computer);*/
    
            proxy_lenovo.show();
        }
    }
    动态代理

    Listener 监听器:

    Listener:监听器
    	概念:web的三大组件之一。
    		事件监听机制
    			事件	:一件事情
    			事件源 :事件发生的地方
    			监听器 :一个对象
    			注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码
    
    
    	ServletContextListener:监听ServletContext对象的创建和销毁
    		方法:
    			void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法
    			void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法
    		步骤:
    			1. 定义一个类,实现ServletContextListener接口
    			2. 复写方法
    			3. 配置
    				1. web.xml
    						<listener>
         					 <listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class>
       						</listener>
    
    						指定初始化参数<context-param>
    				2. 注解:
    					@WebListener
    View Code

    JSON数据和Java对象的相互转换:

    JSON数据和Java对象的相互转换
    
    	JSON解析器:
    		常见的解析器:Jsonlib,Gson,fastjson,jackson
    	
    	a. JSON转为Java对象
    		1. 导入jackson的相关jar包
    		2. 创建Jackson核心对象 ObjectMapper
    		3. 调用ObjectMapper的相关方法进行转换
    			1. readValue(json字符串数据,Class)
    	b. Java对象转换JSON
    		1. 使用步骤:
    			1. 导入jackson的相关jar包
    			2. 创建Jackson核心对象 ObjectMapper
    			3. 调用ObjectMapper的相关方法进行转换
    				1. 转换方法:
    					writeValue(参数1,obj):
    						参数1:
    							File:将obj对象转换为JSON字符串,并保存到指定的文件中
    							Writer:将obj对象转换为JSON字符串,并将json数据填充到字符输出流中
    							OutputStream:将obj对象转换为JSON字符串,并将json数据填充到字节输出流中
    					writeValueAsString(obj):将对象转为json字符串
    
    				2. 注解:
    					1. @JsonIgnore:排除属性。
    					2. @JsonFormat:属性值得格式化
    						@JsonFormat(pattern = "yyyy-MM-dd")
    View Code

    Jedis:

    Jedis: 一款java操作redis数据库的工具.
    使用步骤:
    	1. 下载jedis的jar包
    	2. 使用
    		//1. 获取连接
    		Jedis jedis = new Jedis("localhost",6379);
    		//2. 操作
    		jedis.set("username","zhangsan");
    		//3. 关闭连接
    		jedis.close();
    View Code

    Maven:

    Maven 的正确发音是[ˈmevən],而不是“马瘟”以及其他什么瘟。Maven 在美国是一个口语化的词 语,代表专家、内行的意思。 一个对 Maven 比较正式的定义是这么说的:Maven 是一个项目管理工具,它包含了一个项目对象模 型 (POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管 理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标 (goal)的逻辑。 

    Maven 能解决什么问题:

    可以用更通俗的方式来说明。我们知道,项目开发不仅仅是写写代码而已,期间会伴随着各种 必不可少的事情要做,下面列举几个感受一下:

    1、我们需要引用各种 jar 包,尤其是比较大的工程,引用的 jar 包往往有几十个乃至上百个, 每用 到一种 jar 包,都需要手动引入工程目录,而且经常遇到各种让人抓狂的 jar 包冲突,版本冲突。

    2、我们辛辛苦苦写好了 Java 文件,可是只懂 0 和 1 的白痴电脑却完全读不懂,需要将它编译成二 进制字节码。好歹现在这项工作可以由各种集成开发工具帮我们完成,Eclipse、IDEA 等都可以将代 码即时编译。当然,如果你嫌生命漫长,何不铺张,也可以用记事本来敲代码,然后用 javac 命令一 个个地去编译,逗电脑玩。

    3、世界上没有不存在 bug 的代码,计算机喜欢 bug 就和人们总是喜欢美女帅哥一样。为了追求美为 了减少 bug,因此写完了代码,我们还要写一些单元测试,然后一个个的运行来检验代码质量。

    4、再优雅的代码也是要出来卖的。我们后面还需要把代码与各种配置文件、资源整合到一起,定型 打包,如果是 web项目,还需要将之发布到服务器,供人蹂躏。

    试想,如果现在有一种工具,可以把你从上面的繁琐工作中解放出来,能帮你构建工程,管理 jar 包,编译代码,还能帮你自动运行单元测试,打包,生成报表,甚至能帮你部署项目,生成 Web 站 点,你会心动吗?Maven 就可以解决上面所提到的这些问题

  • 相关阅读:
    项目中遇到的css问题(随手笔记)
    vue项目启动时突然出现漏洞错误(!未解决)
    vue项目本地启动,ip出现500错误
    vue项目安装vconsole的时候出现的bug
    在码云上创建项目
    npm 遇到的坑
    脚手架方式搭建vue项目
    从码云上下载react项目并配置成可运行状态
    ibatis-dynamic的用法
    struts2中s:select标签的使用
  • 原文地址:https://www.cnblogs.com/zach0812/p/13157441.html
Copyright © 2011-2022 走看看