父组件内部接口调用 或 父组件加载 完后再加载子组件;控制子组件的加载时机

目录

一、问题

二、解决方法

1.v-if

三、总结


一、问题

1.场景一:一个父组件有多个子组件,且子组件都需要取同一个配置项,为了复用代码和减少接口调用次数,直接在 父组件中调用一次接口取配置项后传入子组件,在子组件中直接使用即可

但是 发现在子组件中取值时,取到的值 是有时候是空的,有时候又是正确的。------间接的异步问题:子组件取值时,父组件的接口没有可能没有调用完。————所以需要先调用完父组件内部接口再 加载子组件。

二、解决方法

1.v-if

1)子组件加上 v-if=" showSon"

2)父组件内部接口调用完成后加载子组件:父组件定义变量 showSon,接口调用前 showSon赋值 false;接口调用完成后赋值 true;

3)父组件加载完后再加载子组件:父组件定义变量showSon赋值 false, 在mounted或onMounted(组合式API)中赋值为 true

4)伪代码如下


<template>
    <div>
        <Son v-if="showSon"></Son>
    </div>
</template>
<script>
    import api from '@/api/nursingAPI.js'
    import Son from './Son.vue'
    export default {
        data(){
            return{
                showSon:false,
            }
        },
        mounted() {
            api()
            .then((result)=>{
                // 接口调用后处理可以写在这里
                // 接口调用后再加载子组件 son
                showSon=true;
            })
        },
        components:{
            Son
        }
    }
</script>

2.待解锁

三、总结

父组件加载完后再加载子组件的方法:

1.使用v-if="isShow"控制 子组件的加载,在不需要加载子组件时,将isShow设为true;在需要加载子组件时将 isShow设为false。

/*

希望对你有帮助!

如有错误,欢迎指正,非常感谢!

*/

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flex企业应用开发实战源代码 本书由业界4位拥有近10年企业级应用开发经验的资深专家撰写,权威性毋庸置疑。 全书共分为4大部分,涵盖企业级应用开发的整个生命周期。首先介绍了Flex/Flash的工作机理和利用Flex开发企业级应用必须掌握的基础知识和核心元素;接着剖析了Flex与Java的通信机制,以及Flex企业应用的客户端架构和服务器端架构;再接着详细讲解了BlazeDS框架的使用方法和工作原理,并通过迭代的方式完整地演示一个真实的Flex企业级应用的开发全过程,实战性极强;最后重点探讨Flex应用性能优化等方面的高级知识。值得一提的是,本书公开了作者努力开发出来的、经实际项目检验过的Flex企业应用框架的源代码,具有非常高的参考价值。 本书对Flex的基础知识着墨不多,所有内容都围绕Flex+Java这一企业级应用开发技术展开,侧重于对Flex技术原理、企业级应用的架构思想和实现方法的讲解,所以书中的内容不会随着Flex版本的升级而过时。 第1章 Flex企业应用开发入门 1 1.1 B/S企业应用开发者的噩梦 1 1.2 噩梦结束,新时代来临 2 1.3 Flex概述 6 1.4 Flash Player的工作模型 7 1.4.1 Flash Player的帧执行模型 7 1.4.2 Flash Player渲染模型 9 1.4.3 Flash中的事件机制 12 1.5 小结 20 第2章 Flex企业应用开发基础 21 2.1 MXML语言 21 2.1.1 用MXML表示ActionScript对象 22 2.1.2 查看由MXML文件所翻译的ActionScript代码 24 2.1.3 IMXMLObject接口 25 2.2 客户端保持状态 28 2.3 客户端MVC 30 .2.4 数据绑定 34 2.4.1 实现数据绑定的方法 35 2.4.2 数据绑定发生的时机 37 2.4.3 可以用于数据绑定的属性 39 2.4.4 数据绑定的用途 40 2.4.5 使用Bindable元数据标记 40 2.4.6 绑定到函数、对象和数组 43 2.4.7 使用可绑定属性链进行工作 46 2.5 Flex中的组件化编程 46 2.5.1 认识Flex组件组件容器 47 2.5.2 组件生命周期与布局 51 2.5.3 组件的失效机制 59 2.5.4 使用ActionScript创建自定义组件 62 2.6 异步调用 85 2.6.1 异步调用导致模型数据不一致 85 2.6.2 异步调用导致用户反复(误)操作 86 2.7 小结 87 第3章 Flex企业应用开发中的主要元素 88 3.1 企业应用的特点 88 3.2 Flex Application 89 3.2.1 SystemManager 89 3.2.2 Preloader 90 3.2.3 Application的作用和主要属性 95 3.2.4 通过SWFLoader加载Application 97 3.2.5 域及跨域访问 99 3.3 Flex Module 101 3.3.1 创建模块 102 3.3.2 模块的编辑与编译 104 3.3.3 模块文件的加载 104 3.3.4 主应用和模块的交互 107 3.4 Flex库文件SWC 107 3.5 Flex编译模式、链接模式与RSL 109 3.5.1 使用系统RSL—Framework 110 3.5.2 其他的系统RSL 111 3.5.3 RSL的摘要 112 3.5.4 使用私有RSL 113 3.6 小结 114 第4章 Flex on Java企业应用架构 115 4.1 分布式的异构系统所带来的挑战与解决方案 116 4.1.1 确定Flex客户端系统和Java服务器端通信框架 117 4.1.2 保持客户端系统与服务器端系统的数据一致性 122 4.2 大规模企业应用面临的挑战和应采取的解决方案 133 4.2.1 问题和需求 133 4.2.2 解决方案 134 4.3 Flex客户端工程路径规划 137 4.4 小结 138 第5章 BlazeDS框架详解 139 5.1 BlazeDS介绍 139 5.1.1 什么是BlazeDS 139 5.1.2 使用BlazeDS的原因 140 5.1.3 BlazeDS功能特色 140 5.2 BlazeDS体系结构 141 5.2.1 BlazeDS的组件 143 5.2.2 构建一个简单的BlazeDS应用 148 5.2.3 消息的传输过程 150 5.2.4 BlazeDS的序列化机制 156 5.2.5 FlexClient和FlexSession 161 5.3 BlazeDS的配置 162 5.3.1 配置MessageBrokerServlet 162 5.3.2 services-config.xml 163 5.3.3 Channel和Endpoint 165 5.3.4 Service、Adapter和Destination 166 5.3.5 Logging 167 5.3.6 Security 169 5.3.7 运行期配置 170 5.4 BlazeDS的整合 175 5.5 小结 177 第6章 Flex+BlazeDS应用开发实例 178 6.1 需求分析 178 6.2 数据库设计 179 6.3 用Flash Builder配置企业应用开发环境 180 6.3.1 创建一个Flash Builder工程 180 6.3.2 修改Flex SDK包文件链接类型 184 6.3.3 创建一个Flex Library工程 185 6.4 客户端人机界面设计 186 6.5 服务器端配置 187 6.6 服务器端实现 191 6.7 客户端实现 198 6.7.1 客户端数据模型 199 6.7.2 客户端Proxy及工具类 201 6.7.3 客户端界面及界面逻辑 204 6.7.4 复杂查询实现 220 6.8 小结 227 第7章 将范例应用改进为真正的企业应用 228 7.1 简化Flex RemoteObject的异步调用 228 7.2 如何使用户等待远程调用的结束 243 7.3 加入简单的身份认证功能 251 7.4 使Flex应用脱离开发环境部署到运行环境 260 7.4.1 存在的问题 260 7.4.2 封装BlazeDS及封装后的系统调用方式 260 7.4.3 封装RemoteObject及封装后的调用方式 263 7.5 小结 264 第8章 Flex打印 265 8.1 Web打印方案 265 8.1.1 原生打印 265 8.1.2 宿主打印 265 8.1.3 外部打印 266 8.2 为什么使用Flex来打印 267 8.3 使用PrintJob打印 267 8.4 深入了解PrintJob 268 8.5 实战PrintJob—实现一个票据打印组件 270 8.5.1 组件结构 270 8.5.2 IReceiptPrinter和 DefaultReceiptPrinter 271 8.5.3 ReceiptTemplate 273 8.5.4 TemplateDescriptor 274 8.5.5 TemplateLoader 276 8.5.6 为组件编写示例 281 8.6 小结 282 第9章 Flex企业应用性能优化 283 9.1 Flex的内存垃圾回收机理 283 9.1.1 引用计数法 284 9.1.2 标记清除法 285 9.1.3 垃圾回收的时机 286 9.1.4 强制执行垃圾回收的技巧 286 9.2 导致内存泄漏的常见情况 287 9.3 Flash Builder的内存泄漏分析工具 291 9.4 用于性能查看的Flash的系统API 293 9.5 其他有关内存泄漏的问题 293 9.6 有关提高Flex应用性能的技巧 294 9.7 小结 299 第10章 Flex企业应用开发的其他技术 300 10.1 与网页进行交互 300 10.1.1 通过参数接受外部信息 300 10.1.2 使用navigateToURL方法调用JavaScript 302 10.1.3 ExternalInterface API 303 10.2 在Flex中使用Flash CS所开发的控件 306 10.3 Flex访问.Net 308 10.3.1 Fluorine的安装和配置 309 10.3.2 Flex通过Fluorine访问.NET 311 10.4 小结 314 附录 Flex Builder 3中的项目如何迁移至Flash Builder 4 315
第1章 Java概述 1 1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6 1.3.2 Java程序的运行机制和JVM 6 1.4 开发Java的准备 7 1.4.1 安装JDK 8 学生提问:不是说JVM是运行Java程序的虚拟机吗?那JRE和JVM的关系是怎样的呢? 8 学生提问:为什么不安装公共JRE系统呢? 9 1.4.2 设置PATH环境变量 10 学生提问:为什么选择设置用户变量,用户变量和系统变量有什么区别呢? 11 1.5 第一个Java程序 12 1.5.1 编辑Java源代码 12 1.5.2 编译Java程序 13 学生提问:当我们使用编译C程序时,不仅需要指定存放目标文件的位置,也需要指定目标文件的文件名,这里使用javac编译Java程序时怎么不需要指定目标文件的文件名呢? 13 1.5.3 运行Java程序 14 1.5.4 根据CLASSPATH环境变量定位类 15 1.6 Java程序的基本规则 16 1.6.1 Java程序的组织形式 16 1.6.2 Java源文件的命名规则 17 1.6.3 初学者容易犯的错误 18 1.7 垃圾回收机制 20 1.8 何时开始使用IDE工具 21 学生提问:老师,我想学习Java编程,到底是学习Eclipse好呢,还是学习JBuilder好呢? 21 1.9 本章小结 22 本章练习 22 第2章 理解面向对象 23 2.1 面向对象 24 2.1.1 结构化程序设计简介 24 2.1.2 程序的三种基本结构 25 2.1.3 面向对象程序设计简介 27 2.1.4 面向对象的基本特征 28 2.2 UML(统一建模语言)介绍 29 2.2.1 用例图 30 2.2.2 类图 31 2.2.3 组件图 33 2.2.4 部署图 33 2.2.5 顺序图 34 2.2.6 活动图 34 2.2.7 状态机图 36 2.3 Java的面向对象特征 36 2.3.1 一切都是对象 37 2.3.2 类和对象 37 2.4 本章小结 37 第3章 数据类型和运算符 38 3.1 注释 39 3.1.1 单行注释和多行注释 39 3.1.2 文档注释 40 学生提问:API文档是什么? 40 学生提问:为什么要掌握查看API文档的方法? 42 3.2 标识符和关键字 45 3.2.1 分隔符 45 3.2.2 标识符规则 47 3.2.3 Java关键字 47 3.3 数据类型分类 48 3.4 基本数据类型 48 3.4.1 整型 49 3.4.2 字符型 49 3.4.3 浮点型 51 3.4.4 布尔型 52 3.5 基本类型的类型转换 53 3.5.1 自动类型转换 53 3.5.2 强制类型转化 54 3.5.3 表达式类型的自动提升 56 3.6 直接量 57 3.6.1 直接量的类型 57 3.6.2 直接量的赋值 58 3.7 运算符 58 3.7.1 算术运算符 58 3.7.2 赋值运算符 61 3.7.3 位运算符 62 3.7.4 扩展后的赋值运算符 64 3.7.5 比较运算符 65 学生提问:Java为什么要对这些数据进行缓存呢? 67 3.7.6 逻辑运算符 67 3.7.7 三目运算符 68 3.7.8 运算符的结合性和优先级 69 3.8 本章小结 70 本章练习 70 第4章 流程控制和数组 71 4.1 顺序结构 72 4.2 分支结构 72 4.2.1 if条件语句 72 4.2.2 switch分支语句 76 4.3 循环结构 78 4.3.1 while循环语句 78 4.3.2 do while循环语句 79 4.3.3 for循环 80 4.3.4 嵌套循环 83 4.4 控制循环结构 84 4.4.1 使用break结束循环 84 4.4.2 使用continue结束本次循环 86 4.4.3 使用return结束方法 87 4.5 数组类型 87 4.5.1 理解数组:数组也是一种类型 88 学生提问:int[]是一种类型吗?怎么使用这种类型呢? 88 4.5.2 定义数组 88 4.5.3 数组的初始化 89 学生提问:能不能只分配内存空间,不赋初始值呢?89 4.5.4 使用数组 90 学生提问:为什么要我记住这些异常信息? 91 4.5.5 JDK1.5提供了foreach循环 91 4.6 深入数组 93 4.6.1 内存中的数组 93 学生提问:为什么有栈内存和堆内存之分? 93 4.6.2 基本类型数组的初始化 95 4.6.3 引用类型数组的初始化 96 4.6.4 没有多维数组 99 学生提问:我是否可以让图4.13中灰色覆盖的数组元素再次指向另一个数组?这样不可以扩展成三维数组吗?甚至扩展到更多维的数组? 101 4.6.5 操作数组的工具类 102 4.6.6 数组的应用举例 103 4.7 本章小结 106 本章练习 106 第5章 面向对象(上) 107 5.1 类和对象 108 5.1.1 定义类 108 学生提问:构造器不是没有返回值吗?为什么不能用void修饰呢? 110 5.1.2 对象的产生和使用 110 5.1.3 对象、引用和指针 111 5.1.4 对象的this引用 112 5.2 方法详解 116 5.2.1 方法的所属性 116 5.2.2 方法的参数传递机制 116 5.2.3 形参长度可变的方法 120 5.2.4 递归方法 121 5.2.5 方法重载 123 学生提问:为什么方法的返回值类型不能用于区分重载的方法? 124 5.3 成员变量和局部变量 124 5.3.1 成员变量和局部变量 125 5.3.2 成员变量的初始化和内存中的运行机制 128 5.3.3 局部变量的初始化和内存中的运行机制 130 5.3.4 变量的使用规则 130 5.4 隐藏和封装 132 5.4.1 理解封装 132 5.4.2 使用访问控制符 132 5.4.3 package和import 135 5.4.4 Java的常用包 140 5.5 深入构造器 140 5.5.1 使用构造器执行初始化 141 学生提问:构造器是创建Java对象的途径,是不是说构造器完全负责创建Java对象? 141 5.5.2 构造器的重载 142 学生提问:为什么要用this来调用另一个重载的构造器?我把另一个构造器里的代码复制、粘贴到这个构造器里不就可以了吗? 143 5.6 类的继承 144 5.6.1 继承的特点 144 5.6.2 重写类的方法 145 5.6.3 类实例的super引用 146 学生提问:我们只是创建了一个Ostrich对象时,哪来的Bird对象? 147 5.6.4 调用类构造器 148 学生提问:为什么我创建Java对象时从未感觉到java.lang.Object的构造器被调用过? 150 5.7 多态 151 5.7.1 多态性 151 5.7.2 引用变量的强制类型转换 152 5.7.3 instanceof运算符 154 5.8 继承与组合 154 5.8.1 使用继承的注意点 155 5.8.2 利用组合实现复用 156 学生提问:使用组合关系来实现复用时,需要创建两个Animal对象,是不是意味着使用组合关系时系统开销更大? 159 5.9 初始化块 159 5.9.1 使用初始化块 160 5.9.2 初始化块和构造器 161 5.9.3 静态初始化块 162 5.10 本章小结 165 本章练习 165 第6章 面向对象(下) 166 6.1 基本数据类型的包装类 167 6.2 处理对象 170 6.2.1 打印对象和toString方法 170 6.2.2 ==和equals比较运算符 172 6.3 类成员 175 6.3.1 理解类成员 175 6.3.2 单例(Singleton)类 176 6.4 final修饰符 177 6.4.1 final变量 177 6.4.2 final方法 181 6.4.3 final类 182 6.4.4 不可变类 182 6.4.5 缓存实例的不可变类 186 6.5 抽象类 188 6.5.1 抽象方法和抽象类 188 6.5.2 抽象类的作用 191 6.6 更彻底的抽象:接口 192 6.6.1 接口的概念 192 6.6.2 接口的定义 193 6.6.3 接口的继承 195 6.6.4 使用接口 195 6.6.5 接口和抽象类 197 6.6.6 面向接口编程 198 6.7 内部类 202 6.7.1 非静态内部类 202 学生提问:非静态内部类对象和外部类对象的关系是怎样的? 206 6.7.2 静态内部类 207 学生提问:为什么静态内部类实例方法也不能访问外部类的实例属性呢? 207 学生提问:接口里是否能定义内部接口? 208 6.7.3 使用内部类 208 学生提问:既然内部类是外部类的成员,是否可以为外部类定义类,在类中再定义一个内部类来重写其类中的内部类? 211 6.7.4 局部内部类 211 6.7.5 匿名内部类 212 6.7.6 闭包(Closure)和回调 215 6.8 枚举类 217 6.8.1 手动实现枚举类 217 6.8.2 枚举类入门 219 6.8.3 枚举类的属性、方法和构造器 220 6.8.4 实现接口的枚举类 223 6.8.5 包含抽象方法的枚举类 224 6.9 对象与垃圾回收 225 6.9.1 对象在内存中的状态 226 6.9.2 强制垃圾回收 227 6.9.3 finalize方法 228 6.9.4 对象的软、弱和虚引用 230 6.10 修饰符的适用范围 233 6.11 使用JAR文件 234 6.11.1 jar命令详解 235 6.11.2 创建可执行的JAR包 237 6.11.3 关于JAR包的技巧 238 6.12 本章小结 239 本章练习 239 第7章 Java集合 240 7.1 Java集合概述 241 7.2 Collection和Iterator接口 243 7.2.1 使用Iterator接口遍历集合元素 244 7.2.2 使用foreach循环遍历集合元素 246 7.3 Set接口 247 7.3.1 HashSet类 247 学生提问:hashCode方法对于HashSet的作用是什么? 249 7.3.2 TreeSet类 252 7.3.3 EnumSet类 259 7.4 List接口 261 7.4.1 List接口和ListIterator接口 261 7.4.2 ArrayList和Vector实现类 264 7.4.3 固定长度的List 266 7.5 Queue接口 266 7.5.1 LinkedList实现类 266 7.5.2 PriorityQueue实现类 269 7.6 Map 270 7.6.1 HashMap和Hashtable实现类 271 7.6.2 SortedMap接口和TreeMap实现类 276 7.6.3 WeakHashMap实现类 279 7.6.4 IdentityHashMap实现类 280 7.6.5 EnumMap实现类 281 7.7 HashSet和HashMap的性能选项 282 7.8 操作集合的工具类:Collections 283 7.8.1 排序操作 283 7.8.2 查找,替换操作 287 7.8.3 同步控制 288 7.8.4 设置不可变集合 288 7.9 烦琐的接口:Enumeration 289 7.10 本章小结 290 本章练习 290 第8章 泛型 291 8.1 泛型入门 292 8.1.1 编译时不检查类型的异常 292 8.1.2 手动实现编译时检查类型 293 8.1.3 使用泛型 294 8.2 深入泛型 294 8.2.1 定义泛型接口、类 295 8.2.2 从泛型类派生类 296 8.2.3 并不存在泛型类 298 8.3 类型通配符 298 8.3.1 使用类型通配符 300 8.3.2 设定类型通配符的上限 300 8.3.3 设定类型形参的上限 302 8.4 泛型方法 303 8.4.1 定义泛型方法 303 8.4.2 泛型方法和类型通配符的区别 306 8.4.3 设定通配符的下限 307 8.4.4 泛型方法与方法重载 309 8.5 擦除和转换 310 8.6 泛型与数组 311 8.7 本章小结 313 第9章 与运行环境交互 314 9.1 与用户互动 315 9.1.1 运行Java程序的参数 315 9.1.2 使用Scanner获取键盘输入 316 9.1.3 使用BufferedReader获取键盘输入 318 9.2 系统相关 319 9.2.1 System类 319 9.2.2 Runtime类 321 9.3 常用类 322 9.3.1 Object类 322 9.3.2 String、StringBuffer和StringBuilder类 322 9.3.3 Math类 327 9.3.4 Random类 328 9.3.5 BigDecimal类 330 9.4 处理日期的类 333 9.4.1 Date类 333 9.4.2 Calendar类 334 9.4.3 TimeZone类 337 9.5 正则表达式 338 9.5.1 创建正则表达式 338 9.5.2 使用正则表达式 341 9.6 程序国际化 345 9.6.1 Java国际化的思路 346 9.6.2 Java支持的语言和国家 346 9.6.3 完成程序国际化 347 9.6.4 使用MessageFormat处理包含占位符的字符串 349 9.6.5 使用类文件代替资源文件 350 9.6.6 使用NumberFormat格式化数字 350 9.6.7 使用DateFormat格式化日期 352 9.7 本章小结 355 本章练习 355 第10章 异常处理 356 10.1 异常概述 357 10.2 异常处理机制 358 10.2.1 使用try...catch捕获异常 359 10.2.2 异常类的继承体系 360 10.2.3 访问异常信息 363 10.2.4 使用finally回收资源 364 10.2.5 异常处理的嵌套 367 10.3 Checked异常和Runtime异常体系 367 10.3.1 使用throws声明抛出异常 367 10.4 使用throw抛出异常 369 10.4.1 抛出异常 369 10.4.2 自定义异常类 371 10.4.3 catch和throw同时使用 371 10.4.4 异常链 373 10.5 Java的异常跟踪栈 374 10.6 异常处理规则 376 10.6.1 不要过度使用异常 377 10.6.2 不要使用过于庞大的try块 378 10.6.3 避免使用Catch All语句 378 10.6.4 不要忽略捕获到的异常 379 10.7 本章小结 379 本章练习 379 第11章 AWT编程 380 11.1 GUI(图形用户界面)和AWT 381 11.2 AWT容器 382 11.3 布局管理器 385 11.3.1 FlowLayout布局管理器 386 11.3.2 BorderLayout布局管理器 387 学生提问:BorderLayout最多只能放置5个组件吗?那它还有什么作用? 388 11.3.3 GridLayout布局管理器 389 11.3.4 GridBagLayout布局管理器 390 11.3.5 CardLayout布局管理器 393 11.3.6 绝对定位 395 11.3.7 BoxLayout布局管理器 396 学生提问:图11.15和图11.16显示的所有按钮都紧挨在一起,如果希望像FlowLayout、GridLayout等布局管理器指定组件的间距该怎么办? 397 11.4 AWT 常用组件 398 11.4.1 基本组件 398 11.4.2 对话框 400 11.5 事件处理 403 11.5.1 Java事件模型的流程 403 11.5.2 事件和事件监听器 405 11.5.3 事件适配器 409 11.5.4 事件监听器的实现形式 411 11.6 AWT的菜单 413 11.6.1 菜单条、菜单和菜单项 414 11.6.2 右键菜单 416 学生提问:为什么即使我没有给多行文本域编写右键菜单,但当我在多行文本域上单击右键时一样会弹出右键菜单? 418 11.7 在AWT中绘图 418 11.7.1 画图的实现原理 418 11.7.2 使用Graphics类 419 11.8 处理位图 425 11.8.1 Image抽象类和BufferedImage实现类 425 11.8.2 使用ImageIO输入/输出位图 427 11.9 剪贴板 432 11.9.1 数据传递的类和接口 432 11.9.2 传递文本 433 11.9.3 使用系统剪贴板传递图像 435 11.9.4 使用本地剪贴板来传递对象引用 439 11.9.5 通过系统剪贴板传递Java对象 442 11.10 本章小结 445 本章练习 445 第12章 Swing编程 446 12.1 Swing概述 447 12.2 Swing基本组件的用法 448 12.2.1 Swing组件层次 448 12.2.2 AWT组件的Swing实现 449 学生提问:为什么单击Swing多行文本域时不是弹出像AWT多行文本域中的右键菜单? 455 12.2.3 使用JToolBar创建工具条 458 12.2.4 使用JColorChooser和JFileChooser 461 12.2.5 使用JOptionPane 469 12.3 Swing中的特殊容器 475 12.3.1 使用JSplitPane 475 12.3.2 使用JTabbedPane 477 12.3.3 使用JLayeredPane、JDesktopPane和JInternalFrame 481 12.4 Swing拖放功能 490 12.4.1 拖放目标 491 12.4.2 拖放源 494 12.4.3 简化的拖放操作 495 12.5 Applet和JApplet 497 12.5.1 Applet简介及其安全性 498 12.5.2 开发Applet类 498 12.5.3 使用HTML页面装载Applet 500 12.5.4 appletviewer简介 502 12.5.5 Applet的生命周期和动画机制 503 学生提问:程序中重写paint方法时绘制了一个字符串,但图12.28上则绘制出了如此之多的字符串,这是为什么呢? 505 12.5.6 使用Applet创建音乐播放器 505 12.6 使用JProgressBar、ProgressMonitor和BoundedRangeModel创建进度条 508 12.6.1 创建进度条 508 12.6.2 创建进度对话框 512 12.7 使用JSlider和BoundedRangeModel创建滑动条 513 12.8 使用JSpinner和SpinnerModel创建微调控制器 517 12.9 使用JList、JComboBox创建列表框 520 12.9.1 简单列表框 520 12.9.2 不强制存储列表项的ListModel和ComboBoxModel 524 12.9.3 强制存储列表项的DefaultListModel和DefaultComboBoxModel 528 学生提问:为什么JComboBox提供了添加、删除列表项目的方法?而JList没有提供添加、删除列表项的方法呢? 530 12.9.4 使用ListCellRenderer改变列表项外观 530 12.10 使用JTree和TreeModel创建树 532 12.10.1 创建树 533 12.10.2 拖动、编辑树节点 536 12.10.3 监听节点事件 540 12.10.4 使用TreeCellRendering改变节点外观 542 12.11 使用JTable和TableModel创建表格 549 12.11.1 创建JTable 549 学生提问:我们指定的表格数据、表格列标题都是Object类型的数组,JTable如何显示这些Object对象?550 12.11.2 TableModel和监听器 555 12.11.3 TableColumnModel和监听器 561 12.11.4 实现排序 564 12.11.5 绘制单元格内容 567 12.11.6 编辑单元格内容 570 12.12 使用JFormattedTextField和JTextPane创建格式文本 576 12.12.1 监听Document的改变 576 12.12.2 使用JPasswordField 578 12.12.3 使用JFormattedTextField 579 12.12.4 使用JEditorPane 588 12.12.5 使用JTextPane 588 12.13 本章小结 595 本章练习 595 第13章 JDBC编程 596 13.1 JDBC基础 597 13.1.1 JDBC简介 597 13.1.2 JDBC驱动程序类型 598 13.2 SQL语法介绍 599 13.2.1 安装数据库 599 13.2.2 关系数据库基本概念和MySQL基本命令 601 13.2.3 SQL语句基础 603 13.2.4 DDL语句 604 13.2.5 DML语句语法 618 13.2.6 select语句和SQL函数 620 13.2.7 分组和组函数 627 13.2.8 多表连接查询和查询 629 13.3 JDBC的典型用法 635 13.3.1 JDBC常用接口和类简介 635 13.3.2 JDBC编程步骤 637 学生提问:前面给出的仅是MySQL和Oracle两种数据库的驱动,我看不出驱动类字符串有什么规律啊。如果我希望使用其他数据库,我怎么用其他数据库的驱动类呢?637 13.4 执行SQL语句的方式 639 13.4.1 使用executeUpdate执行DDL和DML语句 640 13.4.2 使用execute方法执行SQL语句 642 13.4.3 使用PreparedStatement执行SQL语句 644 13.4.4 使用CallableStatement调用存储过程 650 13.5 管理结果集 652 13.5.1 可滚动、可更新的结果集 652 13.5.2 处理Blob类型数据 654 13.5.3 使用ResultSetMetaData分析结果集 660 13.6 事务处理 663 13.6.1 事务的概念和MySQL事务支持 663 13.6.2 JDBC的事务支持 665 13.6.3 批量更新 667 13.7 分析数据库信息 668 13.7.1 使用DatabaseMetaData分析数据库信息 668 13.7.2 使用系统表分析数据库信息 670 13.7.3 选择合适的分析方式 671 13.8 使用连接池管理连接 671 13.9 本章小结 673 本章练习 673 第14章 Annotation(注释) 674 14.1 基本Annotation 675 14.1.1 限定重写类方法:@Override 675 14.1.2 标示已过时:@Deprecated 676 14.1.3 抑制编译器警告:@SuppressWarnings 677 14.2 自定义Annotation 677 14.2.1 定义Annotation 677 14.2.2 提取Annotation的信息 679 14.2.3 使用Annotation的例 680 14.3 JDK的元Annotation 685 14.3.1 使用@Retention 685 14.3.2 使用@Target 686 14.3.3 使用@Documented 686 14.3.4 使用@Inherited 687 14.4 使用APT处理Annotation 688 14.5 本章小结 693 第15章 输入/输出 694 15.1 File类 695 15.1.1 访问文件和目录 695 15.1.2 文件过滤器 697 15.2 理解Java的IO流 698 15.2.1 流的分类 698 15.2.2 流的概念模型 699 15.3 字节流和字符流 700 15.3.1 InputStream和Reader 701 15.3.2 OutputStream和Writer 703 15.4 输入/输出流体系 705 15.4.1 处理流的用法 705 15.4.2 输入/输出流体系 706 15.4.3 转换流 708 学生提问:怎么没有把字符流转换成字节流的转换流呢? 708 15.4.4 推回输入流 710 15.5 重定向标准输入/输出 711 15.6 Java虚拟机读写其他进程的数据 713 15.7 RandomAccessFile 716 15.8 对象序列化 720 15.8.1 序列化的含义和意义 720 15.8.2 使用对象流实现序列化 720 15.8.3 对象引用的序列化 723 15.8.4 自定义序列化 728 15.8.5 另一种自定义序列化机制 733 15.8.6 版本 735 15.9 Java新IO 736 15.9.1 Java新IO概述 736 15.9.2 使用Buffer 737 15.9.3 使用Channel 740 15.9.4 编码集和Charset 743 15.9.5 文件锁 745 15.10 本章小结 747 本章练习 747 第16章 多线程 748 16.1 线程概述 749 16.1.1 线程和进程 749 16.1.2 多线程的优势 750 16.2 线程的创建和启动 750 16.2.1 继承Thread类创建线程类 751 16.2.2 实现Runnable接口创建线程类 752 16.2.3 两种方式所创建线程的对比 754 16.3 线程的生命周期 754 16.3.1 新建和就绪状态 754 16.3.2 运行和阻塞状态 756 16.3.3 线程死亡 757 16.4 控制线程 758 16.4.1 join线程 758 16.4.2 后台线程 759 16.4.3 线程睡眠:sleep 760 16.4.4 线程让步:yield 761 16.4.5 改变线程优先级 762 16.5 线程的同步 764 16.5.1 线程安全问题 764 16.5.2 同步代码块 766 16.5.3 同步方法 767 16.5.4 释放同步监视器的锁定 770 16.5.5 同步锁(Lock) 770 16.5.6 死锁 772 16.6 线程通信 774 16.6.1 线程的协调运行 774 16.6.2 使用条件变量控制协调 778 16.6.3 使用管道流 780 16.7 线程组和未处理的异常 782 16.8 Callable和Future 785 16.9 线程池 787 16.10 线程相关类 789 16.10.1 ThreadLocal类 789 16.10.2 包装线程不安全的集合 791 16.10.3 线程安全的集合类 792 16.11 本章小结 792 第17章 网络编程 793 17.1 网络编程的基础知识 794 17.1.1 网络基础知识 794 17.1.2 IP地址和端口号 795 17.2 Java的基本网络支持 796 17.2.1 使用InetAddress 796 17.2.2 使用URLDecoder和URLEncoder 797 17.2.3 使用URL和URLConnection 798 17.3 基于TCP协议的网络编程 806 17.3.1 TCP协议基础 806 17.3.2 使用ServletSocket创建TCP服务器端 807 17.3.3 使用Socket进行通信 807 17.3.4 加入多线程 810 17.3.5 记录用户信息 813 17.3.6 使用NIO实现非阻塞Socket通信 823 17.4 基于UDP协议的网络编程 829 17.4.1 UDP协议基础 829 17.4.2 使用DatagramSocket发送、接收数据 829 17.4.3 使用MulticastSocket实现多点广播 834 17.5 使用代理服务器 845 17.5.1 直接使用Proxy创建连接 845 17.5.2 使用ProxySelector选择代理服务器 847 17.6 本章小结 849 本章练习 849 第18章 类加载和反射 850 18.1 类的加载、连接和初始化 851 18.1.1 JVM和类 851 18.1.2 类的加载 852 18.1.3 类的连接 853 18.1.4 类的初始化 853 18.1.5 类初始化的时机 854 18.2 类加载器 855 18.2.1 类加载器简介 856 18.2.2 类加载机制 857 18.2.3 创建并使用自定义的类加载器 858 18.2.4 URLClassLoader类 862 18.3 通过反射查看类信息 863 18.3.1 获得Class对象 863 18.3.2 从Class中获取信息 864 18.4 使用反射生成并操作对象 868 18.4.1 创建对象 868 18.4.2 调用方法 870 18.4.3 访问属性值 873 18.4.4 操作数组 874 18.5 使用反射生成JDK动态代理 875 18.5.1 使用Proxy和InvocationHandler创建动态代理 876 18.5.2 动态代理和AOP 878 18.6 反射和泛型 882 18.6.1 泛型和Class类 882 18.6.2 使用反射来获取泛型信息 884 18.7 本章小结 885 本章练习 886
Hello World -- 您的第一个程序 6 C# 程序的一般结构 8 Main() 和命令行自变量 9 命令行自变量 10 显示命令行自变量 12 使用 foreach 存取命令行自变量 13 Main() 传回值 14 数据型别 15 在变量宣告中指定型别 16 转型和型别转换 21 Boxing 和 Unboxing 24 使用 as 和 is 运算符进行安全转型 27 将字节数组转换为 int 29 将 string 转换为 int 30 在十六进制字符串和数字型别间转换 32 数组 34 将数组当做对象 35 一维数组 36 多维数组 36 不规则数组 37 在数组上使用 foreach 39 传递数组当做参数 40 使用 ref 和 out 传递数组 42 隐含型别数组 44 字符串 45 字符串基本概念 46 串连多个字符串 53 修改字符串内容 56 比较字符串 60 分割字符串 65 使用字符串方法搜寻字符串 66 使用正则表达式搜寻字符串 67 判断字符串是否表示数值 70 将 String 转换为 DateTime 71 在旧版编码方式和 Unicode 间转换 72 转换 RTF 为纯文本 74 语句、表达式和运算符 75 语句 76 表达式 81 运算符 83 匿名函式 86 Lambda 表达式 88 在查询中使用 Lambda 表达式 92 在 LINQ 之外使用 Lambda 表达式 94 匿名方法 94 可多载的运算符 97 转换运算符 98 使用转换运算符 99 在结构之间实作用户定义的转换 101 使用运算符多载建立复数类别 103 覆写 Equals() 和运算符 == 的方针 105 类别和结构 108 类别 112 对象 115 结构 118 使用结构 119 继承 122 多型 126 使用 Override 和 New 关键词进行版本控制 132 了解使用 Override 和 New 关键词的时机 135 覆写 ToString 方法 137 抽象和密封类别以及类别成员 138 定义抽象属性 140 静态类别和静态类别成员 144 成员 148 存取修饰词 149 字段 151 常数 153 在 C# 中定义常数 155 属性 156 使用属性 157 接口属性 165 非对称存取的存取范围 168 宣告和使用读取/写入属性 173 自动实作的属性 176 使用自动实作的属性来实作轻量型类别 176 方法 177 传递参数 181 传递实值型别的参数 181 传递参考型别的参数 184 了解传递结构和传递类别参考给方法之间的差异 187 隐含型别局部变量 188 在查询表达式中使用隐含型别局部变量和数组 191 扩充方法 192 实作和呼叫自定义扩充方法 197 建立列举型别的新方法 199 建构函式 200 使用建构函式 201 实例建构函式 204 私用建构函式 209 静态建构函式 211 撰写复制建构函式 213 对象和集合初始化表达式 217 初始化对象但不呼叫建构函式 219 使用集合初始化表达式来初始化字典 220 嵌套类型 221 部分类别和方法 222 限制 224 匿名型别 227 在查询中传回项目属性的集 229 界面 230 明确界面实作 232 明确实作接口成员 234 使用继承明确实作接口成员 236 索引器 239 使用索引器 240 界面中的索引器 244 属性与索引器之间的比较 246 使用委派 250 使用具名和匿名方法委派的比较 253 使用委派取代接口时机 255 委派中的 Covariance 和 Contravariance 256 组合委派 (多播委派) 258 宣告、产生和使用委派 259 事件 264 订阅及取消订阅事件 265 发行符合 .NET Framework 方针的事件 267 在衍生类别中引发基类事件 271 实作界面事件 276 使用字典储存事件实例 280 实作自定义事件存取 283 泛型 284 泛型简介 285 泛型的优点 287 泛型型别参数 289 泛型类别 295 泛型界面 298 泛型方法 304 泛型和数组 306 泛型委派 307 泛型程序代码中的默认关键词 308 C++ 样板和 C# 泛型之间的差异 309 运行时间中的泛型 310 .NET Framework 类别库中的泛型 311 泛型和反映 312 泛型和属性 313 泛型型别中的变异数 314 LINQ 查询表达式 325 查询表达式基本概念 328 在 C# 中撰写 LINQ 查询 336 查询对象集合 339 从方法传回查询 341 将查询的结果储存在内存中 343 使用各种不同方式分组结果 344 将群组包含在群组中 352 针对分组作业执行查询 353 在运行时间动态指定述词筛选条件 362 执行内部联结 364 执行群组联结 372 执行左外部联接 376 排序 Join 句的结果 378 使用复合索引键执行联结 381 执行自定义联结作业 382 处理查询表达式中的 Null 值 387 处理查询表达式中的例外状况 388 Iterator 390 使用 Iterator 392 建立整数清单的 Iterator 区块 394 建立泛型清单的 Iterator 区块 395 命名空间 398 使用命名空间 399 使用命名空间别名限定符 403 使用 My 命名空间 405 可为 Null 的型别 407 使用可为 Null 的型别 409 Box 处理可为 Null 的型别 413 识别可为 Null 的型别 414 从 bool? 安全转型至 bool 415 Unsafe 程序代码和指标 415 固定大小缓冲区 416 使用 Windows ReadFile 函式 417 指标型别 421 指标转换 422 指标表达式 424 取得指针变量值 424 取得变量地址 425 使用指标存取成员 426 使用指针存取数组元素 428 管理指标 429 递增和递减指标 429 指标的算术运算 430 指标比较 431 使用指针复制字节数组 432 XML 文件批注 434 建议使用的文件批注标签 435 处理 XML 档案 448 文件标签的分隔符 453 使用 XML 文件功能 454 应用程序域 458 在其他应用程序域中执行程序代码 459 建立和使用应用程序域 461 组件和全局程序集缓存 461 Friend 组件 462 判断档案是否为组件 465 加载和卸除组件 466 与其他应用程序共享程序集 466 使用属性 468 明示属性目标 470 使用反映存取属性 472 使用属性建立 C/C++ 等位 475 常见属性 476 全局属性 479 集合类别 483 使用 foreach 存取集合类别 484 使用例外状况 489 例外处理 492 建立和掷回例外状况 495 编译程序所产生的例外状况 498 使用 try/catch 处理例外状况 498 使用 finally 执行清除程序代码 499 拦截非 CLS 例外状况 501 文件系统和登录 502 逐一查看目录树状结构 502 取得档案、文件夹和磁盘驱动器的信息 509 建立档案或文件夹 509 写入文本文件 515 从文本文件读取 516 一次一行读取文本文件 (Visual C#) 516 在登录中建立机码 (Visual C#) 517 写入应用程序事件记录文件 (Visual C#) 518 互操作性 518 使用平台调用播放 WAV 檔 520 范例 COM 类别 523 线程 524 使用线程 525 线程同步处理 526 建立和结束线程 530 同步处理产生者和消费者线程 534 使用线程集区 542 反映 545 C# DLL 547 建立和使用 C# DLL 547 安全性 550
你真的了解Ioc与AOP吗? 收藏 你真的了解Ioc与AOP吗?我现在还不是很了解,而且越学习越发现自己了解的很少,Ioc与AOP中蕴涵了大量的能量等待我们去开发。在这个系列 中,我仅仅利用Sping.net这个框架向大家展示一下Ioc与AOP的强大功能(呵呵,其实写这段话的目的是因为“文章题目”牛皮吹得有点大了,给自 己个台阶下罢了)。 在这个系列中一共包含6个案例,从简单到复杂,也是对问题分解、思考和解决的一个过程,它们分别是: (1)类之间的依赖; 降低 (2)接口依赖; (3)基 于配置文件和Reflection的工厂模式; (4)使用Spring.net实现Ioc; (5)Romoting; (6)利用Ioc在不动一行代码的情 况下实现Remoting。为了更好的理解文中的内容,最好顺序阅读。 作为一个应用系统,代码复用至关重要。如果在你的设计中,类与类存在很强的相互关联,那么你会发现在重用这些组件时就存在很严重的问题。在 Step1到Step3-Reflection的例中,我们试图 利用“针对接口编程”以及自己设计的Ioc对系统进行解耦。在Step3到Step5的例中,我们将利用Spring.net提供的Ioc框架,轻松完 成解耦以及系统改造等工作。 一、类之间的依赖 我们的第一个例主要用于说明程序的基本构造,并且作为一个反面典型,引出为什么要解耦,以及如何下手。在这个例中,我们将创建三个程序集,分别是MainApp.exe、HelloGenerator.dll以及SayHello.dll。它们之间的关系如下图所示: HelloGenerator类根据提供的姓名产生一个问候字符串,代码如下: using System; namespace IocInCSharp { public class EnHelloGenerator { public string GetHelloString(string name) { return String.Format("Hello, {0}", name); } } } SayHello类持有一个对EnHelloGenerator的引用,并负责将生成出来的问候字符串打印出来。 using System; namespace IocInCSharp { public class SayHello { private EnHelloGenerator _helloGen; public EnHelloGenerator HelloGenerator { get { return _helloGen; } set { _helloGen = value; } } public void SayHelloTo(string name) { if(_helloGen != null) Console.WriteLine(_helloGen.GetHelloString(name)); else Console.WriteLine("Client.hello is not initialized"); } } } MainApp.exe负责完成对象的创建、组装以及调用工作: using System; namespace IocInCSharp { public class MainApp { public static void Main() { SayHello sayHello = new SayHello(); sayHello.HelloGenerator = new EnHelloGenerator(); sayHello.SayHelloTo("zhenyulu"); } } } 在这个设计中,组件组件之间、类与类之间存在着紧密的耦合关系。SayHello类中的_helloGen字段类型为 EnHelloGenerator,这将导致我们很难给它赋予一个其它的HelloGenerator(例如CnHelloGenerator,用于生成 中文问候语)。另外MainApp也严重依赖于SayHello.dll以及HelloGenerator.dll,在程序中我们可以看到类似new SayHello();new EnHelloGenerator();的命令。 这种紧密的耦合关系导致组件的复用性降低。试想,如果想复用SayHello组件,那么我们不得不连同HelloGenerator一同拷贝过去, 因为SayHello.dll是依赖与HelloGenerator.dll的。解决这个问题的办法就是“针对抽象(接口)”编程 (依赖倒置原则)。这里的抽象既包括抽象类也包括接口。我不想过多的去谈抽象类和接口的区别,在后续的例中我们将使用接口。由于接口在进行“动态代理” 时仍能保持类型信息,而抽象类可能由于代理的原因导致继承关系的“截断”(如MixIn等)。除此之外,对于单继承的C#语言而言,使用接口可以拥有更大 的弹性。 二、接口依赖 既然类之间的依赖导致耦合过于紧密,按照《设计模式》的理论,我们要依赖于接口。但是人们往往发现,仅仅依赖于接口似乎并不能完全解决问题。我们从上面的例中抽象出接口后,组件间的依赖关系可能变成如下图所示: 经过改造后,SayHello不再依赖于具体的HelloGenerator,而是依赖于IHelloGenerator接口,如此一来,我们可以 动态的将EnHelloGenerator或是CnHelloGenerator赋给SayHello,其打印行为也随之发生改变。接口的定义以及改造后 的SayHello代码如下(为了节省空间,将代码合并书写): using System; namespace IocInCSharp { public interface IHelloGenerator { string GetHelloString(string name); } public interface ISayHello { IHelloGenerator HelloGenerator{ get; set; } void SayHelloTo(string name); } public class SayHello : ISayHello { private IHelloGenerator _helloGen; public IHelloGenerator HelloGenerator { get { return _helloGen; } set { _helloGen = value; } } public void SayHelloTo(string name) { if(_helloGen != null) Console.WriteLine(_helloGen.GetHelloString(name)); else Console.WriteLine("Client.hello is not initialized"); } } } 但是我们的MainApp似乎并没有从接口抽象中得到什么好处,从图中看,MainApp居然依赖于三个组件:ICommon.dll、 HelloGenerator.dll以及SayHello.dll。这是由于MainApp在这里负责整体的“装配”工作。如果这三个组件中的任何一个 发生变化,都将导致MainApp.exe的重新编译和部署。从这个角度来看,似乎“针对接口编程”并没有为我们带来太多的好处。 如果能够将“组件装配”工作抽象出来,我们就可以将MainApp的复杂依赖关系加以简化,从而 进一步实现解耦。为此,我们引入“工厂”模式,并利用配置文件和反射技术,动态加载和装配相关组件。 三、基于配置文件和Reflection的工厂模式 为了消除MainApp对其它组件的依赖性,我们引入工厂模式,并且根据配置文件指定的装配规程,利用.net提供的反射技术完成对象的组装工作。 本部分代码仅仅提供一种功能演示,如果实际应用仍需进一步完善(建议使用一些成型的Ioc框架,例如Spring.net或Castle等)。经过改造后 的系统,组件间依赖关系如下图: 可以看出这次实现了真正的“针对接口编程”。所有的组件只依赖于接口。MainApp所需的对象是由工厂根据配置文件动态创建并组装起来的。当系统 需求发生变化时,只需要修改一下配置文件就可以了。而且MainApp、SayHello和HelloGenerator之间不存在任何的依赖关系,实现 了松耦合。 这是如何实现的呢?我们首先要能够解析配置文件中的信息,然后建立包含相关信息的对象。最后根据这些信息利用反射机制完成对象的创建。首先我们看一下配置文件所包含的内容: 从中我们可以看出,我们实现了一个IocInCSharp.ConfigHandler类,用来处理配置文件中IocInCSharp\ objects结点中的内容。ConfigHandler类将根据该结点下的内容处理并创建一ConfigInfo对象(关于ConfigInfo、 ObjectInfo以及PropertyInfo的代码可自行查看源代码,这里就不再赘述)。ConfigHandler类的代码实现如下: using System; using System.Configuration; using System.Xml; namespace IocInCSharp { public class ConfigHandler:IConfigurationSectionHandler { public object Create(object parent, object configContext, System.Xml.XmlNode section) { ObjectInfo info; PropertyInfo propInfo; ConfigInfo cfgInfo = new ConfigInfo(); foreach(XmlNode node in section.ChildNodes) { info = new ObjectInfo(); info.name = node.Attributes["name"].Value; info.assemblyName = node.Attributes["assembly"].Value; info.typeName = node.Attributes["typeName"].Value; foreach(XmlNode prop in node) { propInfo = new PropertyInfo(); propInfo.propertyName = prop.Attributes["name"].Value; propInfo.assemblyName = prop.Attributes["assembly"].Value; propInfo.typeName = prop.Attributes["typeName"].Value; info.properties.Add(propInfo); } cfgInfo.Objects.Add(info); } return cfgInfo; } } } 通过ConfigHandler的解析,我们最终得到一个ConfigInfo实例,Factory就是根据这个实例中所包含的配置信息,利用反射技术对所需对象生成并组装的。SayHelloFactory的代码如下: using System; using System.IO; using System.Configuration; using System.Reflection; namespace IocInCSharp { public class SayHelloFactory { public static object Create(string name) { Assembly assembly; object o = null; object p; string rootPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar; ConfigInfo cfgInfo = (ConfigInfo)ConfigurationSettings.GetConfig("IocInCSharp/objects"); ObjectInfo info = cfgInfo.FindByName(name); if(info != null) { assembly = Assembly.LoadFile(rootPath + info.assemblyName); o = assembly.CreateInstance(info.typeName); Type t = o.GetType(); for(int i=0; i<info.properties.Count; i++) { PropertyInfo prop = (PropertyInfo)info.properties[i]; assembly = Assembly.LoadFile(rootPath + prop.assemblyName); p = assembly.CreateInstance(prop.typeName); t.InvokeMember(prop.propertyName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, o, new Object[] {p}); } } return o; } } } 在上面这段代码中,重点注意三条命令的使用方法: assembly = Assembly.LoadFile(rootPath + prop.assemblyName); p = assembly.CreateInstance(prop.typeName); t.InvokeMember(prop.propertyName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, o, new Object[] {p}); Assembly.LoadFile()用于将外部文件装载进来;assembly.CreateInstance()根据装载进来的程序集创建一指定类型的对象;t.InvokeMember(prop.propertyName, ........BindingFlags.SetProperty, null, o, new Object[] {p})利用反射机制对创建出来的对象设置属性值。 我们的Factory就是利用这种方式根据配置文件动态加载程序集,动态创建对象并设置属性的。有了这个Factory,MainApp中的内容就很简单了: using System; namespace IocInCSharp { public class MainApp { public static void Main() { ISayHello sayHello = (ISayHello)SayHelloFactory.Create("SayHello"); if(sayHello != null) sayHello.SayHelloTo("zhenyulu"); else Console.WriteLine("Got an Error!"); } } } 现在,MainApp只依赖于接口,不再依赖于其它组件,实现了松耦合。在本例中,大家可以尝试将配置文件中的IocInCSharp.CnHelloGenerator更改为IocInCSharp.EnHelloGenerator,看看是否输出内容由中文变为了英文。这便是“注入”的效果。 从上面这个例我们可以看出,通过自定义配置文件和.net中的Reflection技术,我们自己就可以开发Ioc应用,根据配置文件的信息自行 组装相应的对象。但是Reflection编程的技术门槛还是比较高的,并且在实际应用中配置文件的格式、Handler的设计都不是象上面代码那样的简 单。不过幸好我们现在有很多的Ioc容器可供选择,它们都提供了完整的依赖注入方式,并且比自己写代码更加成熟、更加稳定。使用这些框架可以让程序员在三 两行代码里完成“注入”工作。在我们下一个案例中,我们将使用Spring.net实现依赖注入。我们会发现仅仅添加几行代码并更改一下配置文件就可轻松 实现依赖注入。 四、使用Spring.net实现依赖注入 Spring在Java界可是响当当的名字,现在也有.net平台下的Spring框架了,那就是Spring.net。用户可以从http://www.springframework.net/下载到Spring.net的最新版本。本例中使用的版本为“Spring Interim Build August 15, 2005 ”,并对Spring.Services组件中的Remoting部分做了微小调整,删除了代码中用于输出的部分命令。 Spring.net为我们提供了一种基于配置文件的注入方式,目前Spring.net允许将值注入到属性,也允许将一个工厂绑定到属性,工厂的 产品将注入属性;除此之外,Spring.net还允许将一个方法的返回结果绑定到属性;它还可以在绑定之前强制进行初始化。另外Spring.net还 专门针对.net提供了Remoting以及Windows Service的“注入”方式。Spring.AOP允许完成横切(不过目前是调用的是AopAlliance的代码)。 尽管我对基于配置文件的注入方式仍然有些偏见(我认为它很难Debug、难于理解、没有编译时错误校验、编写效率比较低。另外它还存在安全隐患 ,恶意用户可以借助修改配置文件将恶意代码注入系统。因此,Spring.net在Web开发中应当更具优势),但这并不能掩盖Spring.net的光 芒(据说Castle比Spring.net要好,但目前我还没有尝试过使用Castle)。使用Spring.net,我们只需修改两三行代码,并提供 相应配置文件,就可以轻松实现Ioc。应用Spring.net后,我们的系统依赖关系如下图所示:   从图中可以看出,MainApp、SayHello、HelloGenerator之间并不存在任何依赖关系,它们都依赖于抽象出来的接口文件。除 此之外,MainApp还依赖于Spring.net,这使得MainApp可以借助Spring.net实现组件动态创建和组装。 对于原有代码,我们几乎不用作任何调整,唯一需要修改的就是MainApp中的调用方法,代码如下: using System; using System.Configuration; using Spring.Context; namespace IocInCSharp { public class MainApp { public static void Main() { try { IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; ISayHello sayHello = (ISayHello)ctx.GetObject("mySayHello"); sayHello.SayHelloTo("zhenyulu"); } catch (Exception e) { Console.WriteLine(e); } } } } 首先我们要添加对Spring.Context命名空间的引用,然后解析配置文件的"spring/context"结点,得到一 IApplicationContext对象(就好比在上一个例中我们得到的ConfigInfo对象一样),剩下的事情就是向该Context索要相 关的对象了(ISayHello)ctx.GetObject("mySayHello"),其中"mySayHello"由配置文件指定生成方式和注入 方式。 配置文件的内容如下: 注意观察结点,就是由这里定义对象间相互依赖关系的。其中的结点定义了对什么属性执行注入,以及注入的内容是什么()。大家可以尝试将改为,看一看程序执行结果有什么变化来体会Spring.net的Ioc功能。 如果读者读到这里仍然觉得Ioc没有什么的话,那让我们再来看一个更为复杂的例。在当前例中,MainApp通过依赖注入调用了 HelloGenerator的功能,但所有的调用都发生在本地。当前程序是一个地地道道的本地应用程序。现在如果要求在不更改一行代码的情况下,将 HelloGenerator.dll放到另外一台计算机上,MainApp通过远程调用(Remoting)来访问HelloGenerator的功 能。这似乎就有一定的难度了。 这么作并不是没有任何依据,其实Ioc除了可以实现依赖注入外,我们还应当看到它可以将我们从复杂的物理架构中解脱出来,专心于业务代码的开发。系 统开发中关键是逻辑分层。在一个系统不需要Remoting时,开发的系统就是本地应用程序;当需要Remoting时,不用修改任何代码就可以将系统移 植为分布式系统。Ioc使这一切成为可能。Rod Johnson在他的《J2EE without EJB》一书中有着详细的论述,很值得一读。据说该书的中文译本今年九月份出版(呵呵,就是这个月,不过我还没有看到市面上有卖的)。 为了能够更深入的分析在“Remoting”改造过程中我们可能遇到的麻烦,在后续两部分内容中,我们将分别介绍不使用Ioc的Remoting改造以及使用Ioc的改造,并比较两者之间的区别。 五、使用Remoting对原有系统进行改造 如果使用Remoting技术对HelloGenerator进行改造,使其具有分布式远程访问能力,那么在不使用Ioc技术的情况下,我们将会作出如下调整: (1)让HelloGenerator继承自MarshalByRefObject类 如果要让某个对象具有分布式的功能,必须使其继承自MarshalByRefObject,这样才可以具有远程访问的能力。因此我们需要调整EnHelloGenerator和CnHelloGenerator的代码。这里以EnHelloGenerator为例: using System; namespace IocInCSharp { public class EnHelloGenerator : MarshalByRefObject, IHelloGenerator { public string GetHelloString(string name) { return String.Format("Hello, {0}", name); } } } (2)将修改后的HelloGenerator发布出去 在这一步中,我们创建了一个新的Console应用程序RemotingServer,并在其中注册了一个Channel,发布服务并进行监听。代码如下: using System; using System.Configuration; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; namespace IocInCSharp { public class Server { public static void Main() { int port = Convert.ToInt32(ConfigurationSettings.AppSettings["LocalServerPort"]); try { BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = port; props["timeout"] = 2000; HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel); RemotingConfiguration.RegisterWellKnownServiceType( typeof(EnHelloGenerator), "HelloGenerator.soap", WellKnownObjectMode.Singleton); Console.WriteLine("Server started!\r\nPress ENTER key to stop the server..."); Console.ReadLine(); } catch { Console.WriteLine("Server Start Error!"); } } } } (3)全新的客户端调用代码 为了使得客户端MainApp能够调用到远程的对象,我们需要修改它的代码,注册相应的Channel并进行远程访问。修改后的MainApp代码如下: using System; using System.Configuration; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; namespace IocInCSharp { public class MainApp { public static void Main() { //建立连接 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = System.Convert.ToInt32(ConfigurationSettings.AppSettings["ClientPort"]); HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel ); //创建远程对象 ISayHello sayHello = new SayHello(); string RemoteServerUrl = ConfigurationSettings.AppSettings["RemoteServerUrl"]; sayHello.HelloGenerator = (IHelloGenerator)Activator.GetObject(typeof(IHelloGenerator), RemoteServerUrl); sayHello.SayHelloTo("zhenyulu"); } } } 在这段代码中,远程对象的创建是通过(IHelloGenerator)Activator.GetObject(typeof(IHelloGenerator), RemoteServerUrl)实现的。到此为止,我们就完成了对原有系统的Remoting改造。 经过调整后的系统,其组件间相互依赖关系如下图所示: 注意ICommon.dll文件在Client和Server端都有。 在整个调整过程中,我们修改了Server端的EnHelloGenerator以及CnHelloGenerator的代码,Client端的 MainApp也 作了修改,以加入了远程访问机制。那么能不能对原有代码不作任何修改就实现远程访问机制呢?当然可以!不过我们还要请出Sping.net帮助我们实现这 一切。 六、利用Ioc在不修改任何原有代码的情况下实现Remoting 上文我们提到,为了实现对HelloGenerator.dll的分布式调用,我们不得不修改了原有程序的多处代码。那么有没有可能在不动任何原有 代码的情况下,单纯靠添加组件、修改配置文件实现远程访问呢?当然可以。这次我们还是使用Spring.net完成这个工作。 经过调整后的系统组件构成如下图所示: 该方案没有修改“src\Step3”中的任何代码,仅仅通过修改配置文件和添加了若干个组件就实现了远程访问。修改方案如下: (1)使用Proxy模式代理原有HelloGenerator 如果要让某个对象具有分布式的功能,必须使其继承自MarshalByRefObject。但是由于不能修改任何原有代码,所以这次我们只能绕道而 行, 借助Proxy模式代理原有的HelloGenerator。在RemotingServer项目中,我们定义了一个新类 HelloGeneratorProxy继承自MarshalByRefObject,通过委派的方式对原有的HelloGenerator进行调用,代 码如下: using System; namespace IocInCSharp { public class HelloGeneratorProxy : MarshalByRefObject, IHelloGenerator { private IHelloGenerator _helloGen; public IHelloGenerator HelloGenerator { get { return _helloGen; } set { _helloGen = value; } } public string GetHelloString(string name) { if(_helloGen != null) return _helloGen.GetHelloString(name); return null; } } } 仔细观察,我们会发现HelloGeneratorProxy持有一个对IHelloGenerator的引用,该属性是可以Set的,因此我们可 以借助Ioc的威力,通过调整Sping.net的配置文件动态决定远程服务器究竟发布EnHelloGenerator还是 CnHelloGenerator。 (2)发布HelloGeneratorProxy 通过RemotingServer.exe,我们将HelloGeneratorProxy发布出去,客户端实际上调用的是Proxy对象(不用担心,由于“针对接口编程”,客户端只知道它是IHelloGenerator类型对象)。服务器端代码如下: using System; using System.Configuration; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; using Spring.Context; namespace IocInCSharp { public class Server { public static void Main() { int port = Convert.ToInt32(ConfigurationSettings.AppSettings["LocalServerPort"]); try { BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = port; props["timeout"] = 2000; HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); ChannelServices.RegisterChannel(channel); IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; HelloGeneratorProxy proxy = (HelloGeneratorProxy)ctx.GetObject("myHelloGeneratorProxy"); RemotingServices.Marshal(proxy, "HelloGenerator.soap"); Console.WriteLine("Server started!\r\nPress ENTER key to stop the server..."); Console.ReadLine(); } catch { Console.WriteLine("Server Start Error!"); } } } } 注意其中的几条命令: IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; HelloGeneratorProxy proxy = (HelloGeneratorProxy)ctx.GetObject("myHelloGeneratorProxy"); RemotingServices.Marshal(proxy, "HelloGenerator.soap"); 我们使用Ioc向HelloGeneratorProxy注入具体的HelloGenerator对象,并通过 RemotingServices.Marshal(proxy, "HelloGenerator.soap")命令将该实例发布出去。服务器端的配置文件如下: 用户可以尝试将配置文件中更改为,重新启动服务后看看客户端调用结果是什么? (3)客户端实现技术-1 客户端实现起来要麻烦一些。由于不允许修改MainApp中的任何代码,我们必须能够在合适的时机拦截代码运行并创建远程连接,同时确保在Ioc注 入时注入的是远程对象。所有这些工作Sping.net都考虑的很周到。它提供了depends-on属性,允许在执行某一操作前强制执行某段代码。在客 户端的配置文件中,我们可以看到如下的配置选项: ......... 这表示,当我们初始化mySayHello时,要先去调用ForceInit.dll文件中ForceInit类的Init方法。ForceInit是一个新编写的类,其主要目的就是创建并注册一个用于远程通讯的Channel。代码实现如下: using System; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters; namespace IocInCSharp { public class ForceInit { public static void Init() { //建立连接 BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider(); BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider(); serverProvider.TypeFilterLevel = TypeFilterLevel.Full; IDictionary props = new Hashtable(); props["port"] = 8199; props["name"] = "myHttp"; HttpChannel channel = new HttpChannel(props, clientProvider, serverProvider); //获得当前已注册的通道; IChannel[] channels = ChannelServices.RegisteredChannels; //关闭指定名为MyHttp的通道; foreach (IChannel eachChannel in channels) if (eachChannel.ChannelName == "myHttp") ChannelServices.UnregisterChannel(eachChannel); ChannelServices.RegisterChannel(channel); } } } (4)客户端实现技术-2 剩下的工作就是为mySayHello的HelloGenerator注入远程对象。通常情况下我们需要使用 Activator.GetObject方法调用远程对象,不过Spring.net已经将其封装起来,我们只需修改一下配置文件,就可以确保调用到远程 对象。配置文件对应部分如下: ...... IocInCSharp.IHelloGenerator, ICommon http://127.0.0.1:8100/HelloGenerator.soap 借助Spring.Remoting.SaoFactoryObject,我们轻松实现了远程对象访问,不必书写一行代码。(目前SAO在 Spring.net的实现尚不完整,按照Spring.net帮助手册上的做法,通过配置文件只能实现客户端访问远程对象,还做不到服务器端发布远程对 象) (5)使用AOP拦截调用 Sping.net目前已经实现AOP功能,我们可以很容易的对方法进行拦截和调用。需要做的工作就是设计相应的Interceptor,然后修改 配置文件。目前Sping.net使用的AOP功能是AopAlliance的实现,因此代码编写时命名空间引用让人感觉多少有些别扭,不是以Sping 开头。我编写的MethodInterceptor代码如下: using System; using AopAlliance.Intercept; namespace IocInCSharp { class MethodInterceptor : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { Console.WriteLine("Before Method Call..."); object returnValue = invocation.Proceed(); Console.WriteLine("After Method Call..."); return returnValue; } } } 在方法调用前打印"Before Method Call...",在方法调用后打印"After Method Call..."。剩下的工作就是修改配置文件,将其应用到相应的方法上。配置文件片断如下: ...... MethodAdvice 通过以上操作,我们在没有修改任何原有代码的情况下,让原有系统实现了远程分布式访问。 请大家访问示例代码的“bin\Step5"目录,下面有3个目录:Server、Client、WithoutRemoting。首先运行 Server目录下的RemotingServer.exe,然后运行Client目录下的MainApp.exe进行远程调用。系统通过 Remoting完成远程调用。关闭所有程序后,进入到WithoutRemoting目录,里面有个Readme.txt文件,按照操作步骤将文件: ..\Server\HelloGenerator.dll ..\Client\MainApp.exe ..\Client\ICommon.dll ..\Client\SayHello.dll ..\Client\Spring.Core.dll ..\Client\log4net.dll   拷贝到该目录,再次运行MainApp.exe,你会发现它是一个地地道道的本地应用程序!本地与远程唯一的区别就是配置文件的不同以及增加了几个其它的DLL。这正式我们这个示例的价值体现。 到此为止,我们完成了对Ioc应用的一系列模拟。Ioc写得多一些,AOP写得少了点。欢迎大家批评指正。
本人从其他网站上下载的,333页全,看到CSDN上没有完整的版本,所以共享一下。由于文件比较大,压缩成了两个文件上传。 第1章 Flex企业应用开发入门  1.1 B/S企业应用开发者的噩梦  1.2 噩梦结束,新时代来临  1.3 Flex概述  1.4 Flash Player的工作模型   1.4.1 Flash Player的帧执行模型   1.4.2 Flash Player渲染模型   1.4.3 Flash中的事件机制  1.5 小结 第2章 Flex企业应用开发基础  2.1 MXML语言   2.1.1 用MXML表示ActionScript对象   2.1.2 查看由MXML文件所翻译的ActionScript代码   2.1.3 IMXMLObject接口  2.2 客户端保持状态  2.3 客户端MVC  2.4 数据绑定   2.4.1 实现数据绑定的方法   2.4.2 数据绑定发生的时机   2.4.3 可以用于数据绑定的属性   2.4.4 数据绑定的用途   2.4.5 使用Bindable元数据标记   2.4.6 绑定到函数、对象和数组   2.4.7 使用可绑定属性链进行工作  2.5 Flex中的组件化编程   2.5.1 认识Flex组件组件容器   2.5.2 组件生命周期与布局   2.5.3 组件的失效机制   2.5.4 使用ActionScript创建自定义组件  2.6 异步调用   2.6.1 异步调用导致模型数据不一致   2.6.2 异步调用导致用户反复(误)操作  2.7 小结 第3章 Flex企业应用开发中的主要元素  3.1 企业应用的特点  3.2 Flex Application   3.2.1 SystemManager   3.2.2 Preloader   3.2.3 Application的作用和主要属性   3.2.4 通过SWFLoader加载Application   3.2.5 域及跨域访问  3.3 Flex Module   3.3.1 创建模块   3.3.2 模块的编辑与编译   3.3.3 模块文件的加载   3.3.4 主应用和模块的交互  3.4 Flex库文件SWC  3.5 Flex编译模式、链接模式与RSL   3.5.1 使用系统RSL—Framework   3.5.2 其他的系统RSL   3.5.3 RSL的摘要   3.5.4 使用私有RSL  3.6 小结 第4章 Flex on Java企业应用架构 第5章 BlazeDS框架详解 第6章 Flex+BlazeDS应用开发实例 第7章 将范例应用改进为真正的企业应用 第8章 Flex打印 第9章 Flex企业应用性能优化 第10章 Flex企业应用开发的其他技术 附录 Flex Builder 中的项目如何迁移至Flash Builder
本人从其他网站上下载的,333页全,看到CSDN上没有完整的版本,所以共享一下。由于文件比较大,所以压缩成了两个文件上传。 第1章 Flex企业应用开发入门  1.1 B/S企业应用开发者的噩梦  1.2 噩梦结束,新时代来临  1.3 Flex概述  1.4 Flash Player的工作模型   1.4.1 Flash Player的帧执行模型   1.4.2 Flash Player渲染模型   1.4.3 Flash中的事件机制  1.5 小结 第2章 Flex企业应用开发基础  2.1 MXML语言   2.1.1 用MXML表示ActionScript对象   2.1.2 查看由MXML文件所翻译的ActionScript代码   2.1.3 IMXMLObject接口  2.2 客户端保持状态  2.3 客户端MVC  2.4 数据绑定   2.4.1 实现数据绑定的方法   2.4.2 数据绑定发生的时机   2.4.3 可以用于数据绑定的属性   2.4.4 数据绑定的用途   2.4.5 使用Bindable元数据标记   2.4.6 绑定到函数、对象和数组   2.4.7 使用可绑定属性链进行工作  2.5 Flex中的组件化编程   2.5.1 认识Flex组件组件容器   2.5.2 组件生命周期与布局   2.5.3 组件的失效机制   2.5.4 使用ActionScript创建自定义组件  2.6 异步调用   2.6.1 异步调用导致模型数据不一致   2.6.2 异步调用导致用户反复(误)操作  2.7 小结 第3章 Flex企业应用开发中的主要元素  3.1 企业应用的特点  3.2 Flex Application   3.2.1 SystemManager   3.2.2 Preloader   3.2.3 Application的作用和主要属性   3.2.4 通过SWFLoader加载Application   3.2.5 域及跨域访问  3.3 Flex Module   3.3.1 创建模块   3.3.2 模块的编辑与编译   3.3.3 模块文件的加载   3.3.4 主应用和模块的交互  3.4 Flex库文件SWC  3.5 Flex编译模式、链接模式与RSL   3.5.1 使用系统RSL—Framework   3.5.2 其他的系统RSL   3.5.3 RSL的摘要   3.5.4 使用私有RSL  3.6 小结 第4章 Flex on Java企业应用架构 第5章 BlazeDS框架详解 第6章 Flex+BlazeDS应用开发实例 第7章 将范例应用改进为真正的企业应用 第8章 Flex打印 第9章 Flex企业应用性能优化 第10章 Flex企业应用开发的其他技术 附录 Flex Builder 中的项目如何迁移至Flash Builder

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值