之前的章节都是通过代码注册bean,设置属性等 *** 作,这一章是在Spring框架里是采用xml形式定义Bean对象,以及设置Bean的属性以及引用关系。
就是将以下的标红的框的代码移到xml文件去定义,然后通过加载xml文件,进行解析文件,再进行bean的注册和属性的填充。
按上面改造方式,我们需要加载xml的功能,所以就会出现资源加载器,那么我们根据文件的加载,我们要既能支持ClassPath,还有本地file,以及云文件(http)的加载。这是资源加载的 *** 作。其次是解析xml的功能 *** 作,解析完毕调用上几章的Bean注册以及属性填充 *** 作即可。
1.工程项目test单元测试:
2.UML类图
这一章节需要用到之前的章节,所以下面蓝色的类图是对应的需要更改或添加的内容。
这一章节主要讲黄色类图的内容,这部分内容就是加载资源xml,解析xml,最后注册到Bean里,咱们先来说一下资源加载进来然后转换文件流方式(留到最后进行文件解析)
2.1 资源加载器Resource
Resource获取文件流的接口,需要在解析文件数据之前进行获取文件流的入口的目的。
ClassPathResource
Resource接口的实现,这个类为类路径文件流的处理,所以实现的都是处理classpath下的文件的处理,最后输出流。
FileSystemResource
Resource接口的实现,这个类是本地文件流的处理,所以实现的都是根据路径去获取一个file文件最后得到文件流。
UrlResource
Resource接口的实现,这个类是用来处理http请求方式得到的文件流,所以实现是传进来一个http地址的文件得到文件流的方式。
ResourceLoader
此接口是资源加载的接口,主要是将获取上面介绍的Resource
DefaultResourceLoader
是ResourceLoader实现接口,实现getResource()方法,其实是获取调用Resource的入口,通过参数来实例化不同的Resource的实现类,比如:传递的参数是类路径,那么就new ClassPathResource();
以上是加载文件的 *** 作。
2.2 解析文件以及Bean注册BeanDefinitionReader
此接口为加载资源解析bean的接口,所以定义了获取Bean注册方法,getRegistry(),也定义了获取Resource资源getResourceLoader(),用于在解析xml文件之前进行资源文件的获取。
定义了加载bean定义的方法,loadBeanDefinitions(),通过重载不同此方法用来根据不同的方式得到相同的Resource方式,最后进行根据资源进行解析。
AbstractBeanDefinitionReader
此抽象类继承BeanDefinitionReader,通过构造函数将BeanDefinitionRegistry和ResourceLoader获取到数据。
XmlBeanDefinitionReader
核心类,继承AbstractBeanDefinitionReader,实现其loadBeanDefinitions()方法,通过此方法获取Resource,并抽象出doLoadBeanDefinitions()方法,参数为Resource.getInputStream()获取文件流,根据文件流解析xml中的文件,获取Bean的Class类,Bean的属性,最后通过getRegistry()注册bean和属性的 *** 作,
2.3 原有类修改点BeanDefinitionRegistry
此类里添加了一个判断Bean是否已经存在在容器里的方法,containsBeanDefinition(String beanName);
DefaultListableBeanFactory
作为BeanDefinitionRegistry的实现类,那肯定是要实现查询map里是否存在此key为beanName的数据并返回true或false。
BeanFactory
此类里添加了获取bean返回实体方式的getBean方法();
AbstractBeanFactory
作为BeanFactory的实现类,将获取到的getBean转换为泛型实体。
3.代码实现Resource:资源处理器,用来获取inputStream();
// Spring框架下core.io包主要用来处理资源加载流 // 资源加载接口 public interface Resource { // 获取流的方法 InputStream getInputStream() throws IOException; }
ClassPathResource:类文件下的文件加载方式
// 类路径文件流处理 public class ClassPathResource implements Resource { private final String path; private ClassLoader classLoader; public ClassPathResource(String path) { this(path, (ClassLoader) null); } public ClassPathResource(String path, ClassLoader classLoader) { // spring的断言,如果空就不能往下走报错必须是不为空 Assert.notNull(path, "Path must not be null"); this.path = path; this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); } // 获取文件流返回 @Override public InputStream getInputStream() throws IOException { // 需要动态获取某个位置的文件可以获取文件的资源,可以采用classLoader.getResourceAsStream InputStream is = classLoader.getResourceAsStream(path); if (is == null) { throw new FileNotFoundException(this.path + " cannot be opened because it does not exist"); } return is; } }
FileSystemResource:本地方式文件加载方式
// 本地文件流处理 public class FileSystemResource implements Resource { private final File file; private final String path; public FileSystemResource(File file) { this.file = file; // 有文件就能获取路径 this.path = file.getPath(); } public FileSystemResource(String path) { // 有路径就能创建文件 this.file = new File(path); this.path = path; } // 通过指定文件路径的方式读取文件信息。 @Override public InputStream getInputStream() throws IOException { return new FileInputStream(this.file); } }
UrlResource:http文件流加载方式
// 通过http方式读取文件,我们可以把文件放入不同的服务器或者云上 public class UrlResource implements Resource { private final URL url; public UrlResource(URL url) { Assert.notNull(url, "URL must not be null"); this.url = url; } @Override public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); try { return con.getInputStream(); } catch (IOException ex) { if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).disconnect(); } throw ex; } } }
ResourceLoader:对于上面的资源的包装类,通过此类获取inputStream的入口
// 包装资源加载器 // 按照资源加载的不同方式,资源加载器可以把这些方式集中到统一的类服务下进行处理,外部用户只需要传递资源地址即可 public interface ResourceLoader { String CLASSPATH_URL_PREFIX = "classpath:"; // 获取资源 Resource getResource(String location); }
DefaultResourceLoader:实现ResourceLoader的方法,根据不同的参数获取不同的Resource
public class DefaultResourceLoader implements ResourceLoader { // 根据不同的方式创建不同的资源 @Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); // 检测字符串是否以指定的前缀开始 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length())); } else { URL url = null; try { url = new URL(location); return new UrlResource(url); } catch (MalformedURLException e) { return new FileSystemResource(location); } } } }
以上是对资源加载的类和接口全部内容,下面是获取资源和解析xml的数据
BeanDefinitionReader:
// bean定义读取接口 public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry(); ResourceLoader getResourceLoader(); // getRegistry()与getResourceLoader()都是用于提供给这三个方法的工具,加载和注册 void loadBeanDefinitions(Resource resource); void loadBeanDefinitions(Resource... resources); void loadBeanDefinitions(String location); }
AbstractBeanDefinitionReader:实现BeanDefinitionReader的获取BeanDefinitionRegistry和ResourceLoader
// bean定义读取的准备工作,定义成abstract就可以任意实现需要的类,不想实现的就可不实现 // 此类主要是获取BeanDefinitionRegistry和ResourceLoader public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader { private final BeanDefinitionRegistry registry; private ResourceLoader resourceLoader; protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, new DefaultResourceLoader()); } public AbstractBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) { this.registry = registry; this.resourceLoader = resourceLoader; } @Override public BeanDefinitionRegistry getRegistry() { return registry; } @Override public ResourceLoader getResourceLoader() { return resourceLoader; } }
XmlBeanDefinitionReader:继承AbstractBeanDefinitionReader类,最主要的方法为doLoadBeanDefinitions(),这个是通过获取Resource以后再次获取inputStream(),然后通过流得到Bean的id,name,以及属性等等信息的解析,解析完毕即可注册到beanDeffinition里,最后注册到容器里。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { protected XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) { super(registry, resourceLoader); } @Override public void loadBeanDefinitions(Resource resource) { try { try (InputStream inputStream = resource.getInputStream()) { doLoadBeanDefinitions(inputStream); } } catch (IOException | ClassNotFoundException e) { throw new BeansException("IOException parsing XML document from " + resource, e); } } @Override public void loadBeanDefinitions(Resource... resources) { for (Resource resource : resources) { loadBeanDefinitions(resource); } } @Override public void loadBeanDefinitions(String location) { ResourceLoader resourceLoader = getResourceLoader(); Resource resource = resourceLoader.getResource(location); loadBeanDefinitions(resource); } // 核心方法-从xml中取出bean和属性信息进行bean的注册 protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException { document doc = XmlUtil.readXML(inputStream); Element root = doc.getdocumentElement(); NodeList childNodes = root.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { // 判断元素 if (!(childNodes.item(i) instanceof Element)) continue; // 判断对象 if (!"bean".equals(childNodes.item(i).getNodeName())) continue; // 解析标签 Element bean = (Element) childNodes.item(i); String id = bean.getAttribute("id"); String name = bean.getAttribute("name"); String classname = bean.getAttribute("class"); // 获取Class,方便获取类中的名称 // Class.forName装入类并对类做初始化 Class> clazz = Class.forName(classname); // 优先级 id > name String beanName = StrUtil.isNotEmpty(id) ? id : name; if (StrUtil.isEmpty(beanName)) { beanName = StrUtil.lowerFirst(clazz.getSimpleName()); } // 将解析出来的Bean放入BeanDefination的beanClass BeanDefination beanDefination = new BeanDefination(clazz); // 读取属性并填充 for (int j = 0; j < bean.getChildNodes().getLength(); j++) { if (!(bean.getChildNodes().item(j) instanceof Element)) continue; if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) continue; // 解析标签 Element property = (Element) bean.getChildNodes().item(j); String attrName = property.getAttribute("name"); String attrValue = property.getAttribute("value"); String attrRef = property.getAttribute("ref"); // 获取属性值:引入对象、值对象 Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue; // 创建属性信息 PropertyValue propertyValue = new PropertyValue(attrName, value); // 添加到属性集合里去 beanDefination.getPropertyValues().addPropertyValue(propertyValue); } if (getRegistry().containsBeanDefinition(beanName)) { throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed"); } // 注册beanDefinition getRegistry().registerBeanDefinition(beanName, beanDefination); } } }
对于上节需要修改的内容
beanFactory:bean工厂添加getBean()返回值为实体方法
public interface BeanFactory { Object getBean(String name) throws BeansException; // 04章节-添加,可以传参数 Object getBean(String name,Object... args);T getBean(String name, Class requiredType) throws BeansException; }
AbstractBeanFactory:在此类里实现以下一个方法即可,其他不用动。
@Override publicT getBean(String name, Class requiredType) throws BeansException { return (T) getBean(name); }
BeanDefinitionRegistry:这个接口需要添加是否在容器中包含Bean对象
public interface BeanDefinitionRegistry { void registerBeanDefinition(String beanName, BeanDefination beanDefinition); boolean containsBeanDefinition(String beanName); }
DefaultListableBeanFactory:此类添加此方法即可,其他不用动
@Override public boolean containsBeanDefinition(String beanName) { return beanDefinationMap.containsKey(beanName); }4.测试
测试前准备
UserDao类
public class UserDao { private static MaphashMap = new HashMap<>(); static { hashMap.put("10001", "hahaha"); hashMap.put("10002", "ss"); hashMap.put("10003", "sio"); } public String queryUserName(String uId) { return hashMap.get(uId); } }
UserService类
public class UserService { private String uId; private UserDao userDao; public void queryUserInfo() { System.out.println("查询用户信息:" + userDao.queryUserName(uId)); } public String getuId() { return uId; } public void setuId(String uId) { this.uId = uId; } public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
单元测试
public class ApiTest06 { // @Test public void test_xml() { // 1.初始化BeanFactory DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 2.读取配置文件&注册bean XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("classpath:spring.xml"); // 3.获取Bean对象调用方法 UserService userService = beanFactory.getBean("userService", UserService.class); userService.queryUserInfo(); } }
测试
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)