spring的第一个核心功能 IoC
- IoC在学习Spring之前的体现
- 依赖注入(DI)
- 基于XML的DI
- set方式注入
- 构造注入
- 基于注解的DI
- 创建对象
- 给属性赋值
- 注解与 XML 的对比
IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是由其它外部资源完成。
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理、创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。
正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。例如:
public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。--正转。
}
容器:是一个服务器软件, 一个框架(spring)
为什么要使用IoC: 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
首先,我们需要了解一下java中创建对象有哪几种方式呢?
- 构造方法 ,
new Student()
- 反射
- 序列化
- 克隆
clone()
- ioc :容器创建对象
- 动态代理
IoC在学习Spring之前的体现
主要体现在servlet
上面
- 创建类继承HttpServelt
- 在web.xml 注册servlet , 使用
<servlet-name> myservlet </servlet-name>
<servelt-class>com.bjpwernode.controller.MyServlet1</servlet-class>
- 没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()
- Servlet 是Tomcat服务器它能你创建的。 Tomcat也称为容器
Tomcat作为容器:里面存放的有Servlet对象, Listener , Filter对象
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。
Spring 框架使用依赖注入(DI)实现 IoC
依赖注入(DI)
DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建、赋值、查找都由容器内部实现。
spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
基于XML的DI
注入方式分为两类:一类是set注入,另一类是构造注入
set方式注入
set注入(设值注入):spring调用类的set方法,为属性赋值。它只会调用set方法,而不会去管你set方法里面到底写的什么。
1) 简单类型的set注入
简单类型:spring中规定java的基本数据类型和String都是简单类型
语法:
<bean id="xx" class="yyy">
<property name="属性名字" value="此属性的值"/>
注意:一个property只能给一个属性赋值。如果有多个属性,则需要写多个property标签
</bean>
2). 引用类型的set注入
同样是spring调用类的set方法
区别在于需要将引用类型再单独写个bean来为它创建对象传入主类的set方法里面
例如:我定义了一个Student类,他有一个School属性
public class Student {
private String name;
private int age;
//定义一个引用数据类型
private School school;
}
public class School {
private String name;
private String address;
}
此时配置文件为:
<bean id="myStudent2" class="com.ba02.Student">
<property name="name" value="张三"/>
<property name="age" value="26"/>
<!--引用类型-->
<property name="school" ref="mySchool"/>
</bean>
<!--声明school对象-->
<bean id="mySchool" class="com.ba02.School">
<property name="name" value="北京大学"/>
<property name="address" value="北京海淀区"/>
</bean>
此外,对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为<bean/>
标签设置 autowire 属性值,为引用类型属性进行隐式自动注入,称为引用类型的自动注入
根据自动注入判断标准的不同,可以分为两种:byName和byType
- byName(按名称注入):java类中引用类型的属性名和spring容器中(配置文件)
<bean>
的id名称一样,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型
语法:
<bean id="xx" class="yyy" autowire="byName">
简单类型属性赋值
</bean>
- byType(按类型注入):java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系,这样的bean能够赋值给引用类型
同源就是一类的意思:
1)java类中引用类型的数据类型和bean的class值时一样的
2)java类中引用类型的数据类型和bean的class的值是父子类关系的
3)java类中引用类型的数据类型和bean的class的值是接口和实现类关系的
注意:byType 方式中只能有一种同源关系,如果 bean中两种或以上则会报错
语法:
<bean id="xx" class="yyy" autowire="byType">
简单类型属性赋值
</bean>
以上两种方式事实上就是用autowire
标签代替了语句<property name="school" ref="mySchool"/>
的执行。
构造注入
即直接调用有参构造方法创建对象。spring调用类的有参构造方法,在创建对象的同时,给属性赋值,构造注入使用<constructor-arg>
标签
注意:一个标签只能传一个参数
<constructor-arg>
标签属性:
name: 表示形参名
index:参数的位置,从左往右。0,1,2的顺序
value:简单类型使用value
ref:引用类型
<bean id="myStudent" class="com.ba03.Student">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="21"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
<!--使用index属性-->
<bean id="myStudent2" class="com.ba03.Student">
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="21"/>
<constructor-arg index="2" ref="mySchool"/>
</bean>
<!--省略index属性-->
<bean id="myStudent3" class="com.ba03.Student">
<constructor-arg value="张三"/>
<constructor-arg value="21"/>
<constructor-arg ref="mySchool"/>
</bean>
基于注解的DI
创建对象
@Component: 创建对象的,等同于<bean>
的功能
- 属性: value就是对象的名称,也就是bean的id值,value的值是唯一的,创建的对象在整个spring中就一个
- 位置:在类的上面
@Component(value="myStudent")
等同于 <bean id="myStudent" class="com.ba01.Student"/>
如果不指定对象名称,由spring提供默认名称:类名的首字母小写,即student
spring中和@Component功能一致,创建对象的注解还有:
- @Repository (用在持久层类的上面):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
- @Service(用在业务层类的上面):放在service的实现类上面, 创建service对象,service对象是做业务处理,可以有事务等功能的。
- @Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的控制器对象,能够接受用户提交的参数,显示请求的处理结果。
以上三个注解的使用语法和@Component
一样的。都能创建对象,但是这三个注解还有额外的功能。@Repository
, @Service
, @Controller
是给项目的对象分层的。
在applicationContext.xml加入类所在路径,例如:
<context:component-scan base-package="com.ba06"/>
component-scan
标签,声明组件扫描器,组件就是java对象,加入这个标签后,配置文件的变化:
- 加入了新的约束文件:spring-context.xsd
- 给这个新的约束文件起个命名空间的名称
xmlns:context=“http://www.springframework.org/schema/context”
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
一个标签只能加载一个包,如果有多个包,要使用下面这种方式:
<!--指定多个包的三种方式-->
<!--第一种方式:使用多次组件扫描器,指定不同的包-->
<context:component-scan base-package="com.ba01"/>
<context:component-scan base-package="com.ba02"/>
<!--第二种方式:使用分隔符(;或,)分隔多个包名-->
<context:component-scan base-package="com.ba01; com.ba02"/>
<!--第三种方式:指定父包-->
<context:component-scan base-package="com"/>
给属性赋值
@ VaLue:简单类型的属性赋值
属性:value是String类型的,表示简单类型的属性值
位置:
- 在属性定义的上面,无需set方法,推荐使用。
- 在set方法的上面
例如:
@Value("张飞")
private String name;
@Value("23")
private Integer age;
上面是给简单类型,那么如何给引用类型赋值呢?
我现在有个Student
类,其中有个School
属性,如何实现对School
的赋值操作呢?
我们需要用到另一个注解@Autowired
@ Autowired
:Spring框架提供的注解,实现引用类型的赋值。
Spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName
,byType
@ Autowired:默认使用的是byType自动注入。
位置:
- 在属性定义的上面,无需set方法,推荐使用
- 在set方法的上面
如果要使用byName方式,需要做的是:
1.在属性上面加入@Autowired
2.在属性上面加入@Qualifier(value="bean的id")
:表示使用指定名称的bean完成赋值
例如,我们先给School赋值
然后再对Student中的School进行操作
此时就完成了对引用数据类型的赋值了
注意: 当使用byName,如果 注解中的值和属性的id不一样时 会出现下面的情况:
@Autowired
还有一个属性叫required
返回boolean
类型,默认为true
- required=true :表示引用类型赋值失败,程序报错,并终止执行。
- required=false :引用类型如果赋值失败,程序正常执行,引用类型是null
除了@Autowired
注解外,还可以用@Resource
给引用类型赋值
@Resource
是jdk中的注解,默认是byName
:先使用byName
自动注入,如果byName
赋值失败,再使用byType
如果要只使用byName
,则应该写@Resource(name="mySchool")
使用方式:
注解与 XML 的对比
注解优点是:
- 方便
- 直观
- 高效(代码少,没有配置文件的书写那么复杂)
其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的
XML 方式优点是:
- 配置和代码是分离的
- 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
xml 的缺点是:编写麻烦,效率低,大型项目过于复杂
总结:经常修改就用xml,无需修改就用注解