day11

Servlet JSP

自定义标签

开发步骤

  1. 实现标签类,实现标签接口
    • 集成半成品的支持类更加方便
  2. 登记标签类
    • 在tld文件中定义标签名和标签类之间的关系
  3. 在JSP中使用标签
    • 引入标签库

标签运行原理:

day11_第1张图片
1.png

案例:

  1. 声明标签类

     public class MyTag extends SimpleTagSupport{
    
         @Override
         public void doTag() throws JspException,IOException{
             JspContext ctx = getJspContext();
             JspWriter out = ctx.getOut();
             out.print("Hello World!");
         }
     }
    
  2. 声明标签 mytag.tld

     
    
     
         我的标签不可描述
         mytag
         1
         my
         /mytag
         
         
             hello
             cn.tedu.web.MyTag
             empty
         
     
    

    schema 信息可以从JSTL的c.tld中复制 schema(概要,模式)

  3. 使用标签

     <%@ page 
         contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
     <%@taglib prefix="my" uri="/mytag"%>
     
     
     
     
     Insert title here
     
     
         

    自定义标签演示

  4. 测试

反射

是Java提供的一套API,可以对Java对象进行自我检查,可以检查对象的类型,类包含哪些属性,包含哪些方法,包含哪些构造器等。

反射API还提供了,动态执行功能:包括动态加载类,动态创建对象,动态访问属性,动态调用方法 等。

Java反射框架主要提供以下功能:

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  4. 在运行时调用任意一个对象的方法;

经典面试题目:

Eclipse中的"快捷菜单"用到了哪些技术?
答案:利用反射动态的获取对象的属性和方法。

利用反射检查对象的信息:

public class Demo01 {
    public static void main(String[] args) {
        test(5);
        test("5");
        test('5');
        //...
        
        List list = new ArrayList();
        list.add("TOM");
        
        Iterator ite = list.iterator();
        //检查ite的类型
        System.out.println(ite.getClass());
        
    }

    public static void test(Object obj){
        //obj 实际的类型是什么
        //利用反射,动态检查obj的实际类型
        Class cls = obj.getClass();
        System.out.println(cls);
        //还可以检查 类型全部的信息
        //Declared 声明的,Field 字段、属性
        //getDeclaredFields 返回cls中声明的全部属性信息
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //检查类中声明的方法信息
        //Method 方法
        Method[] methods = cls.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //检查类中声明的构造器信息
        Constructor[] constructors = cls.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c);
        }
        //...
    }
}

动态执行: 在运行期间根据动态信息来确定执行次序。

静态执行:Java虚拟机根据编译期间确定的执行次序顺序执行。

Foo foo = new Foo();
foo.test();

API

Class cls = controller.getClass();

Class    getClass() 
          返回此 Object 的运行时类。
返回:
表示此对象运行时类的 Class 对象。

动态加载类

API语法

Class cls = Class.forName(类名);

原理:

day11_第2张图片
2.png

动态创建对象

利用反射调用类的无参数构造器创建对象

Object obj = cls.newInstance();

动态访问属性

语法:

Object val = field.get(obj);

案例:动态加载类并且读取对象的属性:

public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
        /**
        * 动态加载类到内存方法区中
        */
        Scanner in = new Scanner(System.in);
        System.out.print("输入类名:");
        String className=in.nextLine();
        Class cls = Class.forName(className);
        System.out.println(cls);
        
        //利用反射检查 cls 的内部结构。。。
        
        //动态创建对象
        Object obj = cls.newInstance();
        System.out.println(obj);
        
        //动态访问对象的属性
        System.out.print("属性名:");
        String name = in.nextLine();
        //找到需要访问的属性信息
        Field field = cls.getDeclaredField(name);
        //在对象上查找属性对应的值
        Object val = field.get(obj);
        System.out.println(val);
    }
}

动态设置属性原理:

day11_第3张图片
3.png

利用反射动态设置对象属性:

public class Demo03{

    public static void main(String[] args)throws Exception{
        Scanner in = new Scanner(System.in);
        System.out.print("类名:");
        String className = in.nextLine();
        System.out.print("属性名:");
        String name = in.nextLine();
        System.out.print("属性值:");
        String value = in.nextLine();

        //动态加载类
        Class cls = Class.forName(className);
        //动态创建对象
        Object obj = cls.newInstance();
        //检查对象的默认属性
        System.out.println(obj);

        //查询属性信息,查询当前类中声明的属性信息
        Field fld = cls.getDeclaredField(name);
        Object val = null;
        //根据 fld的类型转换输入数据。
        if(fld.getType()==int.class){
            val = Integer.parseInt(value);
        }else if(fld.getType() == double.class){
            val = Double.parseDouble(value);
        }
        //打开属性的访问权限
        fld.setAccessible(true);
        //设置obj对象的属性值
        fld.set(obj,val);
        //展示设置结果
        System.out.println(obj);
    }
}

new关键字和newInstance()方法的区别
newInstance:弱类型。低效率。只能调用无参构造。
new:强类型。相对高效。能调用任何public构造。

API

Field

fld.getType
Class    getType() 
        返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

fld.set(obj,val);
void    set(Object obj, Object value) 
        将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

ClassLoader API

classLoader 提供了两个常用的功能

  1. 用于动态加载类
    • 返回 Class
  2. 从package 中加载资源: 图片.xml等
    • 返回InputStream

案例:

public class Demo04 {
    public static void main(String[] args) throws ClassNotFoundException, IOException {
        //利用ClassLoader 动态加载类
        
        //获取当前的ClassLoader
        ClassLoader classLoader = Demo04.class.getClassLoader();
        //classLoader 提供了两个常用的功能
        // 1. 用于动态加载类
        //      - 返回 Class
        // 2. 从package 中加载资源:图片,xml等
        //      - 返回InputStream
        String className = "cn.tedu.reflect.Foo";
        Class cls = classLoader.loadClass(className);
        System.out.println(cls);
        
        //读取package中的文件
        String path = "conf/demo.xml";
        InputStream in = classLoader.getResourceAsStream(path);
        byte[] buf = new byte[in.available()];
        in.read(buf);
        in.close();
        String str = new String(buf,"utf-8");
        System.out.println(str);
    }
}

API

ClassLoader

protected  Class loadClass(String name, boolean resolve) 
        使用指定的二进制名称来加载类。

参数:
name - 类的二进制名称
resolve - 如果该参数为 true,则分析这个类
返回:
得到的 Class 对象
抛出:
ClassNotFoundException - 如果无法找到类

InputStream getResourceAsStream(String name) 
          返回读取指定资源的输入流

InputStream

byte[] buf = new byte[in.available()];

int available() 
        返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。

in.read(buf);

int read(byte[] b) 
        从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。


String

String str = new String(buf,"utf-8");

String(byte[] bytes, String charsetName) 
        通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
charset 字符编码

经典案例:

执行一个类中全部标注了 @Test 注解的方法,这些方法都是无参数,无返回值的方法。

实现思路:

  1. 动态得到类名
  2. 动态加载类
  3. 利用反射检查类中全部的方法信息
  4. 检查方法信息上标注的注解
  5. 找到标注了 @Test 注解的方法
  6. 动态创建对象,动态执行方法

注解工作原理:

day11_第4张图片
4.png

实现步骤

  1. 编写注解 @Test

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Test {

    }

  2. 编写测试案例

     public class TestCase {
         
         @Test
         public void demo() {
             System.out.println("Hello World");
         }
         
         @Test
         public void test() {
             System.out.println("Hello Kity");
         }
         
         public void add() {
             System.out.println("Add");
         }
     }
    
  3. 核心功能实现:

     public class Demo05 {
         
         public static void main(String[] args) throws Exception {
             
             //JUnit4 原型案例
             Scanner in = new Scanner(System.in);
             System.out.print("输入类名:");
             String className = in.nextLine();
             
             //动态加载类
             Class cls = Class.forName(className);
             
             //动态创建对象
             Object obj = cls.newInstance();
             
             //检查类的全部方法
             Method[] methods = cls.getDeclaredMethods();
             for (Method method : methods) {
                 //System.out.println(method);
                 //System.out.println(method.getName());
                 //System.out.println(method.getReturnType());
                 //返回当前方法信息中全部的注解
                 //method.getAnnotations();
                 //返回当前方法上的一个特定注解,如果返回
                 //null 表示没有找到特定注解
                 Test t = method.getAnnotation(Test.class);
                 if(t!=null) {
                     System.out.println(method);
                     //执行找到的方法
                     method.invoke(obj);
                 }
             }
         }
     }
    

Method

Annotation[]    getAnnotations() 
        返回此元素上存在的所有注释。

Object  invoke(Object obj, Object... args) 
      对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

 
getAnnotation(Class annotationClass) 
        如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

为什么使用反射

使用反射的目的一般是为了解除耦合! 简称:解耦

url-pattern匹配规则

  1. 精确匹配。以"/"开头,加上servlet名称。

     Java代码
     /ad
    
  2. 路径匹配,以"/"开头,加上通配符"*"

     Java代码
     /*
    
  3. 扩展名匹配。以统配符"*"开头,加上扩展名。

     Java代码
     *.action
    
    
    
     org.dom4j.io 
     Class SAXReader
    
     Document    read(InputSource in) 
             Reads a Document from the given InputSource using SAX
    

重构Web框架

由于Web应用开发繁琐,利用重构抽取公共的Servlet功能,使开发变得更加简便的过程。

原理:

day11_第5张图片
5.png

原型步骤:

  1. 开发注解
        package cn.tedu.base.web;

        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;

        @Retention(RetentionPolicy.RUNTIME)
        public @interface RequestMapping {

            String value();

        }
  1. 开发控制器类
        package cn.tedu.demo.web;

        import cn.tedu.base.web.RequestMapping;

        @RequestMapping("/user")
        public class DemoController {

            @RequestMapping("/ok.do")
            public String hello() {
                System.out.println("Hello World!");
                return "ok";
            }

        }
  1. 编写配置文件 conf/context.xml
        
        
            
            
        
  1. 编写Servlet
        /**
        * 核心前端控制器
        */
        public class DispatcherServlet extends HttpServlet {
            private static final long serialVersionUID = 1L;
            Object controller;
            /**
            * Servlet初始化方法,加载控制器类
            */
            public void init() throws ServletException {
                //读取context.xml
                //解析出类名,并且创建对象
                try {
                    String file = "conf/context.xml";
                    InputStream in = getClass().getClassLoader().getResourceAsStream(file);
                    SAXReader saxReader = new SAXReader();
                    Document doc = saxReader.read(in);
                    in.close();
                    Element root = doc.getRootElement();
                    List list = root.elements("bean");
                    //找到类名
                    for (Element element : list) {
                        String className = element.attributeValue("class");
                        System.out.println(className);
                        Class cls = Class.forName(className);
                        controller = cls.newInstance();
                        System.out.println(controller);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ServletException(e);
                }
            }

            /**
            * doGet中调用doPost 就可以实现一个方法
            * 处理get和post两种请求
            */
            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                doPost(request, response);
            }

            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                System.out.println("Hello World!");
                
                //临时调用控制器方法
                try {
                    Class cls = controller.getClass();
                    Method method = cls.getDeclaredMethod("hello");
                    Object val = method.invoke(controller);
                    System.out.println(val);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ServletException(e);
                }
                
                response.setContentType("text/html");
                response.getWriter().print("OK");
            }
        }
  1. 配置
        
            
            DispatcherServlet
            DispatcherServlet
            cn.tedu.base.web.DispatcherServlet
        
        
            DispatcherServlet
            *.do
        
  1. 测试

作业

  1. 实现课堂案例代码
  2. 编写Web框架原型

你可能感兴趣的:(day11)