Spring 框架基础知识 (01)
1. 什么是框架
框架在项目中的表现就是一系列的 jar 包,例如 Thymeleaf 就是一个框架。
每种框架都会解决某种特定的问题,可能是开发效率的问题,或运行效率的问题,或代码管理维护的问题等等。
项目中使用框架就相当于得到了一个“毛坯房”,使用了框架之后,开发人员只需要关心后续的“装修”即可。
绝大部分的框架都有特定的使用方式,在使用时,必须遵循框架的使用规则!
每个框架都可能是若干个开发人员甚至开发团队多年的工作积累的作品,对于初学者来说,不要过于钻牛角尖,尝试理解框架的底层实现原理!
简单的说:使用框架,可以让编程变得更加简单!在学习框架时,主要学习框架的正确使用方式!
2. 依赖关系
假设在项目中需要开发一个用户注册的功能!在项目中可能存在:
在以上代码中,UserRegServlet
就是依赖于UserDao
的!
3. 耦合度
如果某个类过于依赖于另外一个类,通常称之为了“耦合度较高”,是不利于代码的管理和维护的,简单的说,如果UserRegServlet
依赖于UserDao
,在未来的某一天,UserDao
已经不能满足项目的需求了(可能是因为代码有 Bug,或者使用的技术比较落后等),如果需要把UserDao
替换掉,替换难度大就会影响项目的管理和维护,为了解决这样的问题采取的解决方案就称之为“解耦”,使得依赖关系不那么明确,甚至就是不明确!
就以上UserRegServlet
依赖UserDao
的问题,如果要解耦,可以先创建一个接口:
然后,使得UserDao
是实现了以上接口的:
经过以上调整以后,如果在UserRegServlet
中需要使用到UserDao
,以前的代码是这样的:
现在就可以改为:
以上代码就相当于:
改成这样以后,在同一个项目中,无论多少个Servlet
组件需要使用到UserDao
,都可以使用以上“声明为接口,创建实现类的对象”的语法风格,如果以后UserDao
需要被替换掉,也只需要替换“赋值”的代码,声明部分是不需要替换的!例如需要把UserDao
替换为UserMybatisDao
时,原来的代码是:
新的代码就可以是:
在后续的使用中,就可以是:
也就是说,在UserDao
换成了UserMybatisDao
时,在各个Servlet
中,都只需要调整等于号右侧的内容,而不再需要修改等于号左侧的部分!
当然,关于以上代码的右侧部分,还可以使用“工厂设计模式”作进一步的处理:
当有了工厂后,此前的代码就可以进一步调整为:
可以发现,以上代码中不再出现任何一个实现类的名字了,无论是哪个Servlet
组件需要访问数据库,都声明为以上代码即可,以后,如果实现类需要被替换,也只需要替换工厂方法的返回值即可!
在实际项目开发时,项目中的组件的依赖更加复杂,为每个组件都创建对应的接口及工厂是非常麻烦的,而 Spring 框架就很好的解决了这个问题,可以简单的将 Spring 理解为一个“万能工厂”,当使用了 Spring 框架后,就不必自行开发工厂了!
4. Spring 框架简介
Spring 框架的主要作用:解决了创建对象和管理对象的问题。
5. 通过 Spring 创建对象
创建 Maven Project,在创建过程中,勾选 Create a simple project,Group Id 填为cn.tedu
,Artifact Id 填为spring01
,其它项保持默认即可。
使用 Spring 框架时,必须在项目的 pom.xml 中添加spring-context
的依赖:
首先,在项目中,创建cn.tedu.spring
包,并在这个包下创建BeanFactory
类:
当前,代码放在哪个包中并不重要,应该养成习惯,每个类都应该放在某个包中,不要让任何类不放在任何包中!
以上类的名称也不重要,是自定义的!
如果希望由 Spring 来创建并管理某个类的对象,必须在以上类中添加方法,关于这个方法:
应该使用
public
权限;返回值类型就是需要 Spring 创建并管理的类的对象的类型;
方法名称可以自定义;
参数列表暂时为空;
在方法体中,自行编写创建返回值对象的代码。
假设需要 Spring 来创建Date
类型的对象,则在类中添加方法:
Spring 框架要求:创建对象的方法必须添加@Bean
注解,并且,这样的方法必须在配置类中!任何一个类添加了@Configuration
注解都可以作为配置类!
完成后,应该使用一个可以运行的类,或通过单元测试来检验“是否可以通过 Spring 容器获取对象”。本次先创建一个可以运行的类:
6. 关于 @Bean 注解
当方法的声明之前添加了@Bean
注解,就表示这个方法是需要由 Spring 框架所调用,并且,由 Spring 框架管理该方法返回的对象的!默认情况下,该方法的名称就是后续获取对象时,调用getBean()
方法的参数!
由于添加了@Bean
注解的方法是被 Spring 框架调用的,不需要自行编写代码来调用这个方法,所以,Spring 的建议是“使用合理的属性名称作为方法名,并不需要使用动词或动词为前缀的方法名”,简单的说,如果方法是为了获取Date
类型的对象的,该方法的名称应该是date
,而不是getDate()
,则后续调用getBean()
时,参数就是date
这个名称!
当然,如果不遵循 Spring 的建议,还可以在@Bean
注解中配置注解参数来指定 Bean 的名称,例如:
则后续就根据注解参数来获取对象:
其关系如下图:
其实,在开发项目时,真的不必关心这些问题,也就是说,例如是一个获取Date
对象的方法,其名称到底是date
还是getDate
都是正确的!毕竟这个方法最终就是由 Spring 框架来调用,开发人员不会自行调用该方法!
7. Spring 管理对象的作用域
由 Spring 管理的对象,默认情况下,是单例的!所以,其作用域就非常久!
在 Spring 管理对象的情况下,讨论对象的作用域,其本质就是讨论其是否单例!
在创建对象的方法之前,添加@Scope
注解,并配置注解参数为prototype
,就可以使得该对象不是单例的:
由 Spring 管理的对象,如果是单例模式的,默认情况下,是饿汉式的!在创建对象的方法之前,添加@Lazy
注解,就可以调整为懒汉式的:
一般,在开发项目时,极少调整对象的作用域!
8. 当天小结:
Spring 的主要作用:创建对象,管理对象;
如果某个方法是用于给 Spring 框架创建对象的,这个方法就必须添加
@Bean
注解;所有添加了
@Bean
注解的方法,其所在的类应该添加@Configuration
注解,凡添加了@Configuration
注解的类称之为配置类;默认情况下,由 Spring 管理的对象是单例的,使用
@Scope
注解可以将 Spring 管理的对象调整为“非单例”的;默认情况下,由 Spring 管理的单例的对象是是“饿汉式”的,使用
@Lazy
可以将它们改为“懒汉式”的。
附 1:设计模式之单例模式
单例模式的特点:在同一时期,某个类的对象一定最多只有 1 个!也许会尝试多次的获取对象,但是,获取到的一定是同一个对象!
假设项目中有King
类:
很显然,目前它并不是单例的,因为,可以:
以上代码就创建了 3 个King
类型的对象!如果要实现单例,首先,就必须限制构造方法的访问,例如:
每个类中都可以有若干个构造方法,如果某个类没有显式的声明任何构造方法,编译器就会自动添加 1 个公有的、无参数的构造方法!如果类中已经声明任何构造方法,则编译器不会自动添加构造方法!
由于将构造方法声明为私有的,则原有的King k1 = new King();
这类代码就不能用于创建对象了!
限制构造方法的访问,其目的是“不允许随意创建对象”,并不是“不允许创建对象”,在King
类的内部,还是可以创建对象的,可以添加方法,返回内部创建的对象:
所以,当需要King
类型的对象时,可以通过getInstance()
方法来获取!
但是,以上代码是不可行的!因为,如果要调用getInstance()
方法,必须先获取King
的对象,而获取King
对象的唯一方式就是调用getInstance()
方法!为了解决这个问题,必须在getInstance()
方法的声明之前添加static
修饰符,最终,就可以通过类名.方法名()
的语法格式来调用方法了!同时,由于“被static
修饰的成员,不可以访问其它未被static
修饰的成员”,所以,全局属性king
也必须被static
修饰:
至此,基本的单例模式的代码就设计完成了!
以上代码是“饿汉式”的单例模式,另外,还有“懒汉式”的单例模式!
基本的懒汉式单例模式的代码是:
注意:以上代码是多线程不安全的!
在开发领域中,只要数据的产生、变化不是开发人员预期的,就称之为“不安全”,也就是“数据安全问题”。
为了保障线程安全,应该为以上创建对象的代码片断“加锁”,例如:
当然,无论是哪个线程在什么时候执行以上代码,都必须先“锁住”代码片断后才能开始执行,是没有必要的,“锁”的性能消耗是浪费的,所以,可以进一步调整为:
至此,懒汉式的单例模式就完成了!
版权声明: 本文为 InfoQ 作者【海拥(haiyong.site)】的原创文章。
原文链接:【http://xie.infoq.cn/article/5cca12dbbbcd1c0e2d1891c62】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论