Servlet JSP
自定义标签
开发步骤
- 实现标签类,实现标签接口
- 集成半成品的支持类更加方便
- 登记标签类
- 在tld文件中定义标签名和标签类之间的关系
- 在JSP中使用标签
- 引入标签库
标签运行原理:
案例:
-
声明标签类
public class MyTag extends SimpleTagSupport{ @Override public void doTag() throws JspException,IOException{ JspContext ctx = getJspContext(); JspWriter out = ctx.getOut(); out.print("Hello World!"); } }
-
声明标签 mytag.tld
我的标签不可描述 mytag 1 my /mytag hello cn.tedu.web.MyTag empty schema 信息可以从JSTL的c.tld中复制 schema(概要,模式)
-
使用标签
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="my" uri="/mytag"%>
Insert title here 自定义标签演示
测试
反射
是Java提供的一套API,可以对Java对象进行自我检查,可以检查对象的类型,类包含哪些属性,包含哪些方法,包含哪些构造器等。
反射API还提供了,动态执行功能:包括动态加载类,动态创建对象,动态访问属性,动态调用方法 等。
Java反射框架主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法;
经典面试题目:
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(类名);
原理:
动态创建对象
利用反射调用类的无参数构造器创建对象
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);
}
}
动态设置属性原理:
利用反射动态设置对象属性:
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 提供了两个常用的功能
- 用于动态加载类
- 返回 Class
- 从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 注解的方法,这些方法都是无参数,无返回值的方法。
实现思路:
- 动态得到类名
- 动态加载类
- 利用反射检查类中全部的方法信息
- 检查方法信息上标注的注解
- 找到标注了 @Test 注解的方法
- 动态创建对象,动态执行方法
注解工作原理:
实现步骤
-
编写注解 @Test
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {}
-
编写测试案例
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"); } }
-
核心功能实现:
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匹配规则
-
精确匹配。以"/"开头,加上servlet名称。
Java代码 /ad
-
路径匹配,以"/"开头,加上通配符"*"
Java代码 /*
-
扩展名匹配。以统配符"*"开头,加上扩展名。
Java代码 *.action org.dom4j.io Class SAXReader Document read(InputSource in) Reads a Document from the given InputSource using SAX
重构Web框架
由于Web应用开发繁琐,利用重构抽取公共的Servlet功能,使开发变得更加简便的过程。
原理:
原型步骤:
- 开发注解
package cn.tedu.base.web;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value();
}
- 开发控制器类
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";
}
}
- 编写配置文件 conf/context.xml
- 编写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");
}
}
- 配置
DispatcherServlet
DispatcherServlet
cn.tedu.base.web.DispatcherServlet
DispatcherServlet
*.do
- 测试
作业
- 实现课堂案例代码
- 编写Web框架原型