- XML是重量级的数据交换格式,占带宽比较大
- JSON是轻量级的交换格式,xml占带宽较小
- 很多互联网公司用json作为交换格式,但是很多银行项目还是使用XML作为数据的交换格式。
this.getClass().getClassLoader().getResourceAsStream(xmlPath)
获取当前项目路径(xmlPath是文件名)
XML的解析方式
dom4j,Sax,Pull
Dom4j和Sax的区别
dom4j不适合大文件的解析,因为他是一下子将文件加载到内存中,所以有可能出现内存溢出,sax是基于事件对xml进行解析,所以他可以解析大文件的xml,也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有那么强的灵活性,所以sax经常是用来解析大型XML文件,而要对XML文件进行一些灵活的CRUD操作,就要用dom4j。
- 总结一下,dom4j用来操作xml的,而sax是用来读取的
思路:
(1)解析XML文件
(2)获取bean标签,解析出bean的id和class等属性,利用传进来的beanId找寻指定的bean进行解析
(3)根据获取的class地址,利用反射将其类初始化
java代码演示:
(1)重要,自定义实现ClassPathXmlApplication,负责解析xml,反射创建对应class属性的对象
package com.xiyou.mayi.thread5.shouxieSpringIOC.utils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
/**
* 该类的作用是用来解析xml文件,根据bean标签反射创建对象
*/
public class MyClassPathXmlApplication {
/**
* 保存xml的文件路径,只有文件名
*/
private String xmlPath;
/**
* 构造函数
* @param xmlPath
*/
public MyClassPathXmlApplication(String xmlPath){
this.xmlPath = xmlPath;
}
/**
* 重写getBean方法
* @param beanId
* @return
*/
public Object getBean(String beanId) throws Exception {
// 1.读取配置文件
List<Element> elements = readerXml();
// 2. 使用beanId查询我们配置的bean
String xmlByIDClass = findXmlByIDClass(elements, beanId);
// 3. 根据class属性反射调用
Class<?> clazz = Class.forName(xmlByIDClass);
// 4. 创建对象
return clazz.newInstance();
}
/**
* 解析xml文件
* @return
*/
public List<Element> readerXml() throws DocumentException {
SAXReader saxReader = new SAXReader();
// 判断xml文件名是否为空
if(StringUtils.isEmpty(xmlPath)){
new Exception("文件路径为空");
}
// 读取xml文件
Document document = saxReader.read(getClassXmlInputStream(xmlPath));
// 获取根节点信息(beans标签)
Element rootElement = document.getRootElement();
// 获取子节点
List<Element> elements = rootElement.elements();
// 判断子节点是否为空,为空返回null
if (elements == null || elements.isEmpty()) {
return null;
}
return elements;
}
/**
* 根据文件名读取classpath下的该文件路径,将其转为流的形式
* @param xmlPath
* @return
*/
public InputStream getClassXmlInputStream(String xmlPath) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(xmlPath);
return resourceAsStream;
}
/**
* 根据子节点列表和指定的beanId查询,对应的class路径
* @param elements
* @param beanId
* @return
* @throws Exception
*/
public String findXmlByIDClass(List<Element> elements, String beanId) throws Exception {
// 遍历查询出来的子节点,找到对应的beanId的bean标签,找到其class属性
for(Element element : elements){
// 拿出其id属性对应的值
String beanIdValue = element.attributeValue("id");
// 给子标签的id属性为空或者与传入的不相等
if (StringUtils.isEmpty(beanIdValue) || !beanIdValue.equals(beanId)) {
continue;
}
// 此时beanId已经找到了对应的bean标签.获取对应的class属性的值
String classPath = element.attributeValue("class");
if (StringUtils.isNotEmpty(classPath)) {
return classPath;
}
}
return null;
}
}
(2)自己写的xml文件
<beans>
<bean id="userService" class="com.xiyou.mayi.thread5.shouxieSpringIOC.service.impl.UserServiceImpl"/>
beans>
(3)启动类
package com.xiyou.mayi.thread5.shouxieSpringIOC;
import com.xiyou.mayi.thread5.shouxieSpringIOC.utils.MyClassPathXmlApplication;
public class Test {
public static void main(String[] args) {
MyClassPathXmlApplication myClassPathXmlApplication = new MyClassPathXmlApplication("spring2.xml");
try {
Object userService = myClassPathXmlApplication.getBean("userService");
System.out.println(userService);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(4)输出结果:
com.xiyou.mayi.thread5.shouxieSpringIOC.service.impl.UserServiceImpl@70177ecd
可以发现我们自己实现的ClassPathXmlApplication根据传进的beanId,找到对应的class属性,利用反射成功创建了对象。
IOC容器实现思路:
(1)使用Java反射机制扫描包,获取包下面所有的类
(2)判断类上是否有注入bean的注解
(3)使用Java反射机制初始化该类
(4)将得到的对象存入concurrentHashMap中,key是定义的id的名字(默认是类名小写)
IOC依赖注入实现思路:
(1)使用反射机制,获取当前类的所有属性
(2)判断当前属性上是否有注解
(3)默认使用属性名称,查找bean容器对象,将IOC中初始化好的对象赋值给该属性。
注意一点,如果一个类没有受到Spring的管理,那么里面的属性也不会被Spring注入值,就是类上不加@Servicec等属性,即使属性上有@Autowrited也不会注入
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.xiyougroupId>
<artifactId>spring-annotation-iocartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>3.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>3.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>3.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>3.0.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>2.1_2version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.37version>
dependency>
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6.1version>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
<version>2.6version>
dependency>
dependencies>
project>
(3)自定义注解,一个是如同@Service,一个如同@Resource
类似@Service
package com.xiyou.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 自定义注解 注入到Spring容器
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtService {
}
类似@Resource
package com.xiyou.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解 从Spring容器获取bean
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtResource {
}
(4)Service层
package com.xiyou.service;
public interface OrderService {
public void addOrder();
}
package com.xiyou.service;
public interface UserService {
public void add();
}
package com.xiyou.service.impl;
import com.xiyou.annotation.ExtService;
import com.xiyou.service.OrderService;
@ExtService
public class OrderServiceImpl implements OrderService {
@Override
public void addOrder() {
System.out.println("addOrder");
}
}
package com.xiyou.service.impl;
import javax.annotation.Resource;
import com.xiyou.annotation.ExtResource;
import com.xiyou.annotation.ExtService;
import com.xiyou.service.OrderService;
import com.xiyou.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
//将该类注入到spring容器里面
@ExtService
public class UserServiceImpl implements UserService {
// 从Spring容器中读取bean
@ExtResource
private OrderService orderServiceImpl;
public void add() {
orderServiceImpl.addOrder();
System.out.println("我是使用反射机制运行的方法");
}
}
这里我们在UserService中依赖了OrderServiceImpl,这里需要自动注入对象给属性,即依赖注入
(5)反射工具类,根据指定的包,扫描到该包下面的所有类(这里不做强调,太麻烦了,直接从网上找的)
package com.xiyou.utils;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ClassUtil {
/**
* 取得某个接口下所有实现这个接口的类
*/
public static List<Class> getAllClassByInterface(Class c) {
List<Class> returnClassList = null;
if (c.isInterface()) {
// 获取当前的包名
String packageName = c.getPackage().getName();
// 获取当前包下以及子包下所以的类
List<Class<?>> allClass = getClasses(packageName);
if (allClass != null) {
returnClassList = new ArrayList<Class>();
for (Class classes : allClass) {
// 判断是否是同一个接口
if (c.isAssignableFrom(classes)) {
// 本身不加入进去
if (!c.equals(classes)) {
returnClassList.add(classes);
}
}
}
}
}
return returnClassList;
}
/*
* 取得某一类所在包的所有类名 不含迭代
*/
public static String[] getPackageAllClassName(String classLocation, String packageName) {
// 将packageName分解
String[] packagePathSplit = packageName.split("[.]");
String realClassLocation = classLocation;
int packageLength = packagePathSplit.length;
for (int i = 0; i < packageLength; i++) {
realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
}
File packeageDir = new File(realClassLocation);
if (packeageDir.isDirectory()) {
String[] allClassName = packeageDir.list();
return allClassName;
}
return null;
}
/**
* 从包package中获取所有的Class
*
* @param packageName
* @return
*/
public static List<Class<?>> getClasses(String packageName) {
// 第一个class类的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
List<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
(6)自定义实现IOC的注解方式,可以进行依赖注入
package com.xiyou.spring;
import com.xiyou.annotation.ExtService;
import com.xiyou.utils.ClassUtil;
import org.apache.commons.lang.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 自定义实现注解
*/
public class MyClassPathAnnotationContext {
/**
* 定义扫描包的范围
*/
private String packageName;
/**
* 该Map用来保存初始化的bean(即类上带注解的类)
* key是方法名小写即id,value是创建好的对象
*/
private ConcurrentHashMap<String, Object> initBean;
/**
* 构造函数,初始化传递过来的要扫描的包的范围,即basePackage
* @param packageName
*/
public MyClassPathAnnotationContext(String packageName){
this.packageName = packageName;
}
/**
* 找到类上面标注了我们制定注解的类,返回类的Class对象
* @return
* @throws Exception
*/
public List<Class> findClassExisService() throws Exception{
// 判断待扫描的包是否为空
if (StringUtils.isEmpty(packageName)) {
throw new Exception("扫包地址不能为空!");
}
// 1. 使用反射技术获取当前包写的所有的类(这个方法我们不去纠结,这个是网上找的)
List<Class<?>> classesByPackageName = ClassUtil.getClasses(packageName);
// 2. 循环遍历判断类上是否有注解
List<Class> exisClassesAnnotation = new ArrayList<>();
for (Class clazz : classesByPackageName) {
ExtService annotation = (ExtService) clazz.getDeclaredAnnotation(ExtService.class);
// 如果能得到这个注解,表示这个对象不为空的时候,表示该类有注解
if (annotation != null) {
// 将当前得到的类的class反射对象保存
exisClassesAnnotation.add(clazz);
continue;
}
}
return exisClassesAnnotation;
}
/**
* 根据传进来的带有注解的类的class将其变为map,其key是方法名小写,value是利用反射创建出来的对象
* @param listClassesAnnotation
* @return
*/
public ConcurrentHashMap<String, Object> initBean(List<Class> listClassesAnnotation) throws IllegalAccessException, InstantiationException {
// 创建一个对象
ConcurrentHashMap<String, Object> concurrentHashMap = new ConcurrentHashMap<>();
for (Class clazz : listClassesAnnotation) {
Object newInstance = clazz.newInstance();
// 获取key,就是当前类的名字,将其小写 clazz.getSimpleName()就是拿到父类的名字
String beanId = toLowerCaseFirstOne(clazz.getSimpleName());
// 存入到map中,key是类名小写,value是建立的对象
concurrentHashMap.put(beanId, newInstance);
}
return concurrentHashMap;
}
/**
* 根据传进来的父类的名字,将其首字母小写返回
* @param simpleName
* @return
*/
public String toLowerCaseFirstOne(String simpleName) {
if (Character.isLowerCase(simpleName.charAt(0))) {
return simpleName;
}
else {
return (new StringBuilder()).append(Character.toLowerCase(simpleName.charAt(0))).append(simpleName.substring(1)).toString();
}
}
/**
* 根据beanId查询对象,传进来beanId得到对应的对象
* @param beanId
* @return
*/
public Object getBean(String beanId) throws Exception {
// 1. 使用反射机制获取指定包下面的所有的含有指定注解的类
List<Class> classExisServiceList = findClassExisService();
if (classExisServiceList == null || classExisServiceList.isEmpty()) {
throw new Exception("没有需要初始化的bean");
}
// 2. 初始化这些带指定注解的类放到一个Map中 key是方法名首字母小写, value就是该对象
initBean = initBean(classExisServiceList);
if (initBean == null || initBean.isEmpty()) {
throw new Exception("初始化bean为空!");
}
// 3. 根据传进来的beanId得到bean对象,直接从Map中取值
Object object = initBean.get(beanId);
// 4. 找到该类中哪些属性有指定注解,将其属性进行赋值操作 Object是当前类的对象
attriAssign(object);
return object;
}
/**
* 进行属性赋值 即@Resource的作用,此时的Map已经存了值,存了所有的类上有指定注解的类
* @param object 传进来的是一个对象,表示当前的用户请求的类(根据beanId)
*/
public void attriAssign(Object object) throws IllegalAccessException {
// 得到当前类的class类型
Class<?> classInfo = object.getClass();
// 获得该类的所有属性
Field[] declaredFields = classInfo.getDeclaredFields();
// 循环遍历该属性
for (Field field : declaredFields) {
// 这里假设每个属性都有注解,不做判断,得到属性名
String name = field.getName();
// 使用属性名找class对象,该属性名应该和map中的key一致,也就是和类名首字母小写一致
Object ob = initBean.get(name);
if (ob != null) {
// 设置权限,我们可以操作私有属性
field.setAccessible(true);
// 给该属性赋值,第一个参数是属性是哪个类中的,第二个属性是该属性要赋值的对象的值
field.set(object, ob);
continue;
}
}
}
}
(7)测试类
package com.xiyou;
import com.xiyou.service.UserService;
import com.xiyou.spring.MyClassPathAnnotationContext;
public class Test {
public static void main(String[] args) throws Exception {
MyClassPathAnnotationContext myClassPathAnnotationContext = new MyClassPathAnnotationContext("com.xiyou");
UserService userService = (UserService)myClassPathAnnotationContext.getBean("userServiceImpl");
userService.add();
}
}
(8)输出结果:
addOrder
我是使用反射机制运行的方法