一、什么是数据交换格式?
数据交换格式也叫报文,客户端与服务器常用数据交换格式xml(重量级占宽带比较大,传输重效率不高,所以不流行)、json(轻量级占宽带比较小,传输效率比xml高)、html。
浏览器与服务器进行通讯使用html进行渲染。
服务化(提供接口):把一些功能提供成接口进行通讯。
链家租房、微信项目都是采用分布式、分模块开发的。
一些网站多终端兼容:PC、手机、平板。
1.PC端(电脑端):入口只有一个,网站。
2.移动端(APP):Android、IOS、移动Web(H5)、微信开发、支付宝开发等,入口很广。
bootstrap+h5可以自适应PC端和移动端,微信、支付宝开发里面嵌套的是H5页面。
当多终端开发需求时,可以理解成都是对数据库进行操作,为了减少代码重用,就有了服务器端接口的概念,为多个终端提供接口API即可,多个终端调用服务端接口时一般需要提供个source声明是何种终端调用,接口一般都是通过http+json进行传输。
二、数据交换格式应用场景
移动端(Android、IOS)通讯方式采用http协议+JSON格式,走restful风格。
很多互联网项目都采用Http协议+JSON
因为xml比较重,Webservice服务采用http+xml格式,银行保险项目使用较多。
三、数据交换格式
1、json简单理解
- json简介
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相比于xml这种数据交换格式来说,因为解析xml比较的复杂,而且需要编写大段的代码,所以客户端和服务器的数据交换格式往往通过JSON来进行交换。
JSON:JavaScript 对象表示法(JavaScript Object Notation)。
JSON的形式是用大括号“{}”包围起来的项目列表,每一个项目间用逗号(,)分隔,而项目就是用冒号(:)分隔的属性名和属性值。这是典型的字典表示形式,也再次表明javascript里的对象就是字典结构。不管多么复杂的对象,都可以用一句JSON代码来创建并赋值。在JSON中,名称 / 值对包括字段名称(在双引号中),后面写一个冒号,然后是值
注意:json与jsonp区别?json是做数据格式传输的,jsonp是跨域请求时使用到的 ,浏览器有一个机制,不在同一域名下会有跨域问题,但是jsonp有一个缺点:是get请求。jsonp在公司网关接口搭建时会使用到。
跨域问题只在浏览器中产生,如ajax请求一般使用json作为数据交换格式,不使用xml作为数据交换格式,所以xml不会产生跨域问题。 - JSON格式的分类
JSON有两种结构:
json简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构
1、对象:对象在js中表示为“{}”括起来的内容,数据结构为 {key:value,key:value,…}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象.key 获取属性值,这个属性值的类型可以是 数字、字符串、数组、对象几种。
2、数组:数组在js中是中括号“[]”括起来的内容,数据结构为 [“java”,“javascript”,“vb”,…],取值方式和所有语言中一样,使用索引获取,字段值的类型可以是 数字、字符串、数组、对象几种。
经过对象、数组2种结构就可以组合成复杂的数据结构了。 - 常用JSON解析框架:
fastjson(阿里):web服务器端开发用的比较多、gson(谷歌):安卓开发用的多、jackson(SpringMVC自带) - 解析、组装JSON的代码实现:
- 使用阿里的fastjson解析json
package chauncy.dataswitching.json; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * @classDesc: 功能描述:(fastjson 解析) * @author: ChauncyWang * @verssion: v1.0 */ public class JsonAnalysis { static String JSONSTR = "{\"user\":[{\"name\":\"小红\",\"sex\":\"女\"},{\"name\":\"小黑\",\"sex\":\"男\"}]}"; public static void main(String[] args) { //1.将json字符串转换成jsonObject JSONObject jsonObject = new JSONObject(); //json对象 JSONObject parseObject = jsonObject.parseObject(JSONSTR); //获取json数组对象 JSONArray jsonArray = parseObject.getJSONArray("user"); for (Object object : jsonArray) { JSONObject jo=(JSONObject) object; //不要使用强转String的方法,因为fastjsonAPI中有getString方法 String name=jo.getString("name"); String sex=jo.getString("sex"); System.out.println(name+"---------"+sex); } } }
- 使用阿里的fastjson组装json
package chauncy.dataswitching.json; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; /** * @classDesc: 功能描述:(组装json) * @author: ChauncyWang * @verssion: v1.0 */ public class JsonAssemble { public static void main(String[] args) { JSONObject jsonObject = new JSONObject(); JSONArray jsonArray = new JSONArray(); JSONObject bean1=new JSONObject(); bean1.put("name", "小红"); bean1.put("sex", "女"); jsonArray.add(bean1); JSONObject bean2=new JSONObject(); bean2.put("name", "小黑"); bean2.put("sex", "男"); jsonArray.add(bean2); jsonObject.put("user", jsonArray); System.out.println(jsonObject.toJSONString()); } }
- 使用阿里的fastjson解析json
2、XML简单理解
- XML定义
它是可扩展标记语言(Extensible Markup Language,简称XML),是一种标记语言。
XML 全称为可扩展的标记语言。主要用于描述数据和用作配置文件。
XML 文档在逻辑上主要由一下 5 个部分组成:
1、XML 声明:指明所用 XML 的版本、文档的编码、文档的独立性信息
2、文档类型声明:指出 XML 文档所用的 DTD
3、元素:由开始标签、元素内容和结束标签构成
4、注释:以结束,用于对文档中的内容起一个说明作用
5、处理指令:通过处理指令来通知其他应用程序来处理非 XML 格式的数据,格式为
XML 文档的根元素被称为文档元素,它和在其外部出现的处理指令、注释等作为文档实体的子节点,根元素本身和其内部的子元素也是一棵树。
XML结构类似于生活中的树,有根节点有子节点。 - XML解析方式
Dom4j(小文件)、Sax(大文件)、Pull等
Dom4j与Sax区别:dom4j不适合大文件的解析,因为它是一下子将文件加载到内存中,所以有可能出现内存溢出,sax是基于事件来对xml进行解析的,所以他可以解析大文件的xml,也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性,所以sax经常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操作就用dom4j。 - 解析XML的代码实现:
- 创建user.xml
<?xml version="1.0" encoding="UTF-8"?> <user> <user1 id="01"> <名字>小红</名字> <性别>女</性别> </user1> <user2 id="02"> <名字>小黑</名字> <性别>男</性别> </user2> </user>
- 创建解析xml的java类XmlAnalysis.java
package chauncy.dataswitching.xml; import java.io.File; import java.util.Iterator; import java.util.List; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * @classDesc: 功能描述(使用xml进行解析) * @author: ChauncyWang * @version: 1.0 */ public class XmlAnalysis { static String FILENAME = "{系统中XML绝对路劲}\\src\\main\\resources\\user.xml"; public static void main(String[] args) throws DocumentException { // 获取xml解析器 SAXReader saxReader = new SAXReader(); Document read = saxReader.read(new File(FILENAME)); // 拿到根节点,只要拿到根节点子节点也会一并拿到 Element rootElement = read.getRootElement(); getNodes(rootElement); } static public void getNodes(Element rootElement) { System.out.println("节点名称:" + rootElement.getName()); // 拿到节点属性 List<Attribute> attributes = rootElement.attributes(); for (Attribute attribute : attributes) { System.out.println("属性名称:" + attribute.getName() + "-------" + attribute.getText()); } // 获取节点名称 if (!rootElement.getTextTrim().equals("")) { System.out.println(rootElement.getName() + "------" + rootElement.getText()); } // 返回下一个节点 Iterator<Element> elementIterator = rootElement.elementIterator(); while (elementIterator.hasNext()) { // 获取当前节点值 Element next = elementIterator.next(); getNodes(next); } } }
- 创建user.xml
四、Java反射机制
- 什么是Java反射?
就是正在运行,动态获取这个类的所有信息。 - 反射机制的作用:
1.反编译:.class–>java
2.通过反射机制访问java对象的属性、方法、构造方法等。 - 反射机制的应用场景:
Jdbc加载驱动、SpringIOC实现、框架(Hibernate)、注解的底层实现等。
每个驱动地址jar包不一样所以需要用反射区加载jdbc驱动。
Hibernate框架实体类必须有无参构造方法,因为底层使用反射forName.newInstance(),不声明的话这句话会报错。 - 使用反射技术和new哪个效率高?
使用new效率高,因为反射需要读取class地址之后再执行一些列步骤 。
注意:除了new创建对象,还可以使用反射机制创建对象,使用反射机制创建对象的话,可以使用该类有参或者无参构造函数创建对象,可以只获取当前类的属性、方法,也可以获取当前类+父类的属性、方法,具体实现差异体现在代码上。
可以通过反射机制在不同包下的类访问该类的私有成员变量。 - 反射机制获取类的三种方法:
//第一种方式(根据类的路径创建): Class c1 = Class.forName("com.chauncy.entity.User"); //第二种方式(直接通过类名.class创建): //java中每个类都有class 属性. Class c2 = User.class; //第三种方式(对象的运行时类): //java语言中任何一个java对象都有getClass 方法 User u = new User(); Class c3 = u.getClass(); //c3是运行时类 (u的运行时类是User)
- 反射创建对象的方式:
- 使用无参构造函数
Class<?> forName = Class.forName("chauncy.reflect.entity.UserEntity"); // newInstance使用无参构造函数创建对象,等同于new Object newInstance = forName.newInstance();
- 使用有参构造函数
Class<?> forName = Class.forName("chauncy.reflect.entity.UserEntity"); //使用有参构造函数创建对象 Constructor<?> constructor = forName.getConstructor(String.class); Object newInstance = constructor.newInstance("123"); UserEntity user=(UserEntity) newInstance;
- 使用无参构造函数
- 反射创建api:
getDeclaredMethods [] 获取该类的所有方法
getReturnType() 获取该类的返回值
getParameterTypes() 获取传入参数
getDeclaredFields() 获取该类的所有字段
setAccessible 允许访问私有成员 - 使用反射机制访问私有属性:
//可以使用反射机制访问到私有属性 Field declaredFields = forName.getDeclaredField("userId"); Object obj = forName.newInstance(); //允许访问私有成员属性 declaredFields.setAccessible(true); declaredFields.set(obj, "123"); UserEntity user=(UserEntity) obj; System.out.println(user.getUserId());
- 反射机制完整应用代码:
- 创建需要反射的类UserEntity.java
package chauncy.reflect.entity; /** * @classDesc: 功能描述(用户实体类) * @author: ChauncyWang * @version: 1.0 */ public class UserEntity { private String userId; private String userName; public UserEntity() { System.out.println("使用反射机制执行无参构造函数"); } public UserEntity(String userId) { System.out.println("使用反射机制执行有参构造函数--userId:" + userId); } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
- 创建反射类ReflexMechanismApplication.java动态获取类UserEntity.java的信息
package chauncy.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import chauncy.reflect.entity.UserEntity; /** * @classDesc: 功能描述(使用反射机制,调用类的无参、有参构造方法创建对象,使用反射获取该类的所有属性、方法(包含父类与不包含父类),使用反射机制访问不同包下类的私有成员变量) * @author: ChauncyWang * @version: 1.0 */ public class ReflexMechanismApplication { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException, SecurityException { // 1.除了new创建对象,还可以使用反射机制创建对象。 // UserEntity userEntity = new UserEntity(); // 2.forName() 必须传入class类的完整路径。 // 返回值就是对应类的字节码文件 Class<?> forName = Class.forName("chauncy.reflect.entity.UserEntity"); /* * // 3.newInstance使用无参构造函数创建对象,等同于new * Object newInstance = forName.newInstance(); * UserEntity user = (UserEntity) newInstance; * System.out.println("user:" + user); * user.setUserId("123"); * System.out.println(user.getUserId()); * // 使用反射技术和new哪个效率高?使用new效率高,因为反射需要读取class地址之后再执行一些列步骤 * // 为什么有一些框架需要声明类的无参构造方法,如Hibernate框架实体类必须有无参构造方法,因为底层使用反射forName.newInstance(),不声明的话这句话会报错。 */ /*//使用有参构造函数创建对象 Constructor<?> constructor = forName.getConstructor(String.class); Object newInstance = constructor.newInstance("123"); UserEntity user=(UserEntity) newInstance;*/ /*//使用反射获取该类的所有方法(不包含父类) Method[] declaredMethods = forName.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method.getName()+"---------"+method.getReturnType()); }*/ /*//使用反射获取该类的所有成员属性(不包含父类) Field[] declaredFields = forName.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(field.getName()); }*/ /*//使用反射获取该类的所有方法(包含父类) Method[] declaredMethods = forName.getMethods(); for (Method method : declaredMethods) { System.out.println(method.getName()+"---------"+method.getReturnType()); } //Object类下有哪些方法? //wait---------void //wait---------void //wait---------void //equals---------boolean //toString---------class java.lang.String //hashCode---------int //getClass---------class java.lang.Class //notify---------void //notifyAll---------void */ //可以使用反射机制访问到私有属性 Field declaredFields = forName.getDeclaredField("userId"); Object obj = forName.newInstance(); //允许访问私有成员属性 declaredFields.setAccessible(true); declaredFields.set(obj, "123"); UserEntity user=(UserEntity) obj; System.out.println(user.getUserId()); } }
- 创建需要反射的类UserEntity.java
五、SpringIOC底层实现
- 什么是SpringIOC?
SpringIOC就是把每一个bean(实体类)与bean(实体类)之间的关系都交给第三方容器(Spring)进行管理。
使用Spring的时候,对象不需要new。
Spring原理:反射+dom4j - Spring的加载过程/SpringIOC实现原理:
- 读取bean的XML配置文件
- 使用beanId查找bean配置,并获取配置文件中class地址。
- 使用Java反射技术实例化对象
- 获取属性配置,使用反射技术进行赋值。
详细步骤:- 利用传入的参数获取xml文件的流,并且利用dom4j解析成Document对象。
- 对于Document对象获取根元素对象<beans>后对下面的<bean>标签进行遍历,判断是否有符合的id。
- 如果找到对应的id,相当于找到了一个Element元素,开始创建对象,先获取class属性,根据属性值利用反射建立对象。
- 遍历<bean>标签下的property标签,并对属性赋值.注意,需要单独处理int,float类型的属性.因为在xml配置中这些属性都是以字符串的形式来配置的,因此需要额外处理。
- 如果属性property标签有ref属性,说明某个属性的值是一个对象,那么根据id(ref属性的值)去获取ref对应的对象,再给属性赋值。
- 返回建立的对象,如果没有对应的id,或者下没有子标签都会返回null。
- 模拟SpringIOC底层实现代码:
- 创建模拟Spring的配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="user1" class="chauncy.reflect.entity.UserEntity"> <property name="userId" value="0002"></property> <property name="userName" value="张三"></property> </bean> <bean id="user2" class="chauncy.reflect.entity.UserEntity"> <property name="userId" value="0002"></property> <property name="userName" value="张三"></property> </bean> </beans>
- 创建反射类ClassPathXmlApplicationContext.java解析Spring配置文件
package chauncy.springioc; import java.lang.reflect.Field; import java.util.List; import javax.swing.text.StyledEditorKit.ForegroundAction; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import chauncy.reflect.entity.UserEntity; /** * @classDesc: 功能描述(Spring加载过程/SpringIOC底层代码实现) * @author: ChauncyWang * @version: 1.0 */ public class ClassPathXmlApplicationContext { private String xmlPath; public ClassPathXmlApplicationContext(String xmlPath) { this.xmlPath = xmlPath; } public Object getBean(String beanId) throws DocumentException, ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException { // 1.读取xml配置文件 // 获取XML解析器 SAXReader saxReader = new SAXReader(); // 获取当前项目路径 Document read = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(xmlPath)); // 获取根节点对象 Element rootElement = read.getRootElement(); List<Element> elements = rootElement.elements(); Object obj = null; for (Element sonEle : elements) { String sonBeanId = sonEle.attributeValue("id"); if (!sonBeanId.equals(beanId)) { continue; } // 2.获取到每个bean的配置 获取class地址 String beanClassPath = sonEle.attributeValue("class"); // 3.拿到class地址进行反射实例化对象,使用反射api为私有属性赋值 Class<?> forName = Class.forName(beanClassPath); obj = forName.newInstance(); // 拿到类的成员变量 List<Element> grandSonElement = sonEle.elements(); for (Element element : grandSonElement) { String name = element.attributeValue("name"); String value = element.attributeValue("value"); // 使用反射api为私有属性赋值 Field declaredField = forName.getDeclaredField(name); // 允许往私有成员赋值 declaredField.setAccessible(true); declaredField.set(obj, value); } } return obj; } public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException, DocumentException { ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml"); Object bean = application.getBean("user1"); UserEntity user = (UserEntity) bean; System.out.println(user.getUserId() + "----" + user.getUserName()); } }
- 创建模拟Spring的配置文件applicationContext.xml