一、Spring的由来-替代EJB

Spring是一个轻量级Java 开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题,因此Spring是一个JavaSE/JavaEE的轻量级开源框架。它最大的优势,是使用JavaBean替代了EJB。


Spring框架的优势如下:

  • 方便解耦、简化开发:Spring是一个大工厂,可以将对象创建和依赖关系维护交给Spring管理。

  • AOP编程支持:Spring提供面向切面的编程,可以方便地实现对程序进行权限拦截、运行监控等功能。

  • 声明式事务的支持:只要通过配置就可以完成对事务的管理,无需手工编程。

  • 方便程序的测试:Spring对JUnit4提供支持,可以通过注解方便地测试Spring程序。

  • 降低JavaEE API的使用难度:Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用)提供了封装。



二、Spring体系-20多个模块,5个基础jar。

Spring的20多个功能模块如下图所示:

精选集-论Spring架构的优越性:大魏Java记14_java

模块虽多,Spring基本框架由以下5个jar支撑:

  • spring-core.*.jar: 包含Spring框架的基本的核心工具类,Spring其他组件都要用到这个包里的类;

  • spring-beans.*.jar:所有应用都要用到,包含配置文件、创建和管理bean以及进行IoC或者DI操作相关的所有类

  • spring-context.*.jar:Spring提供在基础IoC功能上的扩展服务,以及其他企业服务,如:EJB集成、JNDI定位、邮件服务、缓存以及各种视图层框架等。

  • spring-expression.*.jar:定义了Spring的表达式语言


Spring常用的第三方Jar包是用于处理日志的:

  • commons.logging.*.jar


使用Spring框架时,只需要将5个包复制到项目的lib目录,发布到类路径即可。


四、部署Spring的步骤:IDE或maven导入jar

Spring的部署非常简单,其实就是加载jar包,可以用IDE手工加载,也可以通过maven加载,我们展示通过IDE加载。


需要注意的是,Spring依赖于JDK。OpenJDK在RHEL上的安装方法,就不赘述了。

我们查看Spring的下载目录:

https://repo.spring.io/simple/libs-release-local/org/springframework/spring/

我们看到最新版本是5.3.1:

我们下载5.3.1版本,如下图所示,下载第一个包:

将压缩包上传到此前我们已经安装了JDK和Tomcat的主机。

[root@repo spring-framework-5.3.1]# ls

docs  libs  license.txt  notice.txt  readme.txt  schema


上面几个目录中。docs文件夹下包含API文件和开发规范:

libs目录中包含开发需要的JAR包和源码,前文提到的Spring框架四个包在下图已经框住:

接下来,我们下载日志包:

http://commons.apache.org/proper/commons-logging/download_logging.cgi

精选集-论Spring架构的优越性:大魏Java记14_java_02

上传到Linux,解压缩文件:

精选集-论Spring架构的优越性:大魏Java记14_java_03

将这5个jar包拷贝到一个目录中,方便后续使用:

[root@repo sp]# pwd

/sp

[root@repo sp]# ls

commons-logging-1.2.jar  spring-context-5.3.1.jar  spring-expression-5.3.1.jar

spring-beans-5.3.1.jar   spring-core-5.3.1.jar


在IDE中创建一个Java Project:

精选集-论Spring架构的优越性:大魏Java记14_java_04

起名为david-firstspring

精选集-论Spring架构的优越性:大魏Java记14_java_05

针对project加载jar包:



一定要选择classpath!:


加载路径的时候,选择ClassPath



查看jar包已经加载:



五、Spring的左膀右臂-IoC:仰仗7个注解

IoC是指在程序的设计中,实例不再由调用者创建,而是由Spring容器直接来创建。这样控制权应由应用代码转移到外部容器,控制权发生了反转,这就是IoC的理念。


从Spring容器角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入它所依赖的实例,这就是Spring的依赖注入,主要目的是为了解耦,体现一种“组合”的理念。


综上所述,控制反转是一种通过描述(在Spring中可以是XML或注解)并通过第三方去产生或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。



5.1、控制权不反转的情况

在正式介绍IoC的代码实现之前,我们先看看不使用IoC时,我们如何创建对象实例。大致有四种方式:

方式1:直接创建类的实例化

如下图红框所示,直接创建Ticket1的实例ticket。

补充说明一下这段代码。

Java中现成的创建主要有两种方式:

(1)继承java.lang包下的Thread类。这个类里有两个方法:start()方法、run()方法。但由于一类只能能有一个父类。所以这种用的相对少。

(2)Thread提供另外构造方法Thread(Runable target),Runnable是一个接口,只有一个run()方法。这个接口这种方式使用的多一些。


在上图中: 

Ticket1类实现了Runnable接口。将名为Ticket1的class进行实例化,实例名为ticket。

通过Thread类的构造方法将实例ticket作为参数传入,并指定线程名(如ticket1),同时调用Thread类的start()方法。

Start()方法会去调用ticket对象中的run()方法。

 


方式2:将类实例化与线程实例化合并

第二种模式与第一种无本质区别。将EmergencyThread类的实例作为参数注入到Thread的构造方法。然后在下一步调用Thread中的start方法。


方式3:实例化时转成父类

YieldThread继承了Thread类。在YieldThread类中,定义了一个有参构造方法,并且构造方法中,用到了super()。super可以理解为是指向自己父类对象的一个指针。所以,当创建线程实例时,可以使用YieldThread()直接将参数传递到父类Thread。

精选集-论Spring架构的优越性:大魏Java记14_java_06


第四种方式:静态方法调用

下图红框位置,就是调用了静态方法getArea()。

精选集-论Spring架构的优越性:大魏Java记14_java_07

精选集-论Spring架构的优越性:大魏Java记14_java_08

在实际使用中,更多的是使用实例化赋值。通过new实例化对象后,会生产对象实例,占用内存空间。


在以上四种方式中,我们看到除了静态方法,其他三种方式,都需要手工对对象进行实例化操作。


5.2、控制权反转的实现

Spring IoC的实现主要有两种:

1.XML

2.注解

(1)声明Bean的注解:告诉别人自己是bean

(2)注入Bean的注解:在一个bean中注入另外一个bean


由于SpringBoot不提倡使用XML的方式,因此我们对此不再赘述。

通过注解实现IoC,注解又分为两类:

声明Bean的注解,就是一个组件对象,把自己声明成bean。

注入Bean的注解,就是有依赖关系的Bean相互注入的注解。


(1)告诉别人自己是bean

声明Bean的注解如下。实际上,后四个注解的功能和第一个一致。但如果我们能够明确bean的层次,最好用后四种,这样代码可读性比较高。

   (1)@Component

    该注解是一个泛化的概念,仅仅表示一个组件对象(Bean),可以作用在任何层次上,没有明确的角色。

   (2)@Repository,对应MVC中的V

    该注解用于将数据访问层(DAO)的类标识为Bean,即注解数据访问层Bean,其功能与@Component()相同。

   (3)@Service,对应MVC中的M

    该注解用于标注一个业务逻辑组件类(Service层),其功能与@Component()相同。

   (4)@Controller,对应MVC中的C

    该注解用于标注一个控制器组件类(Spring MVC的Controller),其功能与@Component()相同。


注解2-4,是与MVC中的架构来对应的。

精选集-论Spring架构的优越性:大魏Java记14_java_09

(2)在一个bean中注入另外一个bean

bean注入一共有三个注解,三个的关系是(1)+(3)=(2)

   (1)@Autowired

    该注解可以对类成员变量、方法及构造方法进行标注,完成自动装配的工作。通过 @Autowired的使用来消除setter 和getter方法。默认按照Bean的类型进行装配。

   (2)@Resource

    该注解与@Autowired功能一样。区别在于,该注解默认是按照名称来装配注入的,只有当找不到与名称匹配的Bean才会按照类型来装配注入;而@Autowired默认按照Bean的类型进行装配,如果想按照名称来装配注入,则需要结合@Qualifier注解一起使用。

    @Resource注解有两个属性:name和type。name属性指定Bean实例名称,即按照名称来装配注入;type属性指定Bean类型,即按照Bean的类型进行装配。

   (3)@Qualifier

    该注解与@Autowired注解配合使用。当@Autowired注解需要按照名称来装配注入,则需要结合该注解一起使用,Bean的实例名称由@Qualifier注解的参数指定。


六、Spring IoC的代码展示

我们按照SpringMVC架构构建代码.

这套代码的注入链和调用链如下:

Dao注入到(V)-->Service(M)注入到--->Contoller(C)

测试类调用-->Contoller(C)调用-->Service(M)调用--->Dao(V)

精选集-论Spring架构的优越性:大魏Java记14_java_10


我们首先编写Dao的接口和实现类:

package annotation.dao;

public interface TestDao {

public void save();

}



package annotation.dao;

import org.springframework.stereotype.Repository;

@Repository("testDao")

/**相当于@Repository,但如果在service层使用@Resource(name="testDao")注入Bean,testDao不能省略。**/

public class TestDaoImpl implements TestDao{

@Override

public void save() {

System.out.println("testDao save");

}

}


接下来,创建server的接口和测试类:

package annotation.service;

public interface TestService {

public void save();

}



package annotation.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import annotation.dao.TestDao;

@Service("testService")//相当于@Service

public class TestSeviceImpl implements TestService{

@Resource(name="testDao")

/**相当于@Autowired,@Autowired默认按照Bean类型注入Bean**/

private TestDao testDao;

@Override

public void save() {

testDao.save();

System.out.println("testService save");

}

}


最后,创建controller的类:

package annotation.controller;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import annotation.service.TestService;

@Controller

public class TestController {

@Autowired

private TestService testService;

public void save() {

testService.save();

System.out.println("testController save");

}

}


创建ConfigAnnotation.java

package annotation;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

@Configuration//声明当前类是一个配置类,相当于一个Spring的XML配置文件。

@ComponentScan("annotation")

//自动扫描annotation包下使用的注解,并注册为Bean。

//相当于在Spring的XML配置文件使用<context:component-scan base-package="Bean所在的包路径"/>语句功能一样。

public class ConfigAnnotation {


}



创建测试类:TestAnnotation.java

package annotation;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import annotation.controller.TestController;

public class TestAnnotation {

public static void main(String[] args) {

//初始化Spring容器ApplicationContext

AnnotationConfigApplicationContext appCon = 

new AnnotationConfigApplicationContext(ConfigAnnotation.class);

TestController tc = appCon.getBean(TestController.class);

tc.save();

appCon.close();

}

}


都书写完后,运行测试类:

精选集-论Spring架构的优越性:大魏Java记14_java_11

我们看到结果是,这个结果是层层调用实现的:

testDao save

testService save

testController save