Spring————程序员的春天

当客户端向服务器发出请求时,服务器把得到的请求发送给控制器Servlet,而在Servlet中需要创建Service对象来调用业务层相关功能(故说控制器层Servlet依赖于业务层Service),而在Service中又需要创建数据库层DAO对象来对数据库进行操作(故说业务层Service依赖于数据库层DAO)。

思考:针对上述过程,我们需要考虑这样几个问题。1.Servlet、Service以及Dao对象的创建时间、创建数量。2.Servlet、Service以及Dao之间的依赖关系。如何处理这些问题呢?Spring就是用来处理对象的创建、以及对象之间依赖关系的一个开发框架。它打破了我们传统开发的观念,我们不再需要像以前那样在具体的类中创建具体的对象,而是将对象的创建交给它去完成。它是我们所要学习的框架中最重要的框架,请务必好好学习。

1.Spring框架中的专业术语

1.1组件/框架设计

  • 侵入式设计:对现有类的结构有影响,即需要实现或继承某些特定类。 如Struts框架。
  • 非侵入式设计:引入了框架,对现有的类结构没有影响。如Spring框架/Hibernate框架。

1.2控制反转

Inversion On Control,简称IOC。对象的创建交给外部容器自动完成,这个就叫做控制反转。(有控制反转就有控制正转,控制正转:对象的创建由我们自己创建)

依赖注入dependency injection,简称DI,用于处理对象间的依赖关系。

二者区别:控制反转(IOC):解决对象创建的问题,(对象的创建交给别人)。依赖注入(DI):在创建完对象后,对象关系的处理就是依赖注入,(通过set方法依赖注入。)

1.3AOP

面向切面编程。切面,简单来说可以理解为一个类,由很多重复代码形成的类。切面举例:事务、日志、权限。 关于AOP的详细讲解我推荐你们看这篇博客:Spring AOP实现原理与应用 ,往后我会单独写篇博客为你们详细介绍AOP。

2.Spring框架概述

Spring框架,可以解决对象创建以及对象之间依赖关系的一种框架。且可以和其它框架一起使用,例如spring与struts、spring和hibernate。(起到整合/粘合作用的一个框架)。

spring提供了一站式解决方案:
1)SpringCore:是Spring的核心功能:IOC容器,解决对象创建及依赖关系。
2)SpringWeb:Spring对web模块的支持。

可以与struts整合,让struts的action创建交给spring。
Spring mvc模式,用springmvc整合了就不用struts了。

3)Spring DAO:是Spring对Jdbc操作的支持。(Jdbc Template模块工具类)

4)Spring ORM:是Spring对ORM的支持。

既可以与hibernate整合(使用原始的session)
也可以使用Spring对Hibernate操作的封装(对上面的session又进行了一层封装)

5)Spring AOP:关于AOP的详细讲解我推荐你们看这篇博客:Spring AOP实现原理与应用 ,往后我会单独写篇博客为你们详细介绍AOP。

6)SpringEE:Spring对javaEE其它模块的支持

3.Spring开发步骤

1)导入jar包:

写在前面的话:当你运行程序出现org.springframework.beans.factory.BeanDefinitionStoreException的报错信息时,不要想了,出现这种报错的信息原因绝对是因为jdk版本和你导入的spring jar包不兼容的问题。由于spring3.x与jdk1.7兼容,而spring4.x与jdk1.8兼容,所以这里提供两种解决方案:

  • 1.将jdk版本调为1.7,我用的开发工具为IDEA,它默认下的JDK使用1.8版本,所以我需要在三个地方将jdk的版本改过来(前提是你已经下载了jdk1.7版本),修改IDEA配置中Project的jdk版本、Modules的jdk版本、SDKs的版本,如果你用到leTomcat还需要修改Tomcat配置的jdk版本。这样jdk1.7与spring3.x才兼容。
  • 2.将spring3.x.jar换成spring4.x.jar包。这种方式比较繁琐,建议大家使用第一种方式。spring4.x与jdk1.8才兼容。

2)配置核心文件applicationContext.xml(文件名称随意):

代码如下:

1
2
3
4
5
6
7
8
9
10
11
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

3)使用:

首先我们创建一个pojo对象User.java:

然后创建测试类使用这个User对象,以前我们要使用User对象时直接像这样User user=new User();new一个对象即可:

而当我们使用Spring后就应该这样使用User对象,首先在applicationContext.xml中添加<bean>标签,一个<bean>标签代表一个pojo对象:

其中各个属性的说明见注释。然后我们通过如下步骤获取该pojo对象:

运行测试类:

说明成功获取到User对象。上述是通过工厂类获取的IOC容器创建的User对象,下面我们看看使用Spring框架获取pojo对象的第二种方式直接得到IOC容器的对象:

运行程序,成功打印出user信息,说明我们通过IOC容器成功获取到user对象。

4.<bean>创建的细节

对上述代码进行改进,对于IOC容器对象,我们只需创建一次即可,所以将创建IOC对象的代码改为成员变量。

代码中我们通过IOC对象创建了两个User对象,运行测试类得到打印结果:

发现答应的这两个User对象id都一样,说明我们获取到的是同一个对象,也说明通过<bean>标签设置的pojo对象是单例的。为什么呢?其实<bean>标签默认有一个scope="singleton"的属性,代表该<bean标签对象的pojo对象是单例的。我们可以将该属性值改为scope="prototype",如下:

然后再运行测试类,输出如下内容:

说明此时获取的两个User不再是同一个对象。

那么由IOC容器管理的pojo对象应该在何时创建呢?我们来看看,首先在User.java中添加一个无参构造器:

然后修改测试类:

运行测试类:

从控制台中输出内容我们可以得知:当程序运行时,IOC容器首先创建,然后当我们需要得到IOC容器中的pojo对象时我们通过语句ac.getBean("user");得到,此时就会在IOC中创建由它管理的pojo对象。当我们删除ac.getBean("user");语句时,再次运行程序,得到如下内容:

这说明什么呢?说明IOC容器中没有创建pojo对象(因为一旦创建就会有”—User对象创建—“的语句输出)。综上情况,即只有当我们用到pojo对象时,IOC容器才会在自己内部创建它。此种情况为<bean>标签的属性为scope="prototype"的结果,那么我们再来看看当属性为scope="singleton"时的输出结果为:

打印台的内容说明该User对象在程序启动时就创建在IOC容器中了,不信我们把通过IOC容器得到User对象的代码注释掉再看输出结果:

发现此时即使我们不通过ac.getBean("user");语句得到User对象,它也在程序启动时就创建了。

总结:在<bean>标签中设置bean对象为单例时,该对象在系统启动时就会创建;设置为多例时,该对象在我们需要使用时才创建。

4.1<bean>标签中的其它属性说明

  • 1.lazy-init:延迟初始化bean对象,默认值为false,即不延迟创建bean对象,在程序启动时就在IOC中创建bean对象;若其值为true则延迟创建bean对象,即在我们需要对象时才在IOC容器中创建该对象。此属性只对单例bean对象有效。
  • 2.init-method:可以给该属性传递一个在pojo对象中创建的方法例如A方法的方法名A作为init-method的属性值,表示当该pojo对象在IOC容器中被创建后就立刻执行这个A方法。
  • 3.destoy-method:同上,给该属性传递一个在pojo对象中创建的方法例如B方法的方法名B作为destoy-method的属性值,表示当IOC容器被销毁时(该pojo对象也会在IOC中销毁)会立刻调用这个B方法。当然我们通过ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");创建的IOC对象ac是没有destoy()方法的,我们需要这个创建IOC对象ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");这样创建出来的IOC对象才有destoy()方法。

5.Spring IOC容器

Spring IOC容器,是Spring的核心内容,用于创建对象和处理对象间的依赖关系。

5.1对象的创建

利用IOC容器创建对象的方式有如下几种:1.调用无参数构造器。2.调用带参数构造器。3.工厂创建对象。包括工厂类的静态方法创建对象和工厂类的非静态方法创建对象4.反射。(IOC的原理就是通过反射来创建对象)

5.1.1调用无参数构造器

在配置文件中加入如下内容:

5.1.2调用带参数构造器

<constructor-arg>标签中还有一个ref的属性,属性值代表引用配置文件(即IOC容器)中的相应对象。

故还可以采用这种方法调用带参数构造器创建对象:

5.1.3工厂创建对象

首先创建一个工厂类:

调用工厂的实例方法创建对象:

调用工厂静态方法创建对象:

5.2处理对象的依赖关系

在IOC容器的配置文件中我们有如下给对象注入属性的方法:1.通过构造方法。2.通过set方法给属性注入值。3.p名称空间。4.自动装配。5.注解。

5.2.1通过构造方法

首先我们来看看如何通过构造方法来给对象的属性赋值,在配置文件中添加如下标签即可通过构造器给该User对象的属性赋值:

5.2.2通过set方法

通过set方法给属性赋值,前提是在User对象中给它的属性添加了set方法:

接下来我们看个案例,以前我们开发时根据MVC模式都会像下面这样建立相应的Service.java、Servlet.java和dao.java:

都需要我们自己在.java文件中添加A a=new A();来创建其所需要的依赖对象,而现在我们就将对象的创建交给IOC了,选择set给属性赋值的方式来给它们注入其所需依赖对象,修改它们的代码:

然后我们需要在application.xml中进行配置:

ref的属性值代表给该对象注入它所依赖的对象,即我们上述讲到的依赖注入(dependency injection),通过上述步骤我们便完成了将对象的创建交给IOC的操作。

上述三个对象的创建我们需要写三个<bean>标签才能完成,接下来我将介绍第二种方法通过内部bean的操作一次性完成它们的创建以及它们之间的依赖关系,修改配置文件中的内容:

通过上述内部<bean>标签的方式我们便可实现和set注入依赖相同的效果。我们来看看它们两者的相同和区别:

  • 相同:都可以创建Service对象,并处理了之间的依赖关系。
  • 区别:set注入创建的Service对象可以给另一个Servlet对象调用,而内部bean将Service对象写在Servlet内部导致该Service对象只能被该Servlet使用,所以内部bean标签的使用场景在只需要一个Servlet对象的项目中。

5.2.3通过p名称空间给对象的属性注入值

此中方法只有在Spring3.0版本及以上版本才能用。首先在配置文件的<beans>根标签中加入属性:xmlns:p="http://www.springframework.org/schema/p"。然后我们便可以在配置文件中这样给对象的属性赋值:

当我们在配置文件中输入p:时,会出现两个属性1.p:userDao 和p:userDao-ref,这里有必要说明一下二者区别:

  • p:userDao:代表直接给UserService对象的userDao属性赋值
  • p:userDao-ref:代表引用的userDao对象

例如使用p名称空间给传统的对象属性赋值时我们这样写:

1
2
<bean id="user" class="pojo.User" p:id="xxx"/>
<bean id="user" class="pojo.User" p:name="xxx"/>

5.2.4.自动装配

当我们在配置文件中用<bean>标签指明相应对象的同时就将这个对象放入到了IOC容器中(其中标签中的id属性唯一指示一个对象),当我们给该bean标签添加了autowrite="byName"的属性后,对于该标签对应的对象注入的属性,会去IOC容器中自动查找与属性同名的对象。

例如如下代码:

通过上述三个<bean>标签我们就将userDao、userService、userServlet三个对象添加到了IOC容器中。我们在UserService对象的bean标签中加上了autowrite="byName"的属性,这样我们查看UserService.java的代码,它有一个UserDao对象名为userDao的属性,此时就会自动去IOC容器中寻找与userDao同名的对象(即在bean标签中寻找id为userDao的对象),然后进行注入,此时我们若将<bean id="userDao" class="pojo.UserDao">id="userDao"属性值改为userDao1或者其它名字,则运行系统会出现空指针异常,道理上述已分析。UserServlet注入userService的属性道理同此。

我们也可以将该属性定义到全局<beans>标签中,设置default-autowrite="byName"的属性,这样就不用每个bean标签中都写上autowrite="byName"属性了。

上述是根据名称自动装配,其实autowrite的属性值还可以为byType即根据类型自动装配。对于<bean id="userService" class="pojo.UserService">,当添加了autowrity="byType"的属性后,此时寻找它依赖的属性userDao的过程如下:查看UserService.java代码,它需要注入的属性类型为UserDao类型,所以就会自动去IOC容器中查找UserDao类型的对象并自动为UserService对象注入该属性,此时各bean标签的id属性值便可以随便写了如果根据类型自动装配,则要保证保证该类型的对象只有一个,否则会报错。该属性同样可以在全局beans标签中进行配置。

利用自动装配的优缺点:简化了配置,但不利用系统维护。所以一般不推荐此中用法,下面我们再来介绍第5中非常简单的配置。

5.2.5注解

注解方式可以简化Spring的IOC容器的配置。

使用步骤:

  • 1.先引入context名称空间
  • 2.开启注解扫描
  • 3.使用注解:通过注解的方式,把对象加入到IOC容器中。

首先在IOC配置文件中引入context名称空间,即在<beans>全局标签中添加xmlns:context="http://www.springframework.org/schema/context"属性。

然后在配置文件中添加如下标签

base-package:表示该扫描器只扫描此包下所有类。

最后我们便可以使用注解了,在pojo对象的.java文件中分别加入如下注解:

@Componet注解:代表将该对象放入到IOC容器中,括号里面的名字代表该对象在IOC容器中的唯一标识名字,名字任意取。该注解写在代码第一行。

@Resource注解:用于将该对象依赖的属性从IOC容器中找到并注入,括号里面的name属性值必须跟@Compenent注解里填入的名字相同。

通过注解方法便可去掉各.java文件中为属性创建设置的set方法。

继续对上述注解方式进行配置优化,去掉括号中的内容:

在测试类中运行依然可以正常运行。

说明:利用@Compenent注解的方式是通用的将对象加入到IOC容器中的方式,而有时候我们需要区别各层对象添加的方式,所以这里我们将Dao层对象添加到IOC容器的注解方式改为:@Repository表示持久层的组件;修改Service层对象添加到IOC容器的注解方式:@Service表示业务逻辑层的组件;修改Servlet层对象添加到IOC容器的注解方式为:@Controller表示控制层的组件。

另外需要说明的是使用注解的方式将对象添加到IOC容器中和在xml文件中添加配置的方式是可以共存的。但通过@Resource不带括号的注解,必须要保证该类型只有一个变量,所以一般情况下我们还是优先使用@Resource(name=””)注解。

到此,Spring框架的学习我们已完成。

2018.3.19更

欢迎加入我的Java交流1群:659957958。

2018.4.21更:如果群1已满或者无法加入,请加Java学习交流2群:305335626 。秋春招时节在群内发布大量的互联网内推方式,话不多说,快上车吧!

6.联系

If you have some questions after you see this article,you can tell your doubts in the comments area or you can find some info by clicking these links.

记得扫一扫领一下红包再走哦