Doc docx xls 等转为 PDF 和 HTML

下面介绍使用 JodConverter + LibreOffice 把 Windows Office 的 doc,docx,xls 等文档转换为 PDF 和 HTML:

  • HTML:
    • 优点: 用浏览器打开方便,便于实现 doc 等在线预览
    • 缺点: 相对于 PDF 大不少,图片是独立文件,格式也没有 PDF 的漂亮
  • PDF:
    • 优点: 比 HTML 格式小,格式比较接近于原文档
    • 缺点: 相对于 HTML 在线预览不够方便,也可以借助 pdf.js + HTML5 实现在线预览

HttpServletResponse 下载文件

实现点击按钮下载文件以及点击 a 标签下载文件,注意一下几个问题:

  • 浏览器中点击链接下载文件没啥好说的,但是点击按钮怎么实现下载呢?

    1
    调用 window.open(url) 就可以了
  • 服务器端需要设置响应头表明是以流的形式下载文件

    1
    response.setContentType("application/octet-stream");
  • 文件名有中文时需要处理乱码问题

    1
    2
    String filename = new String(paper.getOriginalName().getBytes("UTF-8"), "ISO8859_1"); // 解决乱码问题
    response.setHeader("Content-Disposition", "attachment;filename=" + filename);

允许外网访问 MySQL

方式一

  • 任意主机以用户 root 和密码 root 连接到 MySQL 服务器

    1
    2
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
  • 指定 IP 为(如192.168.10.186)的主机以用户 alice 和密码 Passw0rd 连接到 MySQL 服务器

    1
    2
    GRANT ALL PRIVILEGES ON *.* TO 'alice'@'192.168.10.186' IDENTIFIED BY 'Passw0rd' WITH GRANT OPTION;
    FLUSH PRIVILEGES;

方式二

网上还看到说直接修改 user 表中 User root 的 Host 为 %,最好别这么干,不小心会哭的:

1
2
3
4
USE mysql;
SELECT user, host FROM user;
UPDATE user SET host='%' WHERE user='root';
FLUSH PRIVILEGES;

按照上面的修改 host 为 % 后外网可以访问了,但是本地却访问出错:

1
2
3
mysql -uroot -p
提示
Access denied for user 'root'@'localhost' (using password: YES) when trying

可用按下面的方式补救:

1
2
3
4
5
6
7
8
9
10
1. 启动 mysqld_safe
mysqld_safe --user=mysql --skip-grant-tables --skip-networking &
2. 登陆修改
mysql -u root mysql
use mysql
UPDATE user SET host='localhost' WHERE user='root';
FLUSH PRIVILEGES;
quit
这时可以看到 user 中关于 root 的记录会多一条

前 16 名 Java 实用工具类

从 GitHub 随机选择的 50,000 个开源 Java 项目中统计出最常用的 16 个 Java 实用工具类类及其最常用的方法,类列表和方法列表都按人气排序。

  1. org.apache.commons.io.IOUtils
    • closeQuietly ( )
    • toString ( )
    • copy ( )
    • toByteArray ( )
    • write ( )
    • toInputStream ( )
    • readLines ( )
    • copyLarge ( )
    • lineIterator ( )
    • readFully ( )

拖拽普通 Element 到 zTree

zTree 不支持 jQuery ui 的拖拽操作,因为 drop 事件阻止了 mouse up 事件的冒泡以致 zTree 不能调用 onMouseUp 的回调函数。为了拖拽普通的 element 到 zTree 上,需要自己实现拖拽功能,Drag With Other DOMs 演示了具体的实现,但是代码太多,不易于理解,这里把拖拽相关的核心代码提取出来,就能快速的理解拖拽的实现。

Vue DOM 更新完成后再执行函数

Vue 的数据变化后会更新 DOM,DOM 只能保证在当前 tick 里面的代码全部执行完毕后更新,不能保证数据一变化后就能用 document.querySelector() 获取到最新的 DOM。要保证在 DOM 更新以后执行某一块代码,就必须把这块代码放到下一次事件循环里面,比如 setTimeout(fn, 0),这样 DOM 更新后,就会立即执行这块代码。

有些时候 DOM 更新完成后执行某些操作是有必要的,这时就可以使用 Vue.nextTick() 注册一个函数放到 Vue 的事件队列里,使其在下一个 tick 被执行。

例如使用 Vue + Semantic Ui 创建 Popup,新创建的 Popup 需要执行 popup() 后才会生效,此时在 DOM 更新完成后需要执行一下 popup() 函数。

一般有 3 种方式调用 Vue.nextTick():

  • 普通事件处理函数中,下面的 [[1]],当有多个地方修改同一个变量时,每个地方都需要执行一次
  • 监听指定的数据变化时,下面的 [[2]],粒度细,只与数据是否变化有关,和修改数据的地方无关
  • updated() 回调中,下面的 [[3]],只要 DOM 变化了都会调用,无关的数据变化时都会调用,最省事,但是需要小心测试看看是否有副作用

Semantic Ui Tips

JS and CSS

一下代码都是基于 jQuery、Semantic Ui、Layer、Vue 来写的:

1
2
3
4
5
<link rel="stylesheet" href="http://cdn.staticfile.org/semantic-ui/2.2.7/semantic.min.css">
<script src="http://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
<script src="http://cdn.staticfile.org/semantic-ui/2.2.7/semantic.min.js"></script>
<script src="http://cdn.staticfile.org/vue/2.0.3/vue.js"></script>
<script src="http://cdn.staticfile.org/layer/2.3/layer.js"></script>

分页计算工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.xtuer.util;
/**
* 分页时需要计算某一页的起始位置,或则使用记录总数计算共有多少页,PageUtils 的任务就是计算分页时的数据:
* PageUtils.offset(pageNumber, pageSize) 用于计算起始位置
* PageUtils.pageCount(recordCount, pageSize) 用于计算共有多少页
*/
public class PageUtils {
/**
* 根据传入的页数、每页上的最多记录数计算这一页面的开始位置 offset
*
* @param pageNumber 页数
* @param pageSize 每页上的最多记录数
* @return 开始的位置 offset
*/
public static int offset(int pageNumber, int pageSize) {
// 校正参数,pageNumber 从 1 开始,pageSize 最小为 1
pageNumber = Math.max(1, pageNumber);
pageSize = Math.max(1, pageSize);
int offset = (pageNumber -1) * pageSize; // 计算此页开始的位置 offset
return offset;
}
/**
* 根据传入的记录总数、每页上的最多记录数计算总页数 pageCount
*
* @param recordCount 记录的总数
* @param pageSize 每页上的最多记录数
* @return 总页数
*/
public static int pageCount(int recordCount, int pageSize) {
// 校正参数,recordCount 最小为 0,pageSize 最小为 1
recordCount = Math.max(0, recordCount);
pageSize = Math.max(1, pageSize);
int page = (recordCount-1) / pageSize + 1;
return page;
}
}

修改 Semantic UI 的默认字体

Semantic Ui 默认使用的是谷歌提供的字体,并且是直接使用了谷歌的官方链接。谷歌网站在国内访问速度很差,甚至根本无法访问,需要对 Semantic UI 的源文件进行一下手动修改:

  1. 使用 Nodejs 下载 Semantic Ui 源码: http://www.semantic-ui.cn/introduction/getting-started.html

    • 安装 Nodejs
    • 安装 gulp: npm install -g gulp
    • 下载 Semantic Ui: npm install semantic-ui –save
  2. 修改 src\themes\default\globals\site.variables

    • 修改文件中的 @fontName 的值来设置 Semantic UI 的默认字体,这里使用了微软雅黑: Microsoft YaHei
    • 修改文件中的 @importGoogleFonts 为 false 禁止使用 Google 的字体
  3. 使用命令 gulp build 编译一下 Semantic UI

  4. 复制生成的 dist 目录中的文件到项目里即可

Vue 动态显示编辑按钮和计算 Class

下面介绍使用 vue

  • 动态的显示编辑按钮

    当鼠标移动到 segment 上时显示编辑按钮(@mouseenter 事件),鼠标离开 segment 时隐藏编辑按钮(@mouseleave 事件),需要定义一个属性如 editable 表示当前 segment 是否可编辑,使用 v-show 进行判断显示和隐藏

  • 动态的计算 class

    复杂情况下不同元素的 class 不一样,此时可以使用函数动态计算 class,在 :class 中使用此函数,不同的参数计算出来的 class 不一样

Vue Todo

实现一个简单的 Todo 来介绍不同的 Vue 对象共享同一个 data 数组、Todo 的增加、删除、编辑:

左边的 Todo 和右边的 Dropdown 分别用一个 Vue 对象来渲染,它们共享使用同一个数组 todos,当左边的 Todo 修改了 todos 的数据后,右边的 Dropdown 的数据也会同时自动更新。点击编辑按钮,在 todo 原来的地方显示一个 input 进行编辑。

Semantic Ui Grid

Semantic-Ui 是一套类似于 bootstrap 的 ui framework,相比 Bootstrap 有如下优点:

  • 组件采用语义化的组织方式,容易理解,容易记忆
  • 组件库非常丰富,几乎不需要引入第三方的组件,统一性强
  • 由于采用语义化的方式命名,所有组件都有自己的命名空间,相互不受干扰,侵入性弱,对自定义 css 干扰小,bootstrap 则改写了很多元素的默认样式,复写困难
  • 自定义容易,semantic ui 的源码中各个组件相互独立,依赖清晰,自定义方便

Fundamental Concepts

  • container
    A fixed width container 具有固定宽度的容器

  • grid
    网格

  • column
    网格的列

  • row
    网格的行

SpringMvc 响应 JSONP

SpringMvc 中处理 JSONP 需要注意响应的 Content-Type,如果为 text/plain 时在某些浏览器下就不能正确的执行 JSONP 的回调函数,认为其是不可执行的格式。

SpringMvc 返回对象时会把其自动的转换为 JSON 字符串,并且设置 Content-Type 为 application/json,如果返回 String 的话则 Content-Type 会设置为 text/plain,使用 JSONP 时需要返回 JSONP 格式的 String,这时例如在高版本的 Chrome 中就会出错,因为 Content-Type 是 text/plain,而不是 application/javascript

zTree 右键菜单

zTree 使用右边的添加、编辑、删除按钮时容易误操作(踩过坑了),所以下面使用右键菜单来编辑 zTree。

既然需要对树进行编辑,说明数据是需要保存到服务器的,所以树的加载也是需要使用动态加载,不过事例中写死是为了无服务器时便与演示(注释中有动态访问服务器的代码)。

实现时需要注意几点:

  • 重命名操作需要在 zTree 的回调函数 beforeRename 中进行
  • 创建新节点时
    • 节点没有展开,先展开它,这时如果还没有从服务器加载过数据,则会先从服务器中加载子节点数据,展开完成后会调用回调函数 onExpand,这时在 onExpand 中创建新节点。节点没有展开,且没有从服务器加载过子节点数据时不在 onExpand 中创建新节点,就会很有可能看到 2 个一样新建的节点,因为调用 window.tree.addNodes 创建了一个节点,服务器返回的数据中很可能也已经有这个节点的数据(因为是异步的),所以重复了,不过在数据库中并没有重复,只是浏览器本地的 zTree 中内存数据重复
    • 是根节点,或者非根节点且已经展开了,则直接创建新节点,这时不会从服务器加载子节点数据

使用时需要修改 TODO 相关的部分:

  • 配置 setting.async 从服务器加载数据,并删除 nodes 初始化代码

  • 完成函数 createNode(), removeNode(), renameNode(),已有相关的注释作为提示

    $.rest.syncCreate(), $.rest.syncRemove(), $.rest.syncUpdate() 是对 $.ajax() 的简单封装,为了简化 Ajax 请求操作,可以自己实现或则参考 jQuery 的 REST 插件

图片和 Base64 字符串互转

把图片的二进制数据转换为 Base64 编码的字符串表示,格式为 data:image/png;base64,iVBORw0KGgoAAAA…

  • /; 之间的内容为图片的格式
  • , 后面的内容为图片的二进制数据的Base64 编码的字符串

解析 Base64 编码字符串表示的图片为二进制数据,根据上面的算法反过来解析即可。

zTree

zTree is an advanced jQuery ‘tree plug-in’. The performance is excellent, it is easy to configurw (with a full set of options), and has many advanced features (that usually only come with paid software).

zTree is open source and uses the MIT license.

主页是 http://www.treejs.cn

你刚才在淘宝上买了一件东西

你发现快要过年了,于是想给你的女朋友买一件毛衣,你打开了 www.taobao.com。这时你的浏览器首先查询 DNS 服务器,将 www.taobao.com 转换成 IP 地址。不过首先你会发现,你在不同的地区或者不同的网络(电信、联通、移动)的情况下,转换后的 IP 地址很可能是 不一样的,这首先涉及到负载均衡的第一步,通过 DNS 解析域名时将你的访问分配到不同的入口,同时尽可能保证你所访问的入口是所有入口中可能较快的一个 (这和后文的 CDN 不一样)。

你通过这个入口成功的访问了 www.taobao.com 的实际的入口IP地址。这时你产生了一个 PV,即 Page View,页面访问。每日每个网站的总 PV 量是形容一个网站规模的重要指标。淘宝网全网在平日(非促销期间)的PV大概是 16-25 亿之间。同时作为一个独立的用户,你这次访问淘宝网的所有页面,均算作一个 UV(Unique Visitor 用户访问)。12306.cn 的日PV量最高峰在 10 亿左右,而 UV 量却远小于淘宝网十余倍,这其中的原因我相信大家都会知道。

字节序 Endian

Endian 一词来源于乔纳森·斯威夫特的小说格列佛游记。小说中,小人国为水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开而争论,争论的双方分别被称为 Big-Endians 和 Little-Endians。

1980 年,Danny Cohen 在其著名的论文 “On Holy Wars and a Plea for Peace“ 中为平息一场关于字节该以什么样的顺序传送的争论而引用了该词。

Endian 翻译为“字节序”,又称端序尾序。在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。Endianness 有时候也可以用指位序(bit)。

一般而言,字节序指示了一个 UCS-2 字符的哪个字节存储在低地址。如果 LSByte 在 MSByte 的前面,即 LSB 为低地址,则该字节序是小端序;反之则是大端序。在网络编程中,字节序是一个必须被考虑的因素,因为不同的处理器体系可能采用不同的字节序。在多平台的代码编程中,字节序可能会导致难以察觉的 bug

BIG Endian:最低位地址存放高位字节,可称高位优先,内存从最低地址开始按顺序存放(高数位数字先写)。最高位字节放最前面。

LITTLE Endian:最低位地址存放低位字节,可称低位优先,内存从最低地址开始按顺序存放(低数位数字先写)。最低位字节放最前面。

Big Endian 解释

最低位地址存放高位字节,可称高位优先,内存从最低地址开始按顺序存放(高数位数字先写)。最高位字节放最前面。

例如“汉”字的 Unicode 编码是 6C49。如果将 6C 写在前面,就是 big endian,如果将 49 写在前面,就是 little endian。

Java NIO Buffer

Java NIO 由以下几个核心部分组成:

  • Channel
  • Buffer
  • Selector

虽然 Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。
Buffer 缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。所谓输入/输出讲的无非就是把数据移进或移出缓冲区。

滚动插件 Animation Scroll

AnimationScroll 是一个 jQuery 的滚动插件,支持很多缓冲动画效果

AnimateScroll is a jQuery plugin which enables you to scroll to any part of the page in styleby just calling the animatescroll() function with the Id or Classname of the element where you want to scroll to.

Basic usage: $('body').animatescroll();

可到 https://plugins.jquery.com/animatescroll/ 下载,主页为 http://plugins.compzets.com/animatescroll/

Dubbo Hello World

本文介绍 Dubbo 的入门程序:

  • Dubbo Provider: Dubbo + SpringMvc
  • Dubbo Consumer: Dubbo + JUnit

使用传统 RPC 时服务器之间是星形结构,紧耦合的,Dubbo (SOA) 时服务器之间通过调度中心调度,典型的中介者模式,实现了解耦,服务的注册和查找通过中心的 ZooKeeper 使用接口实现,不需要配置 URL,服务器之间互相不知道对方的存在。Spring Http Remote Invoker 也能实现远程方法调用,但是需要配置 URL,而且也是星形结构。

Spring Http 远程方法调用

Spring Http 远程调用 (Spring Http Remote Invocation) 是 Spring 提供的一种特殊的允许通过 HTTP 进行 Java 串行化的远程调用策略,支持任意 Java 接口,相对应的支持类是 HttpInvokerServiceExporter (服务器端) 和 HttpInvokerProxyFactoryBean (客服端)。

远程调用两部分:

  • 服务端
  • 客户端

调用逻辑:

RequireJS 加载非 AMD 的 JS

RequireJS 加载的 JS 要求是 AMD 规范的,但是非 AMD 规范的 JS 文件也能够加载,下面就以 Util.js 中定义了类 Rect,Circle 和普通函数 greeting() 为例,演示 RequireJS 对于非 AMD 规范的 JS 的配置,加载以及使用。

可以看到,使用 RequireJS 加载的 JS 中的类和函数,与使用 <script src="/js/Util.js"> 加载时的使用方式没有区别,如果要以 AMD 的方式使用非 AMD 的 JS,可以参考 http://www.bubuko.com/infodetail-671521.html

用 RequireJS 统一管理 JS 和 CSS

如果页面很多,每个页面里都使用 <script> <link> 来加载 JS, CSS,就会显得很分散,开发环境和生产环境中想要修改多处 JS, CSS 的路径时不够方便,例如生产环境使用七牛等 CDN 加载 jQuery,而测试环境不能访问外网,这时 CDN 就玩了(不要问我为什么,就是有人这么干,我被坑的不要不要的),只能把 jQuery 放到本地了,为此需要到所有页面里修改代码然后测试,很不方便。使用 RequireJS 后就可以集中的管理 JS, CSS,修改起来比较方便,也可以使用 Build 工具根据不同的环境进行修改。

下面介绍使用 RequireJS 加载 jQuery, Layer, Vue, SemanticUi。

PHP 快速入门

开发环境:可以使用 MAMP Download

经典的 Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<?php
echo "Hello World!";
?>
</body>
</html>
  • PHP 脚本可放置于文档中的任何位置
  • PHP 脚本以 <?php 开头,以 ?> 结尾
  • 语句以分号结尾 ;
  • 注释:# 单行注释// 单行注释/* 多行注释 */
  • 变量名对大小写敏感:$color$Color 是不同的变量
  • 用户定义的函数关键字(例如 if、else、echo 等等)都对大小写不敏感:Echo "Ok"echo "Ok" 是一样的效果
  • 输出内容到网页上用 echo
  • var_dump():会返回变量的数据类型和值,调试的时候很有用: var_dump(“text”): string(4) "text";
  • print_r:Prints human-readable information about a variable

Quartz 实现定时任务

定义一个定时任务,需要 Task, Job, Trigger, Scheduler 4 个类,其中 Task 是我们自定义的类,是任务的实现逻辑,另外 3 个类则是由 Spring 和 Quartz 提供的,在 Bean 的配置文件里配置即可。

AspectJ with Annotation

需要添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.1</version>
</dependency>

AspectJ with Xml

需要添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.1</version>
</dependency>

Qualifier

当找到多个同类型的 Bean 的定义时,@Autowired 注入的时候不知道要用哪一个,会报异常,可以使用 @Qualifier 指定要注入的 Bean。

@Resource 相当于 @Autowired 和 @Qualifier 的组合。

Autowired 注入

@Autowired 是基于类型的注入,Spring 会在 IoC 容器里查找类型匹配的 Bean 注入。

spring-beans.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xtuer.beans"/>
<bean class="com.xtuer.beans.Door"/>
</beans>

Component Scan

使用 <context:component-scan> 特性,可以自动扫描 base-package 下类名有注解 @Component@Service 或者 @Controller 的类,为其在 Spring 容易里创建一个对象。

@Service@Controller@Component 在语法上作用是一样的,区别是各自有自己的语义,例如 SpringMVC 里用 @Controller 表明类是 Controller

Property Placeholder

properties 文件里定义 key | value,然后在 Bean Configuration File 里用 ${key} 引用 key 对应的值。

Bean Configuration File 里使用 <context:property-placeholder> 引入 properties 文件。

现在很多时候都使用 Gradle 的资源替换来代替了。

多个配置文件

把 Spring 的 Bean Configuration File 根据模块分散到不同的文件里,便于管理,然后使用 <import> 把它们组织在一起。例如:

Bean Bean Configuration File
user spring-beans.xml
address spring-beans-1.xml
customer spring-beans-2.xml

在 spring-beans.xml 里 import spring-beans-1.xml and spring-beans-2.xml,然后 Spring Context 加载 spring-beans.xml。

Bean 在配置文件中定义的顺序任意,其实最后就是把所有的配置文件和成一个大的配置文件,然后在解析创建对象,所以就像在单一的配置文件里定义 Bean 一样顺序不重要。

Property Editor

在设置一个对象类型的属性时:

  1. 使用 ref 引用已经存在的对象
  2. 使用 bean 标签创建一个匿名对象
  3. 还有一种方式是给 value 赋值一个字符串,Spring 自动的把这个字符串转换为对象

第三种方式的实现需要以下几步:

  1. 需要提供一个把 String 转换为对象的类,称之为自定义编辑器(CustomEditor)
  2. 然后把这个自定义编辑器注册到 Spring Container
  3. 使用字符串配置对象属性

FactoryBean

FactoryBean 是用来创建 Bean 的工厂,实现 FactoryBean 接口 的类就可以用来创建其他 Bean 了,在 Bean Configuration File 里的 <bean> 里定义要生成的 Bean。

FactoryBean 命名规范:Bean 的类名 后跟着 FactoryBean,例如创建 User 的 FactoryBean 的类名应该为 UserFactoryBean。

如果 Bean 的创建不只是简单的 setter, constructor 注入,而是有其他的逻辑,这个时候就可以用 FactoryBean 来创建 Bean(有点像同时用了 Factory Pattern 和 Builder Pattern)。

FactoryBean 是很常见的,例如 Spring 集成 MyBatis 时用 SqlSessionFactoryBean 来创建 SqlSession。

MapFactoryBean 注入 Map

1
2
3
4
5
6
7
8
9
<bean id="mapExample" class="com.xtuer.beans.CollectionHolder">
<property name="map">
<map> <!--表示是 map-->
<entry key="German" value="Gut"/>
<entry key="English" value="Good"/>
<entry key="Chinese" value="没问题"/>
</map>
</property>
</bean>

上面的方式注入 Map,Map 的类型不受我们控制,默认是 java.util.LinkedHashMap。MapFactoryBean 可以创建一个特定类型的 Map。由于 MapFactoryBean 的配置太过麻烦,这里我们只结束和其等效的 <util:map>

SetFactoryBean 注入 Set

1
2
3
4
5
6
7
8
9
<bean id="setExample" class="com.xtuer.beans.CollectionHolder">
<property name="set">
<set> <!--表示是 set-->
<value>Gut</value>
<value>Good</value>
<value>没问题</value>
</set>
</property>
</bean>

上面的方式注入 Set,Set 的类型不受我们控制,默认是 java.util.LinkedHashSet。SetFactoryBean 可以创建一个特定类型的 Set。由于 SetFactoryBean 的配置太过麻烦,这里我们只结束和其等效的 <util:set>

ListFactoryBean 注入 List

1
2
3
4
5
6
7
8
9
<bean id="listExample" class="com.xtuer.beans.CollectionHolder">
<property name="list">
<list> <!--表示是 list-->
<value>Gut</value>
<value>Good</value>
<value>没问题</value>
</list>
</property>
</bean>

上面的方式注入 List,List 的类型不受我们控制,默认是 java.util.ArrayList。ListFactoryBean 可以创建一个特定类型的 List。由于 ListFactoryBean 的配置太过麻烦,这里我们只介绍和其等效的 <util:list>

List, Set, Map, Properties 注入

注入 List

1
2
3
4
5
6
7
8
9
<bean id="listExample" class="com.xtuer.beans.CollectionHolder">
<property name="list">
<list> <!--表示是 list-->
<value>Gut</value>
<value>Good</value>
<value>没问题</value>
</list>
</property>
</bean>

<list> 里不只是可以使用 <value>,还可以使用 <ref bean=""><bean class="ClassName">,如下

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="listExample" class="com.xtuer.beans.CollectionHolder">
<property name="list">
<list> <!--表示是 list-->
<value>Gut</value>
<value>Good</value>
<value>没问题</value>
<ref bean="user"/>
<bean class="com.xtuer.beans.User"/>
</list>
</property>
</bean>

Contructor 注入

演示怎么给构造函数注入参数,使用指定的构造函数创建对象。

Customer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.xtuer.beans;
public class Customer {
private String name;
private int age;
private String telephone;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer(String name, String telephone, int age) {
this.name = name;
this.telephone = telephone;
this.age = age;
}
public Customer(String name, int age, String telephone) {
this.name = name;
this.telephone = telephone;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

Setter 注入

CommonUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.xtuer.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
public class CommonUtils {
private static DefaultPrettyPrinter printer;
static {
// Setup a pretty printer with an indenter (indenter has 4 spaces in this case)
DefaultPrettyPrinter.Indenter indenter = new DefaultIndenter(" ", DefaultIndenter.SYS_LF);
printer = new DefaultPrettyPrinter();
printer.indentObjectsWith(indenter);
printer.indentArraysWith(indenter);
}
/**
* 以 Json 的格式输出对象
* @param obj
*/
public static void output(Object obj) {
ObjectMapper mapper = new ObjectMapper();
try {
// 默认用 2 个空格缩进
// System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj));
// 自定义缩进,用 4 个空格
System.out.println(mapper.writer(printer).writeValueAsString(obj));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}

ID Name Alias

id, name, alias

  • 每个 Bean 都有标志符,用这个标志符从 Spring IoC Container 里获取 Bean
  • id, name, alias 都必须在 IoC 容器里唯一
  • 每个 Bean 可以有一个 id 属性,并可以根据该 id 在 IoC 容器中查找该 Bean,该 id 属性值必须在 IoC 容器中唯一
  • 如果不指定 id,只指定 name,那么 name 为 Bean 的标识符,并且需要在容器中唯一
  • 同时指定 name 和 id,此时 id 为标识符,而 name 为 Bean 的别名,两者都可以找到目标 Bean
  • 可以指定多个 name,之间可以用分号 ;、空格 ` 或逗号,` 分隔开,如果没有指定 id,那么第一个 name 为标识符,其余的为别名;若指定了 id 属性,则 id 为标识符,所有的 name 均为别名
  • 可以使用 <alias> 标签指定别名,别名也必须在 IoC 容器中唯一
  • 如果 id 和 name 都不指定,那么 Spring 容器会为 Bean 生成一个 id,生成规则:
    按 Bean 定义的顺序,在类的全路径名加上 #序号 (序号从 0 开始,第一个 bean 的别名是类的全路径名,所以可以使用类的全路径名来获取),如:
    com.xtuer.beans.User#0 (com.xtuer.beans.User)
    com.xtuer.beans.User#1
    com.xtuer.beans.User#2
    com.xtuer.beans.User#3
|