反射技术优化代码
今天的重点可以说就是理解和使用这个技术了.
之前我们曾经是提到过的.在工厂模式改造Dao层前. 我们就可以利用反射技术拿到实现类的所有细节照成 内部数据泄露的严重问题.
使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)
今天我们就来演示一下如何拿到实现类的所有细节
细节分为:
两大类
- 私有
- 其他
拿到三小种内容
- 拿到构造法
- 拿到方法
- 拿到属性字段
一个简单的类
package com.xjo.entity;
public class Food {
//属性字段
private int id;
private String foodName;//菜名
//构造法
public Food() {
super();
}
//方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public void showFood(String sf){
System.out.println("调用了:公有的,String参数的showFood(): sf = " + sf);
}
@Override
public String toString() {
return "Food [id=" + id + ", foodName=" + foodName + ", foodType_id=" + foodType_id + ", price=" + price
+ ", mprice=" + mprice + ", remark=" + remark + ", img=" + img + "]";
}
}
首先要拿到对象之后就好说了
a) 获取一个Class对象 clazz
b)实例化一个Class表示的类的对象 food
Food food = new Food();
- .getClass()方法,这是一个Object类定义的方法,涉及到强转,用通配符表示泛型可以避免强转。
Class foodclass1 =food .getClass();- 类名.class;返回类型为Class。
Class foodclass2 =Food .class();- Class类的静态方法——Class.forName(String className),参数为类的全限定名|绝对路径。(常用)
Class foodclass3 = Class.forName(“com.Food”);
分析:
第一种对象都有emm…
第二种需要导入类的包,类之间有依赖,不导包就抛编译错误。
第三种,一个字符串可以传入也可写在配置文件中等多种方法。所以常用
接下来拿到构造法[Constructor]
1). 批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
foodclass1 打点调用就可以了 返回一个集合
2). 获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
Constructor con = foodclass1.getConstructor(int.class,String.class);
Food food = con.newInstance(12,“土豆丝”);
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;>
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)这个方法调用后 私有的内容就出现了 后面拿私有方法,私有属性 都要设置
//调用构造方法
Object obj = con.newInstance();
注意事项: Constructor con = foodclass1.getConstructor(null);
拿到方法[Method]
获取成员方法并调用:
1.批量的:
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
2.获取单个的:
public Method getMethod(String name,Class<?>… parameterTypes):
参数:
name : 方法名;
Class … : 形参的Class类型对象
例子:
//拿到
Method method = foodclass1.getMethod("showFood",String.class);
//输入参数:
method.invoke(foodclass.getConstructor().newInstance(),"土豆丝" );
或者
method.invoke(food,"土豆丝" );
//公有方法调用(invoke)调用参数为数组时 new int[]{}
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)
调用方法(配合使用):
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;
拿到属性字段[Field] 使用起来 相近 简写
1.批量的
1).Field[] getFields():获取所有的"公有字段"
2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
foodclass. 打点调用就可以了
2.获取单个的:
1).public Field getField(String fieldName):获取某个"公有的"字段;
Field f = foodclass.getField("id");
f.set(foodclass.getConstructor().newInstance(),3 );
或者
f.set(food,3 );
2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
设置字段的值:
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的字段所在的对象;
2.value:要为字段设置的值;
订单完成 这里注意 有订单 和订单详情 两表查询
业务分析
后台[订单号,餐桌,下单时间,总金额,状态 , 操作{详情 ,结账}]
功能:
- 订单 的全部显示 public List query();
- 订单详情的 全部显示 public List query(int id);对应的
- 订单 的状态修改(结账)
[菜名字, 单价 , 数量 ][返回]
前台
[菜名 ,单价 ,数量 ,总价格 ,][删除 ,下单]
功能:
购物车 又像订单详情 显示所有被选中的商品
订单详情 的添加功能 删除功能
订单 的添加订单 手动添加商品数量 对应的计算出总价(totalPrice)
这时候你就要像怎样的数据表可以显示上面的内容
以此来设计 (类+数据库)
然后重要的Dao层功能总结
再来看一眼数据库
订单 [id,桌子编号,订单产生时间, 总金额 , 订单状态:付清/未结账]
订单详情[id,订单id ,食物id ,食物数量]
public class Food {
private int id;
private String foodName;//菜名
private int foodType_id;//菜系
private double price;//价格
private double mprice;//会员价格
private String remark;//评价
private String img;//图片
}
public class Board {
int id;
String tableName;
int tableStatus;
Date orderDate;
}
public class Orders {
private int id;
private int table_id;//餐桌的id
private Date orderDate;
private double totalPrice;
private int orderStatus ; //0未结账 1已经结账
public class OrderDetail {
private int id;
private int orderId; //外键:订单表的主键
private int food_id; //外键:菜品表的主键
private int foodCount;
我们发现 如果已经存在的数据 拿到ID 然后查出来就可以了 进行访问Dao层考虑 就行 没有的数据自己在加上.
然后编写Dao层
分析的分析
将我分析的和老师讲解的:进行整理对比得出:
- 订单 的全部显示 public List query();
- 订单 的添加订单 手动添加商品数量 对应的计算出总价(totalPrice)
- 订单 的状态修改(结账)
- public int getCount(); //订单中每道菜的数量 我没有
为什么订单要有每到菜的数量?
- 购物车 又像订单详情 显示所有被选中的商品我没有
没有对应的订单我显示什么,显示到哪给谁看?
后台更具订单ID显示 前台 直接显示全部?
-
订单详情 的添加功能
错误思路: 详情内的食物 可以增删改查
所以这里的增加 不是管 订单详情的具体 加了那道菜
而是因为一个订单生成了 而 产生的订单详情的表内容 -
订单详情 的删除功能 老师没有
前台不是有删除按钮么?
这时订单没有生成,即订单详情生成后不能改了
这里的删除应该是购物车模块的内容
-
订单详情的 更具订单号全部显示 public List query(int id);对应的
-
分页 得到数据总数量功能
-
分页 得到分页后的数据功能
ok 带着问题去学习 就事半功倍了
- 一开始选桌子 桌子的数据先产生 ,然后按照 菜系 看到不同的菜品 之前已经完成
- 点击菜品图片 我们看到了加入餐车的按钮 返按钮
- 进入详细订单页面(可以选择食物数量 和删除食物) 点击下单
- 内容仍然在但只有结账按钮了—> 然后后台出现了 订单数据一条未结账状态
public interface IOrdersDao {//订单
public void update(Orders orders);//订单状态修改 是否结账 参数:由业务层判断设置
String sql = "UPDATE orders SET orderStatus =? WHERE id=?";
public List<Orders> query();//前/后台 显示所有订单
String sql = "SELECT * FROM orders";
public void add(Orders orders);//前台调用 下单
String sql="insert orders(table_id,orderDate,totalPrice) values(?,?,?)";
public void getTotalCount(); //订单数
String sql = "select count(*) from orders";
public void getAll(PageBean<Orders> pb); //根据分页查询订单
String sql = "select * from orders limit ?,?";
pb.setPageData(pageData);
public int getCount(); //订单总数量
String sql = "select count(*) from orders";
}
public interface IOrderDetailDao {//订单详情
public List<OrderDetail> findByOrderid(int id);
String sql ="SELECT * FROM orderdetail where orderId=?";
public void add(OrderDetail od);//前台添加的
String sql =" INSERT orderdetail(orderId,food_id,foodCount) VALUES(?,?,?)";
public List<OrderDetail> query();//后台显示细节
String sql ="SELECT * FROM orderdetail";
}
其他步骤十分简单了 按照步骤我们很快就来到了控制层发现这里可以优化就是利用了反射技术
改造前是这样子的
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getParameter("method");
//我们不得不 利用这样的写法来 找到前端按钮对应的方法
if ("add".equals(method)) {
add(request, response);
} else if ("list".equals(method)) {
list(request, response);
} else if ("update".equals(method)) {
update(request, response);
} else if ("delete".equals(method)) {
delete(request, response);
} else if ("search".equals(method)) {
search(request, response);
} else if ("show".equals(method)) {
show(request, response);
} else if ("findFoodType".equals(method)) {
findFoodType(request, response);
uri = request.getRequestDispatcher("/sys/food/saveFood.jsp");
goTo(request, response, uri);
} else if ("query".equals(method)) {
query(request, response);
}
}
改造后及思路
为了简化在servlet中对收到的方法名判断后跳到对应的方法,抽取出一个基类.
前提:是传过来的method方法名(前端)和对应控制层的方法名一致
import java.io.IOException;
import java.lang.reflect.Method;
import javax.servlet.RequestDispatcher;
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 com.xjo.factory.BeanFactory;
import com.xjo.service.IFoodService;
import com.xjo.service.IFoodTypeService;
import com.xjo.service.IOrderDetailService;
import com.xjo.service.IOrdersService;
import com.xjo.service.IBoardService;
public class BaseServlet extends HttpServlet {
//先创建业务层所有的实例对象 这样就不要写了
protected IBoardService tableService = BeanFactory.getInstance("boardService",IBoardService.class);
protected IFoodTypeService foodTypeService = BeanFactory.getInstance("foodTypeService", IFoodTypeService.class);
protected IFoodService foodService = BeanFactory.getInstance("foodService", IFoodService.class);
protected IOrdersService ordersService = BeanFactory.getInstance("ordersService", IOrdersService.class);
protected IOrderDetailService orderDetailService = BeanFactory.getInstance("orderDetailService",
IOrderDetailService.class);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//保存跳转的资源
Object returnValue =null;
//1.如何确定要哪个servlet处理请求 原来用 标签@WebServlet("/board")
//2.如何确定要使用那个方法? 原来用method=? 现在到了baseServlet
String methodName = request.getParameter("method");
//动态调用 那个 servlet
if(methodName==null){
methodName="listTable";//默认
}
Class clazz = this.getClass();//this是当前对象
try {
Method method = clazz.getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);//拿到方法名 参数类型也设置好了
//如:public Object list(HttpServletRequest request, HttpServletResponse response){}//执行方法 返回参数
returnValue =method.invoke(this, request,response);
} catch (Exception e) {
e.printStackTrace();
}
//跳转
goTo(request,response,returnValue);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
// 统一跳转 每个servlet都有 类型修正
public static void goTo(HttpServletRequest request, HttpServletResponse response, Object uri)
throws ServletException, IOException {
if (uri instanceof RequestDispatcher) {
((RequestDispatcher) uri).forward(request, response);
} else {
response.sendRedirect(request.getContextPath() + (String) uri);
}
}
}
被简化后的servlet代码doGet dopost都不用写了
public class OrderServlet extends BaseServlet {
public Object add(HttpServletRequest request, HttpServletResponse response){
System.out.println("add");
return null;
}
public Object list(HttpServletRequest request, HttpServletResponse response){
Object uri=null;
//1.判断当前页,如果没有默认第1页
String currPage=request.getParameter("currentPage");
if(currPage==null||"".equals(currPage.trim())){
currPage="1";
}
int currentPage=Integer.parseInt(currPage);
//2.创建PageBean
PageBean<Orders> pageBean=new PageBean<Orders>();
pageBean.setCurrentPage(currentPage);
pageBean.setPageCount(2);
//3.调用service
ordersService.getAll(pageBean);
//4.保存pagebean到requester
request.setAttribute("pageBean", pageBean);
//5.将方法调用返回值baseServlet
uri=request.getRequestDispatcher("sys/order/orderList.jsp");
System.out.println(uri);
return uri;
}
}
参考:ht tps://blog.csdn.net/sinat_38259539/article/details/71799078