简

人生短暂,学海无边,而大道至简。


  • 首页

  • 归档

  • 分类

  • 标签

二、Spring Web之3-渲染视图

发表于 2017-09-15 | 分类于 Spring实战4版
将控制器中请求处理的逻辑和视图中的渲染实现解耦是Spring MVC的一个重要特性。 如果控制器中的方法直接负责产生HTML的话, 就很难在不影响请求处理逻辑的前提下, 维护和更新视图。 控制器方法和视图的实现会在模型内容上达成一致, 这是两者的最大关联, 除此之外, 两者应该保持足够的距离。
但是, 如果控制器只通过逻辑视图名来了解视图的话, 那Spring该如何确定使用哪一个视图实现来渲染模型呢? 这就是Spring视图解析器的任务了。
在Spring MVC中,定义了一个名为ViewResolver的接口

public interface ViewResolver {

View resolveViewName(String var1, Locale var2) throws Exception;

}

当给resolveViewName()方法传入一个视图名和Locale对象时, 它会返回一个View实例。

public interface View {

String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";

String PATH_VARIABLES = View.class.getName() + ".pathVariables";

String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

String getContentType();

void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;

}

View接口的任务就是接受模型以及Servlet的request和response对象, 并将输出结果渲染到response中。
实际上, 我们并不需要这么麻烦。 尽管我们可以编写ViewResolver和View的实现, 在有些特定的场景下, 这样做也是有必要的, 但是一般来讲, 我们并不需要关心这些接口。 我在这里提及这些接口只是为了让你对视图解析内部如何工作有所了解。 Spring提供了多个内置的实现。
视图解析器 描 述
BeanNameViewResolver 将视图解析为Spring应用上下文中的bean, 其中bean的ID与视图的名字相同
ContentNegotiatingViewResolver 通过考虑客户端需要的内容类型来解析视图, 委托给另外一个能够产生对应内容类型的视图解析器
FreeMarkerViewResolver 将视图解析为FreeMarker模板
InternalResourceViewResolver 将视图解析为Web应用的内部资源(一般为JSP)
JasperReportsViewResolver 将视图解析为JasperReports定义
ResourceBundleViewResolver 将视图解析为资源bundle(一般为属性文件)
TilesViewResolver 将视图解析为Apache Tile定义, 其中tile ID与视图名称相同。 注意有两个不同的TilesViewResolver实现, 分别对应于Tiles 2.0和
Tiles 3.0
UrlBasedViewResolver 直接根据视图的名称解析视图, 视图的名称会匹配一个物理视图的定义
VelocityLayoutViewResolver 将视图解析为Velocity布局, 从不同的Velocity模板中组合页面
VelocityViewResolver 将视图解析为Velocity模板
XmlViewResolver 将视图解析为特定XML文件中的bean定义。 类似于BeanName-ViewResolver
XsltViewResolver 将视图解析为XSLT转换后的结果



每一项都对应Java Web应用中特定的某种视图技术。 InternalResourceViewResolver一般会用于JSP, TilesViewResolver用于Apache Tiles视图, 而FreeMarkerViewResolver和VelocityViewResolver分别对应
FreeMarker和Velocity模板视图。
大多数Java Web应用都会用到JSP, 我们首先将会介绍InternalResourceViewResolver, 这个视图解析器一般会用来解析JSP视图。 接下来, 我们将会介绍TilesViewResolver, 控制JSP页面的布局。
最后还有一个,Thymeleaf是一种用来替代JSP的新兴技术, Spring提供了与Thymeleaf的原生模板(natural template) 协作的视图解析器, 这种模板之所以得到这样的称呼是因为它更像是最终产生的HTML, 而不是驱动它们的Java代
码。 Thymeleaf是一种非常令人兴奋的视图方案。

1、JSP视图解析
Spring提供了两种支持JSP视图的方式:
InternalResourceViewResolver会将视图名解析为JSP文件。 另外, 如果在你的JSP页面中使用了JSP标准标签库(JavaServerPages Standard Tag Library, JSTL) 的话, InternalResourceViewResolver能够将视图名解析为JstlView形式的JSP文件, 从而将JSTL本地化和资源bundle变量暴露给JSTL的格式化(formatting) 和信息(message) 标签。
Spring提供了两个JSP标签库, 一个用于表单到模型的绑定, 另一个提供了通用的工具类特性。

不管你使用JSTL, 还是准备使用Spring的JSP标签库, 配置解析JSP的视图解析器都是非常重要的。 尽管Spring还有其他的几个视图解析器都能将视图名映射为JSP文件, 但 InternalResourceViewResolver是最简单和最常用的视图解析器。
配置适用于JSP的视图解析器
有一些视图解析器, 如ResourceBundleViewResolver会直接将逻辑视图名映射为特定的View接口实现,而InternalResourceViewResolver所采取的方式并不那么直接。 它遵循一种约定, 会在视图名上添加前缀和后缀, 进而确定一个Web应用中视图资源的物理路径。
作为样例, 考虑一个简单的场景, 假设逻辑视图名为home。 通用的实践是将JSP文件放到Web应用的WEB-INF目录下, 防止对它的直接访问。 如果我们将所有的JSP文件都放在“/WEB-INF/views/”目录下, 并且home页的JSP名为home.jsp, 那么我们可以确定物理视图的路径就是逻辑视图名home再加上“/WEB-INF/views/”前缀和“.jsp”后缀。 
当使用@Bean注解的时候, 我们可以按照如下的方式配置Internal-ResourceView Resolver, 使其在解析视图时, 遵循上述的约定。

@Bean

public ViewResolver viewResolver() {

InternalResourceViewResolver resolver = new InternalResourceViewResolver();

resolver.setPrefix("/WEB-INF/views/");

resolver.setSuffix(".jsp");

return resolver;

}

作为替代方案, 如果你更喜欢使用基于XML的Spring配置, 那么可以按照如下的方式配置InternalResourceViewResolver:  
InternalResourceViewResolver配置就绪之后, 它就会将逻辑视图名解析为JSP文件, 如下所示:
home将会解析为“/WEB-INF/views/home.jsp”
productList将会解析为“/WEB-INF/views/productList.jsp”
books/detail将会解析为“/WEB-INF/views/books/detail.jsp”
解析JSTL视图
到目前为止, 我们对InternalResourceViewResolver的配置都很基础和简单。 它最终会将逻辑视图名解析为InternalResourceView实例, 这个实例会引用JSP文件。 但是如果这些JSP使用JSTL标签来处理格式化和信息的话, 那么我们会希望InternalResourceViewResolver将视图解析为JstlView。
JSTL的格式化标签需要一个Locale对象, 以便于恰当地格式化地域相关的值, 如日期和货币。 信息标签可以借助Spring的信息资源和Locale, 从而选择适当的信息渲染到HTML之中。 通过解析JstlView, JSTL能够获得Locale对象以及Spring中配置的信息资源。如果想让InternalResourceViewResolver将视图解析为JstlView, 而不是InternalResourceView的话, 那么我们只需设置它
的viewClass属性即可:

@Bean

public ViewResolver viewResolver() {

InternalResourceViewResolver resolver = new InternalResourceViewResolver();

resolver.setPrefix("/WEB-INF/views/");

resolver.setSuffix(".jsp");

resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);

return resolver;

}

或者
使用Spring的JSP库
当为JSP添加功能时, 标签库是一种很强大的方式, 能够避免在脚本块中直接编写Java代码。 Spring提供了两个JSP标签库, 用来帮助定义Spring MVC Web的视图。 其中一个标签库会用来渲染HTML表单标签, 这些标签可以绑定model中的某个属性。 另外一个标签库包含了一些工具类标签, 我们随时都可以非常便利地使用它们。

Spring的表单绑定JSP标签库包含了14个标签, 它们中的大多数都用来渲染HTML中的表单标签。 但是, 它们与原生HTML标签的区别在于它们会绑定模型中的一个对象, 能够根据模型中对象的属性填充值。 标签库中还包含了一个为用户展现错误的标签, 它会将错误信息渲染到最终的HTML之中。
为了使用表单绑定库, 需要在
JSP页面中对其进行声明:

需要注意, 我将前缀指定为
“sf”, 但通常也可能使用“form”前缀。 你可以选择任意喜欢的前缀, 我之所以选择“sf”是因为它很简洁、 易于输入, 并且还是Spring form的简写形式。 在本书中, 当使用表单绑定库的时候, 我会一直使用“sf”前缀。  
表单绑定标签库
<sf:checkbox> 渲染成一个HTML <input>标签, 其中type属性设置为checkbox


<sf:checkboxes> 渲染成多个HTML <input>标签, 其中type属性设置为checkbox
<sf:errors> 在一个HTML <span>中渲染输入域的错误
<sf:form> 渲染成一个HTML <form>标签, 并为其内部标签暴露绑定路径, 用于数据绑定
<sf:hidden> 渲染成一个HTML <input>标签, 其中type属性设置为hidden
<sf:input> 渲染成一个HTML <input>标签, 其中type属性设置为text
<sf:label> 渲染成一个HTML <label>标签
<sf:option> 渲染成一个HTML <option>标签, 其selected属性根据所绑定的值进行设置
<sf:options> 按照绑定的集合、 数组或Map, 渲染成一个HTML <option>标签的列表
<sf:password> 渲染成一个HTML <input>标签, 其中type属性设置为password
<sf:radiobutton> 渲染成一个HTML <input>标签, 其中type属性设置为radio
<sf:radiobuttons> 渲染成多个HTML <input>标签, 其中type属性设置为radio
<sf:select> 渲染为一个HTML <select>标签
<sf:textarea> 渲染为一个HTML <textarea>标签
Spring通用的标签库
除了表单绑定标签库之外,
Spring还提供了更为通用的JSP标签库。 实际上, 这个标签库是Spring中最早的标签库。 这么多年来, 它有所变化, 但是在最早版本的Spring中, 它就已经存在了。
要使用
Spring通用的标签库, 我们必须要在页面上对其进行声明:
JSP标签 描 述
<s:bind> 将绑定属性的状态导出到一个名为status的页面作用域属性中, 与<s:path>组合使用获取绑定属性的值
<s:escapeBody> 将标签体中的内容进行HTML和/或JavaScript转义
<s:hasBindErrors> 根据指定模型对象(在请求属性中) 是否有绑定错误, 有条件地渲染内容
<s:htmlEscape> 为当前页面设置默认的HTML转义值
<s:message> 根据给定的编码获取信息, 然后要么进行渲染(默认行为) , 要么将其设置为页面作用域、 请求作用域、 会话作用域或应用作用域的变量(通过使
用
var和scope属性实现)
<s:nestedPath> 设置嵌入式的path, 用于<s:bind>之中
<s:theme> 根据给定的编码获取主题信息, 然后要么进行渲染(默认行为) , 要么将其设置为页面作用域、 请求作用域、 会话作用域或应用作用域的变量(通过
使用
var和scope属性实现)
<s:transform> 使用命令对象的属性编辑器转换命令对象中不包含的属性
<s:url> 创建相对于上下文的URL, 支持URI模板变量以及HTML/XML/JavaScript转义。 可以渲染URL(默认行为) , 也可以将其设置为页面作用域、 请求作
用域、 会话作用域或应用作用域的变量(通过使用
var和scope属性实现)
<s:eval> 计算符合Spring表达式语言(Spring Expression Language, SpEL) 语法的某个表达式的值, 然后要么进行渲染(默认行为) , 要么将其设置为页
面作用域、 请求作用域、 会话作用域或应用作用域的变量(通过使用
var和scope属性实现)

2、使用Apache Tiles视图定义布局  
实际中基本是使用css来定义布局,在此没有学习,直接跳过。

3、使用Thymeleaf
JSP已经存在了很长的时间, 并且在Java Web服务器中无处不在, 但是它却存在一些缺陷。 JSP最明显的问题在于它看起来像HTML或XML, 但它其实上并不是。 大多数的JSP模板都是采用HTML的形式, 但是又掺杂上了各种JSP标签库的标签, 使其变得很混乱。所以有很多模板产生,Thymeleaf就是其中最新的一种。
Thymeleaf模板是原生的, 不依赖于标签库。 它能在接受原始HTML的地方进行编辑和渲染。 因为它没有与Servlet规范耦合, 因此Thymeleaf模板能够进入JSP所无法涉足的领域。 现在, 我们看一下如何在Spring MVC中使用Thymeleaf。

在Spring中使用Thymeleaf, 我们需要配置三个启用Thymeleaf与Spring集成的bean:
ThymeleafViewResolver: 将逻辑视图名称解析为Thymeleaf模板视图;
SpringTemplateEngine: 处理模板并渲染结果;
TemplateResolver: 加载Thymeleaf模板。

@Bean//Thymeleaf视图解析器

public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {

ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();

viewResolver.setTemplateEngine(templateEngine);

return viewResolver;

}

@Bean//模板引擎

public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {

SpringTemplateEngine templateEngine = new SpringTemplateEngine();

templateEngine.setTemplateResolver(templateResolver);

return templateEngine;

}


@Bean//模板解析器

public TemplateResolver templateResolver() {

TemplateResolver templateResolver = new ServletContextTemplateResolver();

templateResolver.setPrefix("/WEB-INF/views/");

templateResolver.setSuffix(".html");

templateResolver.setTemplateMode("HTML5");

return templateResolver;

}

xml方式:
ThymeleafViewResolver是Spring MVC中ViewResolver的一个实现类。 像其他的视图解析器一样, 它会接受一个逻辑视图名称, 并将其解析为视图。 不过在该场景下, 视图会是一个Thymeleaf模板。
需要注意的是ThymeleafViewResolver bean中注入了一个对SpringTemplate Engine bean的引用。 SpringTemplateEngine会在Spring中启用Thymeleaf引擎, 用来解析模板, 并基于这些模板渲染结果。 可以看到, 我们为其注入了一个TemplateResolver bean的引用。
TemplateResolver会最终定位和查找模板。 与之前配置InternalResource-ViewResolver类似, 它使用了prefix和suffix属性。 前缀和后缀将会与逻辑视图名组合使用, 进而定位Thymeleaf引擎。 它的templateMode属性被设置成了HTML 5, 这表明我们预期要解析的模板会渲染成HTML 5输出。
所有的Thymeleaf bean都已经配置完成。
使用示例:

<html xmlns="http://www.w3.org/1999/xhtml"

xmlns:th="http://www.thymeleaf.org"><!--命名空间-->

<head>

<title>Spitter</title>

<link rel="stylesheet" type="text/css" th:href="@{/resources/style.css}"></link>

</head>

<body>

<div id="header" th:include="page :: header"></div><!--样式表链接-->

<div id="content">

<h1>Welcome to Spitter</h1>

<!--页面链接-->

<a th:href="@{/spittles}">Spittles</a> |

<a th:href="@{/spitter/register}">Register</a>

<br/>

View: <span th:text="${view}">unknown</span>

</div>

<div id="footer" th:include="page :: copy"></div>

</body>

</html>

红黑树

发表于 2017-09-14 | 分类于 数据结构算法

红黑树简介

R-B Tree,全称是Red-Black Tree,又称为”红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

20200414160745

有如下两个特征:
  
①、节点都有颜色;这里的颜色用于标记,我们可以在节点类Node中增加一个boolean型变量isRed,以此来表示颜色的信息。
  
②、在插入和删除的过程中,要遵循保持这些颜色的不同排列规则。

红黑树的特性:

1
2
3
4
5
(1)每个节点或者是黑色,或者是红色。
(2)根节点总是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。

注意:新插入的节点颜色总是红色的,这是因为插入一个红色节点比插入一个黑色节点违背红-黑规则的可能性更小,原因是插入黑色节点总会改变黑色高度(违背规则5),但是插入红色节点只有一半的机会会违背规则4 。另外违背规则4比违背规则5要更容易修正。当插入一个新的节点时,可能会破坏这种平衡性,那么红-黑树是如何修正的呢?

红-黑树的自我修正

红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。

旋转包括两种:

左旋

20200414161318

右旋

20200414161416

左旋和右旋,它们是对称的。无论是左旋还是右旋,被旋转的树,在旋转前是二叉查找树,并且旋转之后仍然是一颗二叉查找树。

基本操作–添加

1
2
3
第一步: 将红黑树当作一颗二叉查找树,将节点插入。
第二步:将插入的节点着色为"红色"。
第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。

基本操作–删除

1
2
第一步:将红黑树当作一颗二叉查找树,将节点删除。
第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。

二叉树

发表于 2017-09-14 | 分类于 数据结构算法

树(tree)

是一种抽象数据类型(ADT),用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合。把它叫做”树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

20200414160921

①、节点:上图的圆圈,比如A,B,C等都是表示节点。节点一般代表一些实体,在java面向对象编程中,节点一般代表对象。

②、边:连接节点的线称为边,边表示节点的关联关系。一般从一个节点到另一个节点的唯一方法就是沿着一条顺着有边的道路前进。

树有很多种,向上面的一个节点有多余两个的子节点的树,称为多路树。而每个节点最多只能有两个子节点的一种形式称为二叉树。

20200414160938

1
2
3
4
5
6
7
8
9
10
  ①、路径:顺着节点的边从一个节点走到另一个节点,所经过的节点的顺序排列就称为路径。
  ②、根:树顶端的节点称为根。一棵树只有一个根,如果要把一个节点和边的集合称为树,那么从根到其他任何一个节点都必须有且只有一条路径。A是根节点。
  ③、父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;B是D的父节点。
  ④、子节点:一个节点含有的子树的根节点称为该节点的子节点;D是B的子节点。
  ⑤、兄弟节点:具有相同父节点的节点互称为兄弟节点;比如上图的D和E就互称为兄弟节点。
  ⑥、叶节点:没有子节点的节点称为叶节点,也叫叶子节点,比如上图的A、E、F、G都是叶子节点。
  ⑦、子树:每个节点都可以作为子树的根,它和它所有的子节点、子节点的子节点等都包含在子树中。
  ⑧、节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推。
  ⑨、深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;
  ⑩、高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;

二叉树

一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历

四种基本的遍历思想

前序遍历:根结点 ---> 左子树 ---> 右子树
中序遍历:左子树---> 根结点 ---> 右子树
后序遍历:左子树 ---> 右子树 ---> 根结点
层次遍历:仅仅需按层次遍历就可以

比如下面的树遍历:

20200414171741

前序遍历:1  2  4  5  7  8  3  6 
中序遍历:4  2  7  5  8  1  3  6
后序遍历:4  7  8  5  2  6  3  1
层次遍历:1  2  3  4  5  6  7  8
1
2
3
4
public class TreeNode<T> {
public T value;
public TreeNode<T> left;
public TreeNode<T> right;}

前序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void preBinaryTree(TreeNode root) {  
if (root != null) {
System.out.print(root.value+" ");
preBinaryTree(root.left);
preBinaryTree(root.right);
}
}
//非递归实现
public void preBinaryTree(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode pNode = root;
while (pNode != null || !stack.isEmpty()) {
if (pNode != null) {
System.out.print(pNode.value+" ");
stack.push(pNode);
pNode = pNode.left;
} else { //pNode == null && !stack.isEmpty()
TreeNode node = stack.pop();
pNode = node.right;
}
}
}

中序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void inBinaryTree(TreeNode root) {  
if (root != null) {
inBinaryTree(root.left);
System.out.print(root.value+" ");
inBinaryTree(root.right);
}
}
public void inBinaryTree(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode pNode = root;
while (pNode != null || !stack.isEmpty()) {
if (pNode != null) {
stack.push(pNode);
pNode = pNode.left;
} else { //pNode == null && !stack.isEmpty()
TreeNode node = stack.pop();
System.out.print(node.value+" ");
pNode = node.right;
}
}
}

后序遍历

1
2
3
4
5
6
7
public void postBinaryTree(TreeNode root) {  
if (root != null) {
postBinaryTree(root.left);
postBinaryTree(root.right);
System.out.print(root.value+" ");
}
}

层次遍历

1
2
3
4
5
6
7
public void preBinaryTree(TreeNode root) {  
if (root != null) {
System.out.print(root.value+" ");
preBinaryTree(root.left);
preBinaryTree(root.right);
}
}

二叉搜索树

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

查找某个节点,我们必须从根节点开始遍历。

①、查找值比当前节点值大,则搜索右子树;
②、查找值等于当前节点值,停止搜索(终止条件);
③、查找值小于当前节点值,则搜索左子树;

树的效率:查找节点的时间取决于这个节点所在的层数,每一层最多有2n-1个节点,总共N层共有2n-1个节点,那么时间复杂度为O(logn),底数为2。

插入节点,必须先找到插入的位置。与查找操作相似,由于二叉搜索树的特殊性,待插入的节点也需要从根节点开始进行比较,小于根节点则与根节点左子树比较,反之则与右子树比较,直到左子树为空或右子树为空,则插入到相应为空的位置,在比较的过程中要注意保存父节点的信息 及 待插入的位置是父节点的左子树还是右子树,才能插入到正确的位置。

遍历树是根据一种特定的顺序访问树的每一个节点。比较常用的有前序遍历,中序遍历和后序遍历。而二叉搜索树最常用的是中序遍历。

二叉查找树

完美平衡二叉树

二、Spring Web之2-java配置方式实现

发表于 2017-09-14 | 分类于 Spring实战4版
DispatcherServlet是Spring MVC的核心。 按照传统的方式, 像DispatcherServlet这样的Servlet会配置在web.xml文件中。借助于Servlet 3规范和Spring 3.1的功能增强, 使用Java方式将DispatcherServlet配置在Servlet容器中, 而不会再使用web.xml文件。
如果按照这种方式配置DispatcherServlet,它只能部署到支持Servlet 3.0的服务器中才能正常工作, 如Tomcat 7或更高版本。否则只能使用web.xml了。

在Servlet 3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类, 如果能发现的话, 就会用它来配置Servlet容器。
Spring提供了这个接口的实现, 名为SpringServletContainerInitializer, 这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。

Spring 3.2引入了WebApplicationInitializer基础实现, 也就是AbstractAnnotationConfigDispatcherServletInitializer。
因为我们的DispatcherServletDemo扩展了AbstractAnnotationConfigDispatcherServletInitializer(同时也就实现了WebApplicationInitializer) ,
因此当部署到Servlet 3.0容器中的时候, 容器会自动发现它, 并用它来配置Servlet上下文。重写了三个方法:
getServletMappings(), 它会将一个或多个路径映射到DispatcherServlet上。比如映射“/”, 这表示它会是应用的默认Servlet。 它会处理进入应用的所有请求。
为了理解其他的两个方法, 我们首先要理解DispatcherServlet和一个Servlet监听器(也就是ContextLoaderListener) 的关系。

/**

* SpringMVC配置类,通过java方式配置

*/

@Configuration

@EnableWebMvc //启动SpringMVC

@ComponentScan("com.hu.web") //启动组件扫描

public class WebConfig extends WebMvcConfigurerAdapter {

/**

* 配置JSP视图解析器

*

* 他会查找jsp文件,在查找的时候他会在视图名称上加一个特定的前缀和后缀home的视图——解析成为/WEB-INF/views/home.jsp

* @return

*/

@Bean

public ViewResolver viewResolver(){

InternalResourceViewResolver resolver = new InternalResourceViewResolver();

resolver.setPrefix("/WEB-INF/views/");

resolver.setSuffix(".jsp");

resolver.setExposeContextBeansAsAttributes(true);

return resolver;

}

/**

* 配置静态资源的处理

*

* 通过调用enable方法,我们要求DispatcherServelet将 对静态资源的请求转发到Servlet容器中的默认的Servlet上, 不是DispatcherServelet本身处理

*/

public void configureDefaultServleHandling(DefaultServletHandlerConfigurer configurer){

configurer.enable();

}

}

/**

* 配置DispatcherServlet

*

* 任意继承自AbstractAnnotationConfigDispatcherServletInitializer的类都会自动的配置Dispatcher-Servlet和Spring应用上下文*,但是真正完成配置上下文的是WebApplicationInitializer的类

*

* Spring Web中通常会有两种应用上下文,一种是Spring应用上下文,这种上下文通过DispatcherServlet加载,对应上边的getServletConfigClasses()方法,

* 另一种上下文不是spring的就要通过ContextLoaderListerner创建,对应的是方法getRootConfigClasses()

*

* AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoaderListener

*/

public class DispatcherServletDemo extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override

protected Class<?>[] getServletConfigClasses() {

//指定配置类

return new Class<?>[] { WebConfig.class };

}

//将配置映射到 “/”,表示会使用默认的Servlet

@Override

protected String[] getServletMappings() {

return new String[] { "/" };

}

@Override

protected Class<?>[] getRootConfigClasses() {

return new Class<?>[] { RootConfig.class };

}

}


当DispatcherServlet启动的时候, 它会创建Spring应用上下文, 并加载配置文件或配置类中所声明的bean,就是包含了getServletConfigClasses()方法中加载类class, 我们要求DispatcherServlet加载应用上下文时, 使用定义在WebConfig配置类中的bean。
但是在Spring Web应用中, 通常还会有另外一个应用上下文。 另外的这个应用上下文是由ContextLoaderListener创建的。
我们希望DispatcherServlet加载包含Web组件的bean, 如控制器、 视图解析器以及处理器映射, 而ContextLoaderListener要加载应用中的其他bean,这些bean通常是驱动应用后端的中间层和数据层组件。
实际上, AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoaderListener。 getServletConfigClasses()方法返回的带有@Configuration注解的类将会用来定义DispatcherServlet应用上下文中的bean。
getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的bean。

/**

* 配置的是非web组件

*/

@Configuration

@ComponentScan(basePackages = {"com.hu.service"},

//扫描注解过滤的类,这里可以不用,因为扫描的注解是在其他包中的类使用

excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.class)})

public class RootConfig {

}

WebConfig配置类中主要是内容是启用组件扫描,配置视图解析器,配置静态资源的处理。
RootConfig配置类加载的是驱动应用后端的中间层和数据层组件,是父上下文。
spring的ApplicationContext提供加载多层上下文的能力,允许每个上下文只关注特定层,例如一个应用的web层或者中间层服务。
当我们有多个DispatchServlet在一个应用和我们想要共享其中的普通bean例如datasource。这时候我们可以定义一个根上下文即root ApplicationContext,这个根上下文包含了普通的bean和从根上下文继承了普通bean的多个WebApplicationContext。
在web mvc框架中,每个DispatchServlet都有自己独自的WebApplicationContext,这个WebApplicationContexti继承了所有已经在 root WebApplicationContext定义了的bean。
这些被继承的bean可以被特定的复写,而且对于一个给定的servlet实例你能够在本地定义新的指定范围的bean。

再创建一个控制器和jsp就可以使用了。

@Controller

public class HomeController {

@RequestMapping(value="/home",method= RequestMethod.GET)

public String home(){

//视图名为home

//配置的InternalResourceViewResolver方式,

//home会被解析成"/WEB-INF/views/home.jsp"

return "home";

}

@RequestMapping(value = "/",method = RequestMethod.GET)

public ModelAndView home1() {

String message = "Spring MVC";

return new ModelAndView("home", "message", message);

}

}

一、Spring核心之4-AOP

发表于 2017-09-04 | 分类于 Spring实战4版
常用术语有通知(advice) 、 切点(pointcut) 和连接点(join point)
Spring切面可以应用5种类型的通知:
前置通知(Before) : 在目标方法被调用之前调用通知功能;
后置通知(After) : 在目标方法完成之后调用通知, 此时不会关心方法的输出是什么;
返回通知(After-returning) : 在目标方法成功执行之后调用通知;
异常通知(After-throwing) : 在目标方法抛出异常后调用通知;
环绕通知(Around) : 通知包裹了被通知的方法, 在被通知的方法调用之前和调用之后执行自定义的行为。
切点(Poincut)
切点的定义会匹配通知所要织入的一个或多个连接点。 我们通常使用明确的类和方法名称, 或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。 有些AOP框架允许我们创建动态的切点, 可以根据运行时的决策(比如方法的参数值) 来决定是否应用通知。
切面(Aspect)
当抄表员开始一天的工作时, 他知道自己要做的事情(报告用电量) 和从哪些房屋收集信息。 因此, 他知道要完成工作所需要的一切东西。切面是通知和切点的结合。 通知和切点共同定义了切面的全部内容——它是什么, 在何时和何处完成其功能。
引入(Introduction)
引入允许我们向现有的类添加新方法或属性。 例如, 我们可以创建一个Auditable通知类, 该类记录了对象最后一次修改时的状态。 这很简单, 只需一个方法, setLastModified(Date), 和一个实例变量来保存这个状态。 然后, 这个新方法和实例变量就可以被引入到现有的类中, 从而可以在无需修改这些现有的类的情况下, 让它们具有新的行为和状态。
织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。 切面在指定的连接点被织入到目标对象中。 在目标对象的生命周期里有多个点可以进行织入:
编译期: 切面在目标类编译时被织入。 这种方式需要特殊的编译器。 AspectJ的织入编译器就是以这种方式织入切面的。
类加载期: 切面在目标类加载到JVM时被织入。 这种方式需要特殊的类加载器(ClassLoader) , 它可以在目标类被引入应用之前增强该目标类的字节码。 AspectJ 5的加载时织入(load-time weaving, LTW) 就支持以这种方式织入切面。
运行期: 切面在应用运行的某个时刻被织入。 一般情况下, 在织入切面时, AOP容器会为目标对象动态地创建一个代理对象。 SpringAOP就是以这种方式织入切面的。
要掌握的新术语可真不少啊。 再看一下图4.1, 现在我们已经了解了如下的知识, 通知包含了需要用于多个应用对象的横切行为; 连接点是程序执行过程中能够应用通知的所有点; 切点定义了通知被应用的具体位置(在哪些连接点) 。 其中关键的概念是切点定义了哪些连接点会得到通知。

Spring提供了4种类型的AOP支持:
基于代理的经典Spring AOP;
纯POJO切面;
@AspectJ注解驱动的切面;
注入式AspectJ切面(适用于Spring各版本) 。
前三种都是Spring AOP实现的变体, Spring AOP构建在动态代理基础之上, 因此, Spring对AOP的支持局限于方法拦截。

一、Spring核心之3-高级装配

发表于 2017-09-03 | 分类于 Spring实战4版
装配的歧义性
就是当同一个接口有多个实现类时,并且实现类都通过注解被spring管理,这时候如果在使用@Autowired装配的时候,就不知道需要装配哪一个对象,无法选择,这时候就出现装配歧义。
装配歧义解决办法是在bean声明时,设置首选bean。
如果是类配置,使用@Primary注解标注为首选bean
如果是xml配置,使用bean标签属性primary="true"

限定自动装配的bean  
设置首选bean的局限性在于@Primary无法将可选方案的范围限定到唯一一个无歧义性的选项中。 它只能标示一个优先的可选方案。 当首选bean的数量超过一个时, 我们并没有其他的方法进一步缩小可选范围。  
@Qualifier注解是使用限定符的主要方式。 它可以与@Autowired和@Inject协同使用, 在注入的时候指定想要注入进去的是哪个bean。  
创建自定义的限定符  

环境与profile
比如在开发用会有测试生产开发,不同环境中的datasource方法,但是他们都会生成同一个类型的bean。所以,Spring引入了bean profile的功能。 要使用profile, 你首先要将所有不同的bean定义整理到一个或多个profile之中, 在将应用部署到每个环境时, 要确保对应的profile处于激活(active) 的状态。
类配置中使用profile
在Spring 3.1中, 只能在类级别上使用@Profile注解。 不过, 从Spring 3.2开始, 你也可以在方法级别上使用@Profile注解, 与@Bean注解一同使用。
@Bean
@Profile("dev")
public DataSource ......

@Bean
@Profile("prod")
public DataSource ......

xml配置中使用profile
<beans profile="dev">
        <bean id="dataSource" ....../>
</beans>

激活profile
Spring在确定哪个profile处于激活状态时, 需要依赖两个独立的属性: spring.profiles.active和spring.profiles.default。 如果设置了spring.profiles.active属性的话, 那么它的值就会用来确定哪个profile是激活的。 但如果没有设置spring.profiles.active属性的话, 那Spring将会查找spring.profiles.default的值。 如果spring.profiles.active和spring.profiles.default均没有设置的话, 那就没有激活的profile, 因此只会创建那些没有定义在profile中的bean。
有多种方式来设置这两个属性:
        作为DispatcherServlet的初始化参数;
        作为Web应用的上下文参数;
        作为JNDI条目;
        作为环境变量;
        作为JVM的系统属性;
在集成测试类上, 使用@ActiveProfiles注解设置。
一种方式是使用DispatcherServlet的参数将spring.profiles.default设置为开发环境的profile, 我会在Servlet上下文中进行设置(为了兼顾到ContextLoaderListener) 。
在Web应用中, 设置spring.profiles.default的web.xml文件会如下所示:
使用profile进行测试
Spring提供了@ActiveProfiles注解, 我们可以使用它来指定运行测试时要激活哪个profile。 在集成测试时, 通常想要激活的是开发环境的profile。

条件化Bean
假设希望bean只有在应用的类路径下包含特定的库时才创建。 或者我们希望某个bean只有当另外某个特定的bean也声明了之后才会创建。 我们还可能要求只有某个特定的环境变量设置之后, 才会创建某个bean。
在Spring 4之前, 很难实现这种级别的条件化配置, 但是Spring 4引入了一个新的@Conditional注解, 它可以用到带有@Bean注解的方法上。 如果给定的条件计算结果为true, 就会创建这个bean, 否则的话, 这个bean会被忽略。例如, 假设有一个名为MagicBean的类, 我们希望只有设置了magic环境属性的时候, Spring才会实例化这个类。 如果环境中没有这个属性, 那么MagicBean将会被忽略。 在程序清单3.4所展现的配置中,

@Configurable

public class SpringConfig1 {

@Bean

public Lad lad(){

return new Lad();

}

@Bean

@Conditional(MagicExisCondition.class)//条件创建bean

public Ladeee ladeee(Lad lad){

return new Ladeee(lad);

}

public static void main(String[] args){

ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);

System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));

}

}

//检查Condition中是否有magic属性

class MagicExisCondition implements Condition {

public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){

Environment environment = conditionContext.getEnvironment();

return environment.containsProperty("magic");

}

}

通过ConditionContext, 我们可以做到如下几点:
借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;
借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在, 甚至探查bean的属性;
借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;
读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;
借助getClassLoader()返回的ClassLoader加载并检查类是否存在。
AnnotatedTypeMetadata则能够让我们检查带有@Bean注解的方法上还有什么其他的注解。 像ConditionContext一样, AnnotatedTypeMetadata也是一个接口。
借助isAnnotated()方法, 我们能够判断带有@Bean注解的方法是不是还有其他特定的注解。 借助其他的那些方法, 我们能够检查@Bean注解的方法上其他注解的属性。
从Spring 4开始, @Profile注解进行了重构, 使其基于@Conditional和Condition实现。

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Conditional({ProfileCondition.class})

public @interface Profile {

String[] value();

}

@Profile本身也使用了@Conditional注解, 并且引用ProfileCondition作为Condition实现。 如下所示, ProfileCondition实现了Condition接口, 并且在做出决策的过程中, 考虑到了ConditionContext和AnnotatedTypeMetadata中的多个因素。

class ProfileCondition implements Condition {

ProfileCondition() {

}


public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

if (context.getEnvironment() != null) {

MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());

if (attrs != null) {

Iterator var4 = ((List)attrs.get("value")).iterator();


Object value;

do {

if (!var4.hasNext()) {

return false;

}


value = var4.next();

} while(!context.getEnvironment().acceptsProfiles((String[])((String[])value)));


return true;

}

}


return true;

}

}

ProfileCondition通过AnnotatedTypeMetadata得到了用于@Profile注解的所有属性。 借助该信息, 它会明确地检查value属性, 该属性包含了bean的profile名称。 然后, 它根据通过ConditionContext得到的Environment来检查[借
助acceptsProfiles()方法] 该profile是否处于激活状态。

bean的作用域  
在默认情况下, Spring应用上下文中所有bean都是作为以单例(singleton) 的形式创建的。 也就是说, 不管给定的一个bean被注入到其他bean多少次, 每次所注入的都是同一个实例。
在大多数情况下, 单例bean是很理想的方案。 初始化和垃圾回收对象实例所带来的成本只留给一些小规模任务, 在这些任务中, 让对象保持无状态并且在应用中反复重用这些对象可能并不合理。
有时候, 可能会发现, 你所使用的类是易变的(mutable) , 它们会保持一些状态, 因此重用是不安全的。 在这种情况下, 将class声明为单例的bean就不是什么好主意了, 因为对象会被污染, 稍后重用的时候会出现意想不到的问题。
Spring定义了多种作用域, 可以基于这些作用域创建bean, 包括:
单例(Singleton) : 在整个应用中, 只创建bean的一个实例。
原型(Prototype) : 每次注入或者通过Spring应用上下文获取的时候, 都会创建一个新的bean实例。
会话(Session) : 在Web应用中, 为每个会话创建一个bean实例。
请求(Rquest) : 在Web应用中, 为每个请求创建一个bean实例。
单例是默认的作用域, 但是正如之前所述, 对于易变的类型, 这并不合适。 如果选择其他的作用域, 要使用@Scope注解, 它可以与@Component或@Bean一起使用。

运行时值注入
当讨论依赖注入的时候, 我们通常所讨论的是将一个bean引用注入到另一个bean的属性或构造器参数中。 它通常来讲指的是将一个对象与另一个对象进行关联。
但是bean装配的另外一个方面指的是将一个值注入到bean的属性或者构造器参数中。
当然可以通过 硬编码实现,但是有的时候, 我们可能会希望避免硬编码值, 而是想让这些值在运行时再确定。 为了实现这些功能, Spring提供了两种在运行时求值的方式:
属性占位符(Property placeholder) 。
Spring表达式语言(SpEL) 。
很快你就会发现这两种技术的用法是类似的, 不过它们的目的和行为是有所差别的。 让我们先看一下属性占位符, 在这两者中它较为简单, 然后再看一下更为强大的SpEL。
注入外部的值
在Spring中, 处理外部值的最简单方式就是声明属性源并通过Spring的Environment来检索属性。使用@PropertySource注解和Environment 。

用Spring表达式语言进行装配  
Spring 3引入了Spring表达式语言(Spring Expression Language, SpEL) , 它能够以一种强大和简洁的方式将值装配到bean属性和构造器参数中, 在这个过程中所使用的表达式会在运行时计算得到值。 使用SpEL, 你可以实现超乎想象的装配效果, 这是使用其他的装配技术难以做到的(甚至是不可能的) 。
SpEL拥有很多特性, 包括:
使用
bean的ID来引用bean;
调用方法和访问对象的属性;
对值进行算术、 关系和逻辑运算;
正则表达式匹配;
集合操作。
SpEL能够用在依赖注入以外的其他地方。 例如, Spring Security支持使用SpEL表达式定义安全限制规则。
另外, 如果你在
Spring MVC应用中使用Thymeleaf模板作为视图的话, 那么这些模板可以使用SpEL表达式引用模型数据。作为起步, 我们看几个SpEL表达式的样例, 以及如何将其注入到bean中。 然后我们会深入学习一些SpEL的基础表达式, 它们能够组合起来形成更为强大的表达式。  

一、Spring核心之2-装配Bean

发表于 2017-09-02 | 分类于 Spring实战4版
Spring配置的可选方案
分为:在XML中进行显式配置;在Java中进行显式配置;隐式的bean发现机制和自动装配。
自动化装配bean
组件扫描(component scanning) : Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring) : Spring自动满足bean之间的依赖。
    @Component 注解的类都会创建为bean。
    @ComponentScan 设置组件扫描的基础包
添加@Autowired注解实现自动装配
自动装配就是让Spring自动满足bean依赖的一种方法, 在满足依赖的过程中, 会在Spring应用上下文中寻找匹配某个bean需求的其他bean。

构造器注入和Setter方法注入
为了声明要进行自动装配, 我们可以借助Spring的@Autowired注解。
比方说, 构造器上添加了@Autowired注解, 这表明当Spring创建对象时, 会通过这个构造器来进行实例化并且会传入一个可设置给这个构造器需要的类型的bean。
@Autowired注解不仅能够用在构造器上, 还能用在属性的Setter方法上。

@ComponentScan("com.hu")

@Configurable

public class SpringConfig {

public Ladeee lade;


@Autowired

public SpringConfig(Ladeee ladeee){

this.lade = ladeee;

}

public static void main(String[] args){

ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);

System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));

System.out.println(ac.getBean("ladeee", Ladeee.class).string);

}

}

@Component

class Ladeee{

@Value("111")

public String string;

}


不管是构造器、 Setter方法还是其他的方法, Spring都会尝试满足方法参数上所声明的依赖。 假如有且只有一个bean匹配依赖需求的话, 那么这个bean将会被装配进来。
如果没有匹配的bean, 那么在应用上下文创建的时候, Spring会抛出一个异常。 为了避免异常的出现, 你可以将@Autowired的required属性设置为false。
将required属性设置为false时, Spring会尝试执行自动装配, 但是如果没有匹配的bean的话, Spring将会让这个bean处于未装配的状态。但是, 把required属性设置为false时, 你需要谨慎对待。 如果在你的代码中没有进行null检查的话, 这个处于未装配状态的属性有可能会出现NullPointerExceptio

通过Java代码装配bean
尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式, 但有时候自动化配置的方案行不通, 因此需要明确配置Spring。 比如说, 你想要将第三方库中的组件装配到你的应用中, 在这种情况下, 是没有办法在它的类上添加@Component和@Autowired注解的, 因此就不能使用自动化装配的方案了。这时候有两种可选方案: Java和XML。
创建简单配置类

@Configurable

public class SpringConfig {


@Bean

public Ladeee getLadeee(){

return new Ladeee();

}


public static void main(String[] args){

ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);

System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));

}

}

class Ladeee{

@Value("111")

public String string;

}


注入配置

@Configurable

public class SpringConfig {


@Bean

public Lad lad(){

return new Lad();

}


/*@Bean

public Ladeee ladeee(){

return new Ladeee(lad());

}*/


@Bean

public Ladeee ladeee(Lad lad){

return new Ladeee(lad);

}


public static void main(String[] args){

ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);

System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));

}

}

class Ladeee{

private Lad lad;

public Ladeee(Lad lad){

this.lad = lad;

}

}

class Lad{

}


XML装配

<bean id="student4" class="com.hu.pojo.Student">

<property name="teacher" ref="teacher1"></property>

</bean>

<bean id="teacher1" class="com.hu.pojo.Teacher">

<property name="id" value="1"></property>

</bean>

混合配置
混合配置就是当xml配置方式和java类配置方式混合使用,互相引用。
xml配置引用类配置
使用<bean>元素,<bean class="com.hu.SpringConfig"/>

类配置中加载xml配置
@ImportResource("spring-config.xml")

类配置之间引用
@Import({SpringConfig.class,...})

xml配置之间引用
<import>元素只能导入其他的XML配置文件

一、Spring核心之1-介绍

发表于 2017-09-01 | 分类于 Spring实战4版
Spring是一个开源框架, 最早由Rod Johnson创建, 并在《Expert One-on-One: J2EE Design and Development》(http://amzn.com/076454385) 这本著作中进行了介绍。 Spring是为了解决企业级应用开发的复杂性而创建的, 使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。 但Spring不仅仅局限于服务器端开发, 任何Java应用都能在简单性、 可测试性和松耦合等方面从Spring中获益。一个Spring组件可以是任何形式的POJO。 
简化Java开发
基于POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码。
几乎Spring所做的任何事情都可以追溯到上述的一条或多条策略。
应用上下文
Spring自带了多种类型的应用上下文,常见的如下:
AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。
AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。
ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义, 把应用上下文的定义文件作为类资源。
FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。
使用bean
其生命周期如下:
如上图,bean工厂执行了若干启动步骤。
1. Spring对bean进行实例化;
2. Spring将值和bean的引用注入到bean对应的属性中;
3. 如果bean实现了BeanNameAware接口, Spring将bean的ID传递给setBean-Name()方法;
4. 如果bean实现了BeanFactoryAware接口, Spring将调用setBeanFactory()方法, 将BeanFactory容器实例传入;
5. 如果bean实现了ApplicationContextAware接口, Spring将调用setApplicationContext()方法, 将bean所在的应用上下文的引用传入进来;
6. 如果bean实现了BeanPostProcessor接口, Spring将调用它们的post-ProcessBeforeInitialization()方法;
7. 如果bean实现了InitializingBean接口, Spring将调用它们的after-PropertiesSet()方法。 类似地, 如果bean使用initmethod声明了初始化方法, 该方法也会被调用;
8. 如果bean实现了BeanPostProcessor接口, Spring将调用它们的post-ProcessAfterInitialization()方法;
9. 此时, bean已经准备就绪, 可以被应用程序使用了, 它们将一直驻留在应用上下文中, 直到该应用上下文被销毁;
10. 如果bean实现了DisposableBean接口, Spring将调用它的destroy()接口方法。 同样, 如果bean使用destroy-method声明了销毁方法, 该方法也会被调用。
模块
在Spring 4.0中, Spring框架的发布版本包括了20个不同的模块,
每个模块会有
3个JAR文件(二进制类库、 源码的JAR文件以及JavaDoc的JAR文件) 。
 
依据其所属的功能可以划分为6类不同的功能  
Spring核心容器
容器是Spring框架最核心的部分, 它管理着Spring应用中bean的创建、 配置和管理。 在该模块中, 包括了Spring bean工厂, 它为Spring提供了DI的功能。 基于bean工厂, 多种Spring应用上下文的实现, 每一种都提供了配置Spring的不同方式。
除了bean工厂和应用上下文, 该模块也提供了许多企业服务, 例如E-mail、 JNDI访问、 EJB集成和调度。
所有的Spring模块都构建于核心容器之上。 当你配置应用时, 其实你隐式地使用了这些类。

Spring的AOP模块
在AOP模块中, Spring对面向切面编程提供了丰富的支持。 这个模块是Spring应用系统中开发切面的基础。 与DI一样, AOP可以帮助应用对象解耦。 借助于AOP, 可以将遍布系统的关注点(例如事务和安全) 从它们所应用的对象中解耦出来。

数据访问与集成
使用JDBC编写代码通常会导致大量的样板式代码, 例如获得数据库连接、 创建语句、 处理结果集到最后关闭数据库连接。 Spring的JDBC和DAO(Data Access Object) 模块抽象了这些样板式代码, 使我们的数据库代码变得简单明了, 还可以避免因为关闭数据库资源失败而引发的问题。 该模块在多种数据库服务的错误信息之上构建了一个语义丰富的异常层, 以后我们再也不需要解释那些隐晦专有的SQL错误信息了!
对于那些更喜欢ORM(Object-Relational Mapping) 工具而不愿意直接使用JDBC的开发者, Spring提供了ORM模块。 Spring的ORM模块建立在对DAO的支持之上, 并为多个ORM框架提供了一种构建DAO的简便方式。 Spring没有尝试去创建自己的ORM解决方案, 而是对许多流行的ORM框架进行了集成, 包括Hibernate、 Java Persisternce API、 Java Data Object和iBATIS SQL Maps。 Spring的事务管理支持所有的ORM框架以及JDBC。
本模块同样包含了在JMS(Java Message Service) 之上构建的Spring抽象层, 它会使用消息以异步的方式与其他应用集成。 从Spring 3.0开始, 本模块还包含对象到XML映射的特性, 它最初是Spring Web Service项目的一部分。
除此之外, 本模块会使用Spring AOP模块为Spring应用中的对象提供事务管理服务。

Web与远程调用
MVC(Model-View-Controller) 模式是一种普遍被接受的构建Web应用的方法, 它可以帮助用户将界面逻辑与应用逻辑分离。 Java从来不缺少MVC框架, Apache的Struts、 JSF、 WebWork和Tapestry都是可选的最流行的MVC框架。
虽然Spring能够与多种流行的MVC框架进行集成, 但它的Web和远程调用模块自带了一个强大的MVC框架, 有助于在Web层提升应用的松耦合水平。
除了面向用户的Web应用, 该模块还提供了多种构建与其他应用交互的远程调用方案。 Spring远程调用功能集成了RMI(Remote MethodInvocation) 、 Hessian、 Burlap、 JAX-WS, 同时Spring还自带了一个远程调用框架: HTTP invoker。 Spring还提供了暴露和使用REST API的良好支持。

Instrumentation
Spring的Instrumentation模块提供了为JVM添加代理(agent) 的功能。 具体来讲, 它为Tomcat提供了一个织入代理, 能够为Tomcat传递类文件, 就像这些文件是被类加载器加载的一样。
如果这听起来有点难以理解, 不必对此过于担心。 这个模块所提供的Instrumentation使用场景非常有限, 在本书中, 我们不会介绍该模块。

测试
鉴于开发者自测的重要性, Spring提供了测试模块以致力于Spring应用的测试。
通过该模块, 你会发现Spring为使用JNDI、 Servlet和Portlet编写单元测试提供了一系列的mock对象实现。 对于集成测试, 该模块为加载Spring应用上下文中的bean集合以及与Spring上下文中的bean进行交互提供了支持。

Spring Portfolio  
事实上, Spring远不是Spring框架锁包含那些模块功能,那些只不过是spring核心的Spring框架层面, 整个Spring Portfolio包括多个构建于核心Spring框架之上的框架和类库。 几乎为每一个领域的Java开发都提供了Spring编程模型。
如: - Spring Web Flow

- Spring Web Service

- Spring Security

- Spring Integretion

- Spring Batch

- Spring Data

- Spring Social

- Spring Mobile

- Spring for Android

- Spring boot
---------------------
Spring Web Flow 建立于Spring MVC框架之上, 它为基于流程的会话式Web应用(可以想一下购物车或者向导功能) 提供了支持。
Spring Web Service 虽然核心的Spring框架提供了将Spring bean以声明的方式发布为Web Service的功能, 但是这些服务是基于一个具有争议性的架构之上而构建的。 这些服务的契约由bean的接口来决定。 Spring Web Service提供了契约优先的Web Service模型, 服务的实现都是为了满足服务的契约而编写的。
Spring Security 安全对于许多应用都是一个非常关键的切面。 利用Spring AOP, Spring Security为Spring应用提供了声明式的安全机制。
Spring Integration 许多企业级应用都需要与其他应用进行交互。 Spring Integration提供了多种通用应用集成模式的Spring声明式风格实现。
Spring Batch 当我们需要对数据进行大量操作时, 没有任何技术可以比批处理更胜任这种场景。 如果需要开发一个批处理应用, 你可以通过Spring Batch,使用Spring强大的面向POJO的编程模型。
Spring Data 使得在Spring中使用任何数据库都变得非常容易。不管你使用文档数据库, 如MongoDB, 图数据库, 如Neo4j, 还是传统的关系型数据库,Spring Data都为持久化提供了一种简单的编程模型。
这包括为多种数据库类型提供了一种自动化的Repository机制, 它负责为你创建Repository的实现。
Spring Social 社交网络是互联网领域中新兴的一种潮流, 越来越多的应用正在融入社交网络网站,这是Spring的一个社交网络扩展模块。不过, Spring Social并不仅仅是tweet和好友,更多的是关注连接(connect) , 而不是社交social) 。 它能够帮助你通过REST API连接Spring应用, 其中有些Spring应用可能原本并没有任何社交方面的功能目标。
Spring Mobile 移动应用是另一个引人瞩目的软件开发领域。 智能手机和平板设备已成为许多用户首选的客户端。 Spring Mobile是Spring MVC新的扩展模块, 用于支持移动Web应用开发。
Spring for Android 与Spring Mobile相关的是Spring Android项目。 这个新项目, 旨在通过Spring框架为开发基于Android设备的本地应用提供某些简单的支持。 最初, 这个项目提供了Spring RestTemplate的一个可以用于Android应用之中的版本。 它还能与Spring Social协作, 使得原生应用可以通过REST API进行社交网络的连接。
Spring Boot Spring极大地简化了众多的编程任务, 减少甚至消除了很多样板式代码, 如果没有Spring的话, 在日常工作中你不得不编写这样的样板代码。Spring Boot是一个崭新的令人兴奋的项目, 它以Spring的视角, 致力于简化Spring本身。Spring Boot大量依赖于自动配置技术, 它能够消除大部分(在很多场景中, 甚至是全部) Spring配置。 它还提供了多个Starter项目, 不管你使用Maven还是Gradle, 这都能减少Spring工程构建文件的大小。

Spring 3.1新特性
Spring 3.1带来了多项有用的新特性和增强, 其中有很多都是关于如何简化和改善配置的。 除此之外, Spring 3.1还提供了声明式缓存的支持以及众多针对Spring MVC的功能增强。 下面的列表展现了Spring 3.1重要的功能升级:
为了解决各种环境下(如开发、 测试和生产) 选择不同配置的问题, Spring 3.1引入了环境profile功能。 借助于profile, 就能根据应用部署在什么环境之中选择不同的数据源bean;
在Spring 3.0基于Java的配置之上, Spring 3.1添加了多个enable注解, 这样就能使用这个注解启用Spring的特定功能;
添加了Spring对声明式缓存的支持, 能够使用简单的注解声明缓存边界和规则, 这与你以前声明事务边界很类似;
新添加的用于构造器注入的c命名空间, 它类似于Spring 2.0所提供的面向属性的p命名空间, p命名空间用于属性注入, 它们都是非常简洁易用的;
Spring开始支持Servlet 3.0, 包括在基于Java的配置中声明Servlet和Filter, 而不再借助于web.xml;
改善Spring对JPA的支持, 使得它能够在Spring中完整地配置JPA, 不必再使用persistence.xml文件。
Spring 3.1还包含了多项针对Spring MVC的功能增强:自动绑定路径变量到模型属性中;
提供了@RequestMappingproduces和consumes属性, 用于匹配请求中的Accept和Content-Type头部信息;
提供了@RequestPart注解, 用于将multipart请求中的某些部分绑定到处理器的方法参数中;
支持Flash属性(在redirect请求之后依然能够存活的属性) 以及用于在请求间存放flash属性的RedirectAttributes类型。

除了Spring 3.1所提供的新功能以外, 同等重要的是要注意Spring 3.1不再支持的功能。 具体来讲, 为了支持原生的EntityManager, Spring的JpaTemplate和JpaDaoSupport类被废弃掉了。

Spring 3.2新特性
Spring 3.2的控制器(Controller) 可以使用Servlet 3.0的异步请求, 允许在一个独立的线程中处理请求, 从而将Servlet线程解放出来处理更多的请求;
尽管从Spring 2.5开始, Spring MVC控制器就能以POJO的形式进行很便利地测试, 但是Spring 3.2引入了Spring MVC测试框架, 用于为控制器编写更为丰富的测试, 断言它们作为控制器的行为行为是否正确, 而且在使用的过程中并不需要Servlet容器;
除了提升控制器的测试功能, Spring 3.2还包含了基于RestTemplate的客户端的测试支持, 在测试的过程中, 不需要往真正的REST端点上发送请求;
@ControllerAdvice注解能够将通用的@ExceptionHandler、 @ InitBinder和@ModelAttributes方法收集到一个类中, 并应用到所有控制器上;
在Spring 3.2之前, 只能通过ContentNegotiatingViewResolver使用完整的内容协商(full content negotiation) 功能。 但是在Spring 3.2中, 完整的内容协商功能可以在整个Spring MVC中使用, 即便是依赖于消息转换器(message converter) 使用和产生内容的控制器方法也能使用该功能;
Spring MVC 3.2包含了一个新的@MatrixVariable注解, 这个注解能够将请求中的矩阵变量(matrix variable) 绑定到处理器的方法参数中;
基础的抽象类AbstractDispatcherServletInitializer能够非常便利地配置DispatcherServlet, 而不必再使用web.xml。与之类似, 当你希望通过基于Java的方式来配置Spring的时候, 可以使用Abstract- AnnotationConfigDispatcherServletInitializer的子类;
新增了ResponseEntityExceptionHandler, 可以用来替代Default- HandlerExceptionResolver。 ResponseEntityExceptionHandler方法会返回ResponseEntity<Object>, 而不是ModelAndView;
RestTemplate和@RequestBody的参数可以支持范型;
RestTemplate和@RequestMapping可以支持HTTP PATCH方法;
在拦截器匹配时, 支持使用URL模式将其排除在拦截器的处理功能之外。虽然Spring MVC是Spring 3.2改善的核心内容, 但是它依然还增加了多项非MVC的功能改善。 下面列出了Spring 3.2中几项最为有意思的新特性:
@Autowired、 @Value和@Bean注解能够作为元注解, 用于创建自定义的注入和bean声明注解;
@DateTimeFormat注解不再强依赖JodaTime。 如果提供了JodaTime, 就会使用它, 否则的话, 会使用SimpleDateFormat;
Spring的声明式缓存提供了对JCache 0.5的支持;
支持定义全局的格式来解析和渲染日期与时间;
在集成测试中, 能够配置和加载WebApplicationContext;
在集成测试中, 能够针对request和session作用域的bean进行测试。
在本书的多个章节中, 都能看到Spring 3.2的特性, 尤其是在Web和REST相关的章节中。

Spring 4.0新特性

Spring提供了对WebSocket编程的支持, 包括支持JSR-356——Java API for WebSocket;
鉴于WebSocket仅仅提供了一种低层次的API, 急需高层次的抽象, 因此Spring 4.0在WebSocket之上提供了一个高层次的面向消息的编程模型, 该模型基于SockJS, 并且包含了对STOMP协议的支持;
新的消息(messaging) 模块, 很多的类型来源于Spring Integration项目。 这个消息模块支持Spring的SockJS/STOMP功能, 同时提供了基于模板的方式发布消息;
Spring是第一批(如果不说是第一个的话) 支持Java 8特性的Java框架, 比如它所支持的lambda表达式。 别的暂且不说, 这首先能够让使用特定的回调接口(如RowMapper和JdbcTemplate) 更加简洁, 代码更加易读;
与Java 8同时得到支持的是JSR-310——Date与Time API, 在处理日期和时间时, 它为开发者提供了比java.util.Date或java.util.Calendar更丰富的API;
为Groovy开发的应用程序提供了更加顺畅的编程体验, 尤其是支持非常便利地完全采用Groovy开发Spring应用程序。 随这些一起提供的是来自于Grails的BeanBuilder, 借助它能够通过Groovy配置Spring应用;
添加了条件化创建bean的功能, 在这里只有开发人员定义的条件满足时, 才会创建所声明的bean;
Spring 4.0包含了Spring RestTemplate的一个新的异步实现, 它会立即返回并且允许在操作完成后执行回调;
添加了对多项JEE规范的支持, 包括JMS 2.0、 JTA 1.2、 JPA 2.1和Bean Validation 1.1。



Dubbo基于Spring Boot+ZK例子

发表于 2017-08-14 | 分类于 dubbo

idea中创建一个接口模块对外提供的api

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public interface UserService {
User getById(int id);
List<User> getUsers();
}
package com.hu.dubbo.common.entity;

import java.io.Serializable;

public class User implements Serializable {
private static final long serialVersionUID = 5438739023477089251L;
private int id;
private String name;
private String userName;
private int age;
private String passWrod;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getPassWrod() {
return passWrod;
}

public void setPassWrod(String passWrod) {
this.passWrod = passWrod;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", passWrod='" + passWrod + '\'' +
'}';
}
}

引入依赖:

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
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>

<groupId>com.hu</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

创建服务端模块

引入依赖:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
 <?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<groupId>com.hu</groupId>
<artifactId>provider</artifactId>
<version>1.0-SNAPSHOT</version>


<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<zkclient.version>0.10</zkclient.version>
</properties>


<dependencies>
<dependency>
<groupId>com.hu</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.5</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>


<!--zookeeper的客户端依赖-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>${zkclient.version}</version>
</dependency>


<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!-- alibaba的druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.11</version>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

dubbo配置:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
 <?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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--提供方信息,名字,用于计算依赖关系-->
<dubbo:application name="provider-demo"/>

<!-- 注册中心的ip地址 这里使用ZK-->
<!--参考https://blog.csdn.net/fuyuwei2015/article/details/72836075/
username <username> string 可选 服务治理 登录注册中心用户名,如果注册中心不需要验证可不填
password <password> string 可选 服务治理 登录注册中心密码,如果注册中心不需要验证可不填
timeout int 可选 5000 性能调优 注册中心请求超时时间(毫秒)
register boolean 可选 true 服务治理 是否向此注册中心注册服务,如果设为false,将只订阅,不注册
subscribe boolean 可选 true 服务治理 是否向此注册中心订阅服务,如果设为false,将只注册,不订阅-->
<dubbo:registry address="zookeeper://192.168.1.10:2181"/>

<!-- 配置协议,name是使用什么样的协议,port使用dubbo协议在 20880 暴露服务-->
<!--threadpool:线程池类型,可选:fixed/cached ,默认fixed 。-->
<!--threads :服务线程池大小(固定大小) ,默认为100-->
<!--payload:请求及响应数据包大小限制,单位:字节,默认为88388608(=8M)-->
<!--threadpool=""
fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
cached 缓存线程池,空闲一分钟自动删除,需要时重建。
limited可伸缩线程池,但池中的线程数只会增长不会收缩。(为避免收缩时突然来了大流量引起的性能问题)。-->
<!--服务线程模型的配置
如:<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
其中Dispatcher参数和threadpool参数分别配置了请求转发的处理模式,和处理请求的线程池模型;
Dispatcher 可选配置参数:
all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
direct 所有消息都不派发到线程池,全部在IO线程上直接执行。
message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在IO线程上执行。
execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在IO线程上执行。
connection 在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。-->
<!--register boolean 可选 true 服务治理 该协议的服务是否注册到注册中心-->
<dubbo:protocol name="dubbo" port="20880"/>

<!--监控中心配置,用于配置连接监控中心相关信息,可选。
即开启了monitor监控,并且指定了监控中心服务器 9090端口是Prometheus的默认端口,dubbo提供的监控中心比较简单,可以使用Prometheus作为监控中心来存储监控数据。
-->
<dubbo:monitor protocol="" address="127.0.0.1:9090"></dubbo:monitor>
<!--<dubbo:module/> 模块配置,用于配置当前模块信息,可选。-->

<!--提供方配置,
比如初始化缓存,等待相关资源就位等,可以使用delay进行延迟暴露。
accepts 连接数 或在<dubbo:protocol 中accepts配置也是一样
-->
<!--<dubbo:provider delay="-1" />-->

<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类 -->
<dubbo:annotation package="com.hu.dubbo.provider.service.impl"/>

<!-- 服务配置,注册接口,声明暴露的服务接口 version属性是版本号 group是服务分组 ref是服务暴露的接口实现类 -->
<!--version string 可选 版本号 当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。-->
<!--group string 可选 服务分组,当一个接口有多个实现,可以用分组区分-->
<!--delay int 可选 0 性能调优 延迟注册服务时间(毫秒) ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务-->
<!--retries int 可选 2 性能调优 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0-->
<!--executes 服务器端并发执行的限制的配置,接口中的每个方法在并行执行中的线程数-->
<!--loadbalance string 负载均衡策略,可选值:random,roundrobin,leastactive
可选参数有:
random:随机,按权重设计随机概率
roundRobin:轮询,按公约后的权重设置轮询比率
leastActive:最少活跃调用数,相同活跃数的随机,使慢的处理者收到更少的请求
consistentHash:一致性哈希,使得相同参数的请求总是发到同一提供者-->
<!--集群容错配置,在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
配置方式:<dubbo:service cluster="failsafe" />或者:<dubbo:reference cluster="failsafe" />
Failover Cluster(默认选择) 失败自动切换,当出现失败,重试其它服务器 1。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
如:<dubbo:service retries="2" /> //全部请求错误重试次数,不包含第一次。或<dubbo:reference retries="2" />
Failfast Cluster 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
Broadcast Cluster 广播调用所有提供者,逐个调用,任意一台报错则报错 2。通常用于通知所有提供者更新缓存或日志等本地资源信息-->


<dubbo:service interface="com.hu.dubbo.common.api.UserService" ref="userService"/>
<!-- 本地实现服务 -->
<bean id="userService" class="com.hu.dubbo.provider.service.impl.UserServiceImpl"/>
</beans>

spring-boot配置信息:

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
 server:
port: 8082
servlet:
context-path: /
spring:
datasource:
name: test
url: jdbc:mysql://192.168.1.10:33061/demo
username: root
password: 123456
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
mybatis:
type-aliases-package: entity

Mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Mapper
public interface UserMapper {
@Results(id="userMap",value={
@Result(column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "userName", property = "userName"),
@Result(column = "age", property = "age"),
@Result(column = "passWrod", property = "passWrod")
})
@Select("select * from users")
List<User> getAllUser();


@Select("select * from users where id=#{id}")
@ResultMap("userMap")
User getUserById(int id);
}

实现接口:

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
 @Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;

@Override
public User getById(int id) {
return userMapper.getUserById(id);
}

@Override
public List<User> getUsers() {
return userMapper.getAllUser();
}
}

启动:
@SpringBootApplication
@ImportResource({"classpath:spring-dubbo.xml"})
public class ProviderApplication {

public static void main(String[] args) throws IOException {
SpringApplication.run(ProviderApplication.class, args);
System.out.println("服务端启动成功!!!");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}

启动后测试,打开监控中心和管理控制台可以看到有服务注册成功:

20200413172014

创建消费端

依赖:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 <?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>

<groupId>com.hu</groupId>
<artifactId>customer</artifactId>
<version>1.0-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<zkclient.version>0.10</zkclient.version>
</properties>

<dependencies>
<dependency>
<groupId>com.hu</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.5</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>

<!--zookeeper的客户端依赖-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>${zkclient.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>

dubbo:

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
 <?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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--消费方信息,名字,用于计算依赖关系-->
<dubbo:application name="consumer-demo"/>
<!--注册中心配置,用于配置连接注册中心相关信息。-->
<dubbo:registry address="zookeeper://192.168.1.10:2181"/>
<!--协议配置,用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受。-->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 生成远程代理 引用服务配置,用于创建一个远程服务代理,一个引用可以指向多个注册中心,配置多个 reference配置和服务方的server类似-->
<!--check boolean 是否启动检查-->
<!--url string 点对点直连 url="dubbo://localhost:20890"
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,
点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表。-->
<!--connections 连接数配置-->
<!--actives 客户端并发执行的限制,接口中每个方法并行连接数,可以在reference中添加 <dubbo:method子节点配置每个方法连接数
可以配置异步调用 async="true" -->
<!--loadbalance 负载均衡的配置 可选参数有:
random:随机,按权重设计随机概率
roundRobin:轮询,按公约后的权重设置轮询比率
leastActive:最少活跃调用数,相同活跃数的随机,使慢的处理者收到更少的请求
consistentHash:一致性哈希,使得相同参数的请求总是发到同一提供者-->
<!--merger : 分组聚合客户端配置 按组合并返回结果,比如菜单服务,接口一样,但有多种实现,用group区分;
如:<dubbo:reference interface="com.xxx.xxxx" group="aaa,bbb" merger="true" />-->
<dubbo:reference id="userService" check="false" interface="com.hu.dubbo.common.api.UserService"/>
<dubbo:annotation package="com.hu.dubbo.customer.constroller"/>
</beans>

spring boot配置:
不需要配置,使用默认端口8080

控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("/users")
public class UserController {
@Resource
private UserService userService;

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable int id) {
System.out.println("---------远程调用--------");
return userService.getById(id);
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public List<User> getUser() {
System.out.println("---------远程调用--------");
return userService.getUsers();
}
}

入口:

1
2
3
4
5
6
7
8
9
 @SpringBootApplication
@ImportResource({"classpath:spring-dubbo.xml"})
public class CustomerApplication {

public static void main(String[] args) {
SpringApplication.run(CustomerApplication.class, args);
System.out.println("消费端启动成功!!!");
}
}

20200413172151

浏览器访问测试数据:如下

20200413172215

Dubbo介绍功能原理和安装

发表于 2017-08-13 | 分类于 dubbo

介绍

Dubbo是一个分布式、高性能、透明化的RPC服务框架,提供服务自动注册、自动发现等高效服务治理方案。

20200413165713

运行原理

启动容器,相当于在启动Dubbo的Provider
启动后会去注册中心进行注册.注册所有可以提供的服务列表
在Consumer启动后会去Registry中获取服务列表和Provider的地址.进行订阅.
当Provider有修改后,注册中心会把消息推送给Consummer
使用了观察者设计模式(又叫发布/订阅设计模式)
根据获取到的Provider地址,真实调用Provider中功能.
在Consumer方使用了代理设计模式.创建一个Provider方类的一个代理对象.通过代理对象获取Provider中真实功能,起到保护Provider真实功能的作用.
Consumer和Provider每隔1分钟向Monitor发送统计信息,统计信息包含,访问次数,频率等.

功能

很多功能配置都写在了Demo中了。

容错
当服务提供者出现异常时,消费者是否能够正常处理,比如把生产者贵关闭了。

报错!!!

这时,进入dubbo-admin在消费者中把服务容错开启,如下:

20200413171221

当远程调用失败时,返回的是空对象。

屏蔽

当对某个服务启动屏蔽时,此时消费者调用该服务时均会返回空对象。首先我们需要确保提供者与消费者均已启动,然后在消费者中对提供者进行屏蔽。
屏蔽后,调用服务也是返回为空。

其实屏蔽与容错还有更高级的玩法,最基本的屏蔽是返回空对象,我们甚至可以自定义返回内容。
进入动态配置,可以配置更为详细的信息。

服务权重

就是可以通过权重来设置被调用的几率

负载均衡

dubbo提供了三个基本的负载均衡策略,分别是 随机访问、轮询、最少并发。对服务的负载配置。

环境搭建

在Linux安装好zookeeper,直接安装一个单机版供测试用。

下载文件到Linux目录,解压配置启动:

[root@localhost local]# tar -zxf zookeeper-3.4.5.tar.gz
[root@localhost local]# cd zookeeper-3.4.5

[root@localhost zookeeper-3.4.5]# cp conf/zoo_sample.cfg conf/zoo.cfg
[root@localhost zookeeper-3.4.5]# vim conf/zoo
[root@localhost zookeeper-3.4.5]# vim conf/zoo.cfg

修改配置为:

dataDir=/usr/local/zookeeper-3.4.5/data

启动:

[root@localhost zookeeper-3.4.5]# ./bin/zkServer.sh start
[root@localhost zookeeper-3.4.5]# ./bin/zkServer.sh status
JMX enabled by default
Using config: /usr/local/zookeeper-3.4.5/bin/../conf/zoo.cfg
Mode: standalone

这样就使用单机方式

控制台dubbo-admin

前往github上下载dubbo-admin:https://github.com/apache/incubator-dubbo/tree/dubbo-2.6.0
注意:dubbo-2.6.1以后的版本不再有dubbo-admin

解压 进入dubbo-admin文件夹,输入cmd,打开dos窗口,输入以下命令:

mvn package -Dmaven.skip.test=true

查看打包好的war

部署到tomcat

复制dubbo-admin-2.6.0.war,到tomcat的webapps目录下

dubbo-admin-2.6.0\WEB-INF目录下,找到dubbo.properties,修改如下:

dubbo.registry.address=zookeeper://127.0.0.1:2181

监控中心dubbo-monitor

dubbo-monitor主要用来统计服务的调用次调和调用时间的监控中心,服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心,监控中心则使用数据绘制图表来显示。

dubbo-monitor-simple的搭建

下载dubbo-admin,
前往github上下载dubbo-admin:https://github.com/apache/incubator-dubbo/tree/dubbo-2.6.0

解压 进入dubbo-monitor文件夹,输入cmd,打开dos窗口,输入以下命令:

mvn package -Dmaven.skip.test=true

得到包,直接拿去用。

或者

wget http://code.alibabatech.com/mvn/releases/com/alibaba/dubbo‐monitor‐simple/2.4.1/dubbo‐monitor‐simple‐2.4.1‐assembly.tar.gz
tar zxvf dubbo‐monitor‐simple‐2.4.1‐assembly.tar.gz

下载到Linux中,修改配置 conf/dubbo.properties

1
2
3
4
dubbo.registry.address=zookeeper://127.0.0.1:2181设置注册中心地址,这里设置为zk的地址
dubbo.protocol.port=7070,为monitor提供的远程服务监听端口,服务提供者和消费者会调用这个端口提供的服务,发送统计信息到monitor
dubbo.charts.directory和dubbo.statistics.directory为monitor本地存放的监控数据文件的位置
dubbo.jetty.port=9091 设置jetty容器的监听地址,类似于tomcat的8080端口,这里设置为9091

运行;

1
2
3
[root@localhost bin]# ./start.sh
Starting the simple-monitor .......OK!
PID: 49469

20200413171711

在服务提供方和消费方需要配置如下:<dubbo:monitor protocol=”registry”/> protocol为”registry”,表示服务提供方和消费方从注册中心发现监控中心(monitor)地址。

上一页1…202122…25下一页
初晨

初晨

永远不要说你知道本质,更别说真相了。

249 日志
46 分类
109 标签
近期文章
  • WebSocket、Socket、TCP、HTTP区别
  • Springboot项目的接口防刷
  • 深入理解Volatile关键字及其实现原理
  • 使用vscode搭建个人笔记环境
  • HBase介绍安装与操作
© 2018 — 2020 Copyright
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4