文章目录
IOC和DI
IOC(inversion of Control) 控制反转容器
如何理解控制反转?
将对象的创建,配置和维护交给第三方管理,解决了依赖的耦合度,大大降低。
由容器来进行创建,配置和维护等工作,用户需要调用对象,就可以在IOC容器中获取。
对象于对象之间的依赖关系在spring中如何解决
Spring中对象在创建之后,需要依赖其他对象时,就需要依赖注入DI
(Dependecy injection)
控制反转是将对象交给外部管理,依赖注入问题也可以交给IOC容器进行管理
IOC核心在于:对象不由使用方双方管理,而是交予第三方管理。
优点为:
- 资源集中管理,实现资源的可配置和维护
- 降低使用资源双方的依赖程度,降低耦合度。
Spring控制依赖
- 增加一个spring的配置文件
- 解析XML文件获取管理对象,
反射
- 将解析的bean放入BeanFactory工厂类
- 在工厂类中通过反射创建处person类
IOC容器介绍
解决对象的创建和对象之间的依赖关系。
ApplicationContext容器中的接口的继承关系,ApplicationContext是BeanFactory的子接口之一,即BeanFactory是Spring IOC容器定义的最底层的接口,ApplicationCentext是BeanFactory的高级实现之一,是对BeanFactory功能做了许多的扩展。
BeanFactory
BeanFactory是最底层的IOC容器实现
现在已经不用了
//获取IOC容器
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
ApplicationContext
是IOC的高级使用,对BeanFectory的扩展,实际过程中使用此类
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
主要使用的三种创建IOC的方式
ClassPathXmlApplicationContext
相对路径读取classpath中的资源,读本项目下
ClassPathXmlApplicationContext("application1.xml");
FileSystemXmlAplicationContext
读取指定路径下的资源
new FileSystemXmlApplicationContext("全路径");
XmlWebAplicationContext
需要在Web环境下读取资源,读取网络中的IOC路径
其他创建
BeanFactory和ApplicationContext的关系和区别使用和作用点
BeanFactory是最底层的IOC容器实现,而ApplicationContext 而是BeanFactory的拓展,是IOC的高级使用
作用:BeanFactory 负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期
而ApplicationContext除了满足以上的功能外,还提供了完整的框架功能 1.国际化支持 2.资源访问 3.事件传递
使用:
BeanFactory
//获取IOC容器
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
AoolicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Bean的实例化方式
Spring容器装配Bean的方式主要是两种
- 基于XML配置方式
- 基于注解的方式,实际使用最多
基于XML配置方式装配Bean
Bean的装配实例化方式
基于无参构造函数创建对象重点
//id 类名 class 包路径
<bean id="Student" class="org.example.Student"></bean>
必须确保Student的构造函数时无参的
package org.example;
public class Student {
public Student() {
this.name = name;
this.sex = sex;
this.age = age;
}
}
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student student = (Student)context.getBean("Student");
student.setName("张三");
System.out.println(student.getName());
基于静态工厂方式创建对象
在确定实现类中没有无参构造
给定静态工厂类来获取Student类
package org.example;
public class Student {
public Student(String name,String sex , int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public static Student getStudent(){
return new Student("张三","男",20);
}
}
<!--基于静态工厂方式实例化bean-->
<bean id="Student" class="org.example.Student" factory-method="getStudent"/>
class属性指定的静态工厂类的全路径 ,factory-method属性即对应的方法,当前获取Student类在静态工厂下提供的getStudent方法可获取该对象
app实现
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student student = (Student)context.getBean("Student");
student.setName("张三");
System.out.println(student.getName());
基于普通工厂方法实现Bean
package org.example;
public class Student {
private String name;
private String sex;
private int age;
public Student getStudent(){
return new Student();
}
}
配置Bean信息
<bean id="factoty" class="org.example.Student"/>
<bean id="Student" factory-bean="factoty" factory-method="getStudent"/>
基于注解的方式装配Bean
比XML形式装配bean会更加简单
application配置文件引入context
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--开启注解扫描:在指定的包路径下所有的类名,属性等上的注解都会进行扫描-->
<context:component-scan base-package="org.example"/>
<!--开启注解扫描:扫描属性上的注解:不推荐使用-->
<!--<context:annotation-config></context:annotation-config>-->
</beans>
注意:
使用标签一定要引入context约束,该约束下才提供
在交给IOC容器管理的类上添加注解
package org.example.bean;
import org.springframework.stereotype.Component;
//@Component("Student") 与下面等同
@Component(value = "Student")
//等同于<bean id = "person" class = "org.example.bean.Student"></bean>
//默认给定的名称为类名首字母小写
public class Student {
private String name;
private String sex;
private int age;
}
需要给定名字 不然默认时类名首字母小写
通过IOC获取对象
ApplicationContext context = new ClassPathXmlApplicationContext("application2.xml");
Student student = (Student)context.getBean("Student");
student.setName("zs");
System.out.println(student.getName());
四种注解类型标记Bean
使用注解在配置文件中指定扫描的包路径或者类路径后,交给IOC管理得类上添加注解即可
@Component 通用的标注的注解 连接前端
@Repository 对dao层实现类进行标注 连接数据库
@Service 对service层实现类进行标注
@Controller 对Controller层实现类进行标注 让组件扫描将这个类别识别为一个组件
@Component 是 Spring 提供的通用的组件注解
@Repository、@Service 、@Controller都是Component其衍生出来,功能都是一样的,可以互换,
主要是为了区分被注解的类处在不同的业务层。
Spring中DI
DI-Dependency injection 依赖注入
组件之间的依赖关系由容器在运行时决定,IOC动态的为某个依赖注入到组件中。
那么他们之间 谁注入谁? IOC的作用是什么? 为什么要依赖?注入什么?
容器:IOC 组件(某个特定的类) 资源(组件依赖的内容)
谁依赖于谁:应用程序依赖于IOC容器
为什么需要依赖:应用程序需要IOC容器提供组件需要的外部资源
谁注入谁:IOC容器注入应用程序需要的资源,组件依赖的资源
注入什么:注入了某个对象所需要的外部资源
基于XML配置文件注入
基于有参构造函数注入依赖
给定User对象,定义有参构造
public class User1 {
private Integer id;
private String name;
private String passwd;
private String address;
// 有参构造函数
public User1(Integer id, String name, String passwd, String address) {
this.id = id;
this.name = name;
this.passwd = passwd;
this.address = address;
}
}
xml文件配置注入依赖
使用
注意
value只支持基本类型作为string,自定义类型使用的是标签
<!--基于XML形式的依赖注入:有参构造-->
<bean id="user" class="org.example.service.User1">
<!--注入属性-->
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="address" value="address"></constructor-arg>
<constructor-arg name="passwd" value="passwd"></constructor-arg>
</bean>
app实现
ApplicationContext context =new ClassPathXmlApplicationContext("application.xml");
User1 user = (User1) context.getBean("user");
System.out.println(user);
基于set方法注入依赖
public class User2 {
private Integer id;
private String name;
private String passwd;
private String address;
public User2() {
this.id = id;
this.name = name;
this.passwd = passwd;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User1{" +
"id=" + id +
", name='" + name + '\'' +
", passwd='" + passwd + '\'' +
", address='" + address + '\'' +
'}';
}
set方法注入依赖使用注解
xml配置
<!--基于XML形式的依赖注入-->
<bean id = "user2" class="org.example.service.User2">
<property name="id" value="1"></property>
<property name="passwd" value="passwd"></property>
<property name="address" value="address"></property>
<property name="name" value="张三"></property>
</bean>
app实现
ApplicationContext context =new ClassPathXmlApplicationContext("application.xml");
User2 user2 = (User2) context.getBean("user2");
System.out.println(user2);
注入数据基本类型,自定义类型 Spring中还支持List,map,set,array等类型的数据注入
自定义类型注入依赖
前提得先注入自定义类型的依赖
<bean id = "student" class = "org.example.bean.Student"></bean>
<bean id="user" class="org.example.service.User1">
<!--注入属性-->
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="address" value="address"></constructor-arg>
<constructor-arg name="passwd" value="passwd"></constructor-arg>
</bean>
注入集合类型
<!--注入List类型-->
<property name="list">
<list>
<value>12</value>
<value>13</value>
</list>
</property>
<!--注入map类型数据-->
<property name="ms">
<map>
<entry key="a" value="1"/>
<entry key="b" value="2"/>
</map>
</property>
基于注解的注入
在对应需要的依赖上添加注解@Autowired
@Value注入普通的类型属性
@Resource 注入的是对象类型
@Autowired 注入对象类型
@Service(value = "UserLogin")
public class UserLogin {
@Autowired
private LoginText loginText;
public void login(String name){
User2 user2 = new User2(1,"zs","passwd","xa");
loginText.printLogin(user2);
}
}
@Service(value = "LoginText")
public class LoginText {
public void printLogin(User2 user2){
System.out.println(user2.getName()+"欢迎登陆");
}
}
@Component(value = "user2")
public class User2 {
private Integer id;
private String name;
private String passwd;
private String address;
//private Student student;
private List<String> list;
public User2(){
}
public User2(Integer id, String name, String passwd, String address) {
this.id = id;
this.name = name;
this.passwd = passwd;
this.address = address;
}
//以下是一些get和set方法
}
可以看到user2中(idea) 有一个无参构造,再删除掉无参构造时,有参构造会报出错误,而且编译报错。
问题
--------------------------问题----------------------------------------------------------------------------
在注解形式中:Spring的反射要求这个bean必须要有一个无参构造器
可能在idea中不支持带参数的注解注入
通常在javaBean中,一般参数在四个以上的,不推荐使用带参数的构造函数赋值,多使用get/set方法。
------------------------------------------------------------------------------------------------------------
@Resource和@Autowired的区别?
关系:
- @Resource和@Autowired都是用来做bean的注入时使用
- @Resource和@Autowired有时可以互相替换使用,当都作为bean注入使用时,在接口仅有一个实现类时,两个注解的修饰效果相同,可以相互替换
不同点:
-
@Resource是Java自己的注解,@Resource有两个属性较为重要 name,type,spring使用name解析为根据bean中的名称定位,使用type时解析为根据类型定位,如果没有给定属性值,Spring的反射机制通过byName来自动注入属性
-
@Autowired是spring提供的注解,@Autowired在spring2.5后引入的只能根据type进行注入,不用name,如果涉及到type无法识别注入对象时(比如,有多个相同类型的类时),只使用Autowired是无法注入的,需要借助其他注解才能完成注入,比如@Primary @Qualifler,通过它指定哪个是真正需要注入的。@Autowired只有一个属性required,默认值为true,为true时,找不到就抛异常,为false时,找不到就赋值为null。
依赖解析过程
在spring中的依赖解析过程:
- ApplicationContex通过配置的元数据来创建和初始化,这些元数据描述了所有的bean,元数据的信息可以通过注解,xml或者Java代码来描述。
- 对于每一个bean,他的依赖属性,构造方法或者是静态工厂方法等形式来表达,bean被创建好之后这些以来会提供给他。
- 每一个属性或构造方法都要被设置实际定义,或者是对容器的另一个bean(自定义类型)的引用。
- 每个属性或者构造方法上的值的实际定义都会被转化为当前实例bean的实际的值。
在容器创建的过程中,spring容器会验证每一个bean的配置,在实际创建bean之前,bean的属性不会被设置,单例和被设置为首先加载的bean会在容器初始化后就创建出来,其他的bean只会在需要的时候才会创建,创建bean过程可能会引起一系列的bean被创建,
循环依赖
A -->B A依赖B
B -->A 而B又依赖A
解决:这些类可以通过set注入,避免使用构造方法注入
A B
构造函数构造A ,依赖B 创建顺序:B-A
Set方法创建A, 注入B 创建顺序:A-B