前言
最近正在学习Java的反射,这是第二次重新学习接触Java反射,第一次接触到反射时,直观的感受就是
“这玩意有什么用?既然我都能直接通过创建对象进行方法的调用,只需要两行代码,通过反射之后更麻烦了,这不是多此一举吗?”
但是最近遇到的一个代码改良方案让我真正的眼前一亮,原来反射是这么用的!!
一、什么是反射
反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)
二、代码优化实例
1.优化前代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(value = "/linkMan")
public class LinkManServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理编码问题
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//request.getMethod(); 获取请求方式 get|post
//需要改良的位置起点
String method = request.getParameter("method");
if("findall".equals(method)){
System.out.println("查询所有联系人");
findall(request,response);
}else if("add".equals(method)){
System.out.println("增加联系人");
add(request, response);
}else if("delete".equals(method)){
System.out.println("删除联系人");
delete(request,response);
}else if("update".equals(method)){
System.out.println("修改联系人");
update(request, response);
}else if("findpage".equals(method)){
System.out.println("分页展示联系人");
findpage(request, response);
}
//需要改良的位置终点
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
/**
* 查询所有联系人
*/
private void findall(HttpServletRequest request, HttpServletResponse response){
//1.获取请求参数
//2.调用业务处理
//3.响应
}
/**
* 增加联系人
*/
private void add(HttpServletRequest request, HttpServletResponse response){
//1.获取请求参数
//2.调用业务处理
//3.响应
}
/**
* 删除联系人
*/
private void delete(HttpServletRequest request, HttpServletResponse response){
//1.获取请求参数
//2.调用业务处理
//3.响应
}
/**
* 分页展示联系人
*/
private void findpage(HttpServletRequest request, HttpServletResponse response){
//1.获取请求参数
//2.调用业务处理
//3.响应
}
/**
* 修改联系人
*/
private void update(HttpServletRequest request, HttpServletResponse response){
//1.获取请求参数
//2.调用业务处理
//3.响应
}
}
复制代码
通过分析可以看出源代码中对于方法的确定和选择做了大量的if/else,这意味着每添加一个方法,都需要添加多一个if条件,并且这样做也意味着效率低,因为每个方法请求进入到此处时都需要经过(n/2)次的判定才能确定调用的方法,而不存在的方法则白白的遍历了整个条件判断体,效率低下。
2.优化后代码
...
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = null;
try {
//获取请求中的method参数
method = request.getParameter("method");
//通过class字节码获取到LinkManServlet类
Class<LinkManServlet> linkManServletClass = LinkManServlet.class;
//通过字节码对象创建类的对象
LinkManServlet linkManServlet = linkManServletClass.newInstance();
//通过字节码对象和method参数获取到对应的方法Method对象
Method declaredMethod = linkManServletClass.getDeclaredMethod(method,HttpServletRequest.class,HttpServletResponse.class);
//暴力获取私有方法的调用权限
declaredMethod.setAccessible(true);
//通过invoke调用方反射出来的方法
declaredMethod.invoke(linkManServlet,request,response);
} catch (Exception e) {
e.printStackTrace();
}
}
...
复制代码
改良后的代码仅仅只需要6行就完成了之前30多行的功能,并且这6行代码还一劳永逸了,以后都不需要为添加方法而修改。
这短短的6行代码给我带来的震撼不仅仅是它的简短,而是它这种思维方式。
通过自身类反射来实现对方法的选择和控制,还无需判定情况,而是直接通过调用的方式跳过了判定方法名,直接逆向的从类本身出发,类里的方法就直接通过反射放在通过字节码创建的对象里了,调用过程无非就变成了有这个方法就getDeclaredMethod成功,没有这个方法就getDeclaredMethod失败。