2018年春天,我开始学Spring了,目标是在暑假时能找一个实习工作(不知道有没有人要)。路要一步一步走,知识要一点一点学,虽然我很急切,但是没办法。一口吃不了个胖子。
首先在之前,学习了Servlet+JSP+JavaBean的小Demo。当客户端向服务器发出请求是,服务器把得到的请求发送给控制器Servlet,而在Servlet中需要创建Service对象来调用业务层相关功能(所以说控制器层Servlet依赖于业务层Service)而在Service中又需要创建数据库层DAO对象来对数据库进行操作(故说业务层Service依赖于数据库层DAO)
针对上述过程,我们需要考虑这样几个问题。1.Servlet,Service以及DAO对象的创建时间,创建数量。2,Servlet,Service以及Dao之间的依赖关系。如何处理这些问题呢?Spring就是用来处理对象的创建,以及对象之间依赖关系的一个开发框架。它打破了我们传统开发的观念,我们不再需要像以前那样在具体的类中创建具体的对象,而是将对象的创建交给它去完成,他是我们所要学习的框架中最重要的框架。
1.Spring框架中的专业术语
1.1组件/框架设计
1.侵入式设计:对现有类的结构有影响,即需要实现或继承某些特定类。如Struts框架。
2.非侵入式设计:引入了框架,对现有结构没有影响,如Spring框架,Hibernate框架
1.2控制反转
Inversion On Control,简称IOC。对象的创建交给外部容器自动完成,这个就叫做控制反转(有控制反转就有控制正转,控制正转:对象的创建由我们自己创建)
依赖注入:dependency Injection 简称DI。用于处理对象间的依赖关系。
二者区别:控制反转用来解决对象创建问题(对象的创建不需要我们手动创建,而是交给框架去完成)
依赖注入在创建完对象后,对象的关系处理就是依赖注入。(通过set方法依赖注入。也可以理解为另一种对象之间的耦合方式)
1.3 AOP
面向切面编程。切面,简单来说可以理解为一个雷。由很多重复代码形成的类。切面举例:事务,日志,权限。关于AOP的详细讲解会在明天的博文中总结。
2.Spring框架概述
Spring 框架,可以解决对象创建以及对象之间依赖关系的一种框架。且可以和其他框架爱一起使用。例如ssh
一方面可以与structs整合,让struts的action创建交给spring。SpringMVC模式,用springmvc整合了就不用sturts了。
另一方面,可以与hibernate整合可以使用Spring对hibernate操作的封装。这可以用mybatis代替。现在常用ssm框架。
Spring提供了一站式解决问题的方案。可以完美替代前面的Servlet,Service,Dao。
1)SpringCore:Spring的核心功能,包括IOC容器,解决对象创建以及依赖关系
2)SpringWeb: Spring对外部模块的支持。
3)SpringDao :Spring对jdbc操作的支持。
4)SpringORM:Spring对ORM的支持。
5)Spring AOP:关于AOP的详细下一篇吧
6)SpringEE:Spring对javaEE其他模块的支持。
3.Spring开发步骤
1)导入jar包。
可以参考一张图。
红框框标出来的部分是比较重要的。
除了常用的Spring包外,这里面还有个日志包。
写在前面的话:当你运行程序出现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
直接附代码:
<?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(POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称)对象User.java
然后创建测试类。使用这个User对象。以前我们要使用User对象时,直接User user=new User(); new 一个对象即可。
而当我们使用Spring后,就应该这样使用User对象,首先在applicationContext.xml中添加<bean>标签,一个<bean>标签代表一个pojo对象:
其中各个属性的说明见注释。然后我们通过如下步骤获取pojo对象
运行测试类:
说明成功获取到User对象,上述是通过工厂类获取的IOC容器创建的User对象,下面我们看看使用Spring框架获取pojo对象的第二种方式直接得到IOC容器的对象。
虽然我常用第二种方式。
4.<bean>创建的细节
对上述代码进行改进,对于IOC容器对象,我们只需要创建一次即可。所以将创建IOC对象的代码改为成员变量。
得到结果:
发现答应的这两个User对象id都一样,说明我们获取到的是同一个对象,也说明通过<bean>
标签设置的pojo对象是单例的。为什么呢?其实<bean>
标签默认有一个scope="singleton"
的属性,代表该<bean
标签对象的pojo对象是单例的。我们可以将该属性值改为scope="prototype"
,如下:
再次运行可以得到
说明此时获取的两个User不再是同一个对象。
总结:在<bean>
标签中设置bean对象为单例时,该对象在系统启动时就会创建;设置为多例时,该对象在我们需要使用时才创建。
5.SpringIOC容器
SpringIOC容器,是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方法:
接下来的案例中,我们以前用的service,servlet,dao会根据MVC模式进行相应的修改。
都需要我们自己在.java文件中自己添加A a=new A(); 来创建其所需要的依赖对象,而现在我们就将对象的创建交给IOC了,选择set给属性赋值的方式来给他们注入其所依赖对象,修改他们的代码。
然后我们需要在application.xml中进行配置
ref的属性值代表给该对象注入它所以来的对象,即我们上述讲到的依赖注入,通过上述步骤我们便完成了将对象的创建交给IOC的操作。
上述三个对象的创建我们需要写三个<bean>
标签才能完成,接下来我将介绍第二种方法通过内部bean的操作一次性完成它们的创建以及它们之间的依赖关系,修改配置文件中的内容:
通过上述内部<bean>
标签的方式我们便可实现和set注入依赖相同的效果。我们来看看它们两者的相同和区别:
- 相同:都可以创建Service对象,并处理了之间的依赖关系。
- 区别:set注入创建的Service对象可以给另一个Servlet对象调用,而内部bean将Service对象写在Servlet内部导致该Service对象只能被该Servlet使用,所以内部bean标签的使用场景在只需要一个Servlet对象的项目中。
5.2.3自动装配
当我们在配置文件中用<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">的id=“userDao”属性值改为userDao1或者其他名字,则系统会出现空指针异常。
我们也可以将该属性定义到全局<beans>
标签中,设置default-autowrite="byName"
的属性,这样就不用每个bean标签中都写上autowrite="byName"
属性了。利用自动装配的优缺点:简化了配置,但不利用系统维护。所以一般不推荐此用法,下面我们再来介绍第5中非常简单的配置。
5.2.4 注解
注解方式简化了Spring的IOC容器的配置。
使用步骤:1.先引入context名称空间。2.开启注解扫描。3.使用注解,通过注解的方式,把对象加入到IOC容器中。
首先在IOC配置文件中引入context名称空间,即在<beans>全局标签中添加
xmlns:context="http://www.springframework.org/schema/context"
属性。然后在配置文件中添加如下标签。
<context:component-scan base-package="pojo"/>
base-package 表示该扫描器只扫描此包下所有类
最后我们便可以使用注解了,在pojo对象的.java文件中分别加入如下注解:
@component注解:代表将该对象放入到IOC容器中,括号里面的名字代表该对象在IOC容器中唯一标识的名字,该注解卸载代码第一行。
@Resource注解:用于将该对象依赖的属性从IOC容器中找到并注入,括号里面的name属性值必须跟@Compenent注解里填入的名字相同。
通过注解方法便可去掉各.java文件中为属性创建设置的set方法。
继续对上述注解方式进行配置优化,去掉括号中的内容:
在测试类中运行依然可以正常运行。
说明利用@Compenent注解的方式是通用的将对象加入到IOC容器中的方式,而有时候我们需要区别各层对象添加的方式,所以这里我们将Dao层对象添加到IOC容器的注解方式改为:@Repository表示持久层的组件;修改Service层对象添加到IOC容器的注解方式:@Service表示业务逻辑层的组件;修改Servlet层对象添加到IOC容器的注解方式为:@controller表示控制层的组件。其实功能都一样,只不过为了我们更好地区分。
另外需要说明的是使用注解的当时将对象添加到IOC容器中和在xml文件中添加配置的方式是可以共存的。但通过@Resource不带括号的注解,必须要保证该类型只有一个变量,所以一般情况下我们还是优先使用@Resource(name=“”)注解