day36(注解、V23:反射机制重构DispatcherServlet)
作者:互联网
day36(注解、V23:反射机制重构DispatcherServlet)
1.注解
1.定义
-
注解在开发中常被我们利用到反射机制中,辅助反射机制做更多灵活的操作
-
注解在如今JAVA流行的框架中被大量的应用,简化了以前繁琐的配置工作。
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.使用范围
类上,属性上,方法上,构造器上,以及参数上使用
-
可以通过java内置的注解@Target来说明当前注解可以被应用的位置,对应的值被定义在ElementType上
-
例如:
-
@Target(ElementType.TYPE) 注解只能被用于类上
-
@Target({ElementType.TYPE,ElementType.METHOD}) 注解只能被用于类上或方法上
-
当可以用于多个位置时,需要定义成数组的方式包含所有ElementType的值,即"{}"包含
3.@Retention注解,用于标注当前注解的保留级别,有三个选项
-
RetentionPolicy.SOURCE 注解仅保留在源代码中
-
RetentionPolicy.CLASS 注解保留在字节码中,但是反射机制不能调用
-
RetentionPolicy.RUNTIME 注解保留在字节码文件中,并且可以被反射机制所使用
-
当不指定@Retention时,默认的保留级别为CLASS,因此我们通常都需要明确指出保留级别为RUNTIME
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.主要内容
-
利用反射机制重构DispatcherServlet,使得将来添加新的业务时DispatcherServlet 不必再添加分支判断(不进行修改)
-
实现: 1:新建包com.webserver.annotation 2:在annotation包下添加两个注解 @Controller:用于标注哪些类是处理业务的Controller类 @RequestMapping:用于标注处理某个业务请求的业务方法 3:将com.webserver.controller包下的所有Controller类添加注解@Controller 并将里面用于处理某个业务的方法标注@RequestMapping并指定该方法处理的请求 4:DispatcherServlet在处理请求时,先扫描controller包下的所有Controller类 并找到处理该请求的业务方法,使用反射调用.
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