ioc中最基本的JAVA技术_自己动手实现一个简单的 IOC,牛皮!!

我们将分为几步来编写简易 IOC,首先设计组件,再设计接口,然后关注实现。

1. 设计组件。

我们还记得Spring中最重要的有哪些组件吗?BeanFactory 容器,BeanDefinition Bean的基本数据结构,当然还需要加载Bean的资源加载器。大概最后最重要的就是这几个组件。

容器用来存放初始化好的Bean,BeanDefinition 就是Bean的基本数据结构,比如Bean的名称,Bean的属性 PropertyValue,Bean的方法,是否延迟加载,依赖关系等。资源加载器就简单了,就是一个读取XML配置文件的类,读取每个标签并解析。

2. 设计接口

首先肯定需要一个BeanFactory,就是Bean容器,容器接口至少有2个最简单的方法,一个是获取Bean,一个注册Bean.

/**

* 需要一个beanFactory 定义ioc 容器的一些行为 比如根据名称获取bean, 比如注册bean,参数为bean的名称,bean的定义

*

* @author stateis0

* @version 1.0.0

* @Date 2017/11/30

*/public interface BeanFactory {

/**

* 根据bean的名称从容器中获取bean对象

*

* @param name bean 名称

* @return bean实例

* @throws Exception 异常

*/

Object getBean(String name) throws Exception;

/**

* 将bean注册到容器中

*

* @param name bean 名称

* @param bean bean实例

* @throws Exception 异常

*/

void registerBeanDefinition(String name, BeanDefinition bean) throws Exception;}

根据Bean的名字获取Bean对象,注册参数有2个,一个是Bean的名字,一个是 BeanDefinition 对象。

定义完了Bean最基本的容器,还需要一个最简单 BeanDefinition 接口,我们为了方便,但因为我们这个不必考虑扩展,因此可以直接设计为类,BeanDefinition 需要哪些元素和方法呢?

需要一个 Bean 对象,一个Class对象,一个ClassName字符串,还需要一个元素集合 PropertyValues。这些就能组成一个最基本的 BeanDefinition 类了。那么需要哪些方法呢?其实就是这些属性的get set 方法。

我们看看该类的详细:

package cn.thinkinjava.myspring;

/**

* bean 的定义

*

* @author stateis0

*/public class BeanDefinition {

/**

* bean

*/

private Object bean;

/**

* bean 的 CLass 对象

*/

private Class beanClass;

/**

* bean 的类全限定名称

*/

private String ClassName;

/**

* 类的属性集合

*/

private PropertyValues propertyValues = new PropertyValues();

/**

* 获取bean对象

*/

public Object getBean() {

return this.bean;

}

/**

* 设置bean的对象

*/

public void setBean(Object bean) {

this.bean = bean;

}

/**

* 获取bean的Class对象

*/

public Class getBeanclass() {

return this.beanClass;

}

/**

* 通过设置类名称反射生成Class对象

*/

public void setClassname(String name) {

this.ClassName = name;

try {

this.beanClass = Class.forName(name);

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

/**

* 获取bean的属性集合

*/

public PropertyValues getPropertyValues() {

return this.propertyValues;

}

/**

* 设置bean的属性

*/

public void setPropertyValues(PropertyValues pv) {

this.propertyValues = pv;

}

}

有了基本的 BeanDefinition 数据结构,还需要一个从XML中读取并解析为 BeanDefinition 的操作类,首先我们定义一个 BeanDefinitionReader 接口,该接口只是一个标识,具体由抽象类去实现一个基本方法和定义一些基本属性,比如一个读取时需要存放的注册容器,还需要一个委托一个资源加载器 ResourceLoader, 用于加载XML文件,并且我们需要设置该构造器必须含有资源加载器,当然还有一些get set 方法。

package cn.thinkinjava.myspring;

import cn.thinkinjava.myspring.io.ResourceLoader;

import java.util.HashMap;

import java.util.Map;

/**

* 抽象的bean定义读取类

*

* @author stateis0

*

/

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {

/**

* 注册bean容器

*/

private Map registry;

/**

* 资源加载器

*/

private ResourceLoader resourceLoader;

/**

* 构造器器必须有一个资源加载器, 默认插件创建一个map容器

*

* @param resourceLoader 资源加载器

*/

protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {

this.registry = new HashMap<>();

this.resourceLoader = resourceLoader;

}

/**

* 获取容器

*/

public Map getRegistry() {

return registry;

}

/**

* 获取资源加载器

*/

public ResourceLoader getResourceLoader() {

return resourceLoader;

}

}

有了这几个抽象类和接口,我们基本能形成一个雏形,BeanDefinitionReader 用于从XML中读取配置文件,生成 BeanDefinition 实例,存放在 BeanFactory 容器中,初始化之后,就可以调用 getBean 方法获取初始化成功的Bean。形成一个完美的闭环。

3. 如何实现

刚刚我们说了具体的流程:从XML中读取配置文件, 解析成 BeanDefinition,最终放进容器。说白了就3步。那么我们就先来设计第一步。

1. 从XML中读取配置文件, 解析成 BeanDefinition

我们刚刚设计了一个读取BeanDefinition 的接口 BeanDefinitionReader 和一个实现它的抽象类 AbstractBeanDefinitionReader,抽象了定义了一些简单的方法,其中由一个委托类-----ResourceLoader, 我们还没有创建, 该类是资源加载器,根据给定的路径来加载资源。

我们可以使用Java 默认的类库 java.net.URL 来实现,定义两个类,一个是包装了URL的类 ResourceUrl, 一个是依赖 ResourceUrl 的资源加载类。

ResourceUrl 代码实现

/**

* 资源URL

*

/

public class ResourceUrl implements Resource {

/**

* 类库URL

*/

private final URL url;

/**

* 需要一个类库URL

*/

public ResourceUrl(URL url) {

this.url = url;

}

/**

* 从URL中获取输入流

*/

@Override

public InputStream getInputstream() throws Exception {

URLConnection urlConnection = url.openConnection();

urlConnection.connect();

return urlConnection.getInputStream();

}

}

ResourceLoader 实现

/**

* 资源URL

*/

public class ResourceUrl implements Resource {

/**

* 类库URL

*/

private final URL url;

/**

* 需要一个类库URL

*/

public ResourceUrl(URL url) {

this.url = url;

}

/**

* 从URL中获取输入流

*/

@Override

public InputStream getInputstream() throws Exception {

URLConnection urlConnection = url.openConnection();

urlConnection.connect();

return urlConnection.getInputStream();

}

}

当然还需要一个接口,只定义了一个抽象方法

package cn.thinkinjava.myspring.io;

import java.io.InputStream;

/**

* 资源定义

*

* @author stateis0

*/

public interface Resource {

/**

* 获取输入流

*/

InputStream getInputstream() throws Exception;

}

好了, AbstractBeanDefinitionReader 需要的元素已经有了,但是,很明显该方法不能实现读取 BeanDefinition 的任务。那么我们需要一个类去继承抽象类,去实现具体的方法, 既然我们是XML 配置文件读取,那么我们就定义一个 XmlBeanDefinitionReader 继承 AbstractBeanDefinitionReader ,实现一些我们需要的方法, 比如读取XML 的readrXML, 比如将解析出来的元素注册到 registry 的 Map 中, 一些解析的细节。我们还是看代码吧。

XmlBeanDefinitionReader 实现读取配置文件并解析成Bean

package cn.thinkinjava.myspring.xml;

import cn.thinkinjava.myspring.AbstractBeanDefinitionReader;

import cn.thinkinjava.myspring.BeanDefinition;

import cn.thinkinjava.myspring.BeanReference;

import cn.thinkinjava.myspring.PropertyValue;

import cn.thinkinjava.myspring.io.ResourceLoader;

import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.Node;

import org.w3c.dom.NodeList;

/**

* 解析XML文件

*

* @author stateis0

*/

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

/**

* 构造器,必须包含一个资源加载器

*

* @param resourceLoader 资源加载器

*/

public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {

super(resourceLoader);

}

public void readerXML(String location) throws Exception {

// 创建一个资源加载器

ResourceLoader resourceloader = new ResourceLoader();

// 从资源加载器中获取输入流

InputStream inputstream = resourceloader.getResource(location).getInputstream();

// 获取文档建造者工厂实例

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

// 工厂创建文档建造者

DocumentBuilder docBuilder = factory.newDocumentBuilder();

// 文档建造者解析流 返回文档对象

Document doc = docBuilder.parse(inputstream);

// 根据给定的文档对象进行解析,并注册到bean容器中

registerBeanDefinitions(doc);

// 关闭流

inputstream.close();

}

/**

* 根据给定的文档对象进行解析,并注册到bean容器中

*

* @param doc 文档对象

*/

private void registerBeanDefinitions(Document doc) {

// 读取文档的根元素

Element root = doc.getDocumentElement();

// 解析元素的根节点及根节点下的所有子节点并添加进注册容器

parseBeanDefinitions(root);

}

/**

* 解析元素的根节点及根节点下的所有子节点并添加进注册容器

*

* @param root XML 文件根节点

*/

private void parseBeanDefinitions(Element root) {

// 读取根元素的所有子元素

NodeList nl = root.getChildNodes();

// 遍历子元素

for (int i = 0; i < nl.getLength(); i++) {

// 获取根元素的给定位置的节点

Node node = nl.item(i);

// 类型判断

if (node instanceof Element) {

// 强转为父类型元素

Element ele = (Element) node;

// 解析给给定的节点,包括name,class,property, name, value,ref

processBeanDefinition(ele);

}

}

}

/**

* 解析给给定的节点,包括name,class,property, name, value,ref

*

* @param ele XML 解析元素

*/

private void processBeanDefinition(Element ele) {

// 获取给定元素的 name 属性

String name = ele.getAttribute("name");

// 获取给定元素的 class 属性

String className = ele.getAttribute("class");

// 创建一个bean定义对象

BeanDefinition beanDefinition = new BeanDefinition();

// 设置bean 定义对象的 全限定类名

beanDefinition.setClassname(className);

// 向 bean 注入配置文件中的成员变量

addPropertyValues(ele, beanDefinition);

// 向注册容器 添加bean名称和bean定义

getRegistry().put(name, beanDefinition);

}

/**

* 添加配置文件中的属性元素到bean定义实例中

*

* @param ele 元素

* @param beandefinition bean定义 对象

*/

private void addPropertyValues(Element ele, BeanDefinition beandefinition) {

// 获取给定元素的 property 属性集合

NodeList propertyNode = ele.getElementsByTagName("property");

// 循环集合

for (int i = 0; i < propertyNode.getLength(); i++) {

// 获取集合中某个给定位置的节点

Node node = propertyNode.item(i);

// 类型判断

if (node instanceof Element) {

// 将节点向下强转为子元素

Element propertyEle = (Element) node;

// 元素对象获取 name 属性

String name = propertyEle.getAttribute("name");

// 元素对象获取 value 属性值

String value = propertyEle.getAttribute("value");

// 判断value不为空

if (value != null && value.length() > 0) {

// 向给定的 “bean定义” 实例中添加该成员变量

beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));

} else {

// 如果为空,则获取属性ref

String ref = propertyEle.getAttribute("ref");

if (ref == null || ref.length() == 0) {

// 如果属性ref为空,则抛出异常

throw new IllegalArgumentException(

"Configuration problem: element for property '"

+ name + "' must specify a ref or value");

}

// 如果不为空,测创建一个 “bean的引用” 实例,构造参数为名称,实例暂时为空

BeanReference beanRef = new BeanReference(name);

// 向给定的 “bean定义” 中添加成员变量

beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanRef));

}

}

}

}

}

可以说代码注释写的非常详细,该类方法如下:

public void readerXML(String location) 公开的解析XML的方法,给定一个位置的字符串参数即可。

private void registerBeanDefinitions(Document doc) 给定一个文档对象,并进行解析。

private void parseBeanDefinitions(Element root) 给定一个根元素,循环解析根元素下所有子元素。

private void processBeanDefinition(Element ele) 给定一个子元素,并对元素进行解析,然后拿着解析出来的数据创建一个 BeanDefinition 对象。并注册到BeanDefinitionReader 的 Map 容器(该容器存放着解析时的所有Bean)中。

private void addPropertyValues(Element ele, BeanDefinition beandefinition) 给定一个元素,一个 BeanDefinition 对象,解析元素中的 property 元素, 并注入到 BeanDefinition 实例中。

一共5步,完成了解析XML文件的所有操作。 最终的目的是将解析出来的文件放入到 BeanDefinitionReader 的 Map 容器中。

好了,到这里,我们已经完成了从XML文件读取并解析的步骤,那么什么时候放进BeanFactory的容器呢? 刚刚我们只是放进了 AbstractBeanDefinitionReader 的注册容器中。因此我们要根据BeanFactory 的设计来实现如何构建成一个真正能用的Bean呢?因为刚才的哪些Bean只是一些Bean的信息。没有我们真正业务需要的Bean。

2. 初始化我们需要的Bean(不是Bean定义)并且实现依赖注入

我们知道Bean定义是不能干活的,只是一些Bean的信息,就好比一个人,BeanDefinition 就相当你在公安局的档案,但是你人不在公安局,可只要公安局拿着你的档案就能找到你。就是这样一个关系。

那我们就根据BeanFactory的设计来设计一个抽象类 AbstractBeanFactory。

package cn.thinkinjava.myspring.factory;

import cn.thinkinjava.myspring.BeanDefinition;

import java.util.HashMap;

/**

* 一个抽象类, 实现了 bean 的方法,包含一个map,用于存储bean 的名字和bean的定义

*

* @author stateis0

*/

public abstract class AbstractBeanFactory implements BeanFactory {

/**

* 容器

*/

private HashMap map = new HashMap<>();

/**

* 根据bean的名称获取bean, 如果没有,则抛出异常 如果有, 则从bean定义对象获取bean实例

*/

@Override

public Object getBean(String name) throws Exception {

BeanDefinition beandefinition = map.get(name);

if (beandefinition == null) {

throw new IllegalArgumentException("No bean named " + name + " is defined");

}

Object bean = beandefinition.getBean();

if (bean == null) {

bean = doCreate(beandefinition);

}

return bean;

}

/**

* 注册 bean定义 的抽象方法实现,这是一个模板方法, 调用子类方法doCreate,

*/

@Override

public void registerBeanDefinition(String name, BeanDefinition beandefinition) throws Exception {

Object bean = doCreate(beandefinition);

beandefinition.setBean(bean);

map.put(name, beandefinition);

}

/**

* 减少一个bean

*/

abstract Object doCreate(BeanDefinition beandefinition) throws Exception;

}package cn.thinkinjava.myspring.factory;

import cn.thinkinjava.myspring.BeanDefinition;

import cn.thinkinjava.myspring.PropertyValue;

import cn.thinkinjava.myspring.BeanReference;

import java.lang.reflect.Field;

/**

* 实现自动注入和递归注入(spring 的标准实现类 DefaultListableBeanFactory 有 1810 行)

*

* @author stateis0

*/

public class AutowireBeanFactory extends AbstractBeanFactory {

/**

* 根据bean 定义创建实例, 并将实例作为key, bean定义作为value存放,并调用 addPropertyValue 方法 为给定的bean的属性进行注入

*/

@Override

protected Object doCreate(BeanDefinition beandefinition) throws Exception {

Object bean = beandefinition.getBeanclass().newInstance();

addPropertyValue(bean, beandefinition);

return bean;

}

/**

* 给定一个bean定义和一个bean实例,为给定的bean中的属性注入实例。

*/

protected void addPropertyValue(Object bean, BeanDefinition beandefinition) throws Exception {

// 循环给定 bean 的属性集合

for (PropertyValue pv : beandefinition.getPropertyValues().getPropertyValues()) {

// 根据给定属性名称获取 给定的bean中的属性对象

Field declaredField = bean.getClass().getDeclaredField(pv.getname());

// 设置属性的访问权限

declaredField.setAccessible(true);

// 获取定义的属性中的对象

Object value = pv.getvalue();

// 判断这个对象是否是 BeanReference 对象

if (value instanceof BeanReference) {

// 将属性对象转为 BeanReference 对象

BeanReference beanReference = (BeanReference) value;

// 调用父类的 AbstractBeanFactory 的 getBean 方法,根据bean引用的名称获取实例,此处即是递归

value = getBean(beanReference.getName());

}

// 反射注入bean的属性

declaredField.set(bean, value);

}

}

}

可以看到 doCreate 方法使用了反射创建了一个对象,并且还需要对该对象进行属性注入,如果属性是 ref 类型,那么既是依赖关系,则需要调用 getBean 方法递归的去寻找那个Bean(因为最后一个Bean 的属性肯定是基本类型)。这样就完成了一次获取实例化Bean操作,并且也实现类依赖注入。

4. 总结

我们通过这些代码实现了一个简单的 IOC 依赖注入的功能,也更加了解了 IOC, 以后遇到Spring初始化的问题再也不会手足无措了。直接看源码就能解决。哈哈

good luck !!!

觉得不错,别忘了随手点赞+转发哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值