原生Servlet搭建项目详细文档

79 阅读14分钟

JavaWeb全新总结

Thymeleaf-Vue

web项目搭建流程

### JDBCUtils

导入德鲁伊线程池jar包,创建properties文件

username=root
password=zmj.666.999
url=jdbc:mysql://localhost:3306/jdbctest?serverTimezone=UTC
driverClass=com.mysql.jdbc.Driver

创建数据连接

public class DruidUtils {

    public static Connection getDruidConnection() throws Exception{

        Properties properties = new Properties();

        Class clazz = Class.forName("jdbcUtils.DruidUtils");

        ClassLoader classLoader = clazz.getClassLoader();

        InputStream ras = classLoader.getResourceAsStream("druid.properties");

        properties.load(ras);

        DataSource source = DruidDataSourceFactory.createDataSource(properties);
        Connection conn = source.getConnection();
        return conn;
    }
}

DAO

JavaBean

根据数据库表的设计,建造与表一一对应的JavaBean类

package bean;

/**
 * Created by KingsLanding on 2022/7/12 23:50
 */
public class Massage {
    private Integer id;
    private String name;
    private Integer age;
    private String phone;

    public Massage() {
    }

    public Massage(Integer id, String name, Integer age, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.phone = phone;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Massage{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                '}';
    }
}

DbUtils

用于操作数据库的工具类,使用前首先需要导入DbUtils的jar包

功能接口

  • 根据所需功能,创建功能接口,每个表对应一个功能接口
/**
 * Created by KingsLanding on 2022/7/12 23:52
 */
public interface MassageQuery {
    /**
     * 通过id查询一条数据
     * @param id
     * @return
     */
    public Massage getMassageOne(String sql,Integer id);

    /**
     * 通过id查询多条数据
     * @param id
     * @return
     */
    public List<Massage> getMassageMore(String sql,Integer id);

    /**
     * 获取Massage表中的所有数据
     * @param sql
     * @return
     */
    public List<Massage> getMassageAll(String sql);

    /**
     * 获取表中有多少条数据
     * @param sql
     * @return
     */
    public Long getMassageCount(String sql);

    /**
     * 增加一条数据
     * @param sql
     * @param massage
     * @return
     */
    public int addMassage(String sql,Massage massage);

    /**
     * 更新一条数据
     * @param sql
     * @param massage
     * @return
     */
    public int updateMassage(String sql,Massage massage);

    /**
     * 删除一条数据
     * @param sql
     * @param id
     * @return
     */
    public int deleteMassage(String sql,Integer id);
}
  • 实现该功能接口
/**
 * Created by KingsLanding on 2022/7/12 23:56
 */
public class MassageImpl implements MassageQuery {

    @Override
    public Massage getMassageOne(String sql, Integer id) {

        Connection conn = null;
        Massage query = null;
        try {
            conn = DruidUtils.getDruidConnection();
            System.out.println(conn);
            QueryRunner runner = new QueryRunner();
            BeanHandler<Massage> handler = new BeanHandler<>(Massage.class);

            query = runner.query(conn, sql, handler, id);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return query;
    }

    @Override
    public List<Massage> getMassageMore(String sql, Integer page) {
        Connection conn = null;
        List<Massage> query = null;
        try {
            conn = DruidUtils.getDruidConnection();
            QueryRunner runner = new QueryRunner();
            BeanListHandler<Massage> handler = new BeanListHandler<>(Massage.class);

            query = runner.query(conn, sql, handler, page);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return query;
    }

    @Override
    public List<Massage> getMassageAll(String sql) {

        Connection conn = null;
        List<Massage> query = null;
        try {
            conn = DruidUtils.getDruidConnection();
            QueryRunner runner = new QueryRunner();
            BeanListHandler<Massage> handler = new BeanListHandler<>(Massage.class);

            query = runner.query(conn, sql, handler);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return query;
    }

    @Override
    public int addMassage(String sql, Massage massage) {

        Connection conn = null;
        try {
            conn = DruidUtils.getDruidConnection();
            QueryRunner runner = new QueryRunner();

            runner.update(conn, sql, massage.getId(), massage.getName(), massage.getAge(), massage.getPhone());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return 0;
    }

    @Override
    public int updateMassage(String sql, Massage massage) {
        Connection conn = null;
        try {
            conn = DruidUtils.getDruidConnection();
            QueryRunner runner = new QueryRunner();
            runner.update(conn,sql,massage.getName(),massage.getAge(),massage.getPhone(),massage.getId());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return 0;
    }

    @Override
    public int deleteMassage(String sql, Integer id) {
        Connection conn = null;
        try {
            conn = DruidUtils.getDruidConnection();
            QueryRunner runner = new QueryRunner();
            runner.update(conn,sql,id);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return 0;
    }

    public Long getMassageCount(String sql){
        Connection conn = null;
        Long query = null;
        try {
            conn = DruidUtils.getDruidConnection();
            QueryRunner runner = new QueryRunner();
            ScalarHandler handler = new ScalarHandler();

            query = (Long)runner.query(conn, sql, handler);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return query;
    }

}

Service层(这里我写的有问题)

/**
 * Created by KingsLanding on 2022/7/14 17:57
 */
public interface MassageServiceInterface {

    /**
     * 查询所有数据
     * @return
     */
    public List<Massage> getMassageData();

    /**
     * 修改一条数据
     * @param massage
     */
    public void updateMassageData(Massage massage);

    /**
     * 获取一条数据
     * @param id
     * @return
     */
    public Massage getMassageOneData(Integer id);

    /**
     * 添加一条数据
     * @param massage
     */
    public void addMassageOneData(Massage massage);

    /**
     * 删除一条数据
     * @param id
     */
    public void deleteMassageOneData(Integer id);

    /**
     * 查询指定数量的数据,分页功能
     * @param pageNo
     * @return
     */
    public List<Massage> getMassagePage(Integer pageNo);

    /**
     * 查询表总数据条数
     * @return
     */
    public Long getMassageCount();
}
  • 接口实现
/**
 * Created by KingsLanding on 2022/7/13 11:40
 */
public class MassageService implements MassageServiceInterface{
    MassageQuery massageQuery=new MassageImpl();

    public List<Massage> getMassageData(){
        String sql="select id,name,age,phone from massage";
        List<Massage> list = massageQuery.getMassageAll(sql);
        System.out.println(list);
        return list;
    }

    public List<Massage> getMassagePage(Integer pageNo){
        String sql="select * from massage limit ?,3";
        List<Massage> massageMore = massageQuery.getMassageMore(sql,(pageNo-1)*3);
        return massageMore;
    }

    public void updateMassageData(Massage massage){
        String sql="update massage set name=?,age=?,phone=? where id=?";
        massageQuery.updateMassage(sql,massage);
    }

    public Massage getMassageOneData(Integer id){
        String sql="select id,name,age,phone from massage where id=?";
        Massage massageOne = massageQuery.getMassageOne(sql, id);
        return massageOne;
    }

    public void addMassageOneData(Massage massage){
        String sql="insert into massage(id,name,age,phone)value(?,?,?,?)";
        massageQuery.addMassage(sql,massage);
    }

    public void deleteMassageOneData(Integer id){
        String sql="delete from massage where id=?";
        massageQuery.deleteMassage(sql,id);
    }

    public Long getMassageCount(){
        String sql="select count(*) from massage";
        Long massageCount = massageQuery.getMassageCount(sql);
        return massageCount;
    }
}

Servlet

  • 为了使用Thymeleaf渲染页面

首先导入ViewBaseServlet类,导入Thymeleafjar包

package mySpringmvc;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

public class ViewBaseServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决post请求中文乱码问题
        // 一定要在获取请求参数之前调用才有效
        req.setCharacterEncoding("UTF-8");

        String action = req.getParameter("action");
        try {
            // 获取action业务鉴别字符串,获取相应的业务 方法反射对象
            Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
//            System.out.println(method);
            // 调用目标业务 方法
            method.invoke(this, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private TemplateEngine templateEngine;

    @Override
    public void init() throws ServletException {

        // 1.获取ServletContext对象
        ServletContext servletContext = this.getServletContext();

        // 2.创建Thymeleaf解析器对象
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);

        // 3.给解析器对象设置参数
        // ①HTML是默认模式,明确设置是为了代码更容易理解
        templateResolver.setTemplateMode(TemplateMode.HTML);

        // ②设置前缀
        String viewPrefix = servletContext.getInitParameter("view-prefix");

        templateResolver.setPrefix(viewPrefix);

        // ③设置后缀
        String viewSuffix = servletContext.getInitParameter("view-suffix");

        templateResolver.setSuffix(viewSuffix);

        // ④设置缓存过期时间(毫秒)
        templateResolver.setCacheTTLMs(60000L);

        // ⑤设置是否缓存
        templateResolver.setCacheable(true);

        // ⑥设置服务器端编码方式
        templateResolver.setCharacterEncoding("utf-8");

        // 4.创建模板引擎对象
        templateEngine = new TemplateEngine();

        // 5.给模板引擎对象设置模板解析器
        templateEngine.setTemplateResolver(templateResolver);

    }

    protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1.设置响应体内容类型和字符集
        resp.setContentType("text/html;charset=UTF-8");

        // 2.创建WebContext对象
        WebContext webContext = new WebContext(req, resp, getServletContext());

        // 3.处理模板数据
        templateEngine.process(templateName, webContext, resp.getWriter());
    }
}
  • 配置xml文件
<!--
根据逻辑视图名称 得到 物理视图名称
此处的视图名称是 index
那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
逻辑视图名称 : index
物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
所以真实的视图名称是: / index .html
-->

<!-- 配置上下文参数 -->
<context-param>
    <param-name>view-prefix</param-name>
    <param-value>/</param-value>
</context-param>
<context-param>
    <param-name>view-suffix</param-name>
    <param-value>.html</param-value>
</context-param>
    
</web-app>
  • 使用这种方式时,请求i转发的语句是这样的:
super.processTemplate("index",req,resp);

调用一个servlet中的不同方法的方式

switch - case
    String action = req.getParameter("action");

    if(StringUtils.isEmpty(action)){
        action="indexPage";
    }

    switch (action){
        case "index":
            index(req,resp);
            break;
        case "indexPage":
            indexPage(req,resp);
            break;
        case "update":
            update(req,resp);
            break;
        case "getMassageOne":
            getMassageOne(req,resp);
            break;
        case "add":
            add(req,resp);
            break;
        case "delete":
            delete(req,resp);
            break;
            default:
                throw new RuntimeException("action值非法");
    }
}
使用反射,动态获取运行时类的方法
String action = req.getParameter("action");

if(StringUtils.isEmpty(action)){
    action="indexPage";
}

//使用反射,动态获取运行时类的方法
Method[] methods = this.getClass().getDeclaredMethods();
for (Method m : methods){
    //获取方法名
    String methodName = m.getName();
    //判断方法名与action值是否相同
    if(action.equals(methodName)){
        m.setAccessible(true);
        try {
            //通过反射技术调用该方法
            m.invoke(this,req,resp);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
throw new RuntimeException("action值非法");

方法举例

    private void index(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        List<Massage> massageData = massageService.getMassageData();
        HttpSession session = req.getSession();
        System.out.println(massageData);
        //2.把全部信息保存到Session域中
        session.setAttribute("massageData",massageData);
        //此处的视图名称是 index
        //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称 :   index
        //物理视图名称 :   view-prefix + 逻辑视图名称 + view-suffix
        //所以真实的视图名称是:      /       index       .html
        super.processTemplate("index",req,resp);
    }

    private void indexPage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{

        int pageNo = Web_Util.parseInt(req.getParameter("pageNo"), 1);
//        HttpSession sessionPage = req.getSession();
//        sessionPage.setAttribute("pageNo",pageNo);

//        Integer pageNo =1;
//        String pageNoStr = req.getParameter("pageNo");
//        if(StringUtil.isNotEmpty(pageNoStr)){
//            pageNo=Integer.parseInt(pageNoStr);
//        }
        HttpSession session = req.getSession();
        session.setAttribute("pageNo",pageNo);

        Long massageCount = massageService.getMassageCount();
        int pageCount=(massageCount.intValue()+5-1)/5;
        session.setAttribute("pageCount",pageCount);

        List<Massage> massagePage = massageService.getMassagePage(pageNo);
        System.out.println(massagePage);
        session.setAttribute("massagePage",massagePage);
        super.processTemplate("indexPage",req,resp);
    }

    private void getMassageOne(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        String StrId = req.getParameter("id");
        if(StringUtil.isNotEmpty(StrId)){
            int id = Integer.parseInt(StrId);
            Massage massageOneData = massageService.getMassageOneData(id);
            System.out.println(massageOneData);
            //保存到request中
            req.setAttribute("massageOneData",massageOneData);
            //资源跳转
            super.processTemplate("update",req,resp);
        }
    }

引入DispatcherServlet中央控制器

通过读取applicationContext.xml文件、将中央控制器DispatcherServlet和对应的Controller关联起来

优势:避免过多的Servlet类文件,用一个Servlet控制相关功能组件,核心是利用反射原理,获取不同的属性节点

  DispatcherServlet这个类的工作分为两大部分:
   1.根据url定位到能够处理这个请求的controller组件:
    1)从url中提取servletPath : /fruit.do -> fruit
    2)根据fruit找到对应的组件:FruitController , 这个对应的依据我们存储在applicationContext.xml中
      <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController/>
      通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件
    3)根据获取到的operate的值定位到我们FruitController中需要调用的方法
   2.调用Controller组件中的方法:
    1) 获取参数
       获取即将要调用的方法的参数签名信息: Parameter[] parameters = method.getParameters();
       通过parameter.getName()获取参数的名称;
       准备了Object[] parameterValues 这个数组用来存放对应参数的参数值
       另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型
    2) 执行方法
       Object returnObj = method.invoke(controllerBean , parameterValues);
    3) 视图处理
       String returnStr = (String)returnObj;
       if(returnStr.startWith("redirect:")){
        ....
       }else if.....

/**
 * Created by KingsLanding on 2022/7/16 16:12
 */

@WebServlet("/Massage")
public class DispatcherServlet extends ViewBaseServlet{

    HashMap<String, Object> beanMap = new HashMap<>();

    public DispatcherServlet() {

    }

    public void init() throws ServletException {
        super.init();
        try {
            //解析applicationContext.xml文件
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
            //1.创建DocumentBuilderFactory
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            //2.创建DocumentBuilder对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ;
            //3.创建Document对象
            Document document = documentBuilder.parse(inputStream);

            //4.获取所有的bean节点(即applicationContext.xml)中beans标签下的id="Massage" class="servlet.MassageController"
            NodeList beanNodeList = document.getElementsByTagName("bean");
            for (int i=0;i<beanNodeList.getLength();i++){
                Node beanNode = beanNodeList.item(i);
                if(beanNode.getNodeType() == Node.ELEMENT_NODE){
                    Element beanElement = (Element)beanNode;
                    String beanId = beanElement.getAttribute("id");
                    String className = beanElement.getAttribute("class");
                    Class controllerBeanClass = Class.forName(className);
                    Object beanObj = controllerBeanClass.newInstance() ;
                    //将id中的Massage,class中的运行时类实例存到beanMap中
                    beanMap.put(beanId , beanObj) ;
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.设置响应体内容类型和字符集
        resp.setContentType("text/html;charset=UTF-8");
        req.setCharacterEncoding("UTF-8");
        //假设URL是:http://localhost:8080/Massage.do
        //那么servletPath是:/Massage.do

        /*
        思路:
            一、/Massage.do -->   Massage
            二、 Massage  ---->   MassageController

         */
        String servletPath = req.getServletPath();
        servletPath = servletPath.substring(1);
        //去头去尾,没有就不用去
//        int lastDotIndex = servletPath.lastIndexOf(".do");
//        servletPath = servletPath.substring(0,lastDotIndex);
        //Map的查询方法:get(Object key),通过key查询value---此时的controllerBeanObj就是对应的类
        Object controllerBeanObj = beanMap.get(servletPath);

        String action = req.getParameter("action");

        if(StringUtils.isEmpty(action)){
            action="indexPage";
        }
/*  通过反射获取指定的method
        try {
            Method method = controllerBeanObj.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
            method.invoke(controllerBeanObj,req,resp);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {

        } catch (InvocationTargetException e) {e.printStackTrace();
            e.printStackTrace();
        }
 */
        //使用反射,动态获取运行时类controllerBeanObj的方法
        Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
        for (Method method : methods){
            //获取方法名
            String methodName = method.getName();
            //判断方法名与action名是否相同
            if(action.equals(methodName)){
                //确保方法可用(爆破)
                method.setAccessible(true);
                try {
                    //通过反射技术调用controller中的方法
                    //这里的返回值是执行方法,方法的返回值(也就是返回的一个标识)
                    Object returnObj = method.invoke(controllerBeanObj, req);

                    //3.视图处理
                    //将这个标签进行强转为String类型
                    String methodReturnStr = (String)returnObj ;

                    //如果返回值以redirect:开头
                    if(methodReturnStr.startsWith("redirect:")){        //比如:  redirect:Massage
                        //此处是将redirect:后面的截取不要
                        String redirectStr = methodReturnStr.substring("redirect:".length());
                        //如果是返回redirect:开头的String值,先将redirect:截取,然后执行response重定向,目标就是
                        //截取后的Massage
                        resp.sendRedirect(redirectStr);
                    }else{//如果不是以redirect:开头;那就执行资源跳转方法,目标就是返回的String值methodReturnStr
                        super.processTemplate(methodReturnStr,req,resp);    // 比如:  "edit"
                    }
                    return;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        throw new RuntimeException("action值非法");

    }

}

那么之前的Servlet就成为一个普通方法类Controller

下面这个示例中没有将参数的获取抽取到中央控制器中,,只是将方法的定位和试图处理抽取到了中央控制器中,没有完全完成

/**
 * Created by KingsLanding on 2022/7/13 11:38
 */

//@WebServlet("/Massage")
    /*
        去掉这个注解,就相当于没有在web.xml文件里配置,那么这个类就不在是一个Servlet了
        此时MassageController类继承了ViewBaseServlet,但是它本身不是Servlet,所以不会调用ViewBaseServlet
        中的init()方法了
     */
public class MassageController {

    MassageServiceInterface massageService = new MassageService();

    private String index(HttpServletRequest req) throws ServletException, IOException {

        List<Massage> massageData = massageService.getMassageData();
        HttpSession session = req.getSession();
        System.out.println(massageData);
        //2.把全部信息保存到Session域中
        session.setAttribute("massageData",massageData);
        //此处的视图名称是 index
        //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称 :   index
        //物理视图名称 :   view-prefix + 逻辑视图名称 + view-suffix
        //所以真实的视图名称是:      /       index       .html
//        super.processTemplate("index",req,resp);
        return "index";
    }

    private String indexPage(HttpServletRequest req) throws  IOException{

        int pageNo = Web_Util.parseInt(req.getParameter("pageNo"), 1);
//        HttpSession sessionPage = req.getSession();
//        sessionPage.setAttribute("pageNo",pageNo);

//        Integer pageNo =1;
//        String pageNoStr = req.getParameter("pageNo");
//        if(StringUtil.isNotEmpty(pageNoStr)){
//            pageNo=Integer.parseInt(pageNoStr);
//        }
        HttpSession session = req.getSession();
        session.setAttribute("pageNo",pageNo);

        Long massageCount = massageService.getMassageCount();
        int pageCount=(massageCount.intValue()+5-1)/5;
        session.setAttribute("pageCount",pageCount);

        List<Massage> massagePage = massageService.getMassagePage(pageNo);
        System.out.println(massagePage);
        session.setAttribute("massagePage",massagePage);
//        super.processTemplate("indexPage",req,resp);
        return "indexPage";
    }

    private String getMassageOne(HttpServletRequest req) throws IOException{
        String StrId = req.getParameter("id");
        if(StringUtil.isNotEmpty(StrId)){
            int id = Integer.parseInt(StrId);
            Massage massageOneData = massageService.getMassageOneData(id);
            System.out.println(massageOneData);
            //保存到request中
            req.setAttribute("massageOneData",massageOneData);
            //资源跳转
//            super.processTemplate("update",req,resp);
        }
        return "update";
    }
    private String update(HttpServletRequest req){

        //注入数据
        Massage massage = Web_Util.InputForBeanUtil(new Massage(), req.getParameterMap());

        massageService.updateMassageData(massage);
        //重定向,重新给Massage发送请求,重新获取massage数据,覆盖到index页面
//        resp.sendRedirect("Massage");
        return "redirect:Massage";
    }

    private String add(HttpServletRequest req) throws ServletException {

        Massage massage = Web_Util.InputForBeanUtil(new Massage(), req.getParameterMap());

        massageService.addMassageOneData(massage);

//        resp.sendRedirect("Massage");
        return "redirect:Massage";
    }

    private String delete(HttpServletRequest req) throws IOException{

        Massage massage = Web_Util.InputForBeanUtil(new Massage(), req.getParameterMap());

        massageService.deleteMassageOneData(massage.getId());

//        resp.sendRedirect("Massage");
        return "redirect:Massage";
    }
}

拦截器Filter

/**
 * Created by KingsLanding on 2022/7/22 13:02
 */
@WebFilter("/*")
public class CharFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截成功");
        servletRequest.setCharacterEncoding("UTF-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

IOC容器

IOC - 控制反转 / DI - 依赖注入 控制反转:

  • 之前在Servlet中,我们创建service对象 , FruitService fruitService = new FruitServiceImpl();这句话如果出现在servlet中的某个方法内部,那么这个fruitService的作用域(生命周期)应该就是这个方法级别;
  • 如果这句话出现在servlet的类中,也就是说fruitService是一个成员变量,那么这个fruitService的作用域(生命周期)应该就是这个servlet实例级别
  • 之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产生fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory
  • 因此,我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转

依赖注入:

  • 之前我们在控制层出现代码:FruitService fruitService = new FruitServiceImpl();那么,控制层和service层存在耦合。

  • 之后,我们将代码修改成FruitService fruitService = null ; 然后,在配置文件中配置:

     <bean id="fruit" class="FruitController">
            <property name="fruitService" ref="fruitService"/>
       </bean>  
    

BeanFactory

/**
 * Created by KingsLanding on 2022/7/18 22:14
 */
public interface BeanFactory {

    Object getBean(String id);

}
/**
 * Created by KingsLanding on 2022/7/18 22:18
 */
public class ClassPathXmlContext implements BeanFactory {

   private Map<String,Object> beanMap=new HashMap<>();

   private String path;

    public ClassPathXmlContext(){
        this.path="applicationContext.xml";
    }

   public ClassPathXmlContext(String path){

       if (StringUtil.isEmpty(path)){
           throw new RuntimeException("IOC容器的配置文件没有指定");
       }

       try {
           //解析applicationContext.xml文件
           InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path);
           //1.创建DocumentBuilderFactory
           DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
           //2.创建DocumentBuilder对象
           DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder() ;
           //3.创建Document对象
           Document document = documentBuilder.parse(inputStream);

           //4.获取所有的bean节点(即applicationContext.xml)中beans标签下的id="Massage" class="servlet.MassageController"
           NodeList beanNodeList = document.getElementsByTagName("bean");
           for (int i=0;i<beanNodeList.getLength();i++){
               Node beanNode = beanNodeList.item(i);
               if(beanNode.getNodeType() == Node.ELEMENT_NODE){
                   Element beanElement = (Element)beanNode;
                   String beanId = beanElement.getAttribute("id");
                   String className = beanElement.getAttribute("class");
                   Class beanClass = Class.forName(className);
                   //创建bean实例
                   Object beanObj = beanClass.newInstance() ;
                   //将id中的Massage,class中的运行时类实例存到beanMap中
                   beanMap.put(beanId , beanObj) ;
                   //截至目前,需要注意到,bean和bean之间的依赖关系还没有设置
               }
           }

           //5.组装bean之间的依赖关系
            for (int i=0;i<beanNodeList.getLength();i++) {
                Node beanNode = beanNodeList.item(i);
                if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element beanElement = (Element) beanNode;
                    String beanId = beanElement.getAttribute("id");
                    NodeList beanChildNodesList = beanElement.getChildNodes();
                    for (int j=0;j<beanChildNodesList.getLength();j++){
                        Node beanChildNode = beanChildNodesList.item(j);

                        if (beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){
                            Element propertyElement = (Element)beanChildNode;
                            String propertyName = propertyElement.getAttribute("name");
                            System.out.println(propertyName);
                            String propertyRef = propertyElement.getAttribute("ref");
                            //1.找到propertyRef对应的实例
                            Object refObj = beanMap.get(propertyRef);
                            //2.将refObj设置到当前bean对应的实例的property属性上去
                            Object beanObj = beanMap.get(beanId);

                            Class beanClazz = beanObj.getClass();
                            Field propertyField = beanClazz.getDeclaredField(propertyName);
                            propertyField.setAccessible(true);
                            propertyField.set(beanObj,refObj);
                        }
                    }

                }
            }

       } catch (ParserConfigurationException e) {
           e.printStackTrace();
       } catch (SAXException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (NoSuchFieldException e) {
           e.printStackTrace();
       }
   }

    @Override
    public Object getBean(String id) {
        return beanMap.get(id);
    }
}

监听器Listener

ServletContextListener - 监听ServletContext对象的创建和销毁的过程
HttpSessionListener - 监听HttpSession对象的创建和销毁的过程
ServletRequestListener - 监听ServletRequest对象的创建和销毁的过程

ServletContextAttributeListener - 监听ServletContext的保存作用域的改动(add,remove,replace)
HttpSessionAttributeListener - 监听HttpSession的保存作用域的改动(add,remove,replace)
ServletRequestAttributeListener - 监听ServletRequest的保存作用域的改动(add,remove,replace)

HttpSessionBindingListener - 监听某个对象在Session域中的创建与移除
HttpSessionActivationListener - 监听某个对象在Session域中的序列化和反序列化

监听上下文启动创建IOC容器

解耦操作,降低各层之间的耦合程度,其中一层出问题不会在编译期间对其他层造成影响

在xml文件中配置listener的上下文参数

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>applicationContext.xml</param-value>
</context-param>

对应的applicationContext.xml文件

<beans>

    <bean id="MassageQuery" class="daoImpl.MassageImpl"/>

    <bean id="MassageServiceInterface" class="service.MassageService">
    <!--&lt;!&ndash;property标签用来表示属性:name表示属性名;req表示引用其他bean的id值&ndash;&gt;-->
        <property name="MassageQuery" ref="MassageQuery"/>
    </bean>

    <!--这个bean标签的作用:在dispatcherServlet中的servletPath中涉及的名字对应的是Massage,
        那么就要MassageController这个类进行处理-->
    <bean id="Massage" class="servlet.MassageController">
        <property name="MassageServiceInterface" ref="MassageServiceInterface"/>
    </bean>
</beans>

创建IOC容器

/**
 * Created by KingsLanding on 2022/7/21 12:51
 *
 *
 * 监听上下文启动,在上下文启动的时候去创建IOC容器,然后将其保存到application作用域
 * 然后中央控制器再从application作用域中获取IOC容器
 */

@WebListener
//另一种配置方式是配置xml文件的方式
/*  <listener>
        <listener-class>listener.ContextLoaderListener</listener-class>
    </listener>
 */
public class ContextLoaderListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //1.获取ServletContext对象
        ServletContext application = sce.getServletContext();
        System.out.println("chuangjian");
        //2.获取上下文的初始化参数
        String path = application.getInitParameter("contextConfigLocation");
        //3.创建IOC容器
        BeanFactory beanFactory = new ClassPathXmlContext(path);
        //4.将IOC容器保存到application作用域中
        application.setAttribute("beanFactory",beanFactory);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

那么DispatcherServlet再次迭代

/**
 * Created by KingsLanding on 2022/7/16 16:12
 */

@WebServlet("/Massage")
public class DispatcherServlet extends ViewBaseServlet{

//    private Map<String,Object> beanMap=new HashMap<>();

    private BeanFactory beanFactory;

    public DispatcherServlet() {

    }

    public void init() throws ServletException {
        super.init();
        //之前是在此处主动创建IOC容器的,现在优化为从application作用域中获取
//        beanFactory=new ClassPathXmlContext();

        ServletContext application = getServletContext();
        Object beanFactoryObj = application.getAttribute("beanFactory");
        if (beanFactoryObj!=null){
            beanFactory=(BeanFactory)beanFactoryObj;
        }else {
            throw new RuntimeException("IOC容器获取失败!");
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.设置响应体内容类型和字符集
        resp.setContentType("text/html;charset=UTF-8");
//        req.setCharacterEncoding("UTF-8");
        //假设URL是:http://localhost:8080/Massage.do
        //那么servletPath是:/Massage.do

        /*
        思路:
            一、/Massage.do -->   Massage
            二、 Massage  ---->   MassageController

         */
        String servletPath = req.getServletPath();
        servletPath = servletPath.substring(1);
        //去头去尾,没有就不用去
//        int lastDotIndex = servletPath.lastIndexOf(".do");
//        servletPath = servletPath.substring(0,lastDotIndex);
        //Map的查询方法:get(Object key),通过key查询value---此时的controllerBeanObj就是对应的类
        Object controllerBeanObj = beanFactory.getBean(servletPath);

        String action = req.getParameter("action");

        if(StringUtils.isEmpty(action)){
            action="indexPage";
        }
/*  通过反射获取指定的method
        try {
            Method method = controllerBeanObj.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
            method.invoke(controllerBeanObj,req,resp);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {

        } catch (InvocationTargetException e) {e.printStackTrace();
            e.printStackTrace();
        }
 */
        //使用反射,动态获取运行时类controllerBeanObj的方法
        Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
        for (Method method : methods){
            //获取方法名
            String methodName = method.getName();
            //判断方法名与action名是否相同
            if(action.equals(methodName)){
                //确保方法可用(爆破)
                method.setAccessible(true);
                try {
                    //通过反射技术调用controller中的方法
                    //这里的返回值是执行方法,方法的返回值(也就是返回的一个标识)
                    Object returnObj = method.invoke(controllerBeanObj, req);

                    //3.视图处理
                    //将这个标签进行强转为String类型
                    String methodReturnStr = (String)returnObj ;

                    //如果返回值以redirect:开头
                    if(methodReturnStr.startsWith("redirect:")){        //比如:  redirect:Massage
                        //此处是将redirect:后面的截取不要
                        String redirectStr = methodReturnStr.substring("redirect:".length());
                        //如果是返回redirect:开头的String值,先将redirect:截取,然后执行response重定向,目标就是
                        //截取后的Massage
                        resp.sendRedirect(redirectStr);
                    }else{//如果不是以redirect:开头;那就执行资源跳转方法,目标就是返回的String值methodReturnStr
                        super.processTemplate(methodReturnStr,req,resp);    // 比如:  "edit"
                    }
                    return;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
//        throw new RuntimeException("action值非法");
    }

}

事务管理

2. 事务管理
   1) 涉及到的组件:
     - OpenSessionInViewFilter
     - TransactionManager
     - ThreadLocal
     - ConnUtil
     - BaseDAO

   2) ThreadLocal
     - get() , set(obj)
     - ThreadLocal称之为本地线程 。 我们可以通过set方法在当前线程上存储数据、通过get方法在当前线程上获取数据
     - set方法源码分析:
     public void set(T value) {
         Thread t = Thread.currentThread(); //获取当前的线程
         ThreadLocalMap map = getMap(t);    //每一个线程都维护各自的一个容器(ThreadLocalMap)
         if (map != null)
             map.set(this, value);          //这里的key对应的是ThreadLocal,因为我们的组件中需要传输(共享)的对象可能会有多个(不止Connection)
         else
             createMap(t, value);           //默认情况下map是没有初始化的,那么第一次往其中添加数据时,会去初始化
     }
     -get方法源码分析:
     public T get() {
         Thread t = Thread.currentThread(); //获取当前的线程
         ThreadLocalMap map = getMap(t);    //获取和这个线程(企业)相关的ThreadLocalMap(也就是工作纽带的集合)
         if (map != null) {
             ThreadLocalMap.Entry e = map.getEntry(this);   //this指的是ThreadLocal对象,通过它才能知道是哪一个工作纽带
             if (e != null) {
                 @SuppressWarnings("unchecked")
                 T result = (T)e.value;     //entry.value就可以获取到工具箱了
                 return result;
             }
         }
         return setInitialValue();
     }

Cookie

  1. 创建Cookie对象
  2. 在客户端保存Cookie
  3. 设置Cookie的有效时长 cookie.setMaxAge(60) , 设置cookie的有效时长是60秒 cookie.setDomain(pattern); cookie.setPath(uri);
  4. Cookie的应用:十天免登录 4-1: 记住用户名和密码十天 setMaxAge(60 * 60 * 24 * 10)
@WebServlet("/cookie01")
public class CookieServlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.创建一个Cookie对象
        Cookie cookie = new Cookie("uname","jim");
        //2.将这个Cookie对象保存到浏览器端
        response.addCookie(cookie);



        request.getRequestDispatcher("hello01.html").forward(request,response);

    }
}

Kaptcha

  1. 为什么需要验证码
  2. kaptcha如何使用:
    • 添加jar包
    • 在web.xml文件中注册KaptchaServlet,并设置验证码图片的相关属性
    • 在html页面上编写一个img标签,然后设置src等于KaptchaServlet对应的url-pattern
  3. kaptcha验证码图片的各个属性在常量接口:Constants
  4. KaptchaServlet在生成验证码图片时,会同时将验证码信息保存到session中 因此,我们在注册请求时,首先将用户文本框中输入的验证码值和session中保存的值进行比较,相等,则进行注册

验证码的配置

<!--验证码配置-->
<servlet>
    <servlet-name>KaptchaServlet</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>

    <init-param>
        <param-name>kaptcha.textproducer.char.string</param-name>
        <param-value>abcdefg</param-value>
    </init-param>
    <init-param>
        <param-name>kaptcha.noise.impl</param-name>
        <param-value>com.google.code.kaptcha.impl.NoNoise</param-value>
    </init-param>
    <init-param>
        <param-name>kaptcha.image.width</param-name>
        <param-value>100</param-value>
    </init-param>
    <init-param>
        <param-name>kaptcha.image.height</param-name>
        <param-value>30</param-value>
    </init-param>
    <init-param>
        <param-name>kaptcha.textproducer.font.size</param-name>
        <param-value>25</param-value>
    </init-param>
    <init-param>
        <param-name>kaptcha.textproducer.font.names</param-name>
        <param-value>Courier</param-value>
    </init-param>

    <init-param>
        <param-name>kaptcha.textproducer.char.length</param-name>
        <param-value>4</param-value>
    </init-param>
    
    <init-param>
        <param-name>kaptcha.textproducer.font.color</param-name>
        <param-value>blue</param-value>
    </init-param>
    <init-param>
        <param-name>kaptcha.word.impl</param-name>
        <param-value>com.google.code.kaptcha.text.impl.DefaultWordRenderer</param-value>
    </init-param>

</servlet>
<servlet-mapping>
    <servlet-name>KaptchaServlet</servlet-name>
    <url-pattern>/kaptch.jpg</url-pattern>
</servlet-mapping>
<!--通过标签和xml文件中的配置,显示验证码的图片 -->
<img src="/kaptch.jpg">

获取真实的验证码信息

HttpSession session = req.getSession();
Object kaptcha_session= session.getAttribute("KAPTCHA_SESSION_KEY");
System.out.println(kaptcha_session);

正则表达式的使用三步骤:

1. 定义正则表达式对象
   正则表达式定义有两个方式:
   1) 对象形式
      var reg = new RegExp("abc")
   2) 直接量形式
      var reg = /abc/;
   3) 匹配模式:
    - g 全局匹配
    - i 忽略大小写匹配
    - m 多行匹配
    - gim这三个可以组合使用,不区分先后顺序
      例如: var reg = /abc/gim , var reg = new RegExp("abc","gim");
    2. 定义待校验的字符串
    3. 校验
    2)元字符
        . , \w , \W , \s , \S , \d , \D , \b , ^ , $

3)[]表示集合
  [abc] 表示 a或者b或者c
  [^abc] 表示取反,只要不是a不是b不是c就匹配
  [a-c] 表示a到c这个范围匹配

4) 出现的次数

  * 表示多次 (0 ~ n )

  + 至少一次 ( >=1 )
    ? 最多一次 (0 ~ 1)
    {n} 出现n次
    {n,} 出现n次或者多次
    {n,m} 出现n到m次

Axios

添加并引入axios的js文件

基本格式: axios().then().catch()

2. Axios
   Axios是Ajax的一个框架,简化Ajax操作
   Axios执行Ajax操作的步骤:
   1. 添加并引入axios的js文件

   2-1. 客户端向服务器端异步发送普通参数值
    - 基本格式: axios().then().catch()
    - 示例:
      axios({
        method:"POST",
        url:"....",
        params:{
            uname:"lina",
            pwd:"ok"
        }
      })
      .then(function(value){})          //成功响应时执行的回调        value.data可以获取到服务器响应内容
      .catch(function(reason){});       //有异常时执行的回调          reason.response.data可以获取到响应的内容

                                                                   reason.message / reason.stack 可以查看错误的信息

异步请求示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script language="JavaScript" src="/vue_js/axios.min.js"></script>
    <script language="JavaScript" src="/vue_js/vue.js"></script>
    <script language="JavaScript">
        window.onload=function(){
            var vue = new Vue({
                el:"#div0",
                data:{
                    uname:"zmj",
                    pwd:"ok"
                },
                methods:{
                    axios01:function () {
                        axios({
                            method:"POST",
                            url:"axiosTest",
                            params:{
                                uname:vue.uname,
                                pwd:vue.pwd
                            }
                        }).then(function (value) {
                            console.log(value)
                        }).catch(function (reason) {
                            console.log(reason)
                        });
                    }
                }
            });
        }

    </script>
</head>
<body>
<div id="div0">
    uname:<input type="text" v-model="uname"/><br/>
    pwd:<input type="text" v-model="pwd"/><br/>
    <input type="button" @click="axios01" value="发送一个带普通请求参数值的异步请求"/>
</div>
</body>
</html>
/**
 * Created by KingsLanding on 2022/7/24 13:24
 */
@WebServlet("/axiosTest")
public class AxiosTest extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uname = req.getParameter("uname");
        String pwd = req.getParameter("pwd");
        System.out.println(uname+"+"+pwd);

    }
}

JSON

  • JSON是一种数据格式

  • JSON格式表示两个学员信息的代码如下: [{sid:"s001",age:18},{sid:"s002",age:19}]

  • JSON表达数据更简洁,更能够节约网络带宽

    - 什么是JSON
      JSON是一种数据格式
      XML也是一种数据格式
      XML格式表示两个学员信息的代码如下:
      <students>
        <student sid="s001">
            <sname>jim</sname>
            <age>18</age>
        </student>
        <student sid="s002">
            <sname>tom</sname>
            <age>19</age>
        </student>
      </students>
      JSON格式表示两个学员信息的代码如下:
      [{sid:"s001",age:18},{sid:"s002",age:19}]
    - JSON表达数据更简洁,更能够节约网络带宽
    - 客户端发送JSON格式的数据给服务器端
    1) 客户端中params需要修改成:  data:
    2) 服务器获取参数值不再是 request.getParameter()...
       而是:
       StringBuffer stringBuffer = new StringBuffer("");
       BufferedReader bufferedReader = request.getReader();
       String str = null ;
       while((str=bufferedReader.readLine())!=null){
           stringBuffer.append(str);
       }
       str = stringBuffer.toString() ;

    3) 我们会发现 str的内容如下:
       {"uname":"lina","pwd":"ok"}

    -  服务器端给客户端响应JSON格式的字符串,然后客户端需要将字符串转化成js Object

客户端中params需要修改成: data

发送一个带普通请求参数值的异步请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script language="JavaScript" src="/vue_js/axios.min.js"></script>
    <script language="JavaScript" src="/vue_js/vue.js"></script>
    <script language="JavaScript">
        window.onload=function(){
            var vue = new Vue({
                el:"#div0",
                data:{
                    uname:"zmj",
                    pwd:"ok"
                },
                methods:{
                    axios02:function () {
                        axios({
                            method:"POST",
                            url:"JSONTest",
                            data:{
                                uname:vue.uname,
                                pwd:vue.pwd
                            }
                        }).then(function (value) {
                            console.log(value)
                        }).catch(function (reason) {
                            console.log(reason)
                        });
                    }
                }
            });
        }

    </script>
</head>
<body>
<div id="div0">
    uname:<input type="text" v-model="uname"/><br/>
    pwd:<input type="text" v-model="pwd"/><br/>
    <input type="button" @click="axios02" value="发送一个带普通请求参数值的异步请求"/>
</div>
</body>
</html>

服务器响应json格式的数据给客户端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>03.演示Axios发送异步请求给服务器端,服务器响应json格式的数据给客户端</title>
    <script language="JavaScript" src="/vue_js/vue.js"></script>
    <script language="JavaScript" src="/vue_js/axios.min.js"></script>
    <script language="JavaScript">
        window.onload=function(){
            var vue = new Vue({
                "el":"#div0",
                data:{
                    uname:"lina",
                    pwd:"ok"
                },
                methods:{
                    axios03:function(){
                        axios({
                            method:"POST",
                            url:"JSONTest",
                            data:{
                                uname:vue.uname,
                                pwd:vue.pwd
                            }
                        })
                            .then(function (value) {
                                var data = value.data;
                                // data对应的数据:
                                // {uname:"鸠摩智",pwd:"ok"}
                                vue.uname=data.uname;
                                vue.pwd=data.pwd;

                                //此处value中的data返回的是 js object,因此可以直接点出属性
                                //如果我们获取的是一个字符串:  "{uname:\"鸠摩智\",pwd:\"ok\"}"

                                //js语言中 也有字符串和js对象之间互转的API
                                //string JSON.stringify(object)     object->string
                                //object JSON.parse(string)         string->object
                            })
                            .catch(function (reason) {
                                console.log(reason);
                            });
                    }
                }
            });
        }
    </script>
</head>
<body>
    <div id="div0">
        uname:<input type="text" v-model="uname"/><br/>
        pwd:<input type="text" v-model="pwd"/><br/>
        <input type="button" @click="axios03" value="服务器响应json格式的数据给客户端"/>
    </div>
</body>
</html>

JSONServlet

/**
 * Created by KingsLanding on 2022/7/24 13:46
 */
@WebServlet("/JSONTest")
public class JSONServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        BufferedReader reader = req.getReader();
        StringBuffer stringBuffer = new StringBuffer();
        String str=null;
        while ((str=reader.readLine())!=null){
            stringBuffer.append(str);
        }
        str=stringBuffer.toString();
        System.out.println(str);

        //将String转化为Java object

        Gson gson = new Gson();
        //Gson有两个API
        //1、fromJson(String,T)将字符串转化成java object
        //2、toJson(java Object)将java Object转化为json字符串,这样才可以响应给客户端
        JsonUser jsonUser = gson.fromJson(str, JsonUser.class);
        System.out.println(jsonUser);

        jsonUser.setUname("鸠摩智");
        jsonUser.setPwd("123456");

        //假设user是从数据库查询出来的,现在需要将其转化成json格式的字符串,然后响应给客户端
        String userJsonStr = gson.toJson(jsonUser);
        resp.setCharacterEncoding("UTF-8");
        //MIME-TYPE
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().write(userJsonStr);
    }
}

Vue

1. Vue
    1) {{}} - 相当于innerText
    2) v-bind:attr 绑定属性值。例如,v-bind:value - 绑定value值
       简写:    :value
    3) v-model 双向绑定
       v-model:value   , 简写  v-model
    4) v-if , v-else , v-show
       v-if和v-else之间不能有其他的节点
       v-show是通过样式表display来控制节点是否显示
    5) v-for 迭代
       v-for={fruit in fruitList}
    6) v-on 绑定事件
    7) 其他:
       - trim:去除首尾空格 , split() , join()
       - watch表示侦听属性
       - 生命周期

v-bind;v-model;v-if;v-else;v-show

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script language="JavaScript" src="../vue_js/vue.js"></script>
    <script language="JavaScript">
        window.onload=function () {
            var vue = new Vue({
                "el":"#div0",
                data:{
                    msg:"hello",
                    name:"牛逼",
                    num:2,
                }
            });
        }

    </script>
</head>
<body>
<div id="div0">
    <span>{{msg}}</span>
    <!--v-bind:value表示绑定value属性,v-bind可以省略,变成:value --- 单向绑定-->
    <input type="text" v-bind:value="name"><br>
    <!--
        v-model指的是双向绑定,之前的v-bind是通过msg这个变量来控制input的输入框
        而v-model不仅由msg对象来控制input输入框,还可以反过来由input的内容来改变msg的值
    -->
    <input type="text" v-model:value="msg"><br>

    <input type="text" v-model:value="num">
    <!--v-if和v-else之间不能插入其他节点-->
    <div v-if="num%2==0" style="width: 200px;height: 200px;background-color: brown;"></div>
    <div v-else="num%2==0" style="width: 200px;height: 200px;background-color: aqua;"></div>
    <!--v-show通过display来控制节点的显示-->
    <div v-show="num%2==0" style="width: 200px;height: 200px;background-color: crimson ;"></div>

</div>

</body>
</html>

数据填充

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script language="JavaScript" src="../vue_js/vue.js"></script>
    <script language="JavaScript">
        window.onload=function () {
            var vue=new Vue({
                "el":"#div0",
                data:{
                    fruitList:[
                        {fname:"苹果",price:4,fcount:100},
                        {fname:"香蕉",price:3,fcount:5},
                        {fname:"西瓜",price:4,fcount:34},
                    ]
                }
            });
        }
    </script>
</head>
<body>
<div id="div0">
    <table border="1" width="400" cellpadding="4" cellspacing="0">
        <tr>
            <th>名称</th>
            <th>单价</th>
            <th>库存</th>
        </tr>
        <!-- v-for表示迭代 -->
        <tr align="center" v-for="fruit in fruitList">
            <td>{{fruit.fname}}</td>
            <td>{{fruit.price}}</td>
            <td>{{fruit.fcount}}</td>
        </tr>
    </table>
</div>

</body>
</html>

侦听

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script language="JavaScript" src="../vue_js/vue.js"></script>
    <script language="JavaScript">
        window.onload=function(){
            var vue = new Vue({
                "el":"#div0",
                data:{
                    num1:1,
                    num2:2,
                    num3:0
                },
                watch:{
                    //侦听属性num1和num2
                    //当num1的值有改动时,那么需要调用后面定义的方法 , newValue指的是num1的新值
                    num1:function(newValue){
                        this.num3 = parseInt(newValue) + parseInt(this.num2);
                    },
                    num2:function(newValue){
                        this.num3 = parseInt(this.num1) + parseInt(newValue) ;
                    }
                }
            });
        }
    </script>
</head>
<body>
    <div id="div0">
        <input type="text" v-model="num1" size="2"/>
        +
        <input type="text" v-model="num2" size="2"/>
        =
        <span>{{num3}}</span>
    </div>
</body>
</html>

vue的生命周期

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script language="JavaScript" src="/vue_js/vue.js"></script>
    <script language="JavaScript">
        window.onload=function(){
            var vue = new Vue({
                "el":"#div0",
                data:{
                    msg:1
                },
                methods:{
                    changeMsg:function(){
                        this.msg = this.msg + 1 ;
                    }

                },
                /*vue对象创建之前*/
                beforeCreate:function(){
                    console.log("beforeCreate:vue对象创建之前---------------");
                    console.log("msg:"+this.msg);
                },
                /*vue对象创建之后*/
                created:function(){
                    console.log("created:vue对象创建之后---------------");
                    console.log("msg:"+this.msg);
                },
                /*数据装载之前*/
                beforeMount:function(){
                    console.log("beforeMount:数据装载之前---------------");
                    console.log("span:"+document.getElementById("span").innerText);
                },
                /*数据装载之后*/
                mounted:function(){
                    console.log("mounted:数据装载之后---------------");
                    console.log("span:"+document.getElementById("span").innerText);
                },
                beforeUpdate:function(){
                    console.log("beforeUpdate:数据更新之前---------------");
                    console.log("msg:"+this.msg);
                    console.log("span:"+document.getElementById("span").innerText);
                },
                updated:function(){
                    console.log("Updated:数据更新之后---------------");
                    console.log("msg:"+this.msg);
                    console.log("span:"+document.getElementById("span").innerText);
                }
            });
        }
    </script>
</head>
<body>
    <div id="div0">
        <span id="span">{{msg}}</span><br/>
        <input type="button" value="改变msg的值" @click="changeMsg"/>
    </div>
</body>
</html>

前端Thymeleaf的基本用法

具体方法介绍:

heavy_code_industry.gitee.io/code_heavy_…

使用示例

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="css/index.css">
    <script language="JavaScript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
    <div id="div_fruit_list">
        <p class="center f30">欢迎使用员工信息管理系统</p>
    </div>
    <div style="width: 60%;margin-left: 20%;text-align: right" >
        <a th:href="@{/add.html}">添加数据</a>
    </div>
        <table id="tbl_fruit">
            <tr>
                <th class="w20">姓名</th>
                <th class="w20">年龄</th>
                <th class="w20">电话</th>
                <th colspan="2">操作</th>
            </tr>

            <tr th:if="${#lists.isEmpty(session.massagePage)}">
                <td colsapn="4">没有数据</td>
            </tr>

            <tr th:unless="${#lists.isEmpty(session.massagePage)}" th:each="massage : ${session.massagePage}">
                <td><a th:text="${massage.name}" th:href="@{/update.do(id=${massage.id})}"></a></td>
                <td th:text="${massage.age}"></td>
                <td th:text="${massage.phone}"></td>
                <td><a th:href="@{/Massage(id=${massage.id},action='getMassageOne')}">修改</a></td>
                <td><a class="deleteMassage" th:onclick="|deleteMassage(${massage.id})|">删除</a></td>
            </tr>
        </table>
        <div style="width: 60%;margin-left: 20%;padding-top: 5px" class="center">
            <input type="button" value="首页" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}">
            <input type="button" value="上一页" th:onclick="|page(${session.pageNo - 1})|" th:disabled="${session.pageNo==1}">
            <input type="button" value="下一页" th:onclick="|page(${session.pageNo + 1})|" th:disabled="${session.pageNo==session.pageCount}">
            <input type="button" value="尾页" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}">
        </div>
    </div>
</div>
</body>
</html>