Spring学习笔记(一)

关于Spring



Spring 框架是一个基于 Java 的开源框架平台,并且为实现 Java 应用程序提供了全面的基础架构支持。 你可以专注于你的应用,让 Spring 来帮助你处理基础架构的问题。

Java 应用程序小到 HelloWorld,大到N层的复杂架构的企业级应用,这些应用里面包含着各种的协作对象,程序里面的对象是相互之间依赖的。尽管Java平台提供的很多功能性的应用程序,但是它缺少将这些基础组件组织成一个整体的方法,最终把这些整合工作交给了架构师或是开发者。你可以使用设计模式,例如工厂抽象工厂建造者装饰服务定位来将这些不同的类和示例对象组合起来,从而构建一个应用。但是,这些模式仅仅只是:一个被给予名字的最佳实践,说明了该模式做什么,怎样应用,解决了什么问题等等。 模式是形式化的,你必须在你的应用中去实现它。

Spring中的IoC(控制反转)也称为DI(依赖注入),解决了这个问题,通过提供一种有效的方式将各个分开的组件组合成一个完全可供使用的应用。这些组件间的依赖,交由给Spring去管理,从而减少对象依赖间的麻烦。 IOC是Spring重要思想之一。

Spring的目标是致力于全方位的简化Java开发。为了降低Java开发复杂性,Spring采取了以下4种关键策略:

1、基于POJO的轻量级和最小侵入性编程

2、通过依赖注入和面向接口实现松耦合

3、基于切面和惯例进行声明式编程

4、通过切面和模板减少样板式代码

IOC和DI



Spring的核心是IOC容器和DI。

什么是IOC?什么又是DI?

控制反转(Inversion of Control)是一种是面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度。其基本思想是:借助于“第三方”实现具有依赖关系的对象之间的解耦。

这里,对象A、B、C分别都依赖对象1、2、3,这些对象相互协调工作但朴素耦合,一旦其中那一个对象出问题,整个系统就崩溃了。对象之间的耦合是避免不了的,这是协作的基础。但一旦系统做大了,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

由于有了第三方IOC的加入,对象间的耦合就降低了,但对象间的协作需要通过第三方IOC的协调和粘合,全部对象的控制权都得上交给IOC容器。

依赖注入(DI就是将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。通过DI,对象的依赖关系将第系统中负责协调各对象的第三方组件在创建对象的时候进行设定,对象无需要自行创建或管理他们的依赖关系。

IOC和DI两者关系:

控制反转(IOC)是一种软件工程的解耦合的思想。

依赖注入(DI)是一种设计模式,可以作为控制反转的一种实现模式。

Spring IOC容器使用依赖注入作为实现控制反转的方式,但是控制反转还有其他的实现方式,例如说ServiceLocator,所以不能将控制反转和依赖注入等同。

Spring各模块



Spring这个基于Java技术的平台可以理解为一个生态,在这个生态环境里面,有许多的模块组成,扩展到不同的领域。在官方文档上面写到,在Spring框架组织内大约有20个模块。这些模块组成了不同的功能:

  1. Core Container(Spring 核心容器)
  2. AOP(SpringAOP模块)
  3. Data Access/Integration(数据访问与集成)
  4. Web(Spring的Web支持)
  5. Instrumentation
  6. Messaging
  7. Test

构建Spring应用



搭建基于Spring的Maven项目

1、Maven加入依赖

加入Spring核心依赖模块:

<!--Spring-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.8.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>4.3.8.RELEASE</version>
</dependency>
<!--Spring-->

去除Spring依赖的commons-logging日志加入sl4j-log4j日志工具:

<!--Logging-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.5.8</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.5.8</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.5.8</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.14</version>
</dependency>
<!--logging-->

log4j.properties:

log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
log4j.category.org.springframework.beans.factory=DEBUG

2、以一个英勇的骑士执行的任务为例子,对比传统应用和Spring应用

一个Knight接口,代表骑士行为:

public interface Knight {
  void embarkOnQuest();
}

一个Quest接口,代表着探险任务行为:

public interface Quest {
  void embark();
}

一个拯救少女的探险任务:

public class RescueDamselQuest implements Quest {
  public void embark() {
    System.out.println("Embarking on a quest to rescue the damsel.");
  }
}

骑士的实现:

public class DamselRescuingKnight implements Knight {
  private RescueDamselQuest quest;
  public DamselRescuingKnight() {
    this.quest = new RescueDamselQuest();
  }
  public void embarkOnQuest() {
    quest.embark();
  }
}

骑士实现了Knight接口,有了embarkOnQuest功能,DamselRescuingKnight在构造函数中自行构建了RescueDamselQuest,这使得DamselRescuingKnight和RescueDamseQuest紧密地连接在一起,这极大限制了骑士执行的探险任务。如果有其他的Quest让骑士去embarkOnQuest,那骑士就无能为力了。

3、加入Spring支持

改造DamselRescuingKnight变成BraveKnight:

public class BraveKnight implements Knight {
  private Quest quest;
  public BraveKnight(Quest quest) {
    this.quest = quest;
  }
  public void embarkOnQuest() {
    quest.embark();
  }
}

BraveKnight没有自行创建探险任务,而是将探险任务当作构造参数传进来,属于构造函数注入。任何探险任务只实现了Ouest接口都可以被注入进来让BraveKnight响应。

新建一个SlayDragonQuest类:

public class SlayDragonQuest implements Quest {

    private PrintStream stream;

    public SlayDragonQuest(PrintStream stream) {
        this.stream = stream;
    }

    public void embark() {

        stream.println("Embarking on quest to slay the dragon!");
    }

}

这个实现Ouest的接口可以注入到时Knight之中,

项目中新建一个spring-context.xml文件,在源码的路径下。创建对象之间的协作行为称之为装配,Spring有许多装配Bean的方式,XML、JavaCode、基于注解的自动装配。XML是最常见的装配方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--声明一个Bean以及它的依赖关系-->
    <bean id="slayDragonQuest" class="com.test.bean.SlayDragonQuest">
        <constructor-arg value="#{T(java.lang.System).out}"></constructor-arg>
    </bean>
    <bean id="braveKnight" class="com.test.bean.BraveKnight">
        <constructor-arg ref="slayDragonQuest"/>
    </bean>

</beans>

顶级Beans声名这是一个Bean容器,Bean元素代表是一个Bean,可对这个Bean进行配置。

id标识这个Bean,class指定Bean的类路径。Constructor-arg用于构造注入,SlayDragonQuest的构造方法需要注入一个PrintStream对象,这里用Spel表达式,将java.lang.System.out这个PrintStream类型的常量注进去。

BraveKnight的构造方法需要注入一个Ouest类型,ref引用了id为slayDragonQuest的Bean。

新建一个Main方式使用:

//获取IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
//获取一个Bean
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();

Spring通过应用上下文(Application Context)来装配bean的定义把它们组装起来,Spring全权负责bean的创建和组装。Spring有多种上下文的实现,区别就是如何加载bean的配置。

4、应于切面编程

DI能够让相互协作的软件组件保持松散耦合,而面向切面编程AOP能让你把遍布在应用各地的组件分离出来形成可征用的组件。

面向切面编程(AOP)往往定义为促使软件系统实现关注点分离的一项技术。一个系统有许多不同组件组成,每一个组件都负责一项特定的功能,除了完成特定的核心功能,这些组件都承担额外的职责,诸如日志、事务、系统安全这些的系统服务经常融入到自身的核心业务之中,这些系统服务被称为横切关注点,它们横跨系统的各个组件。

AOP能将这些系统服务模块化,并将他们以声明的形式应用到它们需要影响的组件之中。所受影响的组件完全不用关注这些系统的服务,只需要关注自身核心的业务。

例子:

在骑士进行冒险之前用吟游诗人这个服务来记载骑士的所有事迹。

public class Minstrel {

    private PrintStream stream;

    public Minstrel(PrintStream stream) {
        this.stream = stream;
    }

    public void singBeforeQuest() {
        stream.println("Fa la la, the knight is so brave!");
    }

    public void singAfterQuest() {
        stream.println("Tee hee hee, the brave knight " +
                "did embark on a quest!");
    }

}

在骑士进行冒险之前,调用singBeforeQuest,冒险回来之后,会调用singAfterQuest方法。不使用AOP会这样做:

public class BraveKnight implements Knight {

    private Quest quest;
    private Minstrel minstrel;

    public BraveKnight(Quest quest, Minstrel minstrel) {
        this.quest = quest;
        this.minstrel = minstrel;

    }

    public void embarkOnQuest() {
        minstrel.singBeforeQuest();
        quest.embark();
        minstrel.singAfterQuest();
    }

}

但吟诗不是骑士的职责,骑士还要知道一位的吟诗诗人才行。这样简单的BraveKnight就变得复杂了。

可以利用AOP声明Minstrel必需要在骑士进行冒险任务之前进行吟诗,而骑士不必要知道诗人的存在。要将Minstrel变成一个切面,需要在配置文件里面声明它.

Maven依赖文件加入aspectj(一个强大的AOP框架,弥补Spring AOP框架不足):

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.10</version>
</dependency>

sprng-context配置文件声明AOP:

   <bean id="minstrel" class="com.test.bean.Minstrel">
        <constructor-arg value="#{T(java.lang.System).out}"/>
    </bean>
    <aop:config>
        <aop:aspect ref="minstrel">
            <aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/>
            <aop:before method="singBeforeQuest" pointcut-ref="embark"/>
            <aop:after method="singAfterQuest" pointcut-ref="embark"/>
        </aop:aspect>
    </aop:config>

使用SpringAOP命名空间将Minstrel声明为一个切面,首先得将Minstrel声明为一个Bean,然后在<aop:aspect>引用该Bean,

<aop:pointcut>定义一个切入点,并且用expression来应用通知,expression使用的是Aspect表达式。该表达式可灵活配置。

使用<aop:before>在embarkOnQuest方法执行之前调用Minstrel的singBeforeQuest方法。这叫前置通知。Method指定调用方法,pointcut-ref指定切入点。

使用<aop:after>在embarkOnQuest方法执行之后调用Minstrel的singAfterQuest方法,这叫后置通知。Method指定调用方法,pointcut-ref指定切入点。

使用AOP,Minstrel可以应用到Knight中,而且Knight不知道有Minstrel存在。

时间: 2024-11-10 05:51:00

Spring学习笔记(一)的相关文章

Spring学习笔记(一)

Spring学习笔记(一) Spring核心思想: IOC:  Inversion Of Control (控制反转) / DI: Dependency Injection (依赖注入) AOP: Aspect Oriented Programming (面向切面编程) IOC 1. 简单的应用 Model package com.wangj.spring.model; public class User { private String username; private String pas

不错的Spring学习笔记(转)

Spring学习笔记(1)----简单的实例 ---------------------------------   首先需要准备Spring包,可从官方网站上下载.   下载解压后,必须的两个包是spring.jar和commons-logging.jar.此外为了便于测试加入了JUnit包.   在Myeclipse中创建Java项目.   编写一个接口类,为了简单,只加入了一个方法.   Java代码   1.package com.szy.spring.interfacebean;  

《Spring学习笔记》:Spring、Hibernate、struts2的整合(以例子来慢慢讲解,篇幅较长)

<Spring学习笔记>:Spring.Hibernate.struts2的整合(以例子来慢慢讲解,篇幅较长) 最近在看马士兵老师的关于Spring方面的视频,讲解的挺好的,到了Spring.Hibernate.struts2整合这里,由于是以例子的形式来对Spring+Hibernate+struts2这3大框架进行整合,因此,自己还跟着写代码的过程中,发现还是遇到了很多问题,因此,就记录下. 特此说明:本篇博文完全参考于马士兵老师的<Spring视频教程>. 本篇博文均以如下这

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >

Spring学习笔记(三)

Spring学习笔记(三) AOP 一.使用Annotation方式实现AOP.步骤: xml里加入配置:<aop:aspectj-autoproxy /> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org

Spring学习笔记--initmethod和构造函数、setter方法的加载顺序

今天学习了一下spring中bean的初始化和销毁,突然想了解一下初始化方法跟构造函数及setter方法注入的执行顺序,记录在此,仅作为学习笔记. 当实例化一个bean时,可能需要执行一些初始化操作来确保该bean处于可用状态.同样地,当不再需要bean时,将其从容器中移除是,我们可以还需要按顺序 执行一些清除工作. package com.zp.chapter2; public class Auditorium { private String name; public void doBefo

【Spring学习笔记-MVC-3.1】SpringMVC返回Json数据-方式1-扩展

<Spring学习笔记-MVC>系列文章,讲解返回json数据的文章共有3篇,分别为: [Spring学习笔记-MVC-3]SpringMVC返回Json数据-方式1:http://www.cnblogs.com/ssslinppp/p/4528892.html [Spring学习笔记-MVC-4]返回Json数据-方式2:http://www.cnblogs.com/ssslinppp/p/4530002.html [Spring学习笔记-MVC-3.1]SpringMVC返回Json数据-

Spring学习笔记 2014-7-9

Spring需要applicationContext.xml来管理各个Bean,其基本格式: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:t

Spring学习笔记一(Spring核心思想)

通过学习<Spring in action (Third edition)>的第一章,我大概了解了Spring的基本思想: 1,依赖注入(Dependnecy Injection): 在不使用Spring框架的情况下,一个类要跟另一个类建立联系,可能会使用如下的模式: class A{...} class B{ private A a; ...       } 这样的话,每次实例化一个B的对象,如b1,必定实例化一个A的对象,如a1,并且b1和a1是紧耦合的,b1牢牢地和a1绑定在一起了.他们

【Spring学习笔记-MVC-4】返回Json数据-方式2

摘要 本文讲解另外一种利用spring MVC返回json数据的方法. 前文回顾 在<[Spring学习笔记-MVC-3]返回Json数据-方式1>中介绍了通过: @ResponseBody声明返回值: 配置<mvc:annotation-driven />: 来返回json数据.效果如下:   ==>, 从上面的效果看,只能返回一个对象,不能返回多个对象,不能做到形如下图的返回结果, 存在局限性(可能可以返回多个,自己不知道如何实现). 下面介绍的方式2,利用spring