其他分享
首页 > 其他分享> > day36(注解、V23:反射机制重构DispatcherServlet)

day36(注解、V23:反射机制重构DispatcherServlet)

作者:互联网

day36(注解、V23:反射机制重构DispatcherServlet)

1.注解

1.定义

package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 注解
* 注解在开发中常被我们利用到反射机制中,辅助反射机制做更多灵活的操作
* 注解在如今JAVA流行的框架中被大量的应用,简化了以前繁琐的配置工作。
*
* 注解可以在:
* 类上,属性上,方法上,构造器上,以及参数上使用
* 可以通过java内置的注解@Target来说明当前注解可以被应用的位置,对应的值被定义在ElementType上
* 例如:
* @Target(ElementType.TYPE) 注解只能被用于类上
* @Target({ElementType.TYPE,ElementType.METHOD}) 注解只能被用于类上或方法上
* 当可以用于多个位置时,需要定义成数组的方式包含所有ElementType的值,即"{}"包含
*
*
* @Retention注解,用于标注当前注解的保留级别,有三个选项
* * RetentionPolicy.SOURCE 注解仅保留在源代码中
* * RetentionPolicy.CLASS 注解保留在字节码中,但是反射机制不能调用
* * RetentionPolicy.RUNTIME 注解保留在字节码文件中,并且可以被反射机制所使用
* * 当不指定@Retention时,默认的保留级别为CLASS,因此我们通常都需要明确指出保留级别为RUNTIME
*/

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}

2.使用范围

类上,属性上,方法上,构造器上,以及参数上使用

3.@Retention注解,用于标注当前注解的保留级别,有三个选项

4.是否被注解了:isAnnotationPresent

package reflect;

import reflect.annotations.AutoRunClass;

/**
* 在反射机制中查看注解
* */
public class ReflectDemo8 {
   public static void main(String[] args) throws Exception {
  // Class cls=Class.forName("reflect.Person");
   Class cls=Class.forName("reflect.ReflectDemo7");
   boolean tf= cls.isAnnotationPresent(AutoRunClass.class);
   if (tf){
       System.out.println(cls.getName()+":被注解@AutoRunClass标注了");
  }else {
       System.out.println(cls.getName()+":没有被注解@AutoRunClass标注");
  }

  }
}

1.练习:(标注的类)

自动化实例与当前Test3在同一个包中的被@AutoRunClass标注的类
package reflect;

import reflect.annotations.AutoRunClass;

import java.io.File;

/**
* 自动实例化与当前类Test3在同一个包中被@AutoRunClass标注的类
*/
public class Test3 {
   public static void main(String[] args) throws Exception {
       File dir = new File(
               Test3.class.getResource(".").toURI()
      );
       //通过当前类Test3的类对象获取所在的包名
       String packageName = Test3.class.getPackage().getName();
       //获取Test3.class文件所在的目录中所有.class文件
       File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
       for(File sub : subs) {
           //获取字节码文件的文件名
           String fileName = sub.getName();
           String className = fileName.substring(0, fileName.indexOf("."));
           //加载该类的类对象
           Class cls = Class.forName(packageName + "." + className);
           if(cls.isAnnotationPresent(AutoRunClass.class)){
               System.out.println("实例化:"+className);
               Object o = cls.newInstance();
          }
      }
  }
}

2.练习:(标注的方法)

自动调用与Test4在同一个包中那些被@AutoRunClass标注的类中所有被@AutoRunMethod标注的方法
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

/**
* 自动调用与Test4在同一个包中那些被@AutoRunClass标注的类中所有被@AutoRunMethod标注的方法
*/
public class Test4 {
   public static void main(String[] args) throws Exception {
       File dir = new File(
               Test4.class.getResource(".").toURI()
      );
       //通过当前类Test3的类对象获取所在的包名
       String packageName = Test4.class.getPackage().getName();
       //获取Test3.class文件所在的目录中所有.class文件
       File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
       for(File sub : subs) {
           //获取字节码文件的文件名
           String fileName = sub.getName();
           String className = fileName.substring(0, fileName.indexOf("."));
           //加载该类的类对象
           Class cls = Class.forName(packageName + "." + className);
           if(cls.isAnnotationPresent(AutoRunClass.class)){
               Object o = cls.newInstance();
               //获取该类定义的所有方法
               Method[] methods = cls.getDeclaredMethods();
               for(Method method : methods){

                   if(method.isAnnotationPresent(AutoRunMethod.class)){
                       System.out.println("自动调用"+className+"类的方法:"+method.getName());
                       method.invoke(o);
                  }
              }

          }
      }
  }
}

5.注解传参

1.概念

package reflect.annotations;

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


/*该注解只能用于标注方法*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
   /*
        定义参数的格式为:
        格式:类型 参数名() [default 默认值]
        注:default可选,用于为当前参数定义默认值。如果不指定,则使用注解时必须为此参数赋值。

        使用注解传参时格式:
        @注解名(参数名1=参数值1[,参数名2=参数值2,....])

        如果注解@AutoRunMethod只有一个参数,且参数名为num时,那么使用时格式如下:
        @AutoRunMethod(num=1)

        =============重点=============
        如果注解中只有一个参数,参数名建议选取value,这样的好处是,使用时可以不指定参数民,如:
        @AutoRunMethod(1)

        如果指定了默认值,则可以不指定参数,例如:
        @AutoRunMethod()   此时注解中参数的使用default的默认值

     */
   //为注解定义一个int型的参数
//   int num() default 1;//一个参数时,参数名不建议选取value以外的名字。@AutoRunMethod(num=3)
   int value () default 1;//格式:类型 参数名()
}

2.重点

如果注解中只有一个参数,参数名建议选取value,这样的好处是,使用是可以不指定参数民,如: @AutoRunMethod(1)

3.练习

自动调用与Test4在同一个包中那些被@AutoRunClass标注的类中所有被@AutoRunMethod标注的方法n次,n对应的是注解@AutoRunMethod传入的参数值

package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

/**
* 自动调用与Test4在同一个包中那些被@AutoRunClass标注的类中所有被@AutoRunMethod标注的方法
* n次,n对应的是注解@AutoRunMethod传入的参数值
*
*
*/
public class Test5 {
   public static void main(String[] args) throws Exception {
       File dir = new File(
               Test4.class.getResource(".").toURI()
      );
       //通过当前类Test3的类对象获取所在的包名
       String packageName = Test4.class.getPackage().getName();
       //获取Test3.class文件所在的目录中所有.class文件
       File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
       for(File sub : subs) {
           //获取字节码文件的文件名
           String fileName = sub.getName();
           String className = fileName.substring(0, fileName.indexOf("."));
           //加载该类的类对象
           Class cls = Class.forName(packageName + "." + className);
           if(cls.isAnnotationPresent(AutoRunClass.class)){
               Object o = cls.newInstance();
               //获取该类定义的所有方法
               Method[] methods = cls.getDeclaredMethods();
               for(Method method : methods){

                   if(method.isAnnotationPresent(AutoRunMethod.class)){
                       AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
                       int value = arm.value();
                       System.out.println("自动调用"+className+"类的方法:"+method.getName()+"()"+value+"次");
                       for(int i=0;i<value;i++) {
                           method.invoke(o);
                      }
                  }
              }
          }
      }
  }
}

2.V23(反射机制重构DispatcherServlet)

1.主要内容

2.Controller注解

该注解用于标注那些在Controller中用于处理某个请求的业务方法的使用

package com.webserver.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**该注解用于标注那些在Controller中用于处理某个请求的业务方法的使用
* */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {

}

 

3.RequestMapping注解

该注解用于标注那些在Controller中用于处理某个请求的业务方法使用

package com.webserver.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 该注解用于标注那些在Controller中用于处理某个请求的业务方法使用
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
   String value();//用于指定标注的方法所处理的请求路径,例:/myweb/reg
}

4.controller里的所有文件加入Controller和RequestMapping注解

ToolsController、ArticleController、UserController

举例如下:

package com.webserver.controller;

import com.webserver.annotation.Controller;
import com.webserver.annotation.RequestMapping;
import com.webserver.http.HttpServletRequest;
import com.webserver.http.HttpServletResponse;
import qrcode.QRCodeUtil;

import java.io.FileOutputStream;
@Controller
public class ToolsController {
    @RequestMapping("/myweb/createQr")
    public void createQr(HttpServletRequest request, HttpServletResponse response){
        String content = request.getParameter("content");
        System.out.println("content:==========="+content);
        try {
            QRCodeUtil.encode(content,"logo.jpg",response.getOutputStream(),true);
            response.setContentType("image/jpeg");

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        String message = "http://doc.canglaoshi.org";
        try {
            //参数1:二维码上包含的文本信息  参数2:图片生成的位置
//            QRCodeUtil.encode(message,"./qr.jpg");
            //参数1:二维码上包含的文本信息  参数2:图片生成后会通过该流写出
//            QRCodeUtil.encode(message,new FileOutputStream("./qr.jpg"));
            //参数1:二维码上包含的文本信息  参数2:二维码中间的logo图片 参数3:图片生成的位置 参数4:是否需要压缩logo图片到中间大小
//            QRCodeUtil.encode(message,"logo.jpg","./qr.jpg",true);

            QRCodeUtil.encode(message,"logo.jpg",new FileOutputStream("./qr.jpg"),true);

            System.out.println("二维码生成完毕!");
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

5.DispatcherServlet

package com.webserver.core;

import com.webserver.annotation.Controller;
import com.webserver.annotation.RequestMapping;
import com.webserver.controller.ArticleController;
import com.webserver.controller.ToolsController;
import com.webserver.controller.UserController;
import com.webserver.http.HttpServletRequest;
import com.webserver.http.HttpServletResponse;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

/**
 * 用于处理请求
 */
public class DispatcherServlet {
    private static File root;
    private static File staticDir;

    static {
        try {
            root = new File(DispatcherServlet.class.getClassLoader().getResource(".").toURI());
            staticDir = new File(root, "static");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    public void service(HttpServletRequest request, HttpServletResponse response) {
        /*获取路径*/
        String path = request.getRequestURI();

//        首先判断是否为请求一个业务

    /*
            1扫描controller包下所有的类,利用反射机制加载,并判断是否被@Controller标注了
            2如果被@Controller标注了,则获取该类中所有本类中定义的方法,并判断是否被
             @RequestMapping标注了
            3如果被@RequestMapping标注了,则获取该方法上这个注解中指定的参数value,这个参数
             表示的就是该方法处理的是哪个请求。因此得到该参数后判断是否为path的值,如果是,则
             说明该方法就是处理本次请求的业务方法了,从而调用该方法即可

            如果扫描了所有的Controller以及里面所有的业务方法都没有与path匹配的,则说明本次
            请求不是处理业务,则执行下面原有的响应文件或404的操作
         */
        try {
            File dir = new File(
                    DispatcherServlet.class.getClassLoader().getResource("./com/webserver/controller").toURI()
            );
            String packName = "com.webserver.controller";
            File[] subs = dir.listFiles(f -> f.getName().endsWith(".class"));
            for (File sub : subs) {
                String fileName = sub.getName();
                String className = fileName.substring(0, fileName.indexOf("."));
                //加载类对象
                Class cls = Class.forName(packName + "." + className);
                //判断该类是否被@Controller标注了
                if (cls.isAnnotationPresent(Controller.class)) {
                    //实例化该Controller
                    Object obj = cls.newInstance();
                    Method[] methods = cls.getMethods();
                    for (Method method : methods) {
                        //判断该方法是否被@RequestMapping标注了
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            //获取该注解
                            RequestMapping arm = method.getAnnotation(RequestMapping.class);
                            //获取该注解的参数(该方法处理的请求路径)
                            String value = arm.value();
                            System.out.println("方法:" + cls.getName() + ",被注解的值:" + value);
                            if (value.equals(path)) {
                                //执行该方法
                                method.invoke(obj, request, response);
                                return;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        File file = new File(staticDir, path);
        System.out.println("资源是否存在:" + file.exists());

        if (file.isFile()) {//当file表示的文件真实存在且是一个文件时返回true
            response.setContentFile(file);


        } else {//要么file表示的是一个目录,要么不存在
            response.setStatusCode(404);
            response.setStatusReason("NotFound");
            file = new File(staticDir, "root/404.html");
            response.setContentFile(file);

        }

        //测试添加一个额外的响应头
        response.addHeader("Server", "WebServer");

    }
}
 

 

 

 

标签:V23,java,day36,File,import,注解,DispatcherServlet,class,标注
来源: https://www.cnblogs.com/xiaoyezilei/p/16168255.html