之前简单介绍过Spring框架,本文换个角度重新诠释Spring。使用Java语言开发的项目,几乎都绕不过Spring,那么Spring到底是啥,为何被如此广泛的应用,下面从以下两个问题出发来剖析Spring,本文所有讨论基于Spring 4。

  1. Spring是啥

Spring 是一个分层的 JavaSE/EE一站式(full-stack)轻量级开源框架。

引入问题:

1.1 何为分层,为什么要分层?

分层即将一体化的软件系统按不同的功能特性拆分成多个独立的功能模块,分层的目的在于解耦,解耦的目的在于提升开发效率降低维护成本;层是拆分的依据。在Web项目中依据功能的不同拆分成Controller(视图层),Service(服务层),Dao(Data Access Object数据访问层)。

1.2 何为一站式?

Spring对不同的软件层提供了针对性的解决方案:

Dao:Spring极易整合MyBatis|Hibernate等持久层框架,以及dbcp|C3p0等数据库连接池;

Service:Spring Core通过IOC控制反转、DI依赖注入获取了对象的管理权,降低了用户编写程序的复杂度,同时其AOP面向切面的编程使服务功能的升级变得更加友好便捷。

Controller:可以使用SpringMvc或者通过Spring整合Struts2简化开发。

1.3 轻量级从何体现?

Spring的轻量级是相对于EJB而言,Spring针对不同的场景提供了不同的服务,用户在使用时可以选择性的集成需要的功能模块,开发简单;而EJB规范功能复杂,使用难度高。

1.4 开源=免费?

国内的软件生态中,得到最广泛的技术基本都是开源的,其中免费是很重要的因素,如Spring/Linux/MySQL等等。当然也正是由于这些项目遵从了开源协议,使更多的开发人员投入相关的优化升级工作,使得项目不断向前发展。

2. 为何Spring被如此广泛的应用

随着互联网及移动互联网的飞速发展,接入的用户数据暴增,业务复杂度亦不断提高,Web项目的开发维护难度不断加大。基于此,大型项目进行微服务拆分,软件服务进行分层开发是必然选择。由于Spring的上述优点,以及基于Spring良好的扩展性而形成的完善生态,Spring被广泛应用于Java项目之中。

2.1 Spring到底解决了哪些问题?

a. IOC控制反转----替用户管理对象

Java类中的非静态方法及属性只能通过实例对象进行调用,在开发过程中频繁创建对象会比较繁琐,同时也会增加JVM的负担。Spring通过Java的反射机制,利用工厂模式,配合配置文件(Spring2.5版本以后支持注解Annotation)将类实例化的过程放在程序启动时完成,并将创建的实例放在Spring容器(ApplicationContext)中,后续程序进行开发时只需要通过注解注入到相应的代码中即可。

所以Java反射是什么?

反射是框架设计的灵魂。Java的反射机制是在运行状态中,对于任何一个类,都能知道这个类的所有属性和方法;对于任何一个对象,都能调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象方法的功能称为Java语言的反射机制。

反射的实现原理

Java中所有的类都继承了Object,所有.java(Programmer.java)文件都会被编译成.class(Programmer.class)文件,JVM在加载.class文件时会为每一个类(Programmer)创建一个唯一的Class类的对象,该对象用于描述.class文件中类的相关信息。通过调用父类Object中的方法getClass(programmer.getClass)可以获取到内存中代表该类唯一元信息的Class对象。这些元信息描述了该类的所有方法和属性,获取到这些属性即可实现对类的操作。

Java的反射机制是一个打破封装的特性,该特性使开发Java框架成为了可能。

xml配置文件的进阶----Annotation注解(Java1.5版本引入)

xml可扩展标记语言由于其良好的结构被选中,成为Java项目的配置文件,但是随着项目的不断变大,xml需要配置的内容不断增加,终于爱偷懒的程序员决定开发一个更易用的配置方式,基于注解的配置方式。

xml的配置文件的解析原理与注解方式大为不同:xml配置文件的解析是通过文件IO操作,在指定路径读取配置文件,根据标签获取对应配置信息。注解的实现原理是通过反射机制获取被检查的方法或类上的注解信息,再根据注解的元素值进行相关处理。

JDK自带的注解(Java目前只内置了三种标准注解:@Override、@Deprecated、@SuppressWarnings,以及四种元注解:@Target、@Retention、@Documented、@Inherited)。我们接触最多和作用最大的一类注解是第三方的注解,如Spring中的@Sevice,Junit中的@Test等。

b. DI依赖注入----实现对象的创建过程(注入属性)

依赖注入,指容器复制创建和维护对象之间的依赖关系,在 spring 创建对象的过程中,对象所依赖的属性通过配置注入对象中。

Bean----Spring容器管理的Java对象

不是所有Java对象都称之为Bean,只有被Spring容器管理的Java对象称之为Bean。

Spring应用通过容器(Spring上下文Application Context)来管理Bean,负责Bean的创建和装配(常说的依赖注入DI)。

Spring常见的容器有两种类型,一种是Bean工厂,另一种是应用上下文,后者是在前者的基础上构建,比前者多了更多企业级的应用服务。所以后者也是用得最多的一种容器,我们在Spring应用中也经常能看到*application*.xml的配置文件,在SpringBoot应用中常看到@Bean注解。

Spring容器负责管理Bean的创建和装配,开发人员可以定义需要创建的Bean,配置Bean之间的依赖关系。

c. AOP面向切面的编程----在不需要侵入式修改源码的情况下实现方法功能的增强,常用于日志记录,性能统计,安全控制,事务处理,异常处理等等。

AOP 分为静态 AOP 和动态 AOP。静态 AOP 是指 AspectJ 实现的 AOP,他是将切面代码直接编译到 Java 类文件中。动态 AOP 是指将切面代码进行动态织入实现的 AOP。Spring 的 AOP 为动态 AOP,实现的技术为: JDK 提供的动态代理技术 和 CGLIB(动态字节码增强技术)。

问题:代理?静态代理?动态代理?

什么是代理?

Java中的代理类具有与原始Java相同的服务接口,但是在类中持有原始类的对象,利用原有的对象调用原Java类的方法实现功能;代理的好处是在调用原方法时可以在之前或之后进行包装增加额外的功能。如卖机票的代理去哪儿网,代理了各大航空公司的机票,为用户提供搜索比价等等服务,但是用户在去哪儿购票时,实际调用的仍然是各大航空公司自己的票务系统。

静态代理AspectJ

AspectJ 是静态代理的增强,所谓的静态代理就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强。
AspectJ 是 Java 语言的一个 AOP 实现,其主要包括两个部分:

  • 第一个部分定义了如何表达、定义 AOP 编程中的语法规范,通过这套语言规范,我们可以方便地用 AOP 来解决 Java 语言中存在的交叉关注点问题;

  • 另一个部分是工具部分,包括编译器、调试工具等。

AspectJ 是最早、功能比较强大的 AOP 实现之一,对整套 AOP 机制都有较好的实现,很多其他语言的 AOP 实现,也借鉴或采纳了 AspectJ 中很多设计。在 Java 领域,AspectJ 中的很多语法结构基本上已成为 AOP 领域的标准。

示例

一个普通的 Hello 类:

public class Hello {    public void sayHello() {        System.out.println("hello");    }    public static void main(String[] args) {        Hello h = new Hello();        h.sayHello();    }}

使用 AspectJ 编写一个Aspect,命名为 TxAspect.aj:

public aspect TxAspect {    void around():call(void Hello.sayHello()){        System.out.println("开始事务 ...");        proceed();        System.out.println("事务结束 ...");    }}

上面的 TxAspect 根本不是一个 Java 类,所以 aspect 也不是 Java 支持的关键字,它只是 AspectJ 才能识别的关键字。使用 AspectJ 的编译器编译:

ajc -d . Hello.java TxAspect.aj

查看一下编译后的 Hello.class:

public class Hello {    public Hello() {    }    public void sayHello() {        System.out.println("hello");    }    public static void main(String[] args) {        Hello h = new Hello();        sayHello_aroundBody1$advice(h, TxAspect.aspectOf(), (AroundClosure)null);    }}

可以看到,这个类比原来的 Hello.java 多了一些代码,这就是 AspectJ 的静态代理,它会在编译阶段将Aspect 织入 Java 字节码中, 运行的时候就是经过增强之后的 AOP 对象。

动态代理

代理类在程序运行时创建的代理方式被成为动态代理。 上面静态代理的例子中,代理类在程序运行之前就已经编译完成。然而动态代理,代理类是在运行时动态生成的。

Java动态代理有两种----JDK动态代理和CGLIB动态代理

JDK的动态代理类java.lang.reflect.Proxy中newProxyInstance方法可以创建动态代理对象。

第一个参数:目标类的类加载器对象

第二个参数:目标类的实现接口的 Class[]

第三个参数:InvocationHandler 它是一个接口,它的作用是是代理实例的调用处理程序 实现的接口,接口中定义了一个方法

通过上述invoke方法调用原始类的方法实现功能。

CGLIB(Code Generation Library)是一个开源项目是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java接口。CGLIB 包的底层是通过使用一个小而快的字节码处理框架 ASM,来转换字节码并生成新的类。

JDK动态代理只能针对接口操作,CGLIB动态代理还可以针对非接口操作。Spring动态代理针对接口时默认使用JDK。

Spring整合AspectJ实现AOP

在 spring2.0 以后它支持 jdk1.5 注解,而整合 aspectj 后可以使用 aspectj 语法,可以简化开发。

AspectJ 框架它定义的通知类型有6种:

  1. 前置通知 Before 相当于 BeforeAdvice

  2. 后置通知 AfterReturning 相当于 AfterReturningAdvice

  3. 环绕通知 Around 相当于 MethodInterceptor

  4. 抛出通知 AfterThrowing 相当于 ThrowAdvice

  5. 引介通知 DeclareParents 相当于 IntroductionInterceptor

  6. 最终通知 After 不管是否异常,该通知都会执行

基于注解方案的AOP开发是当前使用最广泛的开发方式,具体实现方式略过。

2.2 Spring强大的扩展能力----使用户可以快速集成各种框架实现特定功能

a. Spring整合持久层框架MyBatis|Hibernate;

b. Spring整合数据库连接池Dbutils|C3P0;

c. Spring整合视图层开发框架structs2;

d. Spring整合内存数据库Redis;

e. Spring整合搜索服务Solr;

......

从Spring看Web项目开发的更多相关文章

  1. SpringMVC内容略多 有用 熟悉基于JSP和Servlet的Java Web开发,对Servlet和JSP的工作原理和生命周期有深入了解,熟练的使用JSTL和EL编写无脚本动态页面,有使用监听器、过滤器等Web组件以及MVC架构模式进行Java Web项目开发的经验。

    熟悉基于JSP和Servlet的Java Web开发,对Servlet和JSP的工作原理和生命周期有深入了解,熟练的使用JSTL和EL编写无脚本动态页面,有使用监听器.过滤器等Web组件以及MVC架构 ...

  2. 使用MyEclipse搭建java Web项目开发

    转自:http://blog.csdn.net/jiuqiyuliang/article/details/36875217 首先,在开始搭建MyEclipse的开发环境之前,还有三步工具的安装需要完成 ...

  3. Web项目开发中用到的缓存技术

    在WEB开发中用来应付高流量最有效的办法就是用缓存技术,能有效的提高服务器负载性能,用空间换取时间.缓存一般用来 存储频繁访问的数据 临时存储耗时的计算结果 内存缓存减少磁盘IO 使用缓存的2个主要原 ...

  4. 重新学习Spring一--Spring在web项目中的启动过程

    1 Spring 在web项目中的启动过程 Spring简介 Spring 最简单的功能就是创建对象和管理这些对象间的依赖关系,实现高内聚.低耦合.(高内聚:相关性很强的代码组成,既单一责任原则:低耦 ...

  5. Spring Boot Web应用开发 CORS 跨域请求支持:

    Spring Boot Web应用开发 CORS 跨域请求支持: 一.Web开发经常会遇到跨域问题,解决方案有:jsonp,iframe,CORS等等CORS与JSONP相比 1. JSONP只能实现 ...

  6. IntelliJIdea 2016.2 使用 tomcat 8.5 调试spring的web项目时,bean被实例化两次导致timer和thread被启动了两遍的问题的解决

    今天新搭建了一个spring的web项目,项目启动时会启动一个线程,线程里定时执行任务,另外还启动了一个定时器,每秒钟统计系统吞吐量等业务性能数据.但是调试的时候惊奇的发现定时器和线程均被启动了两次. ...

  7. JAVA WEB项目开发案例精粹

    http://www.blogjava.net/zongbao/archive/2012/07/24/383884.htmlJAVA WEB项目开发案例精粹.pdf main Alt + / => ...

  8. Web项目开发介绍及实战项目介绍

    引言 本系列课程我们将学些Golang语言中的Web开发框架Iris的相关知识和用法.通过本系列视频课程,大家能够从零到一经历一个完整项目的开发,并在课程中了解实战项目开发的流程和项目设涉及的各个模块 ...

  9. 【java项目实战】一步步教你使用MyEclipse搭建java Web项目开发环境(一)

    首先.在開始搭建MyEclipse的开发环境之前.还有三步工具的安装须要完毕,仅仅要在安装配置成功之后才干够进入以下的java Web项目开发环境的搭建. 1.安装工具 第一步,下载并安装JDK,到官 ...

随机推荐

  1. 五十五:WTForms表单验证之渲染模板

    此功能看似强大,实则鸡肋 from wtforms import Form, StringField, BooleanField, SelectFieldfrom wtforms.validators ...

  2. Python中sort和sorted函数代码解析

    Python中sort和sorted函数代码解析 本文研究的主要是Python中sort和sorted函数的相关内容,具体如下. 一.sort函数 sort函数是序列的内部函数 函数原型: L.sor ...

  3. [Python]机器学习:Tensorflow实现线性回归

    源码 #> tutorial:https://www.cnblogs.com/xianhan/p/9090426.html # 步骤一:构建模型 # 1.TensorFlow 中的线性模型 ## ...

  4. iOS检测用户截屏, 并获取所截图片

    // // ViewController.m // CheckScreenshotDemo // // Created by 思 彭 on 2017/4/25. // Copyright © 2017 ...

  5. robotframework-requests--中文注解版

    最近工作原因在研究RobotFramework对REST测试的方案,找到几个相关类库.但使用requests感觉更方便,研究了一下requests类库的源码,并将注释换成中文为方便使用.关于Reque ...

  6. 从gopath到go mod的一次尝试

    windows下的尝试: gomod初尝试下载官方包1.11(及其以上版本将会自动支持gomod) 默认GO111MODULE=auto(auto是指如果在gopath下不启用mod)go mod h ...

  7. react中key的使用

    面试题: 1). react/vue中的key的作用/内部原理 2). 为什么列表的key尽量不要用index 虚拟DOM的key的作用? 1). 简单的说: key是虚拟DOM对象的标识, 在更新显 ...

  8. Unreal Engine 4 优化教程

    本教程旨在帮助开发人员提升基于虚幻引擎(Unreal Engine*4 (UE4))开发的游戏性能.在教程中,我们对引擎内部及外部使用的一系列工具,以及面向编辑器的最佳实践加以概述,还提供了有助于提高 ...

  9. 从 SPIR-V 到 ISPC:将 GPU 计算转化为 CPU 计算

    游戏行业越来越多地趋向于将计算工作转移到图形处理单元 (GPU) 中,导致引擎和/或工作室需要开发大量 GPU 计算着色器来处理不同的计算任务.但有时候在 CPU 上运行这些计算着色器非常方便,不必重 ...

  10. 【VS开发】关于内存泄漏的调试

    没想到造成泄漏的原因是由于保存数据的线程因为事件阻塞在那里,此时要关闭OnClose的时候,这个挂起的线程爆出了内存泄漏,所以在关闭窗口之前,需要SetEvent(m_hSaveDataEvent); ...