Java基础之内省
Java基础之内省
什么是内省
首先,我们要知道什么是内省。按我自己的理解就是在反射的原理上进行封装,来更方便的操作JavaBean
JavaBean就是特殊格式的类,其规范为:
- JavaBean 类必须是一个公共类,即使用关键字 public 声明类。
- JavaBean 类中必须有一个声明为公共的无参构造函数。
- JavaBean 类中的实例变量必须为私有的,即所有的实例变量都使用关键字 private 声明。
- 必须为 JavaBean 类中的实例变量提供公共的 getter / setter 方法。(在 getter / setter 方法中,可以做一些权限控制,数据校验等工作,以保证数据的安全,合法性。)
- JavaBean 类中实例属性的命名规则:
- 属性名前两个字母都小写:将属性名的首字母大写,然后用作 getter / setter 方法中 get / set 的后部分,如属性名为 name, 它的 getter / setter 方法为 getName / setName。
- 属性名的第二个字母大写: 将属性名直接用作 getter / setter 方法中 get / set 的后部分,即属性名大小写不变。如属性名为 uName,它的 getter / setter 方法为 getuName / setuName。
- 属性名前两个字母都大写:将属性名直接用作 getter / setter 方法中 get / set 的后部分,即属性名大小写不变。如属性名为 IDcode, 它的 getter / setter 方法为 getIDcode / setIDcode。
- 属性名首字母大写:将属性名直接用作 getter / setter 方法中 get / set 的后部分,即属性名大小写不变。如属性名为 Ucode, 它的 getter / setter 方法为 getUcode / setUcode。但是这种情况,在应用中会出现找不到属性的错误。
如何使用内省
内省的主要类(接口)有:Introspector
(类)、BeanInfo
(接口)、PropertyDescriptor
(类),这三个都在java.bean包下。
Introspector
是内省的入口,是一个工具类,用来获得BeanInfo
,即一个JavaBean的信息。
测试Bean:Student
public class Student {
private String name;
private String address;
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
//static BeanInfo getBeanInfo(Class<?> beanClass):在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件
BeanInfo beanInfo = Introspector.getBeanInfo(Student.class);
获取到一个bean的信息之后就可以获得所有的属性描述器了:
PropertyDescriptor[] p = beanInfo.getPropertyDescriptors();
遍历打印一下看看结果:
BeanInfo beanInfo = Introspector.getBeanInfo(Student.class);
PropertyDescriptor[] p = beanInfo.getPropertyDescriptors();
System.out.println(p.length);
for (PropertyDescriptor descriptor : p) {
System.out.println(descriptor);
}
//输出--------------
p.length = 3
java.beans.PropertyDescriptor[name=address; propertyType=class java.lang.String; readMethod=public java.lang.String nei_xing.Student.getAddress(); writeMethod=public void nei_xing.Student.setAddress(java.lang.String)]
//为了方便观察加了注释隔开
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()]
//
java.beans.PropertyDescriptor[name=name; propertyType=class java.lang.String; readMethod=public java.lang.String nei_xing.Student.getName(); writeMethod=public void nei_xing.Student.setName(java.lang.String)]
这里,Student只有两个属性但是其对象描述器却又三个,因为:
Object声明了getClass()方法来获得对象本身类型的Class对象,由于所有的类都继承自Object,所以所有的类都有getClass()方法。但类中并没有class字段(class是关键字,不可以作为类名),而且也没有setClass()方法,所以在使用内省编程的时候一般需要过滤掉“class”属性
参考
解决方法:
使用getBeanInfo(Class<?> beanClass)
方法的重载:
static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
:给定断点类,获得其BeanInfo
比如:
BeanInfo beanInfo1 = Introspector.getBeanInfo(Student.class, Object.class);
此时在获取其属性描述器就只能得到Student的两个,而过滤掉了class
属性。
得到每个属性的描述器之后便可以获取和设置其Getter/Setter方法了:
Student stu = new Student();
BeanInfo beanInfo1 = Introspector.getBeanInfo(Student.class, Object.class);
PropertyDescriptor[] p1 = beanInfo1.getPropertyDescriptors();
System.out.println(p1.length);
for (PropertyDescriptor d : p1) {
String name = d.getName();
System.out.println("name = " + name);
Method getter = d.getReadMethod();//获取getter方法
Method setter = d.getWriteMethod();//获取setter方法
if ("name".equals(name)){
setter.invoke(stu, "张三");//使用反射调用该方法
}
if ("address".equals(name)){
setter.invoke(stu, "上海");
}
System.out.println(getter.invoke(stu));
}
输出结果:
2
name = address
上海
name = name
张三
上面是内省实现方式的一种,除了使用BeanInfo获取属性描述器对象,还可以直接创建属性描述器:
PropertyDescriptor pd = new PropertyDescriptor("address", Student.class);
Method getter = pd.getReadMethod();
Method setter = pd.getWriteMethod();
放两个Servlet中内省Demo:
Demo1
前端页面:
<form action="/introspectorServlet" method="post">
姓名 <input type="text" name="name" /> <br/>
住址 <input type="text" name="address"/> <br/>
<input type="submit" value="提交"/>
</form>
IntrospectorServlet:
@WebServlet("/introspectorServlet")
public class IntrospectorServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理请求乱码
request.setCharacterEncoding("UTF-8");
//获取页面提交的所有的数据
/*
map集合的key是form表单标签的name属性值
map集合的value是浏览器输入框输入的值,以String类型的数组形式接收
举例:
住址 <input type="text" name="address"/> <br/>
key:address
value:{"上海"}
*/
Map<String, String[]> m = request.getParameterMap();
// System.out.println(m);
//创建封装属性的目标对象person
Person p = new Person();
try {
//调用方法
setProperty(p,m);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(p.getName());
System.out.println(p.getAddress());
}
private void setProperty(Object obj, Map<String, String[]> m) throws Exception {
// 将请求参数中 map的key 与传入对象属性名称 比较,如果一致,将参数的值赋值给对象属性
//使用内省类获取BeanInfo类的对象
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
//获取所有的属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍历数组取出每一个属性描述器
for (PropertyDescriptor descriptor : propertyDescriptors) {
//获取Person类中的属性
String property_name = descriptor.getName();
//判断属性在map中是否存在对应值
if(m.containsKey(property_name)){
//包含
//获取对应的value值
String value = m.get(property_name)[0];
/*
Method getWriteMethod() 获得应该用于写入属性值的方法。
*/
Method setter = descriptor.getWriteMethod();
//将value 写到属性中
setter.invoke(obj, value);
}
}
}
}
使用request.getParameterMap();
从前台获取的数据都是字符串,那么如何使用内省技术将数据封装到javabean中的基本类型的字段上?
Demo1
Student Bean类
public class Person {
private String name;
private String address;
private int age;
private double money;
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", age=" + age +
", money=" + money +
'}';
}
}
Servlet 代码:
@WebServlet("/introspector2Servlet")
public class IntrospectorServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理中文乱码
request.setCharacterEncoding("UTF-8");
Person p = new Person();
//获取所有的数据
//key 标签 name属性值 value 输入的值
Map<String, String[]> map = request.getParameterMap();
try {
setProperty(p, map);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(p.getName());
System.out.println(p.getAddress());
System.out.println(p.getAge());
System.out.println(p.getMoney());
}
private void setProperty(Object obj, Map<String, String[]> map) throws Exception {
//内省
//获取所有的属性封装到BeanInfo对象中
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
//获取所有的属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍历
for (PropertyDescriptor descriptor : propertyDescriptors) {
//获取属性名
String propertyName = descriptor.getName();
//判断集合中是否包含当前属性名
if (map.containsKey(propertyName)) {
//包含
//获取get方法
Method getterMethod = descriptor.getReadMethod();
//获取get方法返回值类型 String int double
String returnTypeName = getterMethod.getReturnType().getSimpleName();
//获取set方法
Method setterMethod = descriptor.getWriteMethod();
//获取mapvalue
String value = map.get(propertyName)[0];//"18"
//多分支语句
switch (returnTypeName) {
case "int":
int age = Integer.parseInt(value);
//执行set方法
setterMethod.invoke(obj, age);
break;
case "double":
double v = Double.parseDouble(value);
//执行set方法
setterMethod.invoke(obj, v);
break;
case "float":
float v1 = Float.parseFloat(value);
//执行set方法
setterMethod.invoke(obj, v1);
break;
case "long":
long v2 = Long.parseLong(value);
//执行set方法
setterMethod.invoke(obj, v2);
break;
case "boolean":
boolean v3 = Boolean.parseBoolean(value);
//执行set方法
setterMethod.invoke(obj, v3);
break;
default:
//执行set方法
setterMethod.invoke(obj, value);
break;
}
}
}
}
}
BeanUtils工具类
可以看到直接使用内省还是有一些复杂的,因此大多时候可以使用apache的工具类:org.apache.commons.beanutils.BeanUtils
,(使用需要导入commons-beanutils-1.9.2.jar
和commons-logging-1.1.1.jar
两个包),这个是maven坐标:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
这个工具类中有个populate()方法:static populate(Object bean, Map<String,String[]> properties)
,Map中的key即String类型的要和JavaBean中的属性名一致才能完成封装(或者说和Getter/Setter方法的名字有关系,至少Spring的JdbcTemplate就是这样,修改属性名但不修改Getter/Setter方法依旧能完成封装)
接着一行代码就可以完成封装:
BeanUtils.populate(new Student,request.getParameterMap());
Java基础之内省的更多相关文章
- day1 java基础回顾-内省
为什么要学内省? 开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性. 内省是用于操作j ...
- 黑马程序员:Java基础总结----JavaBean 内省
黑马程序员:Java基础总结 JavaBean 内省 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! JavaBean 内省 软件包 java.beans 包含与开 ...
- Java基础扫盲系列(三)— Java内省技术
前言 Java内省技术属于Java基础体系的的一部分,但是很多人都不甚了解.笔者也是在学习Spring源码的过程中遇到该技术模块的.为了完善技术体系,本文将全面的学习该技术.在提到Java内省技术,就 ...
- java基础-3
java基础-3 API Application Programming Interfaces --- 应用程序接口 Object 顶级父类 Bin --- 二进制 Oct --- 八进制 Dec ...
- Java基础之一反射
反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)) 一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够 ...
- Java基础之—反射
反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)) 一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够 ...
- Java基础-反射(reflect)技术详解
Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制 如下图所示,JVM类加载机制分为五个部分 ...
- Java基础反射(二)
原文地址http://blog.csdn.net/sinat_38259539/article/details/71799078 反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Cla ...
- 40道Java基础常见面试题及详细答案
最近看到网上流传着各种面试经验及面试题,往往都是一大堆技术题目贴上去,但是没有答案. 为此我业余时间整理了40道Java基础常见的面试题及详细答案,望各路大牛发现不对的地方不吝赐教,留言即可. 八种基 ...
随机推荐
- 第1节 storm编程:1、storm第一天上次课程内容回顾
上次课程内容回顾:1.kafka的基本介绍:kafka是一个消息队列2.消息队列的作用:解耦3.kafka与传统消息队列的对比: 传统消息队列:支持事务 kafka的特点:比较快,比较快的两个原因:顺 ...
- 【Unity】鼠标指向某物体,在其上显示物体的名字等等等等信息
之前一直用NGUI HUD Text插件做这个功能,感觉一个小功能就导一个插件进来简直丧心病狂.然后就自己写了一个~ Camera cam;//用于发射射线的相机 Camera UIcam;//UI层 ...
- docker-compose 快速部署Prometheus,监控docker 容器, 宿主机,ceph -- cluster集群
话不多说上菜: 现在环境是这样: ceph 4台: 192.168.100.21 ceph-node1 192.168.100.22 ceph-node2 192.168.100.23 ceph ...
- 在 Rolling Update 中使用 Health Check【转】
上一节讨论了 Health Check 在 Scale Up 中的应用,Health Check 另一个重要的应用场景是 Rolling Update.试想一下下面的情况: 现有一个正常运行的多副本应 ...
- 题目:给定一数组 例如:a = [1,2,3,5,2,1] 现用户提供一个数字 请返回用户所提供的数字的所有下标
def test(ary): ds = {} for i in range(len(ary)): if ds.get(ary[i]): ds[ary[i]].append(i) else: ds[ar ...
- crackme---攻防世界
首先下载附件之后,查壳 虽然什么也没有发现,但是看一下区段就知道,这个是北斗的壳.所以我们首先载入od开始把壳脱掉 这里面也可以看到pushfd和pushad是北斗壳的特征 这里面我使用是esp定律脱 ...
- SqlServer查看锁表与解锁
某些情况下,sqlserver的表会被锁住,比如某个会话窗口有数据一直没提交,窗口又没关闭,这时表就会被锁住 其他任何连接查询表数据时都不会返回 这时需要手工杀掉产生死锁的会话ID,才能恢复正常 查看 ...
- Spring源码深度解析-《源码构建》
1.gradle构建eclipse项目时,gradle-5.0版本构建失败,gradle-3.3构建成功!Why 2.导入spring-framework-3.2.x/spring-beans之前先导 ...
- 文本情感分析(二):基于word2vec、glove和fasttext词向量的文本表示
上一篇博客用词袋模型,包括词频矩阵.Tf-Idf矩阵.LSA和n-gram构造文本特征,做了Kaggle上的电影评论情感分类题. 这篇博客还是关于文本特征工程的,用词嵌入的方法来构造文本特征,也就是用 ...
- 01 vue入门
vue简介 官网上有介绍,这里粘出来 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心 ...