reflect(反射)

一 . 类加载机制

    *.java(源文件)–>*.class(字节码文件)
    类加载机制: 将一个类装载到jvm的过程.
    1 . 加载
    —连接操作-------
    2 . 验证()
    3 . 准备: 为静态成员变量设置默认值.
    4 . 解释
    —连接操作-------
    5 . 初始化: 对类中静态成分执行初始化, 为静态变量赋值, 执行静态初始化块.
    6 . 使用
    7 . 卸载

    java中只有两种成员与对象无关:
        – 基本数据类型(char, byte, short, int, long, float, double).
        – 静态成员(静态属性, 静态方法).

二 . Class类

    java中的所有类都是Class类实例, Class代表在jvm中正在执行的一个类的实例.(Object也是Class的一个实例)
    Class中包含了当前实例的运行信息, 内部构造(成员变量, 方法, 注解, 实现接口…)

    反射: 将一个类中所有成分反射为对应的java类.
        – 属性类
        – 构造器类
        – 方法类
        – 注解类

    一个正在运行的类可以通过反射机制获取到类自身中的具体信息(包含在Class实例中)
    **动态绑定:**多态上有体现.

三 . 获取一个正在执行的类的Class(镜子)对象有三种方法

    方法一: 调用类的class属性获取. 如: Student.class
    方法二: 通过实例对象的getClass()方法获取, 如:

Student stu = new Student();
Class clz = stu.getClass();

    方法三: 通过类加载器加载

//1.不会执行类中静态成分(不初始化)
Student.class.getClassLoader.loadClass("com.softeeem.dto.Student");
//2.会执行类中静态成分(初始化)
Class.forName("com.softeem.dto.Student");

    具体的一些方法使用如下:

//利用Class实例化对象(注:该对象的类一定要有无参构造器)
Object obj = clz.newInstance();
//获取类中属性(public属性)
Field[] fields = clz.getFields();

//获取类中所有属性(包括私有private)
fields = clz.getDeclaredFields();

//获取其中某一个属性对象(sname)
Field f = clz.getField("sname");//会有异常(若是私有,则getDeclaredField("sname"))
//获取当前属性的类型(返回值是Class对象)
f.getType();
//获取当前属性的访问修饰符所表示的常量字段值
f.getModifiers();//如:public->1
//将当前属性(public)所在对象的值设置为指定值(注意类型要匹配)
f.set(obj,"狗蛋");
//获取当前属性在指定对象上的值
f.get(obj);//获得sname值
//获取属性名称
f.getName();

/****获取方法****/
//获取当前对象所在类的所有public方法(包括其父类方法)
Method[] methods = clz.getMethod();
//获取一个指定的方法(通过方法名称,属性类型获取方法对象)
Method method = clz.getMethod("setSex",clz.getField("sex").getType());
//方法执行(注:此方法返回值,无则null,有则Object[])
method.invoke(obj,"未知");//sex = "未知";

四 . 反射优化servlet请求

如下一个index.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
	<p><a href="goods?method=add">商品上架</a></p>	
	<p><a href="goods?method=update">商品修改</a></p>
	<p><a href="goods?method=delete">商品下架</a></p>
	<p><a href="goods?method=list">商品列表</a></p>
	<hr>
	<p><a href="category?method=add">类别添加</a></p>	
	<p><a href="category?method=update">类别修改</a></p>
	<p><a href="category?method=delete">类别删除</a></p>
	<p><a href="category?method=list">类别列表</a></p>
	<hr>
	<p><a href="user?method=register">用户注册</a></p>	
	<p><a href="user?method=login">用户登录</a></p>
	<p><a href="user?method=logout">注销</a></p>
	
	<hr>
	<form action="user">
		<input type="hidden" name="method" value="register">
		<input type="text" name="username"/>
		<input type="password" name="password" />
		<button>注册</button>
	</form>
</body>
</html>

    此时我们可能需要三个servlet, 分别为GoodsServlet, CategoryServlet, UserServlet按照博主之前对servlet的掌控能力, 只能让jsp再多传一个flag(如: Servlet?flag=goods&method=add), 然后在Servlet中配合switch解决 , 这样虽然减少了重复代码, 但代码比较混乱, 同时也增加了jsp请求的难度, 如: 表单请求Servlet, flag需要隐藏域传值.
    此时反射可以优化一下Servlet, 首先要建一个BaseServlet, 自然要重写HttpServlet的doGet和doPost方法, 之后我们三个目标Servlet只需要继承它既可(不需要再重写doGet和doPost), 听起来和反射没有关系? 来看一看BaseServlet, 如下:

package com.softeem.servlet;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BaseServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			//获取客户端请求的方法名称
			String methodName = req.getParameter("method");
			//根据客户端请求的方法名称通过反射机制获取方法对象
			Method method = this.getClass().getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
			//执行方法
			method.invoke(this, req,resp);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
}

    看到关系了吧? 运用反射调用方法.
    为了表现一下这样优化的结果, 看一下GoodsServlet:

package com.softeem.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class GoodsServlet
 */
@WebServlet("/goods")
public class GoodsServlet extends BaseServlet {

	public void add(HttpServletRequest request,HttpServletResponse response){
		System.out.println("添加商品");
	}
	
	public void update(HttpServletRequest request,HttpServletResponse response){
		System.out.println("商品修改");
	}
	
	public void delete(HttpServletRequest request,HttpServletResponse response){
		System.out.println("商品下架");
	}
	
	public void list(HttpServletRequest request,HttpServletResponse response){
		System.out.println("商品列表");
	}

}

    在GoodsServlet中只需要直接写出所需方法既可, 代码是不是简洁明了许多?
    其实在Servlet中运用反射还可以做一个优化, 可以直接将来自jsp传递的数据转化为dto对象, 如上index.jsp中user注册信息表单提交, 看一下UserServlet中代码:

package com.softeem.servlet;

import java.util.Map;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.softeem.dto.User;

/**
 * Servlet implementation class GoodsServlet
 */
@WebServlet("/user")
public class UserServlet extends BaseServlet {

	private User user;

	public void register(HttpServletRequest request,HttpServletResponse response){
//		String name = request.getParameter("username");
//		String pass = request.getParameter("password");
		
		//该map中取得了来自表单中的所有数据
		Map<String, String[]> map = request.getParameterMap();
		//直接将jsp传来的数据转化为dto对象.
		user = toDTO(map, User.class);
		System.out.println("该对象为:"+user);
	}
	
	public void login(HttpServletRequest request,HttpServletResponse response){
		System.out.println("登录");
	}
	
	public void logout(HttpServletRequest request,HttpServletResponse response){
		System.out.println("注销");
	}
}

    其中的toDTO方法就是运用反射直接将表单提交的数据转换为dto对象. 具体实现如下:

	protected <T> T toDTO(Map<String,String[]> map,Class<T> clz){
		//获取指定对象的Class对象
//		Class<T> clz = T;//Student.class
		T newObj = null;
		try {
			//根据class对象创建当前类型的实例(空对象)
			newObj = clz.newInstance();
			//获取当前类中包含的所有属性
			Field[] fields = clz.getDeclaredFields();
			for (Field field : fields) {
				//拼接获取setter/getter方法的名称
				String setMethodName = "set"+field.getName().substring(0, 1).toUpperCase()+field.getName().substring(1);
				//根据方法名称获取方法对象
				Method method_set = clz.getMethod(setMethodName, field.getType());//setter方法
				//从map中获取属性值
				String value = map.get(field.getName())[0];
				//执行新对象的setter方法,将源对象的属性值设置给新对象
				method_set.invoke(newObj, value);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return newObj;
	}

    要说明一下, 这可能不是Servlet最简洁最好的最终优化, 这只是我为了加深理解最近学习的reflect(反射).

    博主认为学习应该是一个有过程的进步, 总想一步到位是学不好的.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值