Java17-Filter&Listener&Json&redis&maven

1.Filter

        filter:过滤器(重点掌握),listener:监听器,servlet:等共称为javaweb 的三大核心组件。

1.1 概念:
         * 生活中的过滤器:净水器,空气净化器,土匪、
        * web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,(添加也就是增强)完成一些特殊的功能
         * 过滤器的作用:* 一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤...

1.2. 快速入门:
步骤:
     1. 定义一个类,实现一个接口Filter(import javax.servlet.Filter;
     2. 复写方法(servlet核心方法是service,而过滤器核心方法是doFilter)和放行方法:filterChain.doFilter

public class FilterDemoOne implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    }
    @Override
    public void destroy() {
    }
}

     3. 配置拦截路径(不是访问路径),两种方式:
             1. web.xml                      2. 注解   @WebFilter("/*")

@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() {
   }
}


1.3. 过滤器细节:
1.3.1. web.xml配置    这里很servlet很相似,只是url-pattern是拦截路径

<filter>
    <filter-name>demoOne</filter-name>
    <filter-class>cn.itcast.web.filter.FilterDemoOne</filter-class>
</filter>
    <filter-mapping>
        <filter-name>demoOne</filter-name>
<!--        拦截路径-->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

源码

image
1.3.2. 过滤器执行流程
      1. 执行过滤器(因为doFilter带request和response,所以执行前进行request增强)
      2. 执行放行后的资源
      3. 回来执行过滤器放行代码下边的代码(因为doFilter带request和response,所以执行后,我们进行response增强)

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("filterDemoTwo执行了");
        //增强request
        //放行
        chain.doFilter(req, resp);
        //增强response
        System.out.println("filterDemoTwo回来了");

    }


1.3.2. 过滤器生命周期方法
     1. init方法:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源,服务器自行完成。
     2. doFilter方法:每一次请求被拦截资源时,会执行。执行多次
      3. destroy方法:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源,非正常关闭是指直接关掉tomcat黑框按钮,idea断开是算正常关闭的。
1.3.3. 过滤器配置详解
*注解或xml的配置中url-pattern拦截路径配置:
      1. 具体资源路径: /index.jsp   只有访问index.jsp资源时,过滤器才会被执行
       2. 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行
       3. 后缀名拦截: *.jsp  访问所有后缀名为jsp资源时,过滤器都会被执行
       4. 拦截所有资源:/*    访问所有资源时,过滤器都会被执行
* 拦截方式配置:资源被访问的方式,注解和xml配置两种方式差异
        * 注解配置:
             * 设置dispatcherTypes属性
1. REQUEST:默认值。浏览器直接请求资源,别的跳转来不受影响

@WebFilter(value = "/*",dispatcherTypes = DispatcherType.REQUEST)
2. FORWARD:转发访问资源,别的跳转到指定的来不受影响
3. INCLUDE:包含访问资源,别的跳转来不受影响
4. ERROR:例如jsp里面error的错误跳转资源,别的跳转来不受影响
5. ASYNC:例如ajax中异步访问资源,别的跳转来不受影响

//浏览器直接请求资源时,该过滤器会被执行
@WebFilter(value = "/*",dispatcherTypes = DispatcherType.REQUEST)

//浏览器直接请求或转发来资源时,该过滤器会被执行
@WebFilter(value = "/*",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})

@WebServlet("/servletDemoOne")
public class ServletDemoOne extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ServletDemoOne");
        request.getRequestDispatcher("index.jsp").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}

@WebFilter(value = "/index.jsp",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
public class FilterDemoThree implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("FilterDemoThree");
        chain.doFilter(req, resp);
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

拦截器配置value=“/index.jsp”则转发过去拦截器只执行1次

ServletDemoOne
FilterDemoThree

@WebServlet("/servletDemoOne")
public class ServletDemoOne extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ServletDemoOne");
        request.getRequestDispatcher("index.jsp").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}

@WebFilter(value = "/*",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
public class FilterDemoThree implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("FilterDemoThree");
        chain.doFilter(req, resp);
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

拦截器配置value=“/*”则转发过去,拦截器执行2次

FilterDemoThree
ServletDemoOne
FilterDemoThree


          * web.xml配置
   * 设置<dispatcher></dispatcher>标签即可,而且上面的请求被拦截器访问的方式中,其实DipatcharType是枚举类型
     imageimage
1.3.4. 过滤器链(配置多个过滤器)
             * 执行顺序:如果有两个过滤器:过滤器1和过滤器2
过滤器1→ 过滤器2 → 资源执行→  过滤器2→ 过滤器1

FilterDemoFive执行了......
FilterDemoFour执行了......
index.jsp.......
FilterDemoFour回来了......
FilterDemoFive回来了......


@WebFilter("/*")
public class FilterDemoFive implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("FilterDemoFive执行了......");
        chain.doFilter(req, resp);
        System.out.println("FilterDemoFive回来了......");
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

@WebFilter("/*")
public class FilterDemoFour implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("FilterDemoFour执行了......");
        chain.doFilter(req, resp);
        System.out.println("FilterDemoFour回来了......");
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

<html>
  <head>
    <title>filter</title>
  </head>
  <body>
  index.jsp...
  <%
    System.out.println("index.jsp.......");
  %>
  </body>
</html>

* 过滤器先后顺序问题:
1. 注解配置:按照类名的字符串比较规则比较,值小的先执行
           * 如: AFilter 和 BFilter,AFilter就先执行了。
  2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行

image

  1.3.5.  案例:
1. 案例1_登录验证
             * 需求:
    1. 访问day17_case案例的资源。验证其是否登录(例如index.JSP)
    2. 如果登录了,则直接放行。
    3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
image

@WebFilter("/loginFilter")
public class LoginFilter implements Filter {
    public void destroy() {
    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //类型强制转型
        HttpServletRequest request= (HttpServletRequest) req;
        //1.获取资源来源路径
        String requestURI = ((HttpServletRequest) req).getRequestURI();
        //2.判断是否包含登录资源的相关路径
        if (requestURI.contains("/login.jsp")||requestURI.contains("/loginServlet")||requestURI.contains("/css/")||requestURI.contains("/js/")
                ||requestURI.contains("/fonts/")||requestURI.contains("/checkCodeServlet")){
            chain.doFilter(req, resp);
        }else {
            //3.不包含,需要验证用户是否登录
            Object user = request.getSession().getAttribute("user");
            if (user!=null){
                chain.doFilter(req, resp);
            }else {
                request.setAttribute("login_msg","需要登录");
                request.getRequestDispatcher("/login.jsp").forward(request,resp);
            }
        }
    }
    public void init(FilterConfig config) throws ServletException {
    }
}

        
2. 案例2_敏感词汇过滤
* 需求:
                 1. 对day17_case案例录入的数据进行敏感词汇过滤
                 2. 敏感词汇参考《敏感词汇.txt》
                 3. 如果是敏感词汇,替换为 ***

* 分析:
                 1. 对request对象进行增强。增强获取参数相关方法,因为request对象没有setParemeter方法,不能设置,需要对request对象进行增强,重新产生一个request对象。
                 2. 放行。传递新的request对象,也就是代理对象,后面三个方框的request对象已经是新的request对象。

image
1.4  增强对象的功能:
         * 设计模式:前辈们总结的一套:一些通用的解决固定问题的方式
1.4.1. 装饰模式


1.4.2. 代理模式

image
      * 概念:
        1. 真实对象:被代理的对象-例如联想公司
        2. 代理对象:也就是西安联想代理商
        3. 代理模式:代理对象(西安联想代理商)(联想公司),通过代理真实对象,达到增强真实对象功能的目的
     * 实现方式:差异就是代理对象的实现方式:
        1. 静态代理:有一个.java的类文件描述代理模式
        2. 动态代理:在内存中形成代理类(也就是看不见.java类文件)
     * 固定的实现步骤:
        1. 代理对象和真实对象实现相同的接口:也就是说真实对象要想被代理对象代理,必须要有共同的接口(两个兄弟关系)。
        2. 代理对象 = Proxy.newProxyInstance();其中Proxy是java中的类;
        3. 使用代理对象调用方法。实现相同接口,可以共用方法和增强方法
        4. 增强方法

--------------------------------------------------------------------------------

传统做法:

public interface SaleComputer {
    public String sale(double money);
    public void show();
}
下面是真实类,将来有一个真实类对应的真实对象,真实对象被代理对象代理,
来增强它的功能,也就是sale方法
public class Lenovo implements SaleComputer{
    @Override
    public String sale(double money) {
        System.out.println("花了"+money+"钱买了一台电脑");
        return "联想电脑";
    }

    @Override
    public void show() {
        System.out.println("展示电脑");
    }
}

传统实现

public class ProxyTest {
    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        //2.调用方法
        String sale = lenovo.sale(8000.0);
        System.out.println(sale);
    }
}

结果:

花了8000.0钱买了一台电脑
联想电脑

--------------------------------------------------------------------------------

动态代理方式:

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
动态代理对象增强lenovo,传递三个参数返回一个代理对象,
第一个是classloader,因为代理对象未来要执行,也要类加载器,此处用lenove相同的类加载器
第二个参数是一个class对象的接口数组
第三个参数:invocationHandler,将来是核心业务逻辑部分
返回的是接口对象,自然可以用接口对象进行类型强转
image
代理逻辑代码的参数说明如下:

image


     * 增强方式:
        1. 增强参数列表(入参)
        2. 增强返回值类型(返回)
        3. 增强方法体执行逻辑(业务逻辑)

因为代理对象调用方法只是触发invoke,其本身sale并还没干活,需要真实对象进行处理

public class ProxyTest {
    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        //2. 动态代理对象增强lenovo,传递三个参数返回一个代理对象,第一个是classloader,因为代理对象未来要执行,也要类加载器,此处用lenove相同的类加载器
        // 第二个参数是一个class对象的接口数组,第三个参数:invocationHandler,将来是核心业务逻辑部分
        //返回的是接口对象,自然可以用接口对象进行类型强转
        SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理逻辑编写,当代理对象调用其他方法时,该方法都会触发执行
             * @param proxy
             * @param method
             * @param args
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("执行了");
                //使用真实对象来调用方法和传参
                Object sale = method.invoke(lenovo, args);
                return sale;
            }
        });


//        //2.调用方法
        String sale = proxy_lenovo.sale(8000.0);
        System.out.println(sale);
    }
}

增强参数-----------------------------------------

public class ProxyTest {
    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        //2. 动态代理对象增强lenovo,传递三个参数返回一个代理对象,第一个是classloader,因为代理对象未来要执行,也要类加载器,此处用lenove相同的类加载器
        // 第二个参数是一个class对象的接口数组,第三个参数:invocationHandler,将来是核心业务逻辑部分
        //返回的是接口对象,自然可以用接口对象进行类型强转
        SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理逻辑编写,当代理对象调用其他方法时,该方法都会触发执行
             * @param proxy
             * @param method
             * @param args
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sale")){
                    double money = (double) args[0];
                    money=money*0.85;
                    Object sale = method.invoke(lenovo, money);
                    return sale;
                }else {
                    Object sale = method.invoke(lenovo, args);
                    return sale;
                }
            }
        });


//        //2.调用方法
        String sale = proxy_lenovo.sale(8000.0);
        System.out.println(sale);
        String show = proxy_lenovo.show();
        System.out.println(show);
    }

花了6800.0钱买了一台电脑
联想电脑
展示电脑
null


/**
 * 这是真实类,将来有一个真实类对应的真实对象,真实对象被代理对象代理,来增强它的功能,也就是sale方法
 */
public class Lenovo implements SaleComputer{
    @Override
    public String sale(double money) {
        System.out.println("花了"+money+"钱买了一台电脑");
        return "联想电脑";
    }

    @Override
    public String show() {
        System.out.println("展示电脑");
        return null;
    }
}

增强返回值--------------------------------------------------------------------

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sale")){
                    double money = (double) args[0];
                    money=money*0.85;
                    Object sale = method.invoke(lenovo, money);
                    return sale+"鼠标垫";
                }else {
                    Object sale = method.invoke(lenovo, args);
                    return sale;
                }
            }

花了6800.0钱买了一台电脑
联想电脑鼠标垫
展示电脑
null

增强业务逻辑------------------------------------------------------------------

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sale")){
                    double money = (double) args[0];
                    money=money*0.85;
                    System.out.println("上门接你");
                    Object sale = method.invoke(lenovo, money);
                    System.out.println("免费送货");
                    return sale+"鼠标垫";
                }else {
                    Object sale = method.invoke(lenovo, args);
                    return sale;
                }
            }
        });

上门接你
花了6800.0钱买了一台电脑
免费送货
联想电脑鼠标垫
展示电脑
null

image

-------------------------------------------------------------------------------


@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //1.创建代理对象,增强getParameter方法
        ServletRequest proxy_req = (javax.servlet.ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //此处进行增强
                if (method.getName().equals("getParameter")){
                    //增强返回值
                    String value = (String) method.invoke(req, resp);
                    if (value!=null){
                        for (String s : list) {
                            if (value.contains(s)){
                                value = value.replaceAll(s, "**");
                            }
                        }
                    }
                    return value;
                }
                //不是getParameter的方法,直接执行
                return method.invoke(req,resp);
            }
        });
        //2.放行
        chain.doFilter(proxy_req, resp);
    }
    private List<String> list= new ArrayList<String>();

    public void init(FilterConfig config) throws ServletException {
        //1.加载文件
        ServletContext servletContext = config.getServletContext();
        String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
        //2.读取文件
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(realPath));
            //3.将文件的每一行数据添加到list中
            String line= null;
            while ((line=bufferedReader.readLine())!=null){
                list.add(line);
            }
            bufferedReader.close();
            System.out.println(list);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

-------------------------------------------------------------------------------

2.Listener:有很多,我们今天学习其中一个

回顾下:javaScript中的事件监听机制

  • 事件:一个事情
  • 事件源:事件发生的地方
  • 监听器:一个对象
  • 注册监听:将事件、事件源、监听器绑定在一起,当事件源上发生某个事件后,执行监听器代码

ServletContextListener:

 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

image

public class ContextloaderListener implements ServletContextListener {
    /**
     * 监听servletContext对象创建的,servletContext对象服务器启动后自动创建
     * @param servletContextEvent
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
    //加载资源文件
        //1.获取servletContext对象
        ServletContext servletContext = servletContextEvent.getServletContext();
        //2.加载资源文件,有getInitParameter方法
        String initParameter = servletContext.getInitParameter("contextConfiglation.xml");
        //3.获取真实路径
        String realPath = servletContext.getRealPath(initParameter);
        //4.加载进内存
        try {
            FileInputStream fileInputStream = new FileInputStream(realPath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }
    /**
     * 服务器关闭后,servletContext对象被销毁,当服务器正常关闭后该方法被调用
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
    }
}
一般来说,用于加载全局配置文件,而且未来框架中是我们通过配置文件加载的比较多。也可以在xml中给配置初始化参数 <context-param>


3.Json-javaScript的对象表示法

在java中我们可以用对象来表达一系列属性和值,封装为对象,将对象作为参数传递,在javaScript中表达对象,就是json

  • java中

Person p = new Person();
p.setName("张三");
p.setAge(23);
p.setGender("男");

  • JavaScript中

var p = {"name":"张三","age":23,"gender":"男"};

* json现在多用于存储和交换文本信息的语法, * 进行数据的传输, * JSON 比 XML 更小、更快,更易解析。

3.1.JSON语法:

  • 规则

* 数据在名称/值对中:json数据是由键值对构成的
     * 键用引号(单双都行)引起来,也可以不使用引号
     * 值得取值类型:
         1. 数字(整数或浮点数)
         2. 字符串(在双引号中)
         3. 逻辑值(true 或 false)
         4. 数组(在方括号中)    {"persons":[{},{}]}
         5. 对象(在花括号中) {"address":{"province":"陕西"....}}
         6. null

* 数据由逗号分隔:多个键值对由逗号分隔
* 花括号保存对象:使用{}定义json 格式
* 方括号保存数组:[]

  • 获取数据

获取方法1. json对象.键名
获取方法2. json对象["键名"]
获取方法3. 数组对象[索引]
获取方法4. 遍历
                      //1.定义基本格式
                     var person = {"name": "张三", age: 23, 'gender': true};
            
                     var ps = [{"name": "张三", "age": 23, "gender": true},
                         {"name": "李四", "age": 24, "gender": true},
                         {"name": "王五", "age": 25, "gender": false}];

                     //获取person对象中所有的键和值
                     //for in 循环
                    /* for(var key in person){
                         //这样的方式获取不行。因为相当于  person."name"
                         //alert(key + ":" + person.key);
                         alert(key+":"+person[key]);
                     }*/
            
                    //获取ps中的所有值
                     for (var i = 0; i < ps.length; i++) {
                         var p = ps[i];
                         for(var key in p){
                             alert(key+":"+p[key]);
                         }
                     }

    

image

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

* JSON解析器:
             * 常见的解析器:Jsonlib,Gson(谷歌),fastjson(阿里),jackson(后面框架集成了)
        

1. Java对象转换JSON-javaScript支持解析json。最老套的方式是用获取值,设置值,最后拼接字符串方式转为json也可以,但麻烦。我们一般用json解析器进行数据转换。* 常见的解析器:Jsonlib,Gson(谷歌),fastjson(阿里),jackson(后面SpringMVC框架集成了)
      1. 使用步骤:
         1. 导入jackson的相关jar包和java对象
         2. 创建Jackson核心对象 ObjectMapper
         3. 调用ObjectMapper的相关方法进行转换
                 1. 转换方法两个方法:
* writeValue(参数1,obj):重载形式较多
参数1的几种重载形式:
(1)ile:将obj对象转换为JSON字符串,并保存到指定的文件中
(2)riter:将obj对象转换为JSON字符串,并将json数据填充到字符输出流中
(3)  OutputStream:将obj对象转换为JSON字符串,并将json数据填充到字节输出流中

将文件写入d盘某个文件里

mapper.writeValue(new File("d://a.txt"),person);


* writeValueAsString(obj):将对象转为json字符串

String jsonData = mapper.writeValueAsString(person);

public class JacksonTest {
    @Test
    public void testOne() throws JsonProcessingException {
        Person person= new Person();
        person.setName("zhangsan");
        person.setAge(22);
        person.setGender("");
        System.out.println(person); //Person{name='zhangsan', age=22, gender='男'}

        //创建jackson核心对象 objectMapper
        ObjectMapper mapper = new ObjectMapper();
        //转换
        String jsonData = mapper.writeValueAsString(person);
        System.out.println(jsonData);
    }
}

结果::::::

Person{name='zhangsan', age=22, gender='男'}
{"name":"zhangsan","age":22,"gender":""}

     2. 其他注解使用(在bean对象中进行设置转换要求):
(1)@JsonIgnore:排除属性。将来不会将它(属性键值对)进行json转换,
(2)@JsonFormat:属性值得格式化 @JsonFormat(pattern = "yyyy-MM-dd")

imageimage

imageimage

      3. 复杂java对象转换json
            1. List:数组[{},{},{}]
            2. Map:与java对象转换json格式一致{}

public class JacksonTest {
    @Test
    public void testOne() throws IOException {
        Person person1= new Person();
        Person person2= new Person();
        Person person3= new Person();
        person1.setName("zhangsan");
        person1.setAge(22);
        person1.setGender("");

        person2.setName("zhangsan");
        person2.setAge(22);
        person2.setGender("");

        person3.setName("zhangsan");
        person3.setAge(22);
        person3.setGender("");


        //创建jackson核心对象 objectMapper
        ObjectMapper mapper = new ObjectMapper();
        //转换
        //数组保存
        ArrayList<Person> people = new ArrayList<>();
        people.add(person1);
        people.add(person2);
        people.add(person3);
        String jsonData = mapper.writeValueAsString(people);
        System.out.println(jsonData);
    }
}

结果
[{"name":"zhangsan","age":22,"gender":""},{"name":"zhangsan","age":22,"gender":""},{"name":"zhangsan","age":22,"gender":""}]


public class JacksonTest {
    @Test
    public void testOne() throws IOException {
        Map<String,Object> map= new HashMap<String, Object>();
        map.put("name","zhangsan");
        map.put("age",22);
        map.put("gender","");

        ObjectMapper mapper = new ObjectMapper();
        String mapSon = mapper.writeValueAsString(map);
        System.out.println(mapSon);
    }
}



{"gender":"","name":"zhangsan","age":22}

---------------------------JSON转为Java--------------------------------------

1. JSON转为Java对象步骤 (readValue参数一个是json一个是转换对象class类型)

客户端与服务器端进行数据传输,可以以json数据方式进行转换,当客户端数据去服务器,我们可以将json数据转换为java对象,我们反过来也可以进行java对象转为json数据。
      1. 导入jackson的相关jar包
      2. 创建Jackson核心对象 ObjectMapper
      3. 调用ObjectMapper的相关方法进行转换
                1. readValue(json字符串数据,Class):将json转为什么对象;

public class JacksonTest {
    @Test
    public void testOne() throws IOException {
        //1.初始化json字符串
        String str="{\"gender\":\"男\",\"name\":\"zhangsan\",\"age\":22}\n";
        //2.创建核心对象
        ObjectMapper mapper = new ObjectMapper();
        //3.转换
        Person person = mapper.readValue(str, Person.class);
        System.out.println(person);
    }
}


Person{name='zhangsan', age=22, gender='男'}



# 案例:
     * 校验用户名是否存在
         1. 服务器响应的数据,在客户端使用时,要想当做json数据格式使用。有两种解决方案:
             1. $.get(type):将最后一个参数type指定为"json"
             2. 在服务器端设置MIME类型
                 response.setContentType("application/json;charset=utf-8");

image

1611826949(1)

1611826930(1)

image

image

1612068852(1)

1612068943(1)

1612068898(1)

@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取用户名
        String username = request.getParameter("username");

        //调用service判断用户名是否存在,简化
        /**
         * 期望返回数据格式{“userExist”:true,“msg”:“可以用”}
         */
        HashMap<String, Object> map = new HashMap<>();
        response.setContentType("text/html;charset=utf-8");
        if ("tom".equals(username)){
            map.put("userExist",true);
            map.put("msg","不可以使用");
        }else{
            map.put("userExist",false);
            map.put("msg","可以使用");
        }
        //转为json//传递给客户端,而此处用简化写法,response.getWriter就是给客户端显示的
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getWriter(),map);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}


<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery-3.3.1.js"></script>
    <script>
        //页面加载完成后,进行
        $(function () {
            //给username绑定blur事件
            $("#username").blur(function () {
                //获取username文本框的值
                var username = $(this).val();
                //发送ajax请求,{username:username} 前面是键的名称,后面username是上面的值,第三个参数是回调函数,返回一个值{“userExist”:true,“msg”:不可用}或{“userExist”:false,“msg”:可用}

                $.get("findUserServlet",{username:username},function (data) {
                    var span=$("#s_username");
                    //判断是否是true
                    if(data.userExsit){
                        //用户存在
                        span.css("color","red")
                        span.html("换个吧")
                    }else{
                        //不存在
                        span.html("可以用")

                    }
                },"json")//指定接收类型为json类型

            })
        })
    </script>
</head>
<body>
<form>
    <input type="text" id="username" name="username" placeholder="请输入用户名">
    <span id="s_username"></span>
    <input type="text" name="password" placeholder="请输入密码">
    <input type="submit" value="提交">


</form>
</body>
</html>

json后端指定其json返回响应类型

1612068943(1)


4.redis


         1. 概念
         2. 下载安装
         3. 命令操作
             1. 数据结构
         4. 持久化操作
         5. 使用Java客户端操作redis


# Redis
     1. 概念: redis是一款高性能的NOSQL系列的非关系型数据库


1.1.什么是NOSQL
      NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
      随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。

1.1.1.NOSQL和关系型数据库比较(redis支持事务管理,有些nosql不支持)
优点:
1)成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
2)查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
3)存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
4)扩展性:关系型数据库有类似join这样的多表查询机制限制导致扩展艰难。

缺点:
1)维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而语。
2)不提供对sql的支持,如果不支持sql这样的工业标准,将产生一定用户的学习和使用成本。
3)不提供关系型数据库对事务的处理。

1.1.2.    非关系型数据库的优势:(redis,hbase)
1)性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。
2)可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。

1.1.3.    关系型数据库的优势:(mysql,oracle)
1)复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
2)事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库,对方的优势就是自己的弱势,反之亦然。

1612143400(1)

       一句话,关系型数据库查询存在对数据库的IO操作,对流的内存中消耗较大,而缓存的开始,造成redis具有鲜明的特色。

1.1.4.    总结
    关系型数据库与NoSQL数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用NoSQL的时候使用NoSQL数据库,让NoSQL数据库对关系型数据库的不足进行弥补。 一般会将数据存储在关系型数据库中,在nosql数据库中备份存储关系型数据库的数据

1.2.主流的NOSQL产品
?    键值(Key-Value)存储数据库
       相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
       典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
       数据模型: 一系列键值对
      优势: 快速查询
       劣势: 存储的数据缺少结构化
?    列存储数据库
       相关产品:Cassandra, HBase, Riak
       典型应用:分布式的文件系统
       数据模型:以列簇式存储,将同一列数据存在一起
       优势:查找速度快,可扩展性强,更容易进行分布式扩展
       劣势:功能相对局限
?    文档型数据库
        相关产品:CouchDB、MongoDB
        典型应用:Web应用(与Key-Value类似,Value是结构化的)
       数据模型: 一系列键值对
       优势:数据结构要求不严格
        劣势: 查询性能不高,而且缺乏统一的查询语法
?    图形(Graph)数据库
         相关数据库:Neo4J、InfoGrid、Infinite Graph
         典型应用:社交网络
         数据模型:图结构
         优势:利用图结构相关算法。
         劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
1.3 什么是Redis
             Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
    1) 字符串类型 string
    2) 哈希类型 hash
    3) 列表类型 list
    4) 集合类型 set
    5) 有序集合类型 sortedset

1.3.1 redis的应用场景
     ?    缓存(数据查询、短连接、新闻内容、商品内容等等)
     ?    聊天室的在线好友列表
     ?    任务队列。(秒杀、抢购、12306等等)
     ?    应用排行榜
     ?    网站访问统计
     ?    数据过期处理(可以精确到毫秒)【redis的key支持自动过去清除功能,例如激活码】
     ?    分布式集群架构中的session分离


2. 下载安装
         1. 官网:
https://redis.io
         2. 中文网:http://www.redis.net.cn/
         3. 解压直接可以使用:
             * redis.windows.conf:配置文件
             * redis-cli.exe:redis的客户端
             * redis-server.exe:redis服务器端,类似于mysql按照服务器端一样,打开它即可简单操作:
         image
3. 命令操作
    (1). redis的数据结构:
             * redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构。可以把redis当成大的map集合
     * value的数据结构:
1) 字符串类型 string
2) 哈希类型 hash : map格式  (键-值)
3) 列表类型 list : linkedlist格式。支持重复元素
4) 集合类型 set  : 不允许重复元素
5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序
         image
    (2). 字符串类型 string
             1. 存储: set key value
                 127.0.0.1:6379> set username zhangsan
                 OK
             2. 获取: get key
                 127.0.0.1:6379> get username
                 "zhangsan"
             3. 删除: del key
                 127.0.0.1:6379> del age
                 (integer) 1
    (3). 哈希类型 hash
             1. 存储: hset key field value(field是某个字段名称)
                 127.0.0.1:6379> hset myhash username lisi
                 (integer) 1
                 127.0.0.1:6379> hset myhash password 123
                 (integer) 1
             2. 获取:
                 * hget key field: 获取指定的field对应的值(field是某个字段名称)
                     127.0.0.1:6379> hget myhash username
                     "lisi"
                 * hgetall key:获取所有的field和value(field是某个字段名称)
                     127.0.0.1:6379> hgetall myhash
                     1) "username"
                     2) "lisi"
                     3) "password"
                     4) "123"
                    
             3. 删除: hdel key field(field是某个字段名称)
                 127.0.0.1:6379> hdel myhash username
                 (integer) 1
        
  ( 4). 列表类型 list:类似队列

可以添加一个元素到列表的头部(左边)或者尾部(右边)
             1. 添加:
                 1. lpush key value: 将元素加入列表左表
                    
                 2. rpush key value:将元素加入列表右边
                    
                     127.0.0.1:6379> lpush myList a
                     (integer) 1
                     127.0.0.1:6379> lpush myList b
                     (integer) 2
                     127.0.0.1:6379> rpush myList c
                     (integer) 3
             2. 获取:
                 * lrange key start end :范围获取
                     127.0.0.1:6379> lrange myList 0 -1
                     1) "b"
                     2) "a"
                     3) "c"
             3. 删除:
                 * lpop key: 删除列表最左边的元素,并将元素返回
                 * rpop key: 删除列表最右边的元素,并将元素返回


   (5). 集合类型 set : 不允许重复元素
             1. 存储:sadd key value
                 127.0.0.1:6379> sadd myset a
                 (integer) 1
                 127.0.0.1:6379> sadd myset a
                 (integer) 0
             2. 获取:smembers key:获取set集合中所有元素
                 127.0.0.1:6379> smembers myset
                 1) "a"
             3. 删除:srem key value:删除set集合中的某个元素   
                 127.0.0.1:6379> srem myset a
                 (integer) 1
  (6). 有序集合类型 sortedset:不允许重复元素,且元素有顺序.每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。一般用于热搜排行榜

            1. 存储:zadd key score value
                 127.0.0.1:6379> zadd mysort 60 zhangsan
                 (integer) 1
                 127.0.0.1:6379> zadd mysort 50 lisi
                 (integer) 1
                 127.0.0.1:6379> zadd mysort 80 wangwu
                 (integer) 1
             2. 获取:zrange key start end [withscores]
                 127.0.0.1:6379> zrange mysort 0 -1
                 1) "lisi"
                 2) "zhangsan"
                 3) "wangwu"

                127.0.0.1:6379> zrange mysort 0 -1 withscores
                 1) "zhangsan"
                 2) "60"
                 3) "wangwu"
                 4) "80"
                 5) "lisi"
                 6) "500"
             3. 删除:zrem key value
                 127.0.0.1:6379> zrem mysort lisi
                 (integer) 1

   (7). 通用命令
      1. keys * : 查询所有的键
      2. type key : 获取键对应的value的类型
      3. del key:删除指定的key value


4. 持久化
4.1. redis是一个内存数据库(临时的),当redis服务器重启,或者电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中
4.2. redis持久化机制:RDB-默认,性能消耗低和AOF-配置-性能消耗高
4.2.1. RDB:默认方式,不需要进行配置,默认就使用这种机制
        * 在一定的间隔时间中,检测key的变化情况,然后持久化数据
(1)编辑redis.windwos.conf文件
                     #   after 900 sec (15 min) if at least 1 key changed
                     save 900 1 15分钟内有一个key变化,持久化一次
                     #   after 300 sec (5 min) if at least 10 keys changed
                     save 300 10 5分钟内有10个key变化,持久化一次
                     #   after 60 sec if at least 10000 keys changed
                     save 60 10000 1分钟内有10000key变化,持久化一次
    image
(2)重新启动redis服务器,cmd打开redis-server,并指定配置文件名称
                     D:\JavaWeb2018\day23_redis\资料\redis\windows-64\redis-2.8.9>redis-server.exe redis.windows.conf   
                 image

s最后一个   设置为 save 10  5

image
4.2.2. AOF:日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据-类似于mysql
     1. 编辑redis.windwos.conf文件 392行,下面内容在421行
           appendonly no(默认关闭aof) --> appendonly yes (开启aof)
# appendfsync always : 每一次操作都进行持久化
appendfsync everysec : 每隔一秒进行一次持久化 (开启情况下是默认)
# appendfsync no     : 不进行持久化

image

image

image

        保存在aof文件中中,则下次再次cmd打开,就能找到数据,但存在内存持久化保存数据,容易存在数据丢失,只能保证大部分数据存在,因此还是建议mysql。


5. Java客户端 Jedis
         * Jedis: 一款java操作redis数据库的工具.类似于jdbc
         * 使用步骤:
     1. 下载jedis的jar包
     2. 使用
                 //1. 获取连接     Jedis jedis = new Jedis("localhost",6379);
                 //2. 操作            jedis.set("username","zhangsan");
                 //3. 关闭连接     jedis.close();

    @Test
    public  void testOne(){
        //1.获取连接
        Jedis jedis = new Jedis("localhost", 6379);
        //2.操作
        jedis.set("username", "zhangsan");
        //3.关闭连接
        jedis.close();
    }

* Jedis操作各种redis中的数据结构
             1) 字符串类型 string                    set                    get

image
             2) 哈希类型 hash : map格式     hset     hget          hgetAll

例如激活码有效期,redis对象自带方法,即可使用,比单纯代码限制简单的多

image

             3) 列表类型 list : linkedlist格式。支持重复元素
                 lpush / rpush
                 lpop / rpop
                 lrange start end : 范围获取  返回的是数组,里面对象是字符串

image

image

image


             4) 集合类型 set  : 不允许重复元素    sadd  smembers:获取所有元素

             5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序
                 zadd                                      zrange

         1612667196(1)
* jedis连接池: JedisPool是jedis自带的,不同于jdbc的第三方
             * 使用:
                 1. 创建JedisPool连接池对象
                 2. 调用方法 getResource()方法获取Jedis连接

1612683919(1)

image

image


## 案例:
     案例需求:
         1. 提供index.html页面,页面中有一个省份 下拉列表
         2. 当 页面加载完成后 发送ajax请求,加载所有省份


* 注意:使用redis缓存一些不经常发生变化的数据。
      * 数据库的数据一旦发生改变,则需要更新缓存。
* 数据库的表执行 增删改的相关操作,需要将redis缓存数据情况,再次存入
* 在service对应的增删改方法中,将redis数据删除。
         image

1613616572(1)

代码待补充


5.maven


5.5.maven的标准目录结构

     我们最终开完一个项目最后肯定要打包,然后存在一个核心代码部分(打jar包),有些频繁修改的东西是配置文件,不需要打jar包进入核心代码中,然后也存在测试代码和测试配置文件。项目名-src-目录名容易不统一,造成目录难以阅读理解。

     maven的标准目录中,进行以下划分

src/main/java:核心代码部分

src/main/resources:配置文件部分

src/test/java:测试代码部分

src/test/resources:测试配置文件

src/main/webapp: 页面资源,js,css,图片等等

5.6.maven的常用命令

在项目根目录下,进行入cmd,cd:目录-》d:-》maven-helloworld>mvn clean

mvn clean是清除target目录-也就是说一旦我们看别人项目,必须清空一次他们的编译,否则每个人的编译环境不同,容易产生文件或代码导致bug遗漏

image

mvn compile是编译代码

target是src/main目录下的文件进行打包编译为class文件

image

image

mvn-test:会把test目录下进行编译,且它会自动把正式代码main下面代码进行更新。

image

mvn-package

image

image

mvn install

image

1613965058(1)

5.7 maven的生命周期

我们用默认生命周期会多些

image

5.8 maven概念模型图


1613966059(1)

pom.xml内容

1613965667(1)

1613965696(1)

5.9 idea集成maven-尤其是第二图片,无网络情况下进行maven工程创建

image

image

5.10 maven工程中使用骨架也就是模板进行创建java项目

image

1614043279(1)

image

默认maven创建java工程,但文件夹不完整,需要手动补齐resources,且设置为资源文件

5.11 不使用骨架创建maven的java工程方法

image

image

建议不使用骨架,快捷有效

5.12 使用骨架创建maven的web工程

image

image

image

5.13 案例:用maven工程写servlet跳转等项目

下面操作可以让java文件包下面可以创建jsp文件

image

image

遇到一个问题,因为新创建的web工程,而maven工程中确实没有jar包,他pom中只是放置了java的坐标image

我们在pom中添加手动路径,即可进行,且在下面输入包名,即可模糊出现匹配

image

image

image

image

image

image

image














posted @ 2021-01-27 14:07  芒果侠  阅读(280)  评论(0编辑  收藏  举报