为什么需要学习
spring
?
1.
最大程度的简化了开发
spring
是一个非常优秀的
java
框架,其目标是为了简化
java
企业级开发,
spring
出来已经十几年
了,这期间也一直围绕着这个目标在进行,像后面需要学习的
springmvc
、
springboot
、
springcloud
,这些技术也都是围绕着简化开发的目标在努力,到目前为止已经做的足够好了,可
以说
spring
除了不能帮助我们实现业务逻辑代码之外,其他的事情
spring
都尽量去帮我们简化了,
使用
spring
可以帮助我们节约大量开发时间。
不使用
spring
的情况下,开发一个项目可能需要
2
个月,用了
spring
可能
1
个月都不需要,你说这样
的技术你想学么?
2.
大量公司使用
目前
99%
的公司使用了
spring
,可以去各大招聘网站看一下,
spring
算是必备技能,所以一定要掌
握。
3.
顶级的源码设计
spring
框架源码设计非常优秀,在
java
开源项目中可以说是顶级的,个人到目前为止还没有发现比
spring
代码更优秀的开源项目,所以想提升代码能力的,强烈建议多看看
spring
的源码;关于提升
代码能力的,还可以去看一下
jdk
的源码,也是非常棒的,里面有很多大师的杰作。
Spring
中有
3
个核心的概念:
控制反转
(Ioc)
、依赖注入
(DI)
、面向切面编程
(AOP)
,
spring
中其他的技术
都是依靠
3
个核心的技术建立起来的,所以玩
spring
需要先对这
3
个概念有个深入的理解。
spring
容器
spring
容器的概念,容器这个名字起的相当好,容器可以放很多东西,我们的程序启动的时候会创建
spring
容器,会给
spring
容器一个清单,清单中列出了需要创建的对象以及对象依赖关系,
spring
容器
会创建和组装好清单中的对象,然后将这些对象存放在
spring
容器中,当程序中需要使用的时候,可以
到容器中查找获取,然后直接使用。
IOC
:控制反转
使用者之前使用
B
对象的时候都需要自己去创建和组装,而现在这些创建和组装都交给
spring
容器去给完
成了,使用者只需要去
spring
容器中查找需要使用的对象就可以了;这个过程中
B
对象的创建和组装过程
被反转了,之前是使用者自己主动去控制的,现在交给
spring
容器去创建和组装了,对象的构建过程被
反转了,所以叫做控制反转;
IOC
是是面相对象编程中的一种设计原则,主要是为了降低系统代码的耦
合度,让系统利于维护和扩展。
DI
:依赖注入
依赖注入是
spring
容器中创建对象时给其设置依赖对象的方式,比如给
spring
一个清单,清单中列出了
需要创建
B
对象以及其他的一些对象(可能包含了
B
类型中需要依赖对象),此时
spring
在创建
B
对象的
时候,会看
B
对象需要依赖于哪些对象,然后去查找一下清单中有没有包含这些被依赖的对象,如果有
就去将其创建好,然后将其传递给
B
对象;可能
B
需要依赖于很多对象,
B
创建之前完全不需要知道其他
对象是否存在或者其他对象在哪里以及被他们是如何创建,而
spring
容器会将
B
依赖对象主动创建好并将
其注入到
B
中去,比如
spring
容器创建
B
的时候,发现
B
需要依赖于
A
,那么
spring
容器在清单中找到
A
的
定义并将其创建好之后,注入到
B
对象中。
总结
1. IOC
控制反转,是一种设计理念,将对象创建和组装的主动控制权利交给了
spring
容器去做,控制
的动作被反转了,降低了系统的耦合度,利于系统维护和扩展,
主要就是指需要使用的对象的组装
控制权被反转了,之前是自己要做的,现在交给
spring
容器做了
。
2. DI
依赖注入,表示
spring
容器中创建对象时给其设置依赖对象的方式,通过某些注入方式可以让系
统更灵活,比如自动注入等可以让系统变的很灵活,这个后面的文章会细说。
3. spring
容器:主要负责容器中对象的创建、组装、对象查找、对象生命周期的管理等等操作。
4.
下一篇开始详细讲解
spring
的使用了
IOC
容器
IOC
容器是具有依赖注入功能的容器,负责
对象的实例化、对象的初始化,对象和对象之间依赖关系配
置、对象的销毁、对外提供对象的查找
等操作,对象的整个生命周期都是由容器来控制。我们需要使用
的对象都由
ioc
容器进行管理,不需要我们再去手动通过
new
的方式去创建对象,由
ioc
容器直接帮我们
组装好,当我们需要使用的时候直接从
ioc
容器中直接获取就可以了。
那么
spring ioc
容器是如何知道需要管理哪些对象呢?
需要我们给
ioc
容器提供一个配置清单,这个配置
支持
xml
格式
和
java
注解的方式
,在配置文件中列出需
要让
ioc
容器管理的对象,以及可以指定让
ioc
容器如何构建这些对象,当
spring
容器启动的时候,就会去
加载这个配置文件,然后将这些对象给组装好以供外部访问者使用。
这里所说的
IOC
容器也叫
spring
容器。
Bean
概念
由
spring
容器管理的对象统称为
Bean
对象。
Bean
就是普通的
java
对象,和我们自己
new
的对象其实是一
样的,只是这些对象是由
spring
去创建和管理的,我们需要在配置文件中告诉
spring
容器需要创建哪些
bean
对象,所以需要先在配置文件中定义好需要创建的
bean
对象,这些配置统称为
bean
定义配置元数
据信息,
spring
容器通过读取这些
bean
配置元数据信息来构建和组装我们需要的对象。
Spring
容器使用步骤
1.
引入
spring
相关的
maven
配置
2.
创建
bean
配置文件,比如
bean xml
配置文件
3.
在
bean xml
文件中定义好需要
spring
容器管理的
bean
对象
4.
创建
spring
容器,并给容器指定需要装载的
bean
配置文件,当
spring
容器启动之后,会加载这些配
置文件,然后创建好配置文件中定义好的
bean
对象,将这些对象放在容器中以供使用
5.
通过容器提供的方法获取容器中的对象,然后使用
Spring
容器对象
spring
内部提供了很多表示
spring
容器的接口和对象,我们来看看比较常见的几个容器接口和具体的实
现类。
容器有有形的容器和无形的容器
有形的容器:.xml配置文件
无形的容器:个人认为是在spring配置里
容器创建
bean
实例有多少
种?
1.
通过反射调用构造方法创建
bean
对象
2.
通过静态工厂方法创建
bean
对象
3.
通过实例工厂方法创建
bean
对象
4.
通过
FactoryBean
创建
bean
对象
通过静态工厂方法创建
bean
对象
public class UserStaticFactory {
public static UserModel buildUser1(){
System.out.println(UserStaticFactory.class+".buildUser1");
return new UserModel();
}
public static UserModel buildUser2(){
System.out.println(UserStaticFactory.class+".buildUser1");
return new UserModel();
}
}
<bean id="createBeanByStaticFactoryMethod1"
class="com.ljy.lijinyuanspringboot.pojo.UserStaticFactory"
factory-method="buildUser1">
<property name="name" value="ljy"/>
</bean>
<bean id="createBeanByStaticFactoryMethod2"
class="com.ljy.lijinyuanspringboot.pojo.UserStaticFactory"
factory-method="buildUser2">
<constructor-arg name="name" value="name"/>
<constructor-arg name="age" value="21"/>
</bean>
class
:指定静态工厂完整的类名
factory-method
:静态工厂中的静态方法,返回需要的对象。
constructor-arg
用于指定静态方法参数的值,用法和上面介绍的构造方法一样。
通过实例工厂方法创建
bean
对象
public class UserFactory {
public UserModel buildUser1(){
System.out.println("非静态方法");
return new UserModel();
}
public UserModel buildUser2(String name,int age){
return new UserModel(name,age);
}
}
实例化工厂要先有一个工厂的实例:
<!--通过实例工厂创建bean对象-->
<bean id="userFactory" class="com.ljy.lijinyuanspringboot.pojo.UserFactory"/>
<bean id="createBeanByBeanMethod1"
factory-bean="userFactory"
factory-method="buildUser1">
</bean>
<bean id="createBeanByBeanMethod2"
factory-bean="userFactory"
factory-method="buildUser2">
<constructor-arg name="name" value="ljy"/>
<constructor-arg name="age" value="18"/>
</bean>
在测试类中通过getBean()就可以通过id调用了
bean
作用域
scope
详解
<bean id="" class="" scope="作用域" />
singleton
当
scope
的值设置为
singleton
的时候,整个
spring
容器中只会存在一个
bean
实例,通过容器多次查找
bean
的时候(调用
BeanFactory
的
getBean
方法或者
bean
之间注入依赖的
bean
对象的时候),返回的
都是同一个
bean
对象,
singleton
是
scope
的默认值,所以
spring
容器中默认创建的
bean
对象是单例的,
通常
spring
容器在启动的时候,会将
scope
为
singleton
的
bean
创建好放在容器中(有个特殊的情况,当
bean
的
lazy
被设置为
true
的时候,表示懒加载,那么使用的时候才会创建),用的时候直接返回。
单例
bean
使用注意
单例
bean
是整个应用共享的,所以需要考虑到线程安全问题,之前在玩
springmvc
的时候,
springmvc
中
controller
默认是单例的,有些开发者在
controller
中创建了一些变量,那么这些变量实
际上就变成共享的了,
controller
可能会被很多线程同时访问,这些线程并发去修改
controller
中的共
享变量,可能会出现数据错乱的问题;所以使用的时候需要特别注意。
prototype
如果
scope
被设置为
prototype
类型的了,表示这个
bean
是多例的,通过容器每次获取的
bean
都是不同
的实例,每次获取都会重新创建一个
bean
实例对象。
多例
bean
使用注意
多例
bean
每次获取的时候都会重新创建,如果这个
bean
比较复杂,创建时间比较长,会影响系统的性
能,这个地方需要注意。
request
当一个
bean
的作用域为
request
,表示在一次
http
请求中,一个
bean
对应一个实例;对每个
http
请求都
会创建一个
bean
实例,
request
结束的时候,这个
bean
也就结束了,
request
作用域用在
spring
容器的
web
环境中,这个以后讲
springmvc
的时候会说,
spring
中有个
web
容器接口
WebApplicationContext
,
这个里面对
request
作用域提供了支持,配置方式:
session
这个和
request
类似,也是用在
web
环境中,
session
级别共享的
bean
,每个会话会对应一个
bean
实
例,不同的
session
对应不同的
bean
实例
application
全局
web
应用级别的作用于,也是在
web
环境中使用的,一个
web
应用程序对应一个
bean
实例,通常情
况下和
singleton
效果类似的,不过也有不一样的地方,
singleton
是每个
spring
容器中只有一个
bean
实
例,一般我们的程序只有一个
spring
容器,但是,一个应用程序中可以创建多个
spring
容器,不同的容
器中可以存在同名的
bean
,但是
sope=aplication
的时候,不管应用中有多少个
spring
容器,这个应用中
同名的
bean
只有一个。
总结
1. spring
容器自带的有
2
种作用域,分别是
singleton
和
prototype
;还有
3
种分别是
spring web
容器环
境中才支持的
request
、
session
、
application
2. singleton
是
spring
容器默认的作用域,一个
spring
容器中同名的
bean
实例只有一个,多次获取得
到的是同一个
bean
;单例的
bean
需要考虑线程安全问题
3. prototype
是多例的,每次从容器中获取同名的
bean
,都会重新创建一个;多例
bean
使用的时候需
要考虑创建
bean
对性能的影响
4.
一个应用中可以有多个
spring
容器
5.
自定义
scope 3
个步骤,实现
Scope
接口,将实现类注册到
spring
容器,使用自定义的
sope
依赖注入之手动注入
spring
依赖注入
spring
中依赖注入主要分为手动注入和自动注入,本文我们主要说一下手动注入,手动注入需要我们明
确配置需要注入的对象。
刚才上面我们回顾了,将被依赖方注入到依赖方,通常有
2
种方式:构造函数的方式和
set
属性的方式,
spring
中也是通过这两种方式实现注入的,下面详解
2
种方式。
通过构造器注入
构造器的参数就是被依赖的对象,构造器注入又分为
3
种注入方式:
根据构造器参数索引注入
根据构造器参数类型注入
根据构造器参数名称注入
<bean id="diByConstructorParamIndex"
class="com.javacode2018.lesson001.demo5.UserModel"> <constructor-arg index="0" value="路人甲Java"/>
<constructor-arg index="1" value="上海市"/>
</bean>
constructor-arg
用户指定构造器的参数
index
:构造器参数的位置,从
0
开始
value
:构造器参数的值,
value
只能用来给简单的类型设置值,
value
对应的属性类型只能为
byte,int,long,float,double,boolean,Byte,Long,Float,Double,
枚举,
spring
容器内部注入的时候会
将
value
的值转换为对应的类型。
根据构造器参数类型注入
<bean id="diByConstructorParamType"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg type="参数类型" value="参数值"/>
<constructor-arg type="参数类型" value="参数值"/>
</bean>
constructor-arg
用户指定构造器的参数
type
:构造函数参数的完整类型,如:
java.lang.String,int,double
value
:构造器参数的值,
value
只能用来给简单的类型设置值
根据构造器参数名称注入
<bean id="diByConstructorParamName"
class="com.javacode2018.lesson001.demo5.UserModel">
<constructor-arg name="参数类型" value="参数值"/>
<constructor-arg name="参数类型" value="参数值"/>
</bean>
constructor-arg
用户指定构造器的参数
name
:构造参数名称
value
:构造器参数的值,
value
只能用来给简单的类型设置值
参数名称可能不稳定的问题,
spring
提供了解决方案,通过
ConstructorProperties
注解来定义参数的
名称,将这个注解加在构造方法上面
,如下:
@ConstructorProperties({"第一个参数名称", "第二个参数的名称",..."第n个参数的名称"})
public 类名(String p1, String p2...,参数n) { }
@ConstructorProperties({"name", "desc"})
public CarModel(String p1, String p2) {
this.name = p1; this.desc = p2;
}
这样就可以通过
constructor-arg name="desc"
constructor-arg name="name"
引用了
setter
注入
通常情况下,我们的类都是标准的
javabean
,
javabean
类的特点:
属性都是
private
访问级别的
属性通常情况下通过一组
setter
(修改器)和
getter
(访问器)方法来访问
setter
方法,以
set
开头,后跟首字母大写的属性名,如:
setUserName
,简单属性一般只有一
个方法参数,方法返回值通常为
void;
getter
方法,一般属性以
get
开头,对于
boolean
类型一般以
is
开头,后跟首字母大写的属性
名,如:
getUserName
,
isOk
;
spring
对符合
javabean
特点类,提供了
setter
方式的注入,会调用对应属性的
setter
方法将被依赖的对象
注入进去。
其他类型注入
public class Test {
int age;
String str;
String [] s;
List<Object> list;
Map map;
Set set;
public List<Object> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String[] getS() {
return s;
}
public void setS(String[] s) {
this.s = s;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Set getSet() {
return set;
}
public void setSet(Set set) {
this.set = set;
}
@Override
public String toString() {
return "Test{" +
"age=" + age +
", str='" + str + '\'' +
", s=" + Arrays.toString(s) +
", list=" + list +
", map=" + map +
", set=" + set +
'}';
}
}
<bean id="test" class="com.ljy.lijinyuanspringboot.pojo.Test">
<property name="age" value="21"/>
<property name="str" value="ljy"/>
<property name="list">
<list>
<value>Spring</value>
<value>123</value>
</list>
</property>
<property name="map">
<map>
<entry key="1" value="1"></entry>
<entry key="2" value="22"></entry>
</map>
</property>
<property name="s">
<array>
<value>123</value>
<value>456</value>
<value>abc</value>
</array>
</property>
<property name="set">
<set>
<value>123</value>
<value>abc</value>
</set>
</property>
</bean>
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml");
Test test= (Test) context.getBean("test");
System.out.println(test);
}
运行后:
Test{age=21, str='ljy', s=[123, 456, abc], list=[Spring, 123], map={1=1, 2=22}, set=[123, abc]}
总结
1.
主要讲解了
xml
中
bean
的依赖注入,都是采用硬编码的方式进行注入的,这种算是手动的方
式
2.
注入普通类型通过
value
属性或者
value
元素设置注入的值;注入对象如果是容器的其他
bean
的时
候,需要使用
ref
属性或者
ref
元素或者内置
bean
元素的方式
3.
还介绍了其他几种类型
List
、
Set
、
Map
、数组
的注入,多看几遍加深理解
4.
后面将介绍
spring
为我们提供的更牛逼的自动注入