- 什么是IOC
- 控制反转,把对象创建和对象之间的调用的过程,交给Spring进行管理
- 使用IOC目的:为了降低耦合度
IOC底层原理
IOC底层实现原理由三个部分组成:xml解析、工厂模式、反射
画图来展现一下他是如何降低耦合度的
可以看出这两个类之间的耦合度是比较高的,我们在开发过程中讲究的是高内聚、低耦合。我们使用设计模式中的工厂模式,可以降低耦合度
以上我们通过工厂类降低了耦合度,但我们需要做到的是将耦合度降低到最低限度,这时候我们就需要使用IOC容器,通过xml配置文件和反射来处理
以这种形式将耦合度降低到最低限度,这也是IOC的底层原理
IOC(BeanFactory接口)
-
IOC思想是基于IOC容器完成的,IOC容器就是我们上文所讲的对象工厂
-
Spring提供IOC容器的两种实现方式(两个接口)
-
BeanFactory:IOC容器的基本实现,是Spring使用的内部接口,不建议开发人员使用
在读取配置文件时,加载配置文件并没有创建对象,而是等获取对象的时候再去创建
-
ApplicationContext:BeanFactory的子接口,有着更强大更完善的功能,供开发人员使用
与BeanFactory不同,加载配置文件就将所有的对象创建完毕
在实际开发中我们应该选择ApplicationContext,将所有的对象在服务器启动时创建完成,可以减少运行时间
-
-
ApplicationContext接口的两个实现类
- FileSystemXmlApplicationContext:配置文件路径指的是盘符路径
- ClassPathXmlApplicationContext:配置文件路径指的是工程路径
IOC操作Bean管理(基于xml)
-
基于xml方式创建对象
- 在spring配置文件中使用bean标签,标签里面添加对应属性,就可以实现对象的创建
- 创建对象时候,默认也是执行无参数构造方法完成对象的创建
-
基于xml方式注入属性
- DI:依赖注入,就是注入属性
-
第一种注入方式:使用set方法注入
创建一个User类,使用set方式注入,对应的属性必须有set方法
public class User { private String name; private String sex; public void setSex(String sex) { this.sex = sex; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + ''' + ", sex='" + sex + ''' + '}'; } }xml文件如下配置
-
第二种注入方式:使用构造器注入
创建一个Book类,使用构造方法注入,pojo类中必须要有相对于的有参构造方法
public class Book { private String bname; private String bauthor; public Book(String bname, String bauthor) { this.bname = bname; this.bauthor = bauthor; } @Override public String toString() { return "Book{" + "bname='" + bname + ''' + ", bauthor='" + bauthor + ''' + '}'; } }xml文件如下配置
-
在set注入方式上进行简化:使用p名称空间注入(了解)
假定创建一个Book类,这里就不重复贴代码了,使用set方法,必须要有对应属性的set方法
需要在头文件中配置信息:xmlns:p=“http://www.springframework.org/schema/p”
IOC操作Bean管理(注入其他对象属性)
演示我们均使用set方法
-
注入空值
-
注入带特殊符号的值(比如<<>>)
一种方法是使用转义字符,这里不做介绍,使用CDATA来解决此问题
>]]> -
注入属性(外部Bean)
我们创建一个Serivce和Dao类,Serivce对象中调用Dao的方法
public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add(){ System.out.println("UserService add......"); userDao.update(); } } public class UserDao { public void update(){ System.out.println("UserDao update...."); } }相当于我们将userDao对象作为一个属性创建,并赋值给UserService中
-
注入属性(内部Bean)
内部Bean和外部Bean效果是一样的,只不过写法不同
我们创建一个部门和员工对象,部门为员工属性
public class Dept { private String deptName; public void setDeptName(String deptName) { this.deptName = deptName; } @Override public String toString() { return "Dept{" + "deptName='" + deptName + ''' + '}'; } } public class Emp { private String name; private Dept dept; public void setName(String name) { this.name = name; } public void setDept(Dept dept) { this.dept = dept; } @Override public String toString() { return "Emp{" + "name='" + name + ''' + ", dept=" + dept + '}'; } }xml文件如下配置
-
级联赋值
级联赋值第一种写法相当于外部Bean,这里主要介绍第二种写法
IOC注入数组集合
-
普通数组、集合的注入
编写一个类用于存放这些数组对象
public class Stu { private String[] array; private Listlist; private Map maps; private Set sets; public void setArray(String[] array) { this.array = array; } public void setList(List list) { this.list = list; } public void setMaps(Map maps) { this.maps = maps; } public void setSets(Set sets) { this.sets = sets; } @Override public String toString() { return "Stu{" + "array=" + Arrays.toString(array) + ", list=" + list + ", maps=" + maps + ", sets=" + sets + '}'; } } 在xml文件中对这些对象属性进行注入
其中map集合在注入中与其他不同,可以这样理解,map底层实际上是entry数组,所以不用value,使用entry
数组1 数组2 数组3 List1 List2 List3 set1 set2 set3 -
数组的类型是对象类型
在之前的book类之上,在创建一个BookShelf书架类,讲book作为数组对象传入
public class BookShelf { private Listbooks; //bookname是为抽取集合部分准备的(这里可以忽略) private List bookName; public void setBookName(List bookName) { this.bookName = bookName; } public void setBooks(List books) { this.books = books; } @Override public String toString() { return "BookShelf{" + "books=" + books + ", bookName=" + bookName + '}'; } } 在xml文件中进行如下配置
-
抽取集合注入部分
在实际开发过程中,可能会有一些固定的属性,所有的bean对象实例都需要,这时候可以将他抽取出来,方便引用
这里我们需要在头部文件中进行配置,配置以下两条语句
xmlns:util="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
钢铁是怎样练成的 80天环游世界
IOC操作Bean管理(FactoryBean工厂Bean)
在Bean管理中,Bean分为两大类,一种是普通Bean,另外一种是FactoryBean,之前使用的均为普通Bean,下面介绍两者的区别
普通Bean:在配置文件中定义的类型就是返回值类型
FactoryBean:返回值类型可以和配置文件中定义的类型不同
-
如何创建一个工厂Bean
- 创建一个类,作为工厂Bean,实现FactoryBean接口
- 实现接口里的方法,在getObject方法中定义返回值类型
-
实现
public class MyBean implements FactoryBean
{ @Override public User getObject() throws Exception { User user = new User(); user.setName("Tom"); return user; } @Override public Class> getObjectType() { return null; } } xml配置文件如下
IOC操作Bean管理(Bean工作域)
bean的作用域是指在spring中设置Bean实例是单实例还是多实例,默认是单实例
先举一个单实例的例子,依旧还是使用上面讲到的MyBean对象做例子
设置scope属性为singleton,表示为单实例,不设置scope属性,默认是单实例
我们创建两个对象,区分单实例和多实例最简单的办法就是比较他们地址值是否相同,相同则为单实例,反之亦然
@Test
public void test5(){
ApplicationContext context = new ClassPathXmlApplicationContext("Bean4.xml");
User user1 = context.getBean("myBean", User.class);
User user2 = context.getBean("myBean", User.class);
System.out.println(user1 == user2);//true
}
我们只要将scope属性设置为prototype则代表Bean实例为多实例
@Test
public void test5(){
ApplicationContext context = new ClassPathXmlApplicationContext("Bean4.xml");
User user1 = context.getBean("myBean", User.class);
User user2 = context.getBean("myBean", User.class);
System.out.println(user1 == user2);//false
}
Bean的生命周期
Bean的生命周期分五步
- 空参构造器
- 使用set注入属性
- Bean的初始化方法(需要配置)
- 使用Bean
- 销毁Bean(需要配置)
演示
public class Student {
private String name;
public Student() {
System.out.println("第一步:空参构造器");
}
public void setName(String name) {
System.out.println("第二步:set注入属性");
this.name = name;
}
public void initMethod(){
System.out.println("第三步:Bean初始化方法");
}
public void destroyMethod(){
System.out.println("第五步:销毁Bean");
}
}
@Test
public void test6(){
//这里使用ClassPathXmlApplicationContext,因为ApplicationContext没有close方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Bean4.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student);
System.out.println("第四步:实例化Bean");
//手动销毁Bean
context.close();
}
测试结果:
第一步:空参构造器 第二步:set注入属性 第三步:Bean初始化方法 com.yellowstar.pojo.Student@c81cdd1 第四步:实例化Bean
在配置了Bean的后置处理器之后,Bean的生命周期会有7步
如何配置Bean的后置处理器
创建一个类实现BeanPostProcessor接口,并实现接口中的两个方法
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean初始化之前的方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean初始化之后的方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
重新测试test6方法,结果为
第一步:空参构造器 第二步:set注入属性 Bean初始化之前的方法 第三步:Bean初始化方法 Bean初始化之后的方法 com.yellowstar.pojo.Student@c81cdd1 第四步:实例化Bean 第五步:销毁Bean
可以发现这两个方法分别在Bean初始化方法之前和之后
Bean自动装配
自动装配指的是:根据指定装配的规则(属性名或类型),spring自动注入匹配的属性值(个人不太喜欢使用)
使用上文中提到的部门类和员工类
根据属性名自动注入
设置autowire为byName,需要注意的是dept的id值必须跟emp中Dept的属性名一样
根据类型自动注入
设置autowire为byType,需要注意的是,xml文件中配置的Dept对象必须只有一个,否则会报错
IOC操作Bean管理(外部属性文件)
我们在创建数据库连接池的时候,经常将连接数据库的配置写在一个properties文件中,这时候我们就需要配置操作外部属性文件
我们先使用原始办法注入属性
下面我们将这些属性写道一个jdbc.properties文件夹中,左边的属性名可以自行修改
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test username=root password=123456
要想在xml文件中使用外部文件属性,需要在头部文件中进行配置
IOC操作Bean管理(基于注解)
-
使用注解+xml配置文件创建Bean对象
相比于之前的jar包,基于注解的方式需要另外导入一个jar包
我们对xml文件进行如下配置
这里我们创建一个Teacher类,需要使用注解创建对象,我们需要在类的前面添加以下注解信息,四种注解的效果都一样
- @Component:推荐在dao层使用
- @Service:推荐在service层使用
- @Controller:推荐在web层使用
- @Repository
说明注解的value值相当于xml配置的id值,这里也可以忽略不写,系统默认生成单词的首字母小写格式
@Controller(value = "teacher") public class Teacher { public void add(){ System.out.println("add..."); } } -
xml文件中配置过滤器
use-default-filters:表示不使用系统默认的过滤器 -
使用注解注入属性
-
注入对象属性
注入对象属性可以使用以下三种方法
- @Autowired :根据对象类型注入属性
- @Qualifier:根据对象名注入属性,需配合@Autowired 使用
- @Resource:可以根据对象类型或对象名注入属性
创建一个service类和一个dao类,并对这两个类设置注解。
在类中的属性上设置以上三种注解
-
注入普通属性
使用@Value(value = ?)即可
@Service public class StudentService { @Autowired //根据类型注入属性 @Qualifier(value = "studentDaoImpl") //根据名称注入属性,要配合Autowired使用 private StudentDao studentDao; // @Resource //根据类型注入属性 @Resource(name = "studentDaoImpl") //根据名称注入属性 private StudentDao studentDao1; @Value(value = "abc") private String name; public void test(){ System.out.println("StudentDao test... " + name); studentDao.add(); studentDao1.add(); } } @Component public class StudentDaoImpl implements StudentDao { @Override public void add() { System.out.println("StudentDaoImpl add...."); } }
-
-
完全注解开发
以上形式还是需要配合xml文件才能使用,可以创建一个配置类,来代替xml配置文件
-
创建一个类,设置为配置文件类
@Configuration:表示该类作为配置文件类来使用
@ComponentScan:等同于设置xml文件中的basePackages属性
@Configuration //作为配置文件类 @ComponentScan(basePackages = {"com.yellowstar"}) public class SpringConfig { } -
在测试类中需要更改以下读取配置文件方法
@Test public void test3() { //使用AnnotationConfigApplicationContext(配置文件类的class)来创建 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); StudentService service = context.getBean("studentService", StudentService.class); service.test(); }
-



