2018年12月3日

     摘要: from:https://cloud.tencent.com/developer/article/1004475最近研发的项目对 DB 依赖比较重,梳理了这段时间使用MySQL遇到的8个比较具有代表性的问题,答案也比较偏自己的开发实践,没有 DBA专业和深入,有出入的请使劲拍砖!MySQL读写性能是多少,有哪些性能相关的配置参数?MySQL负载高时,如何找到是由哪些SQL引起的?如何针对具体的SQ...  阅读全文
posted @ 2018-12-03 15:55 小马歌 阅读(23) | 评论 (0)编辑 收藏
 
from://mingxinglai.com/cn/2015/12/material-of-mysql/

我这里推荐几本MySQL的好书,应该能够有效避免学习MySQL的弯路,并且达到一个不错的水平。 我这里推荐的书或材料分为两个部分,分别是MySQL的使用和MySQL的源码学习。在介绍的过程中,我会穿插简单的评语或感想。

1.MySQL的使用

1.1 MySQL技术内幕:InnoDB存储引擎

学习MySQL的使用,首推姜承尧的《MySQL技术内幕:InnoDB存储引擎》,当然不是因为姜sir是我的经理才推荐这本书。这本书确实做到了由渐入深、深入浅出,是中国人写的最赞的MySQL技术书籍,符合国人的思维方式和阅读习惯,而且,这本书简直就是面试宝典,对于近期有求职MySQL相关岗位的朋友,可以认真阅读,对找工作有很大的帮助。当然,也有人说这本书入门难度较大,这个就自己取舍了,个人建议就以这本书入门即可,有不懂的地方可以求助官方手册和google。

MySQL技术内幕

1.2 MySQL的官方手册

我刚开始学习MySQL的时候误区就是,没有好好阅读MySQL的官方手册。例如,我刚开始很难理解InnoDB的锁,尤其是各个情况下如何加锁,这个问题在我师弟进入百度做DBA时,也困扰了他一阵子,我们两还讨论来讨论去,其实,MySQL官方手册已经写得清清楚楚,什么样的SQL语句加什么样的锁,当然,MySQL的官方手册非常庞大,一时半会很难看完,建议先看InnoDB相关的部分。

//dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html

1.3 MySQL排错指南

MySQL排错指南》是2015年夏天引入中国的书籍,这本书可以说是DBA速成指南,介绍的内容其实比较简单,但是也非常实用,对于DBA这个讲究经验的工种,这本书就是传授经验的,可能对有较多工作经验的DBA来说,这本书基本没有什么用,但是,对于刚入职场的新人,或学校里的学生,这本书会有较大的帮助,非常推荐。

MySQL排错指南

1.4 高性能MySQL

高性能MySQL》是MySQL领域的经典之作,拥有广泛的影响力,学习MySQL的朋友都应该有所耳闻,所以我就不作过多介绍,唯一的建议就是仔细看、认真看、多看几遍,我每次看都会有不小的收获。这就是一本虽然书很厚,但是需要一页一页、一行一行都认真看的书。

高性能MySQL

1.5 数据库索引设计与优化

如果认真学习完前面几本书,基本上都已经对MySQL掌握得不错了,但是,如果不了解如何设计一个好的索引,仍然不能成为牛逼的DBA,牛逼的DBA和不牛逼的DBA,一半就是看对索引的掌握情况,《数据库索引设计与优化》就是从普通DBA走向牛逼DBA的捷径,这本书在淘宝内部非常推崇,但是在中国名气却不是很大,很多人不了解。这本书也是今年夏天刚有中文版本的,非常值得入手以后跟着练习,虽然知道的人不多,豆瓣上也几乎没有什么评价,但是,强烈推荐、吐血推荐!

数据库索引设计与优化

1.6 Effective MySQL系列

Effective MySQL系列》是指:

  • Effective MySQL Replication Techniques in Depth
  • Effective MySQL之SQL语句最优化
  • Effective MySQL之备份与恢复

effective

这一系列并不如前面推荐的好,其中,我只看了前两本,这几本书只能算是小册子,如果有时间可以看看,对某一个”???#8221;进入深入了解。

2.MySQL的源码

关于MySQL源码的书非常少,还好现在市面上有两本不错的书,而且刚好一本讲server层,一本讲innodb存储引擎层,对于学习MySQL源码会很有帮助,至少能够更加快速地了解MySQL的原理和宏观结构,然后再深入细节。此外,还有一些博客或PPT将得也很不错,这里推荐最好的几份材料。

2.1 InnoDB - A journey to the core

InnoDB - A journey to the core》 是MySQL大牛Jeremy Cole写的PPT,介绍InnoDB的存储???,即表空间、区、段、页的格式、记录的格式、槽等等。是学习Innodb存储的最好的材料。感谢Jeremy Cole!

2.2 深入MySQL源码

登博的分享《深入MySQL源码》,相信很多想了解MySQL源码的朋友已经知道这份PPT,就不过多介绍,不过,要多说一句,登博的参考资料里列出的几个博客,都要关注一下,干货满满,是学习MySQL必须关注的博客。

2.3 深入理解MySQL核心技术

深入理解MySQL核心技术》是第一本关于MySQL源码的书,着重介绍了MySQL的Server层,重点介绍了宏观架构,对于刚开始学习MySQL源码的人,相信会有很大的帮助,我在学习MySQL源码的过程中,反复的翻阅了几遍,这本书刚开始看的时候会很痛苦,但是,对于研究MySQL源码,非常有帮助,就看你是否需要,如果没有研究MySQL源码的决心,这本书应该会被唾弃。

深入理解MySQL核心技术

2.4 MySQL内核:InnoDB存储引擎

我们组的同事写的《MySQL内核:InnoDB存储引擎》,可能宇宙范围内这本书就数我学得最认真了,虽然书中有很多编辑错误,但是,平心而论,还是写得非常好的,相对于《深入理解MySQL核心技术》,可读性更强一些,建议研究Innodb存储引擎的朋友,可以了解一下,先对Innodb有一个宏观的概念,对大致原理有一个整体的了解,然后再深入细节,肯定会比自己从头开始研究会快很多,这本书可以帮助你事半功倍。

MySQL内核

2.5 MySQL Internals Manual

MySQL Internals Manual》相对于MySQL Manual来说,写的太粗糙,谁让人家是官方文档呢,研究MySQL源码的时候可以简单地参考一下,但是,还是不要指望文档能够回答你的问题,还需要看代码才行。

//dev.mysql.com/doc/internals/en/

2.6 MariaDB原理与实现

评论里提到的《MariaDB原理与实现》我也买了一本,还不错,MariaDB讲的并不多,重点讲了Group Commit、线程池和复制的实现,都是MySQL Server层的知识,对MySQL Server层感兴趣的可以参考一下。

MariaDB

3. 后记

希望这里推荐的材料对学习MySQL的同学、朋友有所帮助,也欢迎推荐靠谱的学习材料,大家共同进步。

posted @ 2018-12-03 15:54 小马歌 阅读(30) | 评论 (0)编辑 收藏

2018年11月23日

     摘要: from:https://yq.aliyun.com/articles/69520?utm_content=m_10360#6摘要: # 我的问题排查工具箱 ## 前言 平时的工作中经常碰到很多疑难问题的处理,在解决问题的同时,有一些工具起到了相当大的作用,在此书写下来,一是作为笔记,可以让自己后续忘记了可快速翻阅,二是分享,希望看到此文的同学们可以拿出自己日常觉得帮助很大的工具,大家一...  阅读全文
posted @ 2018-11-23 10:47 小马歌 阅读(32) | 评论 (0)编辑 收藏

2018年9月3日

     摘要: from:https://www.cnblogs.com/Anker/p/3271773.html1、前言  之前在看《unix环境高级编程》第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊。今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎么解决,我只停留在概念上面,没有深入,倍感惭愧。晚上回来google了一下,再次参考APUE,认真总结一下,加深理解。2、基本概念  我...  阅读全文
posted @ 2018-09-03 19:53 小马歌 阅读(31) | 评论 (0)编辑 收藏

2017年10月12日

from://blog.csdn.net/vfush/article/details/51086274

最近做了个Web项目, 架构上使用了 Nginx +tomcat 集群, 且全站HTTPS,用nginx 做负载,nginx和tomcat 使用内网http通信,遇到http css,js静态资源被浏览器拦截问题,网上搜索到的很多文章在描述 Nginx + Tomcat 启用 HTTPS 支持的时候,都必须在 Nginx 和 Tomcat 两边同时配置 SSL 支持,今天做个总结。

遇到问题

  1. nginx强制使用https访问(http跳转到https)
  2. http的js,css 等静态资源被浏览器拦截(http不被信任)

最后的解决方案

首先解决第一个问题全站https 
参考 
三种方式,跟大家共享一下

nginx的rewrite方法

server {   listen  192.168.1.111:80;   server_name test.com;   rewrite ^(.*)$  https://$host$1 permanent; }   

nginx的497状态码,我选择了这种方式

server {       listen       192.168.1.11:443;  #ssl端口       listen       192.168.1.11:80;   #用户习惯用http访问,加上80,后面通过497状态码让它自动跳到443端口       server_name  test.com;       #为一个server{......}开启ssl支持       ssl                  on;       #指定PEM格式的证书文件        ssl_certificate      /etc/nginx/test.pem;        #指定PEM格式的私钥文件       ssl_certificate_key  /etc/nginx/test.key;        #让http请求重定向到https请求        error_page 497  https://$host$uri?$args;   }   

index.html刷新网页

<html>   <meta http-equiv="refresh" content="0;url=https://test.com/">   </html>  

当http访问到index.html时候自动跳转到https


接下来解决第二个问题 
如果tomcat 和nginx 双方没有配置X-Forwarded-Proto tomcat就不能正确区分实际用户是http 还是https,导致tomcat 里配置的静态资源被认为是http而被浏览器拦截,request.getScheme()总是 http,而不是实际的http或https

分别配置一下 Nginx 和 Tomcat ,果然好了。 
配置 Nginx 的转发选项:

 proxy_set_header       Host $host;       proxy_set_header  X-Real-IP  $remote_addr;       proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;       proxy_set_header X-Forwarded-Proto  $scheme;  
  • 1
  • 2
  • 3
  • 4

配置Tomcat server.xml 的 Engine ??橄屡渲靡桓?Valve:

<Valve className="org.apache.catalina.valves.RemoteIpValve"   remoteIpHeader="X-Forwarded-For"   protocolHeader="X-Forwarded-Proto"   protocolHeaderHttpsValue="https"/>  
  • 1
  • 2
  • 3
  • 4

非80端口配置 
Nginx增加以下配置 
proxy_set_header Host $host:$server_port; 非80端口 ,用80端口时 不需要$server_port 
proxy_set_header X-Real-IP $remote_addr; 
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
proxy_set_header X-Forwarded-Proto $scheme; 

Tomcat server.xml配置 
<Engine name="Catalina" defaultHost="localhost"> 
<Valve className="org.apache.catalina.valves.RemoteIpValve" 
remoteIpHeader="X-Forwarded-For" 
protocolHeader="X-Forwarded-Proto" 
protocolHeaderHttpsValue="https" httpsServerPort="7001"/> 非80端口时,必须增加httpsServerPort配置,不然request.getServerPort()方法返回 443. 
</Engine>

关于 RemoteIpValve,可以阅读下 doc

//tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/valves/RemoteIpValve.html

posted @ 2017-10-12 11:02 小马歌 阅读(202) | 评论 (0)编辑 收藏

2017年8月11日

gerrit还是轻易不要尝试引入,它的权限管理,真是复杂极了。对于小型团队,初期这将是个噩梦,但是对于像OpenStack,安卓这种大型team,又是一把利器。
下面尝试测试了两个用户的简单情况,很多配置都是系统默认,没有进行啥复杂配置,即使这样也是错误百出,光一个commit就要折腾半天,而且还有些机制没搞清楚。
首先要做的准备工作就是准备两个gerrit用户,user1和user2,并且分别把user1和user2的ssh pub-key通过gerrit setting添加好。
1. 首先user1创建一个叫HelloWord的project。
   如何创建project请参考前期博客或者官方文档。
2. user1在自己的工作环境中把HelloWord clone下来
[user1@jenkins ~]$ git clone ssh://user1@gerrit.example.com:29418/HelloWorld.git
Initialized empty Git repository in /home/user1/HelloWorld/.git/
remote: Counting objects: 2, done
remote: Finding sources: 100% (2/2)
remote: Total 2 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (2/2), done.
加入user1没有添加ssh pubkey的话,这一步会出permission deny

clone后,创建一个README文件并add,commit
[user1@jenkins ~]$ cd HelloWorld
[user1@jenkins HelloWorld]$ ls
[user1@jenkins HelloWorld]$ touch README
[penxiao@jenkins test]$ git add README 
[penxiao@jenkins test]$ git commit -m add README
这里注意一点,在下面要push之前,一定要配置好git config的 username和email
可以通过命令行或者直接编辑 ~/.gitconfig文件实现,而且email一定要和gerrit里注册的email一致,否者push也会出错。
[user1@jenkins HelloWorld]$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 213 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Processing changes: refs: 1, done    
To ssh://user1@gerrit.example.com:29418/HelloWorld.git
 * [new branch]      master -> master
[user1@jenkins HelloWorld]$

在gerrit的gitweb链接可以查看push的文件。
3. user2加入
[user2@jenkins ~]$ git clone ssh://user1@gerrit.example.com:29418/HelloWorld.git
Initialized empty Git repository in /home/user2/HelloWorld/.git/
remote: Counting objects: 3, done
remote: Finding sources: 100% (3/3)
remote: Total 3 (delta 0), reused 3 (delta 0)
Receiving objects: 100% (3/3), done.
[user2@jenkins ~]$ cd HelloWorld
[user2@jenkins HelloWorld]$ ls
README
[user2@jenkins HelloWorld]$ 
user2对README文件进行修改,然后要commit,push
?。?!也同样注意,user2的git config,username和email的配置,email要和gerrit setting里的一致。
commit完以后可以看到
[user2@jenkins HelloWorld]$ git log
commit 7959fe47bc2d2f53539a1861aa6b0d71afe0a531
Author: user2 <user2@gerrit.com>
Date:   Thu Dec 12 00:24:53 2013 -0500
    edit README
commit 98099fc0de3ba889b18cf36f9a5af267b3ddb501
Author: user1 <user@gerrit.com>
Date:   Thu Dec 12 00:15:08 2013 -0500
    add README
[user2@jenkins HelloWorld]$
现在user2要把这次的改变push到gerrit,可以么?
不行的,可以看到
[user2@jenkins HelloWorld]$ git push origin master
Counting objects: 5, done.
Writing objects: 100% (3/3), 249 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Branch refs/heads/master:
remote: You are not allowed to perform this operation.
remote: To push into this reference you need 'Push' rights.
remote: User: user2
remote: Please read the documentation and contact an administrator
remote: if you feel the configuration is incorrect
remote: Processing changes: refs: 1, done    
To ssh://user2@gerrit.example.com:29418/HelloWorld.git
 ! [remote rejected] master -> master (prohibited by Gerrit)
error: failed to push some refs to 'ssh://user2@gerrit.example.com:29418/HelloWorld.git'
[user2@jenkins HelloWorld]$ 
这就是gerrit的精髓所在了。原因是gerrit不允许直接将本地修改同步到远程仓库??突Щ匦胂萷ush到远程仓库的refs/for/*分支上,等待审核。这也是为什么我们需要使用gerrit的原因。gerrit本身就是个代码审核工具。

接下来更该push的地址:  
[user2@jenkins HelloWorld]$git config remote.origin.push refs/heads/*:refs/for/*  
此命令实际是更改的是本地仓库test_project/.git/config文件。 
再次push   
[user2@jenkins HelloWorld]$git push origin  
这次不要加master
[user2@jenkins HelloWorld]$ git push origin
Counting objects: 5, done.
Writing objects: 100% (3/3), 249 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Processing changes: refs: 1, done    
remote: ERROR: missing Change-Id in commit message footer
remote: Suggestion for commit message:
remote: edit README
remote: 
remote: Change-Id: I7959fe47bc2d2f53539a1861aa6b0d71afe0a531
remote: 
remote: Hint: To automatically insert Change-Id, install the hook:
remote:   gitdir=$(git rev-parse --git-dir); scp -p -P 29418 user2@gerrit.example.com:hooks/commit-msg ${gitdir}/hooks/
remote: 
remote: 
To ssh://user2@gerrit.example.com:29418/HelloWorld.git
 ! [remote rejected] master -> refs/for/master (missing Change-Id in commit message footer)
error: failed to push some refs to 'ssh://user2@gerrit.example.com:29418/HelloWorld.git'
尼玛,还是不行,说缺change-Id,为了能让每次commit能自己insert 这个change-id,需要从gerrit server上下载一个脚本
[user2@jenkins HelloWorld] scp -p 29418 user2@gerrit.example.com:hooks/commit-msg <local path to your git>/.git/hooks/
然后重新commit
[user2@jenkins HelloWorld]$ git commit --amend
再次查看git log
[user2@jenkins HelloWorld]$ git log
commit f6b5919170875b5b4870fca2ab906c516c97006e
Author: user2 <user2@gerrit.com>
Date:   Thu Dec 12 00:24:53 2013 -0500
    edit by user2
    
    Change-Id: Ieac68bebefee7c6d4237fa5c058386bf7c4f66b7
commit 98099fc0de3ba889b18cf36f9a5af267b3ddb501
Author: user1 <user1@gerrit.com>
Date:   Thu Dec 12 00:15:08 2013 -0500
    add README
[user2@jenkins HelloWorld]$ 
这次就有了change id
然后再次push
[user2@jenkins HelloWorld]$ git push origin
Counting objects: 5, done.
Writing objects: 100% (3/3), 289 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Processing changes: new: 1, refs: 1, done    
remote: 
remote: New Changes:
remote:   //gerrit.example.com:8080/1
remote: 
To ssh://user2@gerrit.example.com:29418/HelloWorld.git
 * [new branch]      master -> refs/for/master
[user2@jenkins HelloWorld]$ 
posted @ 2017-08-11 11:23 小马歌 阅读(85) | 评论 (0)编辑 收藏

2017年8月10日

from://blog.csdn.net/acmman/article/details/50848595

版权声明:本文为博主原创文章,未经博主允许不得转载。

目前,业内关于OSGI技术的学习资源或者技术文档还是很少的。我在某宝网搜索了一下“OSGI”的书籍,结果倒是有,但是种类少的可怜,而且几乎没有人购买。
因为工作的原因我需要学习OSGI,所以我不得不想尽办法来主动学习OSGI。我将用文字记录学习OSGI的整个过程,通过整理书籍和视频教程,来让我更加了解这门技术,同时也让需要学习这门技术的同志们有一个清晰的学习路线。

我们需要解决一下几问题:
1.如何正确的理解和认识OSGI技术?

我们从外文资料上或者从翻译过来的资料上看到OSGi解释和定义,都是直译过来的,但是OSGI的真实意义未必是中文直译过来的意思。OSGI的解释就是Open Service Gateway Initiative,直译过来就是“开放的服务入口(网关)的初始化”,听起来非常费解,什么是服务入口初始化?

所以我们不去直译这个OSGI,我们换一种说法来描述OSGI技术。

我们来回到我们以前的某些开发场景中去,假设我们使用SSH(struts+spring+hibernate)框架来开发我们的Web项目,我们做产品设计和开发的时候都是分??榈?,我们分??榈哪康木褪鞘迪帜?橹涞?#8220;解耦”,更进一步的目的是方便对一个项目的控制和管理。
我们对一个项目进行??榛纸庵?,我们就可以把不同??榻桓煌目⑷嗽崩赐瓿煽?,然后项目经理把大家完成的??榧性谝黄?,然后拼装成一个最终的产品。一般我们开发都是这样的基本情况。

那么我们开发的时候预计的是系统的功能,根据系统的功能来进行??榈幕?,也就是说,这个产品的功能或客户的需求是划分的重要依据。

但是我们在开发过程中,我们??橹浠挂舜吮3至?,比如A??橐覤??槟玫揭恍┦?,而B??榭赡芤饔肅??橹械囊恍┓椒?除了公共底层的工具类之外)。所以这些??橹皇且恢致呒庖迳系幕?。

最重要的一点是,我们把最终的项目要去部署到tomcat或者jBoss的服务器中去部署。那么我们启动服务器的时候,能不能关闭项目的某个??榛蚬δ苣??很明显是做不到的,一旦服务器启动,所有??榫鸵黄鹌舳?,都要占用服务器资源,所以关闭不了???,假设能强制拿掉,就会影响其它的功能。

以上就是我们传统??槭娇⒌囊恍┚窒扌?。

我们做软件开发一直在追求一个境界,就是??橹涞恼嬲?#8220;解耦”、“分离”,这样我们在软件的管理和开发上面就会更加的灵活,甚至包括给客户部署项目的时候都可以做到更加的灵活可控。但是我们以前使用SSH框架等架构模式进行产品开发的时候我们是达不到这种要求的。

所以我们“架构师”或顶尖的技术高手都在为??榛⑴Φ拿骱统⑹?,然后我们的OSGI的技术规范就应运而生。

现在我们的OSGI技术就可以满足我们之前所说的境界:在不同的??橹凶龅匠沟椎姆掷?,而不是逻辑意义上的分离,是物理上的分离,也就是说在运行部署之后都可以在不停止服务器的时候直接把某些??槟孟吕?,其他??榈墓δ芤膊皇苡跋?。

由此,OSGI技术将来会变得非常的重要,因为它在实现??榛怦畹穆飞?,走得比现在大家经常所用的SSH框架走的更远。这个技术在未来大规模、高访问、高并发的Java??榛⒘煊?,或者是项目规范化管理中,会大大超过SSH等框架的地位。

现在主流的一些应用服务器,Oracle的weblogic服务器,IBM的WebSphere,JBoss,还有Sun公司的glassfish服务器,都对OSGI提供了强大的支持,都是在OSGI的技术基础上实现的。有那么多的大型厂商支持OSGI这门技术,我们既可以看到OSGI技术的重要性。所以将来OSGI是将来非常重要的技术。

但是OSGI仍然脱离不了框架的支持,因为OSGI本身也使用了很多spring等框架的基本控件(因为要实现AOP依赖注入等功能),但是哪个项目又不去依赖第三方jar呢?



2.OSGI技术对我们项目的开发有什么帮助?

(1)项目展示
接下来我们同过项目代码来展示一下OSGI的魅力:
我们先不要去急着理解如何使用OSGI,我们通过一个项目先来看一下OSGI的效果。
(以下工程代码是网上教学视频中的样例,源码我这里是没有的)
(提前说一下:我们要学习的重点就是我们这个购物网站如何结合OSGI技术,使得项目更加的灵活可控,而购物网站本身并不是重点。)


首先在Eclipse中先打开我们的单服务器版本的项目:

启动成功:




这是一个Web项目,我们打开浏览器看一下效果:

可以看出是一个网上购物的项目。

我们来看一下我们基于OSGI技术的项目和我们一般的项目有什么区别。
首先介绍一下这个项目的???

1.大类展示


2.小类展示(大类的子产品)

点进去之后就是产品的具体信息



3.购物车
没买东西是空的:

买完之后:



4.商品管理(上架、下架)



可以看到,这个项目和我们平??⒌南钅棵挥惺裁床煌?我知道界面很简陋= =),重点是它的启动和加载过程。


(2)关于服务器
我们是通过动态加载,也就是“热部署”来启动我们的项目的。就是说,我们这个项目把它放在Web容器中之后,我们可以将某些功能给它拿下来,而且拿下来的时候不会对其他??樵斐捎跋?。

我们以前运行tomcat的时候,启动一下服务器,将Web项目一次性装载完毕,控制台会出现类似这种信息:


但是我们启动这个项目的时候并不是这样:


那么我们没有用tomcat和jBoss,那是如何部署和启动Web项目的呢?不可能没有Web服务器中间件的???这里告诉大家,OSGI技术里面也是内嵌了一个Web服务器的,就是jetty。


我们打开这个项目的Run Configuration配置窗口,看一下运行这个项目所需要的插件包:


可以看到,除了一些Web项目需要的jar包,还是有jetty的存在的。所以用到的服务器是jetty,不再是tomcat。


大家可能还是比较熟悉tomcat,对于jetty不是太熟悉,那么我们简单介绍一下jetty:
jetty也是一个比较优秀的Web容器,在某些性能方面要比tomcat强大的多(如高并发,长连接)。而且它的整个结构比tomcat轻巧很多(tomcat更臃肿),具体区别大家可以去网上自己看一下。


(3)运行模式和插件
我们接下来正式看一下此项目在OSGI下的运行模式:
我们在启动的时候,加载了四个???,分别是:

按照??榛乃枷胨蔷褪撬母龆杂Φ墓δ苣??。
他们对应的四个功能??榈墓こ檀胛颐强梢栽贓clipse中看到:



我们看一下我们的启动配置(依然打开是Run Configuration配置窗口):


配置分为“WorkSpace”和“Target Platform”,分别是我们工作空间(我们自己写的项目??楹凸ぞ呃?的插件和运行平台(一些依赖jar的配置)的插件,两者结合启动我们的项目就会正常运行。

我们启动项目之后,在控制台输入指令“ss”,就会出现我们所有加载的插件的运行情况:


一启动的时候,它会首先加载Eclipse的OSGI插件(Eclipse本身也是一种OSGI的容器):

我们打开我们的Eclipse安装目录,然后找到plugins文件夹,可以看到Eclipse所有的插件:

可以看到有文件夹形式的,有jar形式的插件。

我们怎么去理解插件呢?
插件其实就是被开发工具或OSGI容器管理和配置起来的jar包。  

我们随便打开一个文件夹类型的插件,可以看到:

可以看到里面除了lib之外还有其它东西,然后有一个“OSGI-INF”文件夹。且不管它是什么,这都足以说明我们的Eclipse就是一个OSGI容器。

(4)热部署和热启动
我们接下来回到重点,在我们启动的过程中,我们不停止运行,然后去停掉其中的一个???

假如我们要停掉“管理”???

也就是停掉id为22的插件

结果:


然后刷新我们的网站主页面:

发现我们的“管理”??橄Я?

这个??榈南Р⒉皇莏avascript的技术,而是一种服务器技术,我们是通过服务器内部把它动态卸载掉的。

我们的管理??槿サ糁?,网站的其它功能不受任何影响。至此我们的服务器没有进行任何的暂?;蚬乇?。

我们再停掉“购物车”???

效果:

其它??橐谰刹皇苡跋?。

我们关闭了两个???,现在输入ss看一下所有插件和??榈脑诵星榭?

可以看到我们的两个??榇τ赗ESOLVED状态,也就是待解决状态。

当然我们也可以将我们的??樵诜衿骺糇刺虏渴鹕先?
如我们启动购物车???


发现购物车回来了:


这就是所谓的热部署,即是这个项目把它放在Web容器中之后,我们可以将某些功能给它拿下来,而且拿下来的时候不会对其他??樵斐捎跋?。


通过购物网站这个项目让大家真实的感受一下OSGI这个技术在项目开发和管理的一些强大的功能。

想进一步了解更多关于OSGI的知识可以查看以后的总结文章。

转载请注明出处://blog.csdn.net/acmman/article/details/50848595

20
posted @ 2017-08-10 15:57 小马歌 阅读(133) | 评论 (0)编辑 收藏

2017年8月2日

from://www.jianshu.com/p/ccadc2bdb6d7

第5章 Spring Boot自动配置原理

5.1 SpringBoot的核心组件???/h2>

首先,我们来简单统计一下SpringBoot核心工程的源码java文件数量:

我们cd到spring-boot-autoconfigure工程根目录下。执行

$ tree | grep -c .java$
???/th> java文件数
spring-boot 551
spring-boot-actuator 423
spring-boot-autoconfigure 783
spring-boot-devtools 169
spring-boot-cli 180
spring-boot-tools 355

我们可以看到有783个java文件。spring-boot核心工程有551个java文件。从上面的java文件数量大致可以看出,SpringBoot技术框架的核心组成部分:

spring-boot-autoconfigure spring-boot spring-boot-tools

我们把SpringBoot源码导入IntelliJ IDEA,查看artifact的全部依赖关系。

IDEA有个Maven Projects窗口,一般在右侧能够找到,如果没有可以从菜单栏打开:View>Tool Windows>Maven Projects;

选择要分析的maven module(idea的module相当于eclipse的project),右击show dependencies,会出来该module的全部依赖关系图,非常清晰细致。

例如,spring-boot-starter-freemarker的依赖图分析如下:


在spring-boot-build 的pom中,我们可以看到:

           <modules>                 <module>spring-boot-dependencies</module>                 <module>spring-boot-parent</module>                 <module>spring-boot-tools</module>                 <module>spring-boot</module>                 <module>spring-boot-test</module>                 <module>spring-boot-autoconfigure</module>                 <module>spring-boot-test-autoconfigure</module>                 <module>spring-boot-actuator</module>                 <module>spring-boot-devtools</module>                 <module>spring-boot-docs</module>                 <module>spring-boot-starters</module>                 <module>spring-boot-actuator-docs</module>                 <module>spring-boot-cli</module>             </modules>

其中,在spring-boot-dependencies中,SpringBoot项目维护了一份庞大依赖。这些依赖的版本都是经过实践,测试通过,不会发生依赖冲突的。就这样一个事情,就大大减少了Spring开发过程中,出现jar包冲突的概率。spring-boot-parent依赖spring-boot-dependencies。

下面我们简要介绍一下SpringBoot子modules。

spring-boot

SpringBoot核心工程。

spring-boot-starters

是SpringBoot的启动服务工程。

spring-boot-autoconfigure

是SpringBoot实现自动配置的核心工程。

spring-boot-actuator

提供SpringBoot应用的外围支撑性功能。 比如:

  • Endpoints,SpringBoot应用状态监控管理
  • HealthIndicator,SpringBoot应用健康指示表
  • 提供metrics支持
  • 提供远程shell支持

spring-boot-tools

提供了SpringBoot开发者的常用工具集。诸如,spring-boot-gradle-plugin,spring-boot-maven-plugin就是这个工程里面的。

spring-boot-cli

是Spring Boot命令行交互工具,可用于使用Spring进行快速原型搭建。你可以用它直接运行Groovy脚本。如果你不喜欢Maven或Gradle,Spring提供了CLI(Command Line Interface)来开发运行Spring应用程序。你可以使用它来运行Groovy脚本,甚至编写自定义命令。

5.2 SpringBoot Starters

Spring boot中的starter概念是非常重要的机制,能够抛弃以前繁杂的配置,统一集成进starter,应用者只需要引入starter jar包,spring boot就能自动扫描到要加载的信息。

starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。Spring Boot会自动通过classpath路径下的类发现需要的Bean,并织入bean。

例如,如果你想使用Spring和用JPA访问数据库,你只要依赖 spring-boot-starter-data-jpa 即可。

目前,github上spring-boot项目的最新的starter列表spring-boot/spring-boot-starters如下:

spring-boot-starter spring-boot-starter-activemq spring-boot-starter-actuator spring-boot-starter-amqp spring-boot-starter-aop spring-boot-starter-artemis spring-boot-starter-batch spring-boot-starter-cache spring-boot-starter-cloud-connectors spring-boot-starter-data-cassandra spring-boot-starter-data-couchbase spring-boot-starter-data-elasticsearch spring-boot-starter-data-jpa spring-boot-starter-data-ldap spring-boot-starter-data-mongodb spring-boot-starter-data-mongodb-reactive spring-boot-starter-data-neo4j spring-boot-starter-data-redis spring-boot-starter-data-rest spring-boot-starter-data-solr spring-boot-starter-freemarker spring-boot-starter-groovy-templates spring-boot-starter-hateoas spring-boot-starter-integration spring-boot-starter-jdbc spring-boot-starter-jersey spring-boot-starter-jetty spring-boot-starter-jooq spring-boot-starter-jta-atomikos spring-boot-starter-jta-bitronix spring-boot-starter-jta-narayana spring-boot-starter-log4j2 spring-boot-starter-logging spring-boot-starter-mail spring-boot-starter-mobile spring-boot-starter-mustache spring-boot-starter-parent spring-boot-starter-reactor-netty spring-boot-starter-security spring-boot-starter-social-facebook spring-boot-starter-social-linkedin spring-boot-starter-social-twitter spring-boot-starter-test spring-boot-starter-thymeleaf spring-boot-starter-tomcat spring-boot-starter-undertow spring-boot-starter-validation spring-boot-starter-web spring-boot-starter-web-services spring-boot-starter-webflux spring-boot-starter-websocket

(源代码目录执行shell:l|awk '{print $9}', l|awk '{print $9}'|grep -c 'starter')

共52个。每个starter工程里面的pom描述有相应的介绍。具体的说明,参考官网文档[1]。关于这些starters的使用例子,可以参考spring-boot/spring-boot-samples

比如说,spring-boot-starter是:

Core starter, including auto-configuration support, logging and YAML

这是Spring Boot的核心启动器,包含了自动配置、日志和YAML。它的项目依赖图如下:



可以看出,这些starter只是配置,真正做自动化配置的代码的是在spring-boot-autoconfigure里面。同时spring-boot-autoconfigure依赖spring-boot工程,这个spring-boot工程是SpringBoot的核心。

SpringBoot会基于你的classpath中的jar包,试图猜测和配置您可能需要的bean。

例如,如果你的classpath中有tomcat-embedded.jar,你可能会想要一个TomcatEmbeddedServletContainerFactory Bean (SpringBoot通过获取EmbeddedServletContainerFactory来启动对应的web服务器。常用的两个实现类是TomcatEmbeddedServletContainerFactory和JettyEmbeddedServletContainerFactory)。

其他的所有基于Spring Boot的starter都依赖这个spring-boot-starter。比如说spring-boot-starter-actuator的依赖树,如下图:


5.3 @EnableAutoConfiguration自动配置原理

通过@EnableAutoConfiguration启用Spring应用程序上下文的自动配置,这个注解会导入一个EnableAutoConfigurationImportSelector的类,而这个类会去读取一个spring.factories下key为EnableAutoConfiguration对应的全限定名的值。

这个spring.factories里面配置的那些类,主要作用是告诉Spring Boot这个stareter所需要加载的那些xxxAutoConfiguration类,也就是你真正的要自动注册的那些bean或功能。然后,我们实现一个spring.factories指定的类,标上@Configuration注解,一个starter就定义完了。

如果想从自己的starter种读取应用的starter工程的配置,只需要在入口类上加上如下注解即可:

@EnableConfigurationProperties(MyProperties.class)

读取spring.factories文件的实现

是通过org.springframework.core.io.support.SpringFactoriesLoader实现。

SpringFactoriesLoader的实现类似于SPI(Service Provider Interface,在java.util.ServiceLoader的文档里有比较详细的介绍。java SPI提供一种服务发现机制,为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在??榛杓浦姓飧龌朴绕渲匾猍3])。

SpringFactoriesLoader会加载classpath下所有JAR文件里面的META-INF/spring.factories文件。

其中加载spring.factories文件的代码在loadFactoryNames方法里:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";  ....      public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {         String factoryClassName = factoryClass.getName();         try {             Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));             List<String> result = new ArrayList<>();             while (urls.hasMoreElements()) {                 URL url = urls.nextElement();                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));                 String factoryClassNames = properties.getProperty(factoryClassName);                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));             }             return result;         }         catch (IOException ex) {             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +                     "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);         }     }

通过org.springframework.boot.autoconfigure.AutoConfigurationImportSelector里面的getCandidateConfigurations方法,获取到候选类的名字List<String>。该方法代码如下:

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,             AnnotationAttributes attributes) {         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());         Assert.notEmpty(configurations,                 "No auto configuration classes found in META-INF/spring.factories. If you "                         + "are using a custom packaging, make sure that file is correct.");         return configurations;     }

其中,getSpringFactoriesLoaderFactoryClass()方法直接返回的是EnableAutoConfiguration.class, 代码如下:

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {         return EnableAutoConfiguration.class;     }

所以,getCandidateConfigurations方法里面的这段代码:

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名对应的值。全限定名都使用如下命名方法:

包名.外部类名 包名.外部类名$内部类名  e.g:  org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

SpringBoot中的META-INF/spring.factories(完整路径:spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories)中关于EnableAutoConfiguration的这段配置如下:

# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAnnotationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

当然了,这些AutoConfiguration不是所有都会加载的,会根据AutoConfiguration上的@ConditionalOnClass等条件,再进一步判断是否加载。我们下文通过FreeMarkerAutoConfiguration实例来分析整个自动配置的过程。

5.4 FreeMarkerAutoConfiguration自动配置的实例分析

我们首先看spring-boot-starter-freemarker工程,目录结构如下:

. ├── pom.xml ├── spring-boot-starter-freemarker.iml └── src     └── main         └── resources             └── META-INF                 └── spring.provides  4 directories, 3 files

我们可以看出,这个工程没有任何Java代码,只有两个文件:pom.xml跟spring.provides。starter本身在你的应用程序中实际上是空的。

其中,
spring.provides文件

provides: freemarker,spring-context-support

主要是给这个starter起个好区分的名字。

Spring Boot 通过starter对项目的依赖进行统一管理. starter利用了maven的传递依赖解析机制,把常用库聚合在一起, 组成了针对特定功能而定制的依赖starter。

我们可以使用IDEA提供的maven依赖图分析的功能(如下图),得到spring-boot-starter-freemarker依赖的module。


IDEA提供的maven依赖图分析

spring-boot-starter-freemarker依赖的module

从上面的依赖图,我们可以清晰看出其间依赖关系。

当Spring Boot Application中自动配置EnableAutoConfiguration的相关类执行完毕之后,Spring Boot会进一步解析对应类的配置信息。如果我们配置了spring-boot-starter-freemarker ,maven就会通过这个starter所依赖的spring-boot-autoconfigure,自动传递到spring-boot-autoconfigure工程中。

我们来简单分析一下spring-boot-autoconfigure工程的架构。

其中,FreeMarker的自动配置类是org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration。

下面我们来简要分析一下FreeMarkerAutoConfiguration这个类。

在FreeMarkerAutoConfiguration类上面有四行注解:

@Configuration @ConditionalOnClass({ freemarker.template.Configuration.class,         FreeMarkerConfigurationFactory.class }) @AutoConfigureAfter(WebMvcAutoConfiguration.class) @EnableConfigurationProperties(FreeMarkerProperties.class) public class FreeMarkerAutoConfiguration {     ... }

其中,
(1)@Configuration,是org.springframework.context.annotation包里面的注解。这么说吧,用@Configuration注解该类,等价 与XML中配置beans;用@Bean标注方法等价于XML中配置bean。

(2)@ConditionalOnClass,org.springframework.boot.autoconfigure.condition包里面的注解。意思是当类路径下有指定的类的条件下,才会去注册被标注的类为一个bean。在上面的代码中的意思就是,当类路径中有freemarker.template.Configuration.class,FreeMarkerConfigurationFactory.class两个类的时候,才会实例化FreeMarkerAutoConfiguration这个Bean。

(3)@AutoConfigureAfter,org.springframework.boot.autoconfigure包里面的注解。这个通过注解的名字意思就可以知道,当WebMvcAutoConfiguration.class这个类实例化完毕,才能实例化FreeMarkerAutoConfiguration(有个先后顺序)。SpringBoot使用@ AutoConfigureBefore、@AutoConfigureAfter注解来定义这些配置类的载入顺序。

(4)@EnableConfigurationProperties,表示启动对FreeMarkerProperties.class的内嵌配置支持,自动将FreeMarkerProperties注册为一个bean。这个FreeMarkerProperties类里面就是关于FreeMarker属性的配置:

@ConfigurationProperties(prefix = "spring.freemarker") public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {      public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";      public static final String DEFAULT_PREFIX = "";      public static final String DEFAULT_SUFFIX = ".ftl";      /**      * Well-known FreeMarker keys which will be passed to FreeMarker's Configuration.      */     private Map<String, String> settings = new HashMap<>();      /**      * Comma-separated list of template paths.      */     private String[] templateLoaderPath = new String[] { DEFAULT_TEMPLATE_LOADER_PATH };      /**      * Prefer file system access for template loading. File system access enables hot      * detection of template changes.      */     private boolean preferFileSystemAccess = true;      public FreeMarkerProperties() {         super(DEFAULT_PREFIX, DEFAULT_SUFFIX);     }      public Map<String, String> getSettings() {         return this.settings;     }      public void setSettings(Map<String, String> settings) {         this.settings = settings;     }      public String[] getTemplateLoaderPath() {         return this.templateLoaderPath;     }      public boolean isPreferFileSystemAccess() {         return this.preferFileSystemAccess;     }      public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {         this.preferFileSystemAccess = preferFileSystemAccess;     }      public void setTemplateLoaderPath(String... templateLoaderPaths) {         this.templateLoaderPath = templateLoaderPaths;     }  }

综上,当(1)(2)两个条件满足时,才会继续(3)(4)的动作,同时注册FreeMarkerAutoConfiguration这个Bean。该类的结构如下图:


我们来看其内部类FreeMarkerWebConfiguration的代码:

    @Configuration     @ConditionalOnClass(Servlet.class)     @ConditionalOnWebApplication(type = Type.SERVLET)     public static class FreeMarkerWebConfiguration extends FreeMarkerConfiguration {          @Bean         @ConditionalOnMissingBean(FreeMarkerConfig.class)         public FreeMarkerConfigurer freeMarkerConfigurer() {             FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();             applyProperties(configurer);             return configurer;         }          @Bean         public freemarker.template.Configuration freeMarkerConfiguration(                 FreeMarkerConfig configurer) {             return configurer.getConfiguration();         }          @Bean         @ConditionalOnMissingBean(name = "freeMarkerViewResolver")         @ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)         public FreeMarkerViewResolver freeMarkerViewResolver() {             FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();             this.properties.applyToViewResolver(resolver);             return resolver;         }          @Bean         @ConditionalOnMissingBean         @ConditionalOnEnabledResourceChain         public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {             return new ResourceUrlEncodingFilter();         }      }

其中,
(1)@ConditionalOnWebApplication(type = Type.SERVLET), 是当该应用是基于Servlet的Web应用时。

(2)@ConditionalOnMissingBean(name = "freeMarkerViewResolver"),是当Spring容器中不存在freeMarkerViewResolver的Bean时。

(3)@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true),指定的spring.freemarker.enabled属性是否有。如果没有(IfMissing),设为true。

当(1)(2)(3)三个条件都满足,则注册freeMarkerViewResolver这个Bean。

我们也可以自定义我们自己的my-starter,以及实现对应的@MyEnableAutoConfiguration。SpringBoot有很多第三方starter,其自动配置的原理基本都是这样,比如mybatis-spring-boot-starter的MybatisAutoConfiguration,阅读源码https://github.com/mybatis/spring-boot-starter[4]。

上面文字描述了这么多,再用一张形象生动的图来说明[5]:


SpringBoot Autoconfigure 工作原理图

5.5 spring.factories与定义应用程序的初始化行为

上面说了这么多,讲的都是读取properties文件中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的全限定名对应的值。SpringBoot内部还有许多其他的key用于过滤得到需要加载的类。

# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer  # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer  # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener  # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnClassCondition  # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer  # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

这些key仍然是定义在spring-boot/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories文件中。

还有对应的用于测试的自动配置,在
spring-boot/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories文件中定义。

另外,我们使用spring.factories里还可以定制应用程序的初始化行为。这样我们就可以在应用程序载入前操纵Spring的应用程序上下文ApplicationContext。

例如,可以使用ConfigurableApplicationContext类的addApplicationListener()方法,在应用上下文ApplicationContext中创建监听器。

自动配置运行日志报告功能就是这么实现的。我们来看在spring.factories中,Initializers一段的配置:

# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

其中,AutoConfigurationReportLoggingInitializer监听到系统事件时,比如上下文刷新ContextRefreshedEvent或应用程序启动故障ApplicationFailedEvent之类的事件,Spring Boot可以做一些事情。这里说的代码在AutoConfigurationReportLoggingInitializer.AutoConfigurationReportListener里面。关于支持的事件类型supportsEventType的如下:

    private class AutoConfigurationReportListener implements GenericApplicationListener {  ...         @Override         public boolean supportsEventType(ResolvableType resolvableType) {             Class<?> type = resolvableType.getRawClass();             if (type == null) {                 return false;             }             return ContextRefreshedEvent.class.isAssignableFrom(type)                     || ApplicationFailedEvent.class.isAssignableFrom(type);         }          @Override         public boolean supportsSourceType(Class<?> sourceType) {             return true;         }          @Override         public void onApplicationEvent(ApplicationEvent event) {     AutoConfigurationReportLoggingInitializer.this.onApplicationEvent(event);         }      }

要以调试模式启动应用程序,可以使用-Ddebug标识,或者在application.properties文件这添加属性debug= true。这样,当我们以调试模式启动应用程序时,SpringBoot就可以帮助我们创建自动配置的运行报告。对于每个自动配置,通过报告我们可以看到它启动或失败的原因。 这个报告内容格式大致如下:

========================= AUTO-CONFIGURATION REPORT =========================   Positive matches: -----------------     DataSourceAutoConfiguration matched:       - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)     DataSourceAutoConfiguration#dataSourceInitializer matched:       - @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer; SearchStrategy: all) did not find any beans (OnBeanCondition)     DataSourceAutoConfiguration.PooledDataSourceConfiguration matched:       - AnyNestedCondition 2 matched 0 did not; NestedCondition on DataSourceAutoConfiguration.PooledDataSourceCondition.PooledDataSourceAvailable PooledDataSource found supported DataSource; NestedCondition on DataSourceAutoConfiguration.PooledDataSourceCondition.ExplicitType @ConditionalOnProperty (spring.datasource.type) matched (DataSourceAutoConfiguration.PooledDataSourceCondition)       - @ConditionalOnMissingBean (types: javax.sql.DataSource,javax.sql.XADataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)     ...  Exclusions: -----------      None   Unconditional classes: ----------------------      org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration      org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration      org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration      org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration

除了SpringBoot官方提供的starter外,还有社区贡献的很多常用的第三方starter,列表可参考[2]。

另外,国内很多公司使用RPC框架dubbo,关于SpringBoot集成dubbo,可参考:https://github.com/linux-china/spring-boot-dubbo。

参考资料:

1.//docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter
2.https://github.com/spring-projects/spring-boot/tree/master/spring-boot-starters
3.//www.cnblogs.com/javaee6/p/3714719.html
4.https://github.com/mybatis/spring-boot-starter
5.https://afoo.me/posts/2015-07-09-how-spring-boot-works.html



作者:华夏商周秦汉唐宋元明清中华民国
链接://www.jianshu.com/p/ccadc2bdb6d7
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2017-08-02 16:35 小马歌 阅读(1319) | 评论 (0)编辑 收藏
 
     摘要: from://blog.csdn.net/lylwo317/article/details/52163304序言注解在Java中到底是什么样的东西?具体是如何实现的? 本文将一层一层深入探究注解的实现原理。为了尽可能的将分析的过程呈现出来,所以文章包含了大量的截图和代码。(ps:如果图片看不清楚,请将网页放大来看,chrome可以通过ctrl+鼠标滚轮放大)前期准备知识方面开始...  阅读全文
posted @ 2017-08-02 11:15 小马歌 阅读(216) | 评论 (1)编辑 收藏
 

如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。


注解处理器类库(java.lang.reflect.AnnotatedElement):

  Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:

  Class:类定义
  Constructor:构造器定义
  Field:累的成员变量定义
  Method:类的方法定义
  Package:类的包定义

  java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
  AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:

  方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
  方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
  方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
  方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。

  一个简单的注解处理器:  

复制代码
/***********注解声明***************/  /**  * 水果名称注解  * @author peida  *  */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitName {     String value() default ""; }  /**  * 水果颜色注解  * @author peida  *  */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitColor {     /**      * 颜色枚举      * @author peida      *      */     public enum Color{ BULE,RED,GREEN};          /**      * 颜色属性      * @return      */     Color fruitColor() default Color.GREEN;  }  /**  * 水果供应者注解  * @author peida  *  */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider {     /**      * 供应商编号      * @return      */     public int id() default -1;          /**      * 供应商名称      * @return      */     public String name() default "";          /**      * 供应商地址      * @return      */     public String address() default ""; }  /***********注解使用***************/  public class Apple {          @FruitName("Apple")     private String appleName;          @FruitColor(fruitColor=Color.RED)     private String appleColor;          @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")     private String appleProvider;          public void setAppleColor(String appleColor) {         this.appleColor = appleColor;     }     public String getAppleColor() {         return appleColor;     }          public void setAppleName(String appleName) {         this.appleName = appleName;     }     public String getAppleName() {         return appleName;     }          public void setAppleProvider(String appleProvider) {         this.appleProvider = appleProvider;     }     public String getAppleProvider() {         return appleProvider;     }          public void displayName(){         System.out.println("水果的名字是:苹果");     } }  /***********注解处理器***************/  public class FruitInfoUtil {     public static void getFruitInfo(Class<?> clazz){                  String strFruitName=" 水果名称:";         String strFruitColor=" 水果颜色:";         String strFruitProvicer="供应商信息:";                  Field[] fields = clazz.getDeclaredFields();                  for(Field field :fields){             if(field.isAnnotationPresent(FruitName.class)){                 FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);                 strFruitName=strFruitName+fruitName.value();                 System.out.println(strFruitName);             }             else if(field.isAnnotationPresent(FruitColor.class)){                 FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);                 strFruitColor=strFruitColor+fruitColor.fruitColor().toString();                 System.out.println(strFruitColor);             }             else if(field.isAnnotationPresent(FruitProvider.class)){                 FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);                 strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();                 System.out.println(strFruitProvicer);             }         }     } }  /***********输出结果***************/ public class FruitRun {      /**      * @param args      */     public static void main(String[] args) {                  FruitInfoUtil.getFruitInfo(Apple.class);              }  }  ====================================  水果名称:Apple  水果颜色:RED  供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦
复制代码

   Java注解的基础知识点(见下面导图)基本都过了一遍,下一篇我们通过设计一个基于注解的简单的ORM框架,来综合应用和进一步加深对注解的各个知识点的理解和运用。

 

 


分类: java
posted @ 2017-08-02 11:07 小马歌 阅读(78) | 评论 (0)编辑 收藏
仅列出标题  四川福利彩票快乐12
 
321| 535| 440| 204| 460| 222| 20| 101| 9| 448|