Blog

java学习路线图(4)-七年成为架构师

前言:我用了七年的时间,一步一步走到了现在,中途也有了解过其他的技术,也想过要转其他的语言,但是最后还是坚持下来走Java这条路,希望我的经历可以帮助到后来的人,要是觉得对你有帮助的话,可以点赞关注一下。

导读

1、架构师应不应该写代码

2、为什么别人的系统总是那么烂

3、成为架构师最困难的门槛是什么?

4、如何更高效的学习?

1.架构师应不应该写代码

合格的程序员对于明确分配的任务会完成的很好,但是大部分情况下“架构”这个词意味着架构师并不会涉及太多细节,架构图和代码实现之间总还是有些距离,你无法保证所有人都会正确的理解你的设计,或者是程序员写代码时遇到障碍时会立刻想出足够优雅的解决方案。

在我看来,写代码的架构师更像是在做后勤保障的工作:在代码中第一时间发现可能存在的问题,向其他人提出警告,或是给予其他人改进的意见,必要的时候或是给其他人演示一下正确的姿势。

大部分情况下我作为架构师并不需要揽下“核心模块”开发这种工作,毕竟我能调配的时间太零散了,效率难以保证,很多人在专注的情况下比我做的好很多,我只需要保持大局观需要适度参与就可以了。

总的来说,架构师和程序员在某些方面上有点像产品经理和用户的关系,大部分程序员并不会主动告诉你他们想要什么、哪里需要优化,甚至自己也不知道这些。想要做出好的产品,捷径之一就是跟用户做同样的事情。

2.为什么别人的系统总是那么烂

很多程序员解决问题的能力很强,说要解决一个什么问题,下午就能写出几百行代码把功能实现了。但是做出来的东西有种少考虑了什么东西的感觉。大部分程序都能实现功能,但是如果把“时间”这个也作为一个考虑的维度的话,就会意识到一个合格的项目需要考虑更多的东西:更通用的使用方式、易于理解的文档、简单而易于扩展的设计,等等。

很多公司应该都会有一些遗留系统,它们庞大、笨重、难用、几乎无法维护,所有人都在抱怨这些系统,并且每天都在想方设法换掉那些遗留系统。但是一段时间过去之后,又会发现身边的新人又开始吐槽当时替代遗留系统的那个系统了。

“大多数系统当初都很好使,功能当时够用,扩展性看起来也可以,但是这些系统都是开发的人离职之后变坏的。”

3.成为架构师最困难的门槛是什么?

很多人自称架构师的人跟你讲一个架构时简直滔滔不绝,各种技术名词像是说相声一样从他嘴里说出来,三句话不离高并发大数据,但是稍微追问一下,就会发现很多基本概念的缺失,例如自称精通高并发的人说不清楚他所谓的高并发系统的瓶颈在哪里,自称精通架构设计的人说不明白他的系统怎么保证高可用,自称超大数据量的系统实际上只有不到100万条数据,等等。

架构师虽然听起来很高大上,但本质上仍然是工程师,不是科学家,也不是忽悠人的江湖骗子。学习再多,也需要实践落地。设计架构方案更多的是在做一些抽象和权衡:把复杂的需求抽象成简单的模型,从功能、性能、可用性、研发成本等等方面规划如何构建一个系统,这些内容需要更多的实践练习。

4.如何更高效的学习?

大多数人每天能留给自己学习的时间有限,这个阶段如何提升学习效率就成了要解决的重点。

说说自己提升学习效率的心得,其实非常简单:体系化的学习。

在重复了几次痛苦的学习-梳理过程后,再去看一些独立的文章或者资料往往会事半功倍,因为能在体系内找到相对应的知识,甚至有时候一本书里一页只需要看一句话,点破那层窗户纸,就可以掌握新的知识。

跟很多人一样,刚毕业时我觉得作为程序员,只要努力,加上少许天赋便可以获得一些成绩。

工作一段时间后,对自己和其他人的认识也越来越清晰,逐渐的发现程序员之间的差距或许比人和猴子之间的差距还大,接受这个事实这让我郁闷了很久。

再过一段时间,发现自己已经能够客观的评价自己的能力,也意识到了距离并不是那么重要,只要想办法跑的更快,就足够了。

5.快速成为架构师的学习路线

以下五大知识体系是我从业多年总结出来的经验,都是当前最主流的技术。想学习这些技术的朋友可以加群:650385180。群里会分享这些技术知识点供大家学习免费下载。希望能够帮助大家全面、科学地建立自己的技术体系和技术认知!

分布式学习路线

微服务学习路线

性能优化学习路线

经典设计模式与源码分析

开发必备工具

动手实践项目

总结:

以上就是我总结出来的这些年的知识路线,中途也有了解过其他的技术,但是最后还是坚持下来走Java这条路,想要学习提升的,可以加上面的群,希望这些知识点可以帮助在这个行业发展的朋友和伙伴们,在论坛博客等地方少花些时间找资料,把有限的时间,真正放在学习和前进上。

来自:https://blog.csdn.net/person_limit/article/details/79992772

java学习路线图(1)

section 1

1.Core Java,就是Java基础、JDK的类库,很多童鞋都会说,JDK我懂,但是懂还不足够,知其然还要知其所以然,JDK的源代码写的非常好,要经常查看,对使用频繁的类,比如String,集合类(List,Map,Set)等数据结构要知道它们的实现,不同的集合类有什么区别,然后才能知道在一个具体的场合下使用哪个集合类更适合、更高效,这些内容直接看源代码就OK了

2.多线程并发编程,现在并发几乎是写服务端程序必须的技术,那对Java中的多线程就要有足够的熟悉,包括对象锁机制、synchronized关键字,concurrent包都要非常熟悉,这部分推荐你看看《Java并发编程实践》这本书,讲解的很详细

3.I/O,Socket编程,首先要熟悉Java中Socket编程,以及I/O包,再深入下去就是Java NIO,再深入下去是操作系统底层的Socket实现,了解Windows和Linux中是怎么实现socket的

4.JVM的一些知识,不需要熟悉,但是需要了解,这是Java的本质,可以说是Java的母体, 了解之后眼界会更宽阔,比如Java内存模型(会对理解Java锁、多线程有帮助)、字节码、JVM的模型、各种垃圾收集器以及选择、JVM的执行参数(优化JVM)等等,这些知识在《深入Java虚拟机》这本书中都有详尽的解释,或者去oracle网站上查看具体版本的JVM规范.

5.一些常用的设计模式,比如单例、模板方法、代理、适配器等等,以及在Core Java和一些Java框架里的具体场景的实现,这个可能需要慢慢积累,先了解有哪些使用场景,见得多了,自己就自然而然会去用。

6.常用数据库(Oracle、MySQL等)、SQL语句以及一般的优化

7.JavaWeb开发的框架,比如Spring、iBatis等框架,同样他们的原理才是最重要的,至少要知道他们的大致原理。

8.其他一些有名的用的比较多的开源框架和包,Netty网络框架,Apache common的N多包,Google的Guava等等,也可以经常去Github上找一些代码看看。

 

section 2

每逢长假都会有很多程序员跳槽,十一、过年是跳槽黄金时刻,尤其是过年。过年的时候年终奖到手,没有了多少牵挂,年终同学同事聚会比较多,沟通的就多,各种工作机会的消息也相应会多,所以跳槽的机会也就会多。跳槽就必不可少的要经过面试,那么作为一个Java程序员需要准备哪些面试知识呢?下面就给大家说说。

1、集合框架:

从上图可以看到主要是Collection和Map的继承类和Iterator的实现类,重点掌握ArrayList、LinkedList、Vector、Stack、PriorityQueue、HashSet、LinkedHashSet、TreeSet、HashMap、LinkedHashMap、TreeMap、WeakHashMap、EnumMap、TreeMap、HashTable的特点和实现原理,还有多线程使用场景和插入多还是删除多的时候使用什么样的集合类效率会更高。

2、IO框架:

主要掌握Reader、Writer、InputStream、OutputStream的继承类,重点掌握字节流(FileInputStream、FileOutputSteam、BufferedInputStream、BufferedOutputStream、DataInputStream、DataOutputStream)和字符流(FileReader、FileWriter、BufferedReader、BufferedWriter、InputStreamReader、OutputStreamWriter),并熟练运用。还有就是NIO的工作原理要知道。

3、String先关:

主要考察对String的处理,顺带考察多线程和算法,大公司就是喜欢考察字符串的算法,主要是字符串查找或者剔除,多线程打印字符串,超大字符串处理。

4、异常处理:

掌握Throwable继承类,理解Error和Exception的区别,理解运行时异常和编译异常的区别,掌握异常的处理方法,掌握try、catch、finally、throw、throws关键字的意义,掌握try-catch-finally语句的执行顺序,掌握异常的工作原理,知道常见的异常有哪些。

5、多线程:

如何创建和启动一个线程,有哪些方法?多线程如何交互,线程之间的调度、让步、合并,如何同步?生产和消费者模型。还有就是掌握线程安全、线程池,死锁。

6、JVM知识:

掌握常见的垃圾回收机制,掌握程序计数器、堆、栈、方法区的作用,掌握类的加载机制。掌握内存分代模型和工作原理。掌握JVM启动参数常见配置,了解JVM调优。

7、数据结构与算法:

掌握常见查找和排序算法实现及其时间、空间复杂度。掌握常见数据结构如链表、队列、栈的基本原理和实现。

section 3

经常主持对高级工程师和中基层Tech Leader的面试,一年下来总有百八十场,我给大家分享一下我对于高级工程师的定位吧,虽然我主要负责.Net团队,但是对于其他语言应该也是适用的。

一 理论基础:高级工程师必须有较好的理论基础,既包含一些基础的学科知识,也要包含个人专攻方向的前沿理论。我遇到过形形色色的求职者,有人自称精通领域模式,却不明白什么是聚合根(领域根)。甚至有人无法列举常用的非线性结构,更别提伪码实现和应用场景分析了。这些人普遍振振有词地说, 我有丰富的开发经验,项目需要实现的功能我都能做出来,我只是没有去记那些术语名词。抱歉,你不是没有记术语名词,你是根本没有潜心看技术文章的心态,你所谓的学习就是看一些诸如《21天精通Java》,《7天,从菜鸟到专家》之类的垃圾书,或者看点视频。那么你这个高工,比别人高在哪里呢?当单位需要你代表团队和合作机构沟通,甚至发表专栏的时候,你是不是也准备跟大家讲”这个东西我不知道该怎么讲,大家可以看一下我的代码“。只有代码可秀的人是码农,不是高工,所以我给这类求职者的综评是中+封顶。

二 工作经验:高级工程师必须有过在项目中担心核心模块开发或者整体架构设计的经验,并且项目越高大上越好,比如你在互联网企业搞过读写分离的实现,你在电商搞过秒杀算法,你在电信行业做过网络层,我必定会高看一眼。如果你就仅仅只是一个人全栈搞过几个小网站,啪啪啪一大堆成熟框架用得倍溜,那么你只是一个装配工。我给这类求职者的综评是中+封顶。

三 培养潜力:高级工程师必须有全局思维,如果在你过往的工作历程中,你对你兄弟团队或者部门的工作情形一问三不知,甚至于一个项目中,你只懂自己的模块,其他同事负责的模块你就只会call个API,那么你就是一个很难再进一步的人。对于企业来说,有你不多,无你不少,所以我给这类求职者的综评是中+封顶。

四 沟通能力:高级工程师在技术团队中是绝对的核心人中,小项目中你就是负责人,大项目中你也要负责带新人,那么良好的沟通能力必不可少,如果你仅仅是自己一个人心里亮堂,你对企业的价值就要大打折扣。这类求职者,我也有录用过不少,因为程序员木讷可以说是一种共性,在这方面没法太挑剔,但是我给他们的综评是高-封顶。

section 4

如果要应聘高级开发工程师职务,仅仅懂得Java的基础知识是远远不够的,还必须懂得常用数据结构、算法、网 络、操作系统等知识。因此本文不会讲解具体的技术,笔者综合自己应聘各大公司的经历,整理了一份大公司对Java高级开发工程师职位的考核纲要,希望可以帮助到需要的人。

1 Java基础

1.1 Collection和Map

(1)掌握Collection和Map的继承体系。

(2)掌握ArrayList、LinkedList、Vector、Stack、PriorityQueue、HashSet、 LinkedHashSet、TreeSet、HashMap、LinkedHashMap、TreeMap、WeakHashMap、EnumMap、 TreeMap、HashTable的特点和实现原理。

(3)掌握CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap的实现原理和适用场景。

1.2 IO

(1)掌握InputStream、OutputStream、Reader、Writer的继承体系。

(2)掌握字节流(FileInputStream、DataInputStream、BufferedInputStream、 FileOutputSteam、DataOutputStream、BufferedOutputStream)和字符流 (BufferedReader、InputStreamReader、FileReader、BufferedWriter、 OutputStreamWriter、PrintWriter、FileWriter),并熟练运用。

(3)掌握NIO实现原理及使用方法。

1.3 异常

(1)掌握Throwable继承体系。

(2)掌握异常工作原理。

(3)了解常见受检异常(比如FileNotFoundException)、非受检异常(比如NullPointerException)和错误(比如IOError)。

1.4 多线程

(1)掌握Executors可以创建的三种(JAVA8增加了一种,共四种)线程池的特点及适用范围。

(2)掌握多线程同步机制,并熟练运用。

1.5 Socket

(1)掌握Socket通信原理。

(2)熟练使用多线程结合Socket进行编程。

2 Java虚拟机

2.1 JVM内存区域划分

(1)掌握程序计数器、堆、虚拟机栈、本地方法栈、方法区(JAVA8已移除)、元空间(JAVA8新增)的作用及基本原理。

(2)掌握堆的划分:新生代(Eden、Survivor1、Survivor2)和老年代的作用及工作原理。

(3)掌握JVM内存参数设置及调优。

2.2 类加载

(1)掌握类的加载阶段:加载、链接(验证、准备、解析)、初始化、使用、卸载。

(2)掌握类加载器分类及其应用:启动类加载器、扩展类加载器、应用程序类加载器、自定义加载器。

3 J2EE

(1) 掌握JSP内置对象、动作及相关特点和工作原理。

(2) 掌握Servlet的特点和工作原理。

(3) 掌握Spring框架的IOC和AOP实现原理(反射和动态代理)。

(4) 至少掌握一个MVC框架(Spring MVC,Struts等)的工作原理,并熟练运用。

(5) 至少掌握一个ORM框架(Hibernate,MyBatis等)的工作原理,并熟练运用。

4 数据结构与算法

(1)掌握线性表和树的特点并熟练运用。

(2)掌握常用排序和查找算法:插入排序(直接插入排序、希尔排序)、选择排序(直接选择排序、堆排序)、交换排序(冒泡排序、快速排序)、归并排序,顺序查找、二分查找、哈希查找。

(3) 熟练运用常见排序和查找算法思想解决编程问题。

(4)了解几大基本算法:贪心算法、分治策略、动态规划。

5 计算机网络

(1)掌握网络的分层结构,及每层的功能特点。

(2)掌握TCP/IP的通信原理(三次握手、四次挥手)

6 数据库

(1)掌握复杂的SQL语句编写。

(2)掌握数据库的优化(SQL层面和表设计层面)。

(3)至少掌握一款数据库产品。

(4)熟悉高并发、大数据情况下的数据库开发。

7 Web技术

(1)掌握AJAX的工作原理。

(2)至少熟悉一款JS框架(比如JQuery)。

8 设计模式

(1)熟悉常见的设计模式。

(2)会将设计模式理论应用到实际开发中。

9 Linux

(1)熟练运用Linux常见命令。

(2)熟悉Linux操作系统基本概念及特点。

(3)熟悉Shell脚本。

10 操作系统

(1)掌握操作系统的进程管理。

(2)了解操作系统的I/O。

11 正则表达式

(1)掌握常见正则表达式符号。

(2)熟练运用正则表达式解决实际问题(比如匹配电话号码、邮箱、域名等)。

Section  5

h5edu 的源码
pure–响应式css框架
Hadoop大数据
ERP系统开发
linux
freemarker
weblogic/jobss
webwhpere
webservice http://www.iteye.com/topic/1135747
bootstrap
HTML5
android
css/jslever
spring aop ssh ssm 徒手搭建
jquery源码分析融汇
缓存 echacge
并发
页面静态化
UML
shell
pathy
github
网络编程
ExtJS
网络爬虫
设计模式
多线程与并发
第三方支付/登录http://wiki.connect.qq.com/%E7%BD%91%E7%AB%99%E6%8E%A5%E5%85%A5%E6%B5%81%E7%A8%8B

=========================高级工程========================
找一些高级题目去做
借鉴理解源码http://www.java1234.com/a/kaiyuan/javaWeb/
=========================================================
1.Core Java部分
这是最基础的,对于一个java高级开发/设计人员,你需要对这一部分达到精通的水平,重点内容如下:
a.面向对象编程思想(封装继承多态接口)
b.字符串处理
c.java.lang包,java.util包等常用包
4.java异常处理
2.Java高级部分

a.Java I/O流
b.Java多线程技术
c.Java网络编程
d.Java Swing
后两项可以了解即可,如果项目需要可以深入研究

3.前端基本技能
* HTML + CSS网页开发
* JavaScript
* Jquery
* 浏览器兼容性 CSS hack(了解)
4.熟练使用JSP + Servlet进行开发

5.MVC设计模式,原理,以及相关框架,如Struts

6.SSH框架

7.缓存技术 session & cookie

8.熟练使用一种以上Java开发工具
Eclipse/MyEclipse/Jbuilder/Jcreator

9.熟练使用XML
JDOM w3c.dom SAX

10.Java设计模式
工厂模式,单例模式 ==

11.Java反射机制
反射的各种用法

12.了解或熟悉 C, C++, .NET

13.熟悉JDK的配置,环境变量

14.数据库oracle必学,其他最好了解一种以上(mysql,sql server,access==)
oracle:视图,索引,存储过程,触发器,游标,包,常用函数 ==

15.数据库原理
事务的原理,锁机制,表连接,复杂查询语句(工作经验),性能调优,锁表以及解决方案==

16.JDBC,连接池

17.Ajax,反向Ajax

18.HTTP协议,request 和 response的原理,HTTP status(了解常用的),Https原理

19.熟悉Linux基本命令,使用过Linux/Unix系统,可以编写shell脚本,可以在Linux上部署项目

20.了解windows系统批处理脚本bat

21.了解HTML5,最好学习过

22.熟悉一种JS框架,如Prototype

23.J2EE原理 熟悉一种以上web容器如Tomcat,JBoss,websphere,weblogic==

24.熟悉ant或maven

25.熟悉一门脚本语言,如python ,ruby

26.了解php/ asp

27.了解ftp协议及原理

28.熟练使用Junit测试,熟悉Mockito等测试工具

29,熟悉javac,javadoc,native,native2ascii等常用命令

30.熟悉常用的排序算法,如冒泡排序,快速排序等,最好自己研究过一些的算法,下面的这个就是我曾经研究过的一个:
http wenku baidu com /view/391db3f0941ea76e58fa04e9点html
http://wenku.baidu.com/view.391db3f0941ea76e58fa04e9.html

31.了解Flex(不学也没关系)

32.了解敏捷开发模式

33.工作流workflow至少用过一种,如OSworkflow,了解原理

34.使用过VPN了解其原理

35.熟悉jstl表达式和el表达式

36.熟悉webservice,WSDL,SOAP

37.图片处理,如图片上传,预览,限制大小等,下面共享一(垃圾勿喷):
http wenku baidu com/view/05337bfc770bf78a6529541b点html

38.版本控制工具,CVS VSS SVN

39,JSON技术,JSON+AJAX

40.分页技术,最好自己实现过不仅仅是用过要知道原理

41.Java Mail

42.Java读写txt,excel
JXL技术

43.JVM原理,JVM内存管理,GC,Java堆栈池

44.熟练使用下面的工具:
office办公软件,word,excel,ppt等
plsql,sqldevelop 数据库开发工具
outlook大公司都用
ue编辑器
浏览器控制台,调试
SHH/PUTTY 远程
45.UML建模工具 Rational Rose等

46.使用log4j

47.使用过开放API如百度地图,腾讯街景,新浪微博等

48.页面静态化技术(伪静态页面)UrlRwriter

49.报表技术,使用过报表制作工具,如水晶易表

50.定时任务,如Spring batch,学会自定义batch任务(不适用第三方工具)

51.了解uuid

52.B/s 和 C/s架构

53.正则表达式

54.了解JNDI JMS

55. ERP

56.unicode编码,乱码的解决

57.开源网络编辑器,如ckEditor

58.二进制原理

59,使用过,了解开源论坛框架,如discuzz

60, GWT ,Closure框架

61.了解 大数据,云计算

62.搜索引擎搜索技术

63.软件工程,项目管理

来自:https://blog.csdn.net/zhibuguonicuo/article/details/60345715

java基础-编译和运行-命令行模式下

先看一下命令行帮助:

javac -help
Usage: javac <options> <source files>
where possible options include:
  -g                         Generate all debugging info
  -g:none                    Generate no debugging info
  -g:{lines,vars,source}     Generate only some debugging info
  -nowarn                    Generate no warnings
  -verbose                   Output messages about what the compiler is doing
  -deprecation               Output source locations where deprecated APIs are used
  -classpath <path>          Specify where to find user class files and annotation processors
  -cp <path>                 Specify where to find user class files and annotation processors
  -sourcepath <path>         Specify where to find input source files
  -bootclasspath <path>      Override location of bootstrap class files
  -extdirs <dirs>            Override location of installed extensions
  -endorseddirs <dirs>       Override location of endorsed standards path
  -proc:{none,only}          Control whether annotation processing and/or compilation is done.
  -processor <class1>[,<class2>,<class3>...] Names of the annotation processors to run; bypasses default discovery process
  -processorpath <path>      Specify where to find annotation processors
  -d <directory>             Specify where to place generated class files
  -s <directory>             Specify where to place generated source files
  -implicit:{none,class}     Specify whether or not to generate class files for implicitly referenced files
  -encoding <encoding>       Specify character encoding used by source files
  -source <release>          Provide source compatibility with specified release
  -target <release>          Generate class files for specific VM version
  -version                   Version information
  -help                      Print a synopsis of standard options
  -Akey[=value]              Options to pass to annotation processors
  -X                         Print a synopsis of nonstandard options
  -J<flag>                   Pass <flag> directly to the runtime system
  -Werror                    Terminate compilation if warnings occur
  @<filename>                Read options and filenames from file

从上面可以看出,-cp 和 -classpath 效果是一样的,估计是简写了。
注意,在Windows系统中多个路径 用;来连接,在Unix系统中用:来连接。至于.表示当前路径

一、命令行编译 javac

案例一:

cooldeMacBook-Pro:~ cool$ javac -d /Users/cool/Desktop  /Users/cool/Documents/test/*.java
#或者 因为当前 操作路径 为 Users/cool/Desktop
javac -d /Users/cool/Desktop  Documents/test/*.java

编译java文件时,必须要指定 清楚源文件,且需要后缀名.java (*.java 指全部编译)
-d 代表编译好的class文件存放位置(如果java源文件,里面有包名,生成的目标class文件也会带有指定的包名)

案例二:

javac -classpath .;../previousRecord.properties;../lib/jtds-1.2.5.jar;com/cjb/properities/jdbc.properties com/cjb/jdbc/*.java com/cjb/client/*.java -encoding UTF-8

上面命令,表示:编译com/cjb/jdbc下所有java类和com/cjb/client下所有java类,这些类用到了previousRecord.properties文件(它的路径是相对的)和jtds-1.2.5.jar的文件jar包(它的路径也是相对的),同时用的编码是UTF-8。

案例三:
-classpath,设定要搜索类的路径,可以是目录,jar文件,zip文件(里面都是class文件),会覆盖掉所有在CLASSPATH里面的设定。
-sourcepath, 设定要搜索编译所需java 文件的路径,可以是目录,jar文件,zip文件(里面都是java文件)。
所以一个完整的javac命令行应该是这样的,
假设abc.java在路径c:\src里面,在任何的目录的都可以执行以下命令来编译。

javac -classpath c:\classes;c:\jar\abc.jar;c:\zip\abc.zip -sourcepath c:\source\project1\src;c:\source\project2 \lib\src.jar;c:\source\project3\lib\src.zip c:\src\abc.java

表示编译需要c:\classed下面的class文件,c:\jar\abc.jar里面的class文件,c:\zip\abc.zip里面的class文件
还需要c:\source\project1\src下面的源文件,c:\source\project2 \lib\src.jar里面的源文件,c:\source\project3\lib\src.zip里面的源文件,
注意:jar,zip里面的源文件不会有什么改动,目录下的源文件,有可能会被重新编译。

二、命令行运行 java

案例一:

cooldeMacBook-Pro:~ cool$ java -classpath /Users/cool/Desktop/  test.Test

运行test.Test 表示 包名为test 的 Test.class文件。因为这个 目标文件 无法指定 路径。只能靠搜索路径找到。所以 -classpath /Users/cool/Desktop/ 必须要包括 目标执行文件的路径。

案例二:

java -Xms64m -Xmx512m -cp .;../previousRecord.properties;../lib/jtds-1.2.5.jar;com/cjb/properities/jdbc.properties com/cjb/client/MailDispatcherOperation

上面命令,表示:执行com/cjb/client下的MailDispatcherOperation.class(它里面有Main函数)。

案例三:
-classpath, 设定要搜索的类的路径,可以是目录,jar文件,zip文件(里面都是class文件),会覆盖掉所有的CLASSPATH的设定。
由于所要执行的类也是要搜索的类的一部分,所以一定要把这个类的路径也放到-classpath的设置里面。
表现在,在要执行的类的路径里面执行java时,一定要添加上点号(.)标示本目录也要搜索。

假设abc.class在路径c:\src里面
可以在任何路径下执行以下命令
java -classpath c:\classes;c:\jar\abc.jar;c:\zip\abc.zip;c:\src abc

问题:如果main.class属于c:\jar\abc.jar,并且在com.cnblogs.jeffchen这个包里,那么执行java -classpath c:\classes;c:\jar\abc.jar;c:\zip\abc.zip;com.cnblogs.jeffchen.main即可, 但是如果classpath中包含多个jar包呢?并且其他jar包中也有com.cnblogs.jeffchen会发生什么情况?错误?


三、不同系统,表示路径的区别

在windows下,
文件路径的分割符为反斜杠  \
类或者java文件列表的分割符为分号 ;

在linux下
文件路径的分隔符位斜杠 /
类或者java文件列表的分隔符为冒号 :

一个linux下编译和运行的例子

/usr/local/java/bin/javac -classpath /tmp/javatest/lib/mail-1.3.3.jar -d /tmp/javatest/bin/ /tmp/javatest/src/jp/co/realseed/Capability.java

/usr/local/java/bin/java -classpath /tmp/javatest/lib/mail-1.3.3.jar:/tmp/javatest/bin/ jp.co.realseed.Capability
四、关于-classpath 的细节

google了一下,发现在java6以及后续的版本中,提供了对通配符的支持。

如果您的jdk还是老版本,那么就没法用通配符了,就只能一个一个写了,或者如果是在unix系统中,可以用shell的功能把路径下的所有jar文件拼接起来,
比如 java -classpath $(echo libs/*.jar | tr ‘ ‘ ‘:’) Test

那么java6以后的通配符怎么用呢?
我们看看这个例子
java -classpath “./libs/*” Test
这里的*是指libs目录里的所有jar文件,不能这么写 java -classpath “./libs/*.jar” Test

如果libs目录中既有jar文件又有class文件,我们都想引用,那么就需要这么写
java -classpath “./libs/*;./libs/” Test
注意:windows系统里的分隔符是; Unix系统的分隔符是:

另外需要注意的就是 libs/* 不包含libs目录下的子目录里的 jar文件,比如 libs/folder1/A.jar
如果想包含子目录,那就需要都明确指出,比如
java -cp “./libs/*;./libs/folder1/*” Test

maven中打包依赖的路径配置
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <excludes>
            <exclude>*.properties</exclude>
            <exclude>*.xml</exclude>
            <exclude>*.sh</exclude>
        </excludes>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib</classpathPrefix>
                <mainClass>com.hhht.riskcontrol.thirdparty.tongdun.LoginServer</mainClass>
            </manifest>
            <manifestEntries>
                <Class-Path>conf/</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>
<classpathPrefix>系统会将这个路径下所有的jar包加入到classpath路径中,
<Class-Path>系统会将这个路径加入到classpath中,主要是用于加载配置文件。

参考自:
http://www.cnblogs.com/JeffChen/archive/2008/01/15/1039708.html
https://zhidao.baidu.com/question/371769855407478484.html

java安装配置-配置环境变量

初学java的时候,最害怕的就是下载JDK后要配置环境变量了,关键是当时不理解,所以战战兢兢地照着书籍上或者是网络上的介绍进行操作。然后下次再弄的时候,又忘记了而且是必忘。当时,心里的想法很气愤的,想着是–这东西一点也不人性化,为什么非要自己配置环境变量呢?太不照顾菜鸟和新手了,很多菜鸟就是因为卡在环境变量的配置上,遭受了太多的挫败感。

一、Window平台
配置环境变量主要有3个:JAVA_HOME、PATH、CLASSPATH。

1、JAVA_HOME
指的是你JDK安装的位置,一般默认安装在C盘,如
C:\Program Files\Java\jdk1.8.0_91

2、PATH
将程序路径包含在PATH当中后,在命令行窗口就可以直接键入它的名字了,而不再需要键入它的全路径,比如上面代码中我用的到javac和java两个命令。
一般的PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;
也就是在原来的PATH路径上添加JDK目录下的bin目录和jre目录的bin.

3、CLASSPATH(其实现在已经不需要配置了)
CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
一看就是指向jar包路径。
需要注意的是前面的.;,.代表当前目录。

如果用命令行进行编译运行时:
说明:网上有人说配置java开发环境时没必要在classpath添加dt.jar和tool.jar,编译时可以找到。classpath中只需放一些第三方或自己编写的jar包路径。classpath 变量的含义是告诉jvm要使用或执行的class放在什么路径上,便于JVM加载class文件。

环境变量的设置与查看
设置可以右击我的电脑,然后点击属性,再点击高级,然后点击环境变量,具体不明白的自行查阅文档。
查看的话可以打开命令行窗口

echo %JAVA_HOME%
echo %PATH%
echo %CLASSPATH%

二、mac平台:
mac 系统 默认自带了 jdk。
查看的话可以打开命令行窗口 。好像 没有看到 CLASSPATH 环境变量 。
echo $CLASSPATH 输出是空白

echo $JAVA_HOME
#结果显示 /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home
echo $PATH
# 结果显示 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/toy/Documents/maven/apache-maven-3.5.3/bin:/Library/Tomcat/my_script_diy:/Library/Tomcat/home/bin:/usr/local/mysql/bin:/Users/cool/Library/Android/sdk/platform-tools

查看java是否安装:

java -version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

java安装配置-安装版本选择

java 下载时,经常 会看到 有两个 版本:
比如
http://www.oracle.com/technetwork/cn/java/javase/downloads/jdk8-downloads-2133151-zhs.html

Java SE Development Kit 8u171
Java SE Development Kit 8u172

那我们究竟应该 下载 哪个版本 比较好呢。

简单来说,奇数版本 是修复了之前 的严重错误  的版本。经过了很多测试。
下一代奇数版本,将包括上一代偶数版本所修复的全部bug。

偶数版本,修复了之前严重错误 和 额外错误的版本。仍需要机构再多测试。

查看 网站讨论 链接:https://www.reddit.com/r/java/comments/8d3i9e/jdk_8u171_vs_jdk_8u172/?st=jhk8n1q9&sh=49328aa6

官方说明链接:
http://www.oracle.com/technetwork/java/javase/cpu-psu-explained-2331472.html

Java CPU and PSU Releases Explained

October 14, 2014

Starting with the release of Java SE 7 Update 71 (Java SE 7u71) in October 2014, Oracle will release a Critical Patch Update (CPU) at the same time as a corresponding Patch Set Update (PSU) for Java SE 7.


Which Java version should I choose: the CPU or the PSU?

Oracle strongly recommends that all Java SE users upgrade to the latest CPU release available for a release family. Most users should choose the CPU release.

Users should only use the corresponding PSU release if they are being impacted by one of the additional bugs fixed in that version as noted in the release notes.

The subsequent CPU release will contain all of the fixes from the current PSU. For this reason, organizations should test the current PSU in their environment in anticipation of these fixes being included in the next CPU.

What is the difference between a Java CPU and PSU release?

Java SE Critical Patch Updates (CPU) contain fixes to security vulnerabilities and critical bug fixes. Oracle strongly recommends that all Java SE users upgrade to the latest CPU releases as they are made available. Java SE CPU releases are odd numbered versions (i.e. 7u71, 7u65 – see more on Java SE version numbering schemes here).

Java SE Patch Set Updates (PSU) contain all of fixes in the corresponding CPU, as well as additional non-critical fixes. Java PSU releases should only be used if you are being impacted by one of the additional bugs fixed in that version. The release notes call out the additional fixes available in Java SE PSU releases.

Is the cadence of CPU releases changing?

As before, Java SE CPU releases are scheduled for release on the Tuesday closest to the 17th day of January, April, July and October under the normal Oracle Critical Patch Update schedule.

Starting in October 2014 with the release of Java SE 7u71 (CPU) and Java SE 7u72 (PSU), Oracle plans to additionally release a corresponding PSU release along with each CPU release for Java SE 7. PSU releases provide organizations and developers with access to non-critical fixes in addition to the critical fixes contained in the corresponding CPU.

Resources and Additional Information:

 


发布说明:release note
http://www.oracle.com/technetwork/java/javase/8u-relnotes-2225394.html

This page lists all the release notes for public releases and Bundled Patch Release (BPR) builds of JDK 8.

BPR builds are available only as commercial offerings to Oracle customers.

For a complete listing of release notes for all JDK 8 update releases in a single page, see Release Notes for JDK 8 and JDK 8 Update Releases. This page compiles information for many releases (loading this page through slow connections will take longer than loading release notes for individual releases).

 

eclipse-注释模板自动填充注释

开发时,经常看到 别人的 代码注释是 很规范。
一开始 我觉得 人家很 认真啊,每一个 方法 的注释 都是 自己一行一行 敲出来的 啊 。
后来,我觉悟到,注释 可以 自动 生成的 啊 。。。

设置注释模板的入口:Window->Preference->Java->Code Style->Code Template 然后展开Comments节点就是所有需设置注释的元素。本文现就每一个元素逐一给大家介绍一下。
文件(Files)注释标签:

/**
* @Title: ${file_name}
* @Package ${package_name}
* @Description: ${todo}(用一句话描述该文件做什么)
* @author ${user}
* @date ${date}
* @version V1.0
*/

类型(Types)注释标签(类的注释):

/**
* @ClassName: ${type_name}
* @Description: ${todo}(这里用一句话描述这个类的作用)
* @author ${user}
* @date ${date}
*
* ${tags}
*/

字段(Fields)注释标签:

/**
* @Fields field:field:{todo}(用一句话描述这个变量表示什么)
*/

构造函数(Constructor)标签:

/**
* 创建一个新的实例 ${enclosing_type}.
*
* ${tags}
*/

方法(Methods)标签:

/**
* @Title: ${enclosing_method}
* @Description: ${todo}(这里用一句话描述这个方法的作用)
* @param ${tags}    参数
* @return ${return_type}    返回类型
* @throws
*/

覆盖方法(Overriding Methods)标签:

/* (非 Javadoc)
* <p>Title: ${enclosing_method}</p>
* <p>Description: </p>
* ${tags}
* ${see_to_overridden}
*/

代理方法(Delegate Methods)标签:

/**
* ${tags}
* ${see_to_target}
*/

getter方法标签:

/**
* @return ${bare_field_name}
*/

setter方法标签:

/**
* @param paramtheparamthe{bare_field_name} to set
*/

要实现上面的注释模板,这需要将下面的配置文件导入就可以了:

<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template
                autoinsert="false"
                context="filecomment_context"
                deleted="false"
                description="Comment for created Java files"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.filecomment"
                name="filecomment">
    /**  
    * @Title: ${file_name}
    * @Package ${package_name}
    * @Description: ${todo}(用一句话描述该文件做什么)
    * @author ${user}
    * @date ${date}
    * @version V1.0  
    */
    </template>
    <template
                autoinsert="false"
                context="typecomment_context"
                deleted="false"
                description="Comment for created types"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.typecomment"
                name="typecomment">
    /**
    * @ClassName: ${type_name}
    * @Description: ${todo}(这里用一句话描述这个类的作用)
    * @author ${user}
    * @date ${date}
    *
    * ${tags}
    */
    </template>
    <template
                autoinsert="false"
                context="fieldcomment_context"
                deleted="false"
                description="Comment for fields"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.fieldcomment"
                name="fieldcomment">
    /**
    * @Fields field:field:{todo}(用一句话描述这个变量表示什么)
    */
    </template>
    <template
                autoinsert="false"
                  context="constructorcomment_context"
                deleted="false"
                description="Comment for created constructors"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.constructorcomment"
                name="constructorcomment">
    /**
     * 创建一个新的实例 ${enclosing_type}.
     *
     * ${tags}
     */
    </template>
        <template
                autoinsert="false"
                context="methodcomment_context"
                deleted="false"
                description="Comment for non-overriding methods"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.methodcomment"
                name="methodcomment">
    /**
    * @Title: ${enclosing_method}
    * @Description: ${todo}(这里用一句话描述这个方法的作用)
    * @param ${tags}    参数
    * @return ${return_type}    返回类型
    * @throws
    */
    </template>
    <template
                autoinsert="true"
                context="overridecomment_context"
                deleted="false"
                description="Comment for overriding methods"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.overridecomment"
                name="overridecomment">
    /* (非 Javadoc)
    * <p>Title: ${enclosing_method}</p>
    * <p>Description: </p>
    * ${tags}
    * ${see_to_overridden}
    */
    </template>
        <template
                autoinsert="true"
                context="delegatecomment_context"
                deleted="false"
                description="Comment for delegate methods"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.delegatecomment"
                name="delegatecomment">
    /**
     * ${tags}
     * ${see_to_target}
     */
    </template>
    <template
                autoinsert="false"
                context="gettercomment_context"
                deleted="false"
                description="Comment for getter method"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.gettercomment"
                name="gettercomment">
    /**
    * @return ${bare_field_name}
    */
    </template>
    <template
                autoinsert="true"
                context="settercomment_context"
                deleted="false"
                description="Comment for setter method"
                enabled="true"
                id="org.eclipse.jdt.ui.text.codetemplates.settercomment"
                name="settercomment">
    /**
     * @param paramtheparamthe{bare_field_name} to set
     */
    </template>
</templates>

参考:https://blog.csdn.net/paul342/article/details/52982585

在实际开发过程中,新建一个类以后,通过输入/**,然后回车即自动按照刚才定义的注释模板格式补齐注释内容,也可以按shift+alt+j快捷键完成该动作

spring特点-DI依赖注入实现原理

以前一直有个疑惑,为什么我创建的controller中注入的service类有时候是代理类,有时候是普通javabean,当时能力不够,现在已经有了点经验就大胆跟了跟源码,看看到底咋回事。

首先看看问题现象:

a1:service是代理类,并且是CGLIB类型代理  

 

a2:service是代理类,并且是jdk 动态代理

b:serivce不是代理类,而是普通类

问题分析

我对service类进行了以下的测试:(前提开启事务注解<tx:annotation-driven/>)

1)service方法添加@Transactional注解或者加入其它的aop拦截配置,没有实现任何接口。   对应问题现状 a1

2)service方法添加@Transactional注解或者加入其它的aop拦截配置,实现了接口。              对应问题现状a2

3)serice方法没有添加@Transactional注解或者其它的aop拦截配置。                                      对应问题现状b

看来出现这种问题的原因就是spring的问题,因为这个类是它创建的,这就需要我们来看下spring创建bean的代码,由于spring太庞大了

我们只看最关键的部分,在创建bean是都会调用getBean()方法,

@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
       @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    return createBean(beanName, mbd, args);
}

经过不断的流转会进入AbstractAutowireCapableBeanFactory的createBean方法

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
     try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
               logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
      }
      catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
          // A previously detected exception with proper bean creation context already,
          // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
          throw ex;
      }

 }

然后调用doCreateBean方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
if (instanceWrapper == null) {
       
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }// Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }return exposedObject;
    }

然后进入核心的createBeanInstance方法,省去了不相关方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
}

然后调用instantiateBean进行bea的实例化

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

实例化时会调用SimpleInstantiationStrategy的instantiate方法

@Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

通过上面的代码分析,如果Bean定义中没有方法覆盖,就使用JDK的反射机制进行实例化,否则,使用CGLIB进行实例化。

1、通过CGLIB的方式instantiateWithMethodInjection(bd, beanName, owner)
2、或者java的反射方式BeanUtils.instantiateClass(constructorToUse)
实例化一个bean

这是时候都是一个纯洁无瑕的javabean,那每个bean的额外加工,例如为某个bean添加事务支持,添加aop配置,还有就是将springmvc的controller进行url和handler的映射,等等这些都是在spring的扩展点完成的,回到
上面的doCreateBean方法,执行完实例化bean后执行
1、populateBean(beanName, mbd, instanceWrapper);
其中的populateBean是为了给生成的bean装配属性,
2、initializeBean(beanName, exposedObject, mbd);
这不是我们这次讨论的重点,关键是initializebean方法 ,为了配置bean的拓展方法 aop 事务等

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

这个方法就是对生成的bean进行一些扩展处理,主要就是这个方法会调用我们自定义的扩展点
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到这里是获取所有的beanProcessor,调用postProcessAfterInitialization方法,我们要关注是的一个叫InfrastructureAdvisorAutoProxyCreator的扩展类。

/**
 * Auto-proxy creator that considers infrastructure Advisor beans only,
 * ignoring any application-defined Advisors.
 *
 * @author Juergen Hoeller
 * @since 2.0.7
 */
@SuppressWarnings("serial")
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {}

看下这个类的注释可以发现这个类是为配置了aop配置(包括注解和xml配置两种方式)的类,生成代理类。
核心方法是下面这个方法wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

下面解析下这个函数

首先看下getAdvicesAndAdvisorsForBean这个方法:名字很明显用来获取当前bean的advisor和adices的,这些都是生成代理类时需要的信息。

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

然后调用findEligibleAdvisors,获取配置的advisor信息

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

来看下findCandidateAdvisors方法,最终调用BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the auto-proxy creator apply to them!
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<>();
        }

        List<Advisor> advisors = new LinkedList<>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                      throw ex;
                    }
                }
            }
        }
        return advisors;
    }

1)首先获取spring管理的Advisor类型的类名称。

2)通过beanFactory获取该bean对应的实体类,并装入advisors。

生成的这个advisor可是相当复杂,这里我们以事务advisor为例说明

可以看到这个advisor包含了advice(aop中的通知),pointcut(aop中的切入点),


advice是TransactionInterceptor,这个通知是用来管理spring的事务的可以看到包含事务的管理器等管理事务的属性,具体的方法见TransactionAspectSupport.invokeWithinTransaction
pointcut是TransactionAttributeSourcePointcut,

public boolean matches(Method method, @Nullable Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

这个是pointcut的核心方法,用来匹配某个类是否符合事务管理的aop拦截要求。
ok,回到之前的wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

我们之前分析道getAdvicesAndAdvisorsForBean方法,可以看到如果得到的结果是DO_NOT_PROXY,就会将这个bean直接返回,

如果不是DO_NOT_PROXY,(其实DO_NOT_PROXY就是null,但是使用DO_NOT_PROXY会使得代码逻辑更加清晰),就会执行

createProxy方法,创建一个代理类,然后返回一个代理类,ok,现在我们就清楚了问题分析中的 第3)和第 1) 2) 区别,那就是

service类是否配置了相关的aop拦截配置,无论是注解还是xml形式,目前我们还不清楚第1)和 第2)的区别,就是为什么有时候

生成jdk代理,有时候生成cglib代理,这就需要继续向下看creatProxy方法了,最终会进入一个DefaultAopProxyFactory的createAopProxy

方法:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

如果目标类是接口就一定会使用jdk代理,如果目标类没有可以代理的接口就一定会使用Cglib代理。

问题总结

这个问题我们现在知道了,那他有什么意义呢,换句话说,我们为什么要知道这个,即使不知道原理,我们也可以去搜搜去解决,在我看来
把他弄明白的过程学会了很多知识,而且我们如果在工作过程中遇到了需要扩展的地方,我们可以很容易的去解决。

最后欢迎大家在评论区留言,有什么想法说出来,共同进步。

来自:https://www.cnblogs.com/zcmzex/p/8822509.html

 

 

参考另一篇IOC源码理解分析:

总结:

//AbstractAutowireCapableBeanFactory创建Bean实例对象:
//AbstractAutowireCapableBeanFactory类实现了ObejctFactory接口,创建容器指定的Bean实例对象,同时还对创建的Bean实例对象进行初始化处理。
//  创建Bean实例对象  方法中 调用了 核心 三句话  
   protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)  
  
//调用一 :createBeanInstance:生成Bean所包含的java对象实例。
          instanceWrapper = createBeanInstance(beanName, mbd, args);  
//调用二:populateBean :对Bean属性的依赖注入进行处理(解析bean的属性,并对bean的属性赋值[值注入])
          populateBean(beanName, mbd, instanceWrapper);  
//调用三:对 bean 的 扩展功能 进行配置 比如 aop ,事务。(可以 插入到任何操作 后)    
// BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件  
        exposedObject = initializeBean(beanName, exposedObject, mbd);  

// 创建bean的 过程 都是 普通的 实例bean ,如果 是接口 就执行 不创建。
// 然后  bean 赋值 过程 没有变化,
// 到了 bean 拓展功能阶段,就看 有没有 代理 或者 接口问题了。比如 上面的 举例  明天写后续

spring特点-DI依赖注入三种方式

一、Spring 容器 自动创建的Bean的方法有两种:

1、在xml文件配置 (即spring 配置文件)
在xml文件中 配置bean, 标注 各个bean之间的依赖关系。
2、(1)在代码中用注解:
在介绍注解注入的方式前,先简单了解bean的一个属性autowire,autowire主要有三个属性值:constructor,byName,byType。

constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。

byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。

byType:查找所有的set方法,将符合符合参数类型的bean注入。

下面进入正题:注解方式注册bean,注入依赖

主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
@Component:可以用于注册所有bean
@Repository:主要用于注册dao层的bean
@Controller:主要用于注册控制层的bean
@Service:主要用于注册服务层的bean

描述依赖关系主要有两种:
@Resource:java的注解,默认以byName的方式去匹配与属性名相同的bean的id,如果没有找到就会以byType的方式查找,如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。

@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;

public UserService(){

}

@Autowired:spring注解,默认也是以byName的方式去匹配与属性名相同的bean的id,如果没有找到,就通过byType的方式去查找,如果查找到多个,用@Qualifier注解限定具体使用哪个。

@Autowired
@Qualifier("userDaoJdbc")
private IUserDao userDao;<br>

(2)同时在spring 配置文件中 配置自动扫描的包:

 <!-- 配置自动扫描的包 -->
 <context:component-scan base-package="com.webproject.srcdir">             
 </context:component-scan>   <br>
在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean
注意:如果配置了<context:component-scan>那么<context:annotation-config/>标签就可以不用再xml中配置了,因为前者包含了后者。另外<context:component-scan>还提供了两个子标签

1.     <context:include-filter>
2.     <context:exclude-filter>

在说明这两个子标签前,先说一下<context:component-scan>有一个use-default-filters属性,改属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类,并注册成bean.也就是@Component的子注解@Service,@Reposity等。所以如果仅仅是在配置文件中这么写
<context:component-scan base-package="com.webproject.srcdir"/>

 Use-default-filter此时为true那么会对base-package包或者子包下的所有的进行java类进行扫描,并把匹配的java类注册成bean。


 可以发现这种扫描的粒度有点太大,如果你只想扫描指定包下面的Controller,该怎么办?此时子标签<context:incluce-filter>就起到了勇武之地。如下所示

<context:component-scan base-package="com.webproject.srcdir.controller">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

这样就会只扫描base-package指定下的有@Controller下的java类,并注册成bean

但是因为use-dafault-filter在上面并没有指定,默认就为true,所以当把上面的配置改成如下所示的时候,就会产生与你期望相悖的结果(注意base-package包值得变化)

<context:component-scan base-package="com.webproject.srcdir">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

此时,spring不仅扫描了@Controller,还扫描了指定包所在的子包service包下注解@Service的java类

此时指定的include-filter没有起到作用,只要把use-default-filter设置成false就可以了。这样就可以避免在base-packeage配置多个包名这种不是很优雅的方法来解决这个问题了。

另外在我参与的项目中可以发现在base-package指定的包中有的子包是不含有注解了,所以不用扫描,此时可以指定<context:exclude-filter>来进行过滤,说明此包不需要被扫描。综合以上说明

Use-dafault-filters=”false”的情况下:<context:exclude-filter>指定的不扫描,<context:include-filter>指定的扫描

写在最后:虽然有这么多的注入方式,但是实际上开发的时候自己编写的类一般用注解的方式注册类,用@Autowired描述依赖进行注入,一般实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变动,所以@Qualifier标签用的也较少;但是在使用其他组件的API的时候用的是通过xml配置文件来注册类,描述依赖,因为你不能去改人家源码嘛。

二、Spring 依赖注入 三种方式:

(Dependency Injection) 依赖注入:两个对象之间的依赖关系在程序运行时由外部容器动态的注入依赖行为方式称为依赖注入(DI) 。 DI 是 IOC 的一种形式。

IoC 在应用开发中是一个非常有力的概念。如 Martin Flower 所述, IoC的一种表现形式就是依赖性注射。依赖性注射用的是好莱坞原则, ” 不要找我,我会找你的。 “。换句来说,你的类不会去查找或是实例化它们所依赖的类。控制恰好是反过来的,某种容器会设置这种依赖关系。使用 IoC常常使代码更加简洁,并且为相互依赖的类提供一种很好的方法。

依赖注入的三种实现类型:接口注入、 Setter 注入和构造器注入。
1、接口注入:

public class ClassA {
private InterfaceB clzB;
public void doSomething() {
Ojbect obj=Class.forName(Config.BImplementation).newInstance();
clzB = (InterfaceB)obj;
clzB.doIt()
}
……
}

上面的代码中, ClassA 依赖于 InterfaceB 的实现,如何获得 InterfaceB实现类的实例?传统的方法是在代码中创建 InterfaceB 实现类的实例,并将起赋予 clzB 。
而这样一来, ClassA 在编译期即依赖于 InterfaceB的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码.
我们根据预先在配置文件中设定的实现类的类名 (Config.BImplementation) ,动态加载实现类,并通过InterfaceB 强制转型后为 ClassA 所用。这就是接口注入的一个最原始的雏形。
而对于一个 Type1 型 IOC 容器而言,加载接口实现并创建其实例的工作由容器完成,见如下代码。

public class ClassA {
private InterfaceB clzB;
public Object doSomething(InterfaceB b) {
clzB = b;
return clzB.doIt();
}
……
}

在运行期, InterfaceB 实例将由容器提供。
Type1 型 IOC 发展较早(有意或无意),在实际中得到了普遍应用,即使在 IOC的概念尚未确立时,这样的方法也已经频繁出现在我们的代码中。
下面的代码大家应该非常熟悉:

public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponseresponse)throws ServletException, IOException { …… } }

这也是一个 接口注入, HttpServletRequest 和 HttpServletResponse 实例由Servlet Container 在运行期动态注入。
参考 J2EE API文档: https://docs.oracle.com/javaee/6/api/
HttpServletRequest HttpServletRequest 都是个接口。
这两个接口是由各个 j2ee厂商提供的实现的。比如tomcat ,weblogic都有自己的j2ee实现。
可以说j2ee只是一堆规范和定义好的接口。实现是由J2EE厂商实现,实例化是由web容器实例化

java通过 动态代理 反射实列化接口,以此实现 接口注入。

2、set注入:
采用属性的set方法进行初始化,就成为set注入。
1)给普通字符类型赋值。

public class User{
   privateString username;
 
   publicString getUsername() {
       returnusername;
   }
   publicvoid setUsername(String username) {
      this.username= username;
   }
}

我们只需要提供属性的set方法,然后去属性文件中去配置好让框架能够找到applicationContext.xml文件的beans标签。标签beans中添加bean标签, 指定id,class值,id值不做要求,class值为对象所在的完整路径。bean标签再添加property 标签,要求,name值与User类中对应的属性名称一致。value值就是我们要给User类中的username属性赋的值。

<bean id="userAction"class="com.test.spring.action.User" >
<property name="username" value="admin"></property>
</bean>

2)给对象赋值

同样提供对象的set方法

public class User{
     private UserService userservice;
     public UserServicegetUserservice() {
          returnuser;
     }
     public void setUserservice(UserService userservice){
         this.userservice= userservice;
     }
}

配置文件中要增加UserService的bean标签声明及User对象对UserService引用。

<!--对象的声明-->
<bean id="userService" class="com.test.spring.service.UserService"></bean>
 
<bean id="userAction"class="com.test.spring.action.User" >
   <property name="userservice" ref="userService"></property>
</bean>

这样配置,框架就会将UserService对象注入到User类中。

3)给list集合赋值

同样提供set方法

public class User{
    privateList<String> username;
    publicList<String> getUsername() {
        returnusername;
    }
    publicvoid setUsername(List<String> username) {
        this.username= username;
    }
}
<bean id="userAction"class="com.test.spring.action.User" >
     <propertyname="username">
           <list>
               <value>zhang,san</value>
               <value>lisi</value>
               <value>wangwu</value>                                
               </list>
    </property>
</bean>

4)给属性文件中的字段赋值

public class User{
    privateProperties props ;
    publicProperties getProps() {
        returnprops;
    }
    publicvoid setProps(Properties props) {
        this.props= props;
    }
}
<bean>
    <propertyname="props">
        <props>
           <propkey="url">jdbc:oracle:thin:@localhost:orl</prop>
           <propkey="driverName">oracle.jdbc.driver.OracleDriver</prop>
           <propkey="username">scott</prop>
           <propkey="password">tiger</prop>
        </props>
    </property>
</bean>

标签中的key值是.properties属性文件中的名称

注意:

无论给什么赋值,配置文件中标签的name属性值一定是和对象中名称一致。

3、构造方法注入

1)构造方法一个参数

public class User{
    privateString usercode;
    publicUser(String usercode) {
        this.usercode=usercode;
    }
}
<bean id="userAction"class="com.test.spring.action.User">              
    <constructor-argvalue="admin"></constructor-arg>                    
</bean>

2)构造函数有两个参数时

当参数为非字符串类型时,在配置文件中需要制定类型,如果不指定类型一律按照字符串类型赋值。
当参数类型不一致时,框架是按照字符串的类型进行查找的,因此需要在配置文件中制定是参数的位置

<constructor-argvalue="admin"index="0"></constructor-arg>                
<constructor-argvalue="23" type="int"index="1"></constructor-arg><br>

这样制定,就是构造函数中,第一个参数为string类型,第二个参数为int类型。

参考自:https://blog.csdn.net/sinat_34093604/article/details/52485270
https://blog.csdn.net/lishuangzhe7047/article/details/20740835#
https://blog.csdn.net/a909301740/article/details/78379720

spring整合基础-spring和springMVC父子容器的原理

要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的。spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。

spring的启动过程:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

说完了spring上下文的初始化过程,这三个上下文的关系应该就了解了。如还是不太清楚,我就爱莫能助了,只能自行看代码去了。

===============================================================================================================

最近在做项目时牵扯到有关父子上下文的概念。

何为父子上下文呢?

父上下文:

使用listener监听器来加载配置文件,如下:

<listener>   

  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   

</listener>

Spring 会创建一个WebApplicationContext上下文,称为父上下文(父容器),保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

子上下文:

使用Spring MVC 来处理拦截相关的请求时,会配置DispatchServlet:

<servlet>

    <servlet-name>dispatcherServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet

    </servlet-class>

    <init-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/applicationContext-mvc.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

</servlet>

 
<servlet-mapping>

    <servlet-name>dispatcherServlet</servlet-name>

    <url-pattern>/</url-pattern>

</servlet-mapping>

每个DispatchServlet会有一个自己的上下文,称为子上下文,它也保存在 ServletContext中,key 是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名称。当一 个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是 DispatcherServlet.class.getName() + “.CONTEXT”。

可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);

父上下文(父容器)和子上下文(子容器)的访问权限:

子上下文可以访问父上下文中的bean,但是父上下文不可以访问子上下文中的bean。

父上下文使用与否

方案一,传统型:

父上下文容器中保存数据源、服务层、DAO层、事务的Bean。

子上下文容器中保存Mvc相关的Action的Bean.

事务控制在服务层。

由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。

当然,做为“传统型”方案,也没有必要这要做。

 

方案二,激进型:

Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。所以“激进型”方案就出现了—–没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。

本文不想说这是不是正确的思想,我想说的是Spring不会限制你这样做。

由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。

总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了,什么麻烦事儿也没有了。

 

 

 

Java–大项目能做好–按传统方式做,规规矩矩的做,好扩展,好维护。

Java–小项目能做快–按激进方式做,一周时间就可以出一个版本,先上线接受市场(用户)的反馈,再改进,再反馈,时间就是生命(成本)。

 

来自:https://blog.csdn.net/caomiao2006/article/details/51290494