(IOC)控制反转首先而是一种思想,控制反转主要是对对象控制权的转移。springioc容器创建对象,注入依赖。然后将对象的使用权交出去。
如果想要理解控制反转,我们应该了解“谁控制谁?控制什么?为何是反转?(有反转就有正转),哪些方面反转了?”
1. 谁控制谁?控制什么:在传统程序开发过程中,主要是由程序通过new来创建对象。而控制反转思想中,是由IOC容器创建对象,并注入对象需要的依赖对象。谁控制谁?肯定是IOC容器控制了对象。控制什么?主要就是控制了外部对资源的获取。
2. 为什么是反转?哪些方面反转了?:有反转肯定就有正转。在传统程序开发过程中,由程序自己去创建对象,创建该对象依赖的对象,这就是正转。而在IOC中,由IOC容器负责对象的创建和该对象的依赖对象。这就是反转。那为什么叫反转,主要是因为IOC容器帮助我们查找和注入依赖对象,对象只是被动的接受依赖对象,所以叫做反转。
首先这么做,降低了对象之间的耦合度,提高了代码的复用。创建对象不在由程序自己创建,而是交给容器。开发人员只需简单的配置就可以从容器中获取需要的对象,增加了组件的利用效率。
在spring的官网中,对IOC容器的介绍,就是指IOC又可以成为DI(依赖注入),其实IOC和DI是对同一概念不同角度的理解。
1. 谁依赖谁:应用程序依赖IOC容器
2. 为什么要依赖:因为应用程序需要从IOC容器中获取外部资源
3. 谁注入谁:IOC容器注入应用程序某个对象。应用程序依赖的对象。
4. 注入了什么:肯定是注入了某个外部程序应用的资源。
通过maven方式构建。
引用spring相关依赖
org.springframework
spring-context
5.1.17.RELEASE
junit
junit
4.12
test
将需要IOC管理的类型通过标签来注册
/**
* IoC的方式获取 UserBean 对象
*/
@Test
public void fun2(){
// 1.IoC容器的初始化操作 调用UserBean中无参构造器创建对象
ApplicationContext ac =
new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.从容器中获取UserBean对象 没有显示的new
UserBean user = (UserBean) ac.getBean("userBean");
user.say();
}
这么操作,默认调用的是Bean类的无参构造器。
如果调用有参构造器怎么做?我们直接改写xml中的配置就好。
或者:
bean标签中的id声明有一个
bean标签中的name可以声明一个或多个
id="u1,u2,u3" 只表示一个
name="n1,n2,n3" 表示会被拆分为3个name属性【拆分会根据 ',' ';' ' ' 空格 】
也可以根据需要调用对象的类型获取对象
@Test
public void fun05(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext-3.xml");
Cat cat=context.getBean(Cat.class);
System.out.println(cat);
}
如果IOC容器中,注入了多个同类型的对象,就会报错。
@Test
public void fun06() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
Cat cat = context.getBean(Cat.class);
System.out.println(cat);
}
报错如下:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.iocdemo.projo.Cat' available: expected single matching bean but found 2: cat1,cat2
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1166)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:416)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1124)
at com.iocdemo.test.MaiinTest.fun06(MaiinTest.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
这个时候如果获取对象时,只指定一个Cat类型,还是不行的,必须加上id或name:
@Test
public void fun06() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
Cat cat = context.getBean("cat1",Cat.class);
System.out.println(cat);
}
结果如下:
Cat{nick='小猫咪', color='red'}
除了以上的方法外,我们还可以在xml配置文件中,给bean标签增加primary。设置 primary属性为true,那么当同一类型有多个对象时,会优先返回primary属性的对象
在上方几个获取IOC容器对象的方法中,其实代码中主要默认的是使用ApplicationContext类来获取。接下来讲一个BeanFactory接口实现获取对象的方法。前面ApplicationContext类通过id,name,类型获取的方法,BeanFactory接口都有这些方法。获取方式如下:
@Test
public void fun08() {
ClassPathResource classPathResource = new ClassPathResource("applicationContext-4.xml");
BeanFactory beanFactory = new XmlBeanFactory(classPathResource);
System.out.println("===========");
Cat cat = (Cat) beanFactory.getBean("cat1");
System.out.println(cat);
}
在这里,我们主要讲一下BeanFactory和ApplicationContext的联系和区别。
通过类图我们可以看出,BeanFactory是个接口,而ApplicationContext继承了BeanFactory。所以BeanFactory中有的功能,ApplicationContext也有。同时ApplicationContext拓展了很多BeanFactory不具备的功能【事件广播,资源加载,web支持等等…】以下是两个类的具体区别。
BeanFactory:
是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和获取IOC容器对象的功能;
ApplicationContext:
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
@Test
public void fun08() {
ClassPathResource classPathResource = new ClassPathResource("applicationContext-4.xml");
BeanFactory beanFactory = new XmlBeanFactory(classPathResource);
System.out.println("===========");
Cat cat = (Cat) beanFactory.getBean("cat1");
System.out.println(cat);
}
结果:
===========//先执行的打印
调用的无参构造器//再执行的构造函数,说明BeanFactory在初始化的时,不会构造对象,而是当引用对象时,才会进行构建。
Cat{nick='小猫咪', color='red'}
Process finished with exit code 0
ApplicationContext:
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;
代码:
@Test
public void fun06() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
System.out.println("=======");
Cat cat = context.getBean(Cat.class);
System.out.println(cat);
}
调用的无参构造器//先构造对象
======= //再进行打印,说明ApplicationContext在初始化IOC容器时,会先构建对象,
Cat{nick='小猫咪', color='red'}
当然我们在使用ApplicationContext初始化IOC对象时,也可以通过指定属性lazy-init=true来使其达到BeanFactory延迟加载的效果。如下:
applicationContext.xml文件内容:
再次运行下方代码:
@Test
public void fun06() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-4.xml");
System.out.println("=======");
Cat cat = context.getBean(Cat.class);
System.out.println(cat);
}
结果实现了延迟加载的效果
=======
调用的无参构造器
Cat{nick='小猫咪', color='red'}
属性注入主要是指如何给对象中的属性赋值
通过构造方法注入
首先得提供对应的构造方法
既可以通过name也可以通过index来指定要赋值的参数
xml配置文件如下:
@Test
public void fun09() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-5.xml");
Cat cat = (Cat) context.getBean("cat");
System.out.println(cat);
}
运行结果如下:
调用的有参构造器
Cat{nick='red', color='小猫咪'}
这种做法要求实体类中有相应的构造函数。否则就会报如下错误:
"C:\Program Files\Java\jdk1.8.0_261\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:E:\IDEA\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar=9600:E:\IDEA\IntelliJ IDEA 2019.1.4\bin" -Dfile.encoding=UTF-8 -classpath "E:\IDEA\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar;E:\IDEA\IntelliJ IDEA 2019.1.4\plugins\junit\lib\junit-rt.jar;E:\IDEA\IntelliJ IDEA 2019.1.4\plugins\junit\lib\junit5-rt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar;E:\01大圣工作\A55项目\车型导入\2021022301\p5\ioc_xmldemo\target\test-classes;E:\01大圣工作\A55项目\车型导入\2021022301\p5\ioc_xmldemo\target\classes;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-context\5.1.17.RELEASE\spring-context-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-aop\5.1.17.RELEASE\spring-aop-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-beans\5.1.17.RELEASE\spring-beans-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-core\5.1.17.RELEASE\spring-core-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-jcl\5.1.17.RELEASE\spring-jcl-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\org\springframework\spring-expression\5.1.17.RELEASE\spring-expression-5.1.17.RELEASE.jar;C:\Users\gacneadmin\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\gacneadmin\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.iocdemo.test.MaiinTest,fun09
五月 16, 2021 9:17:34 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cat' defined in class path resource [applicationContext-5.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cat' defined in class path resource [applicationContext-5.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:268)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:554)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:514)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:319)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:85)
at com.iocdemo.test.MaiinTest.fun09(MaiinTest.java:95)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Process finished with exit code -1
注入的属性必须提供对应的setter方法
配置如下:
假如实体类如下:
package com.iocdemo.projo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @program: ioc_xmldemo
* @description:
* @author:
* @create: 2021-05-16 21:11
*/
public class User {
private Integer id;
private String userName;
private Cat cat;
private String[] favorites;
private List cats;
private Map map;
private Properties props;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String[] getFavorites() {
return favorites;
}
public void setFavorites(String[] favorites) {
this.favorites = favorites;
}
public List getCats() {
return cats;
}
public void setCats(List cats) {
this.cats = cats;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", cat=" + cat +
", favorites=" + Arrays.toString(favorites) +
", cats=" + cats +
", map=" + map +
", props=" + props +
'}';
}
}
篮球
登山
游泳
root
123456
实体类:
public class User {
}
java配置类:
@Configuration //通过@Configuration初始化IOC容器
public class UserConfig {
@Bean(name = {"a1", "a2"})
public User getUser() {
User user = new User();
return user;
}
测试类:
public class MainTest01 {
@Test
public void fun01() {
ApplicationContext ac = new AnnotationConfigApplicationContext(UserConfig.class);
User user = (User) ac.getBean("a1");
System.out.println(user);
}
}