tc11o8

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

spring特性:

1,方便解耦,简化开发 通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2,AOP编程的支持 通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3,声明式事务的支持 在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4,方便程序的测试 可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。
5,方便集成各种优秀框架 Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hession、Quartz)等的直接支持。
6,降低Java EE API的使用难度 Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
7,Java 源码是经典学习范例 Spring的源码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。
 
如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
 
对Spring aop的理解
AOP,也就是面向切面编程,我是这么理解的:学java的应该都知道面向对象编程(oop),而OOP是从静态解读考虑程序结构,但AOP是从动态角度考虑程序运行过程。也可以说AOP是OOP的补充和完善。OOP引入了封装、继承和多态性等概念来建立一种对象层次结构, 用以模拟公共的一个集合。当我们需要为分散的对象引入公共行为的时候,则OOP显得无能为力了。也就是说,OOP允许你定义从上到下的关系,但并不适合从左到右的关系,例如日志功能。日志代码往往水平的散步在所有对象层次中,而与它所散步到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切代码,在oop设计中,他导致了大量代码的重复,而不利于各个模块的重用。而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将哪些影响了多个类的公共行为封装到一个可重用模块,简单的来说就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来。便于减少系统的重复代码,降低模块间的耦合度等。
 
aop 底层有两种代理(jdk代理,CGLIB代理):
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
cglib代理,是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。(例如service的事务处理)
 
对Spring IOC的理解
IoC(控制反转),将类的创建和依赖关系写在配置文件里,由配置文件注入,实现了松耦合
IOC 底层实现原理:JAVA反射
 
springmvc的运行流程,实现原理?
执行流程:
1). 若一个请求匹配 DispatcherServlet 的请求映射路径(在 web.xml 中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
2). DispatcherServlet 接收到请求后, 将根据请求信息(包括 URL、HTTP 方法、请求 头、请求参数、Cookie 等)及 HandlerMapping 的配置找到处理请求的处理器(Handler). 可将 
 
HandlerMapping 看成路由控制器,将 Handler 看成目标主机。
3). 当 DispatcherServlet 根据 HandlerMapping 得到对应当前请求的 Handler 后, 通过 HandlerAdapter 对 Handler 进行封装,再以统一的适配器接口调用 Handler。
4). 处理器完成业务逻辑的处理后将返回一个 ModelAndView 给 DispatcherServlet, ModelAndView 包含了视图逻辑名和模型数据信息
5). DispatcherServlet 借助 ViewResoler 完成逻辑视图名到真实视图对象的解析
6). 得 到 真 实 视 图 对 象 View 后 , DispatcherServlet 使用这个 View 对 ModelAndView 中的模型数据进行视图渲染
 
实现原理:
基于servlet实现,同样流行的mvc框架还有struts2,两者区别是:
1,SpringMVC的入口是servlet,而Struts2是filter(filter执行顺序 > serlvet)
2,spring会稍微比struts快。 spring mvc是基于方法的设计 ,而sturts是基于类 ,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。spring3mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring3mvc中,一个方法对应一个request上下文,所以说从架构本身上SpringMVC就容易实现restful,且SpringMVC执行和开发效率都应该是高于Struts2的。
 
深入分析ClassLoader
先说说为什么要知道java的类加载机制。个人认为主要有以下几个原因:
按需加载。JVM启动时不能确定我要加载哪些东西,或者有些类非常大,我只希望用到它时再加载,并非一次性加载所有的class,所以这时候了解了加载机制就可以按需加载了。
类隔离。比如web容器中部署多个应用,应用之间互相可能会有冲突,所以希望尽量隔离,这里可能就要分析各个应用加载的资源和加载顺序之间的冲突,针对这些冲突再自己定些规则,让它们能够愉快地玩耍。资源回收。如果你不了解java是如何加载资源的,又怎么理解java是如何回收资源的?一般说到java的类加载机制,都要说到“双亲委派模型”(其实个人很不理解为什么叫“双亲”,其实英文叫“parent”)。使用这种机制,可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。JVM根据 类名+包名+ClassLoader实例ID 来判定两个类是否相同,是否已经加载过(所以这里可以略微扩展下,可以通过创建不同的classloader实例来实现类的热部署)。
a) BootStrapClassLoader。它是最顶层的类加载器,是由C++编写而成, 已经内嵌到JVM中了。在JVM启动时会初始化该ClassLoader,它主要用来读取Java的核心类库JRE/lib/rt.jar中所有的class文件,这个jar文件中包含了java规范定义的所有接口及实现。
b) ExtensionClassLoader。它是用来读取Java的一些扩展类库,如读取JRE/lib/ext/*.jar中的包等(这里要注意,有些版本的是没有ext这个目录的)。
c) AppClassLoader。它是用来读取CLASSPATH下指定的所有jar包或目录的类文件,一般情况下这个就是程序中默认的类加载器。
d) CustomClassLoader。它是用户自定义编写的,它用来读取指定类文件 。基于自定义的ClassLoader可用于加载非Classpath中(如从网络上下载的jar或二进制)的jar及目录、还可以在加载前对class文件优一些动作,如解密、编码等。很多资料和文章里说,ExtClassLoader的父类加载器是BootStrapClassLoader,其实这里省掉了一句话,容易造成很多新手(比如我)的迷惑。严格来说,ExtClassLoader的父类加载器是null,只不过在默认的ClassLoader 的 loadClass 方法中,当parent为null时,是交给BootStrapClassLoader来处理的,而且ExtClassLoader 没有重写默认的loadClass方法,所以,ExtClassLoader也会调用BootStrapLoader类加载器来加载,这就导致“BootStrapClassLoader具备了ExtClassLoader父类加载器的功能”查看classloader的源码可以发现三个重要的方法:
a) loadClass。classloader加载类的入口,此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从父ClassLoader中寻找,如仍然没找到,则从BootstrapClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法,如加载顺序相同,则可通过覆盖findClass来做特殊的处理,例如解密、固定路径寻找等,当通过整个寻找类的过程仍然未获取到Class对象时,则抛出ClassNotFoundException。如类需要 ,则调用resolveClass进行链接。
b) findClass。此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
c) defineClass。此方法负责将二进制的字节码转换为Class对象,这个方法对于自定义加载类而言非常重要,如二进制的字节码的格式不符合JVM Class文件的格式,抛出ClassFormatError;如需要生成的类名和二进制字节码中的不同,则抛出NoClassDefFoundError;如需要加载的class是受保护的、采用不同签名的或类名是以java.开头的,则抛出SecurityException;如需加载的class在此ClassLoader中已加载,则抛出LinkageError。
 
类的加载的过程
一个java文件从被加载到被卸载这个生命过程
加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载
 
类的加载全过程
加载->验证->准备->解析->初始化
 
1,首先是加载:
这一块虚拟机要完成3件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
关于第一点,很灵活,很多技术都是在这里切入,因为它并没有限定二进制流从哪里来:
从class文件来->一般的文件加载
从zip包中来->加载jar中的类
从网络中来->Applet
 
2,加载完成后就要开始对那些字节流进行检验
检验的目的:确保class文件的字节流信息符合jvm的口味,不会让jvm感到不舒服。
检验主要经历几个步骤:文件格式验证->元数据验证->字节码验证->符号引用验证
文件格式验证:验证字节流是否符合Class文件格式的规范并验证其版本是否能被当前的jvm版本所处理。
ok没问题后,字节流就可以进入内存的方法区进行保存了。后面的3个校验都是在方法区进行的。
元数据验证:对字节码描述的信息进行语义化分析,保证其描述的内容符合java语言的语法规范。
字节码检验:校验java编译成的字节码文件是否破损或格式错误
符号引用验证:来验证一些引用的真实性与可行性,比如代码里面引了其他类,这里就要去检测一下那些来究竟是否存在;
或者说代码中访问了其他类的一些属性,这里就对那些属性的可访问性进行检验。(这一步将为后面的解析工作打下基础)
 
3,接着就上面步骤完成后,就会进入准备阶段了:
这阶段会为类变量(指那些静态变量)分配内存并设置初始值的阶段,这些内存在方法区中进行分配。这里不包含用final修饰的static,因为final在编译的时候就会分配了
4,完成上步后,就要进行解析了。解析好像是对类的字段,方法等东西进行转换,具体涉及到Class文件的格式内容,并没深入去了解。
5,在前面的类加载过程中,除了在加载阶段用户可以通过自定义类加载器参与之外,其他的动作完全有jvm主导,到了初始化这块,才开始真正执行java里面的代码。
 
这一步将会执行一些预操作,注意区分在准备阶段,已经为类变量执行过一次系统赋值了。
其实说白了,这一步就是执行程序的构造器
 
GC:
垃圾检测方式:
引用计数法:给一个对象添加引用计数器,每当有个地方引用它,计数器就加1;引用失效就减1。好了,问题来了,如果我有两个对象A和B,互相引用,除此之外,没有其他任何对象引用它们,实际上这两个对象已经无法访问,即是我们说的垃圾对象。但是互相引用,计数不为0,导致无法回收,所以还有另一种方法:
可达性分析算法:以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java栈中引用的对象、方法区常良池中引用的对象本地方法中引用的对象等。
总之,JVM在做垃圾回收的时候,会检查堆中的所有对象是否会被这些根集对象引用,不能够被引用的对象就会被垃圾收集器回收。
 
回收算法:
1.标记-清除(Mark-sweep)
说明:算法和名字一样,分为两个阶段:标记和清除。标记所有需要回收的对象,然后统一回收。这是最基础的算法,后续的收集算法都是基于这个算法扩展的。
不足:效率低;标记清除之后会产生大量碎片
 
2.复制(Copying)
说明:此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。
缺点:就是需要两倍内存空间
 
3.标记-整理(Mark-Compact)
说明:此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题
缺点:
 
4.分代收集算法
说明:
这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。
为什么要运用分代垃圾回收策略?
在java程序运行的过程中,会产生大量的对象,因每个对象所能承担的职责不同所具有的功能不同所以也有着不一样的生命周期,有的对象生命周期较长,比如Http请求中的Session对象,线程,Socket连接等;有的对象生命周期较短,比如String对象,由于其不变类的特性,有的在使用一次后即可回收。试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对会很长,而且对于存活时间较长的对象进行的扫描工作等都是徒劳。因此就需要引入分治的思想,所谓分治的思想就是因地制宜,将对象进行代的划分,把不同生命周期的对象放在不同的代上使用不同的垃圾回收方式。
 
如何划分?
将对象按其生命周期的不同划分成:年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,所以与java对象的回收关系不大,与回收息息相关的是年轻代和年老代。这里有个比喻很形象“假设你是一个普通的 Java 对象,你出生在 Eden 区,在 Eden 区有许多和你差不多的小兄弟、小姐妹,可以把 Eden 区当成幼儿园,在这个幼儿园里大家玩了很长时间。Eden 区不能无休止地放你们在里面,所以当年纪稍大,你就要被送到学校去上学,这里假设从小学到高中都称为 Survivor 区。开始的时候你在 Survivor 区里面划分出来的的“From”区,读到高年级了,就进了 Survivor 区的“To”区,中间由于学习成绩不稳定,还经常来回折腾。直到你 18 岁的时候,高中毕业了,该去社会上闯闯了。于是你就去了年老代,年老代里面人也很多。在年老代里,你生活了 20 年 (每次 GC 加一岁),最后寿终正寝,被 GC 回收。有一点没有提,你在年老代遇到了一个同学,他的名字叫爱德华 (慕光之城里的帅哥吸血鬼),他以及他的家族永远不会死,那么他们就生活在永生代。”
年轻代:是所有新对象产生的地方。年轻代被分为3个部分——Enden区和两个Survivor区(From和to)当Eden区被对象填满时,就会执行Minor GC。并把所有存活下来的对象转移到其中一个survivor区(假设为from区)。Minor GC同样会检查存活下来的对象,并把它们转移到另一个survivor区(假设为to区)。这样在一段时间内,总会有一个空的survivor区。经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。需要注意,Survivor的两个区是对称的,没先后关系,from和to是相对的。涉及了复制算法
年老代:在年轻代中经历了N次回收后仍然没有被清除的对象,就会被放到年老代中,可以说他们都是久经沙场而不亡的一代,都是生命周期较长的对象。对于年老代和永久代,就不能再采用
像年轻代中那样搬移腾挪的回收算法,因为那些对于这些回收战场上的老兵来说是小儿科。通常会在老年代内存被占满时将会触发Full GC,回收整个堆内存。涉及了“标记-整理(Mark-Sweep)”的算法。
 
持久代:用于存放静态文件,比如java类、方法等。持久代对垃圾回收没有显著的影响。
posted on 2017-04-21 10:24  tc11o8  阅读(171)  评论(0编辑  收藏  举报