本文是作者原创,版权归作者所有.若要转载,请注明出处

我们知道Spring Framework 最重要的功能就是IoC (Inversion of Control ),也叫DI(dependency injection),这不是我说的,是官网这么说的,截图如下

spring官网说IoC,也叫DI,是同一个意思.我会在文章最后写一下自己的个人理解

首先复习一下spring的IOC相关应用

1.将对象交给spring管理

首先是测试类

public class UserService  {

    private String userName;

    public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} }

然后是spring的配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="no"> <bean class="com.lusaisai.service.UserService" id="userService" >
<!--此处name的值与set方法要一致-->
<property name="userName" value="lusai"></property>
</bean> </beans>

最后是测试

public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");       System.out.println(userService.getUserName());
 }

可以看到注入成功

2.依赖注入

首先是UserService依赖的对象UserDao

public interface UserDao {
void test();
}

然后是它的实现类

public class UserDaoImpl implements UserDao {
@Override
public void test() {
System.out.println("UserDaoImpl");
}
}

在UserService增加依赖的对象UserDao和set方法,注意,这里需要UserService的空构造(不写的时候编译器会帮我们自动构建一个无参构造)

public class UserService  {

    private String userName;

    private UserDao userDao;

    public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
} public void setUserDao(UserDao userDao) {
this.userDao = userDao;
} public void serviceTest(){
userDao.test();
} }

然后是xml配置

<!--必须有空构造-->
<bean class="com.lusaisai.service.UserService" id="userService" >
<property name="userName" value="lusai"></property>
<!--这里的ref指定下方bean标签配置对象的id-->
<!--此处name的值与UserService的属性userDao的set方法名要一致-->
<property name="userDao" ref="userDao"></property>
</bean> <bean class="com.lusaisai.dao.UserDaoImpl" id="userDao" ></bean>

然后是测试

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getUserName());
userService.serviceTest();

看下结果

好了,注入成功

3.我们发现一个问题,我们已经在代码里写了UserDao,还有它的set和get方法,为啥还要在xml里用配置告诉spring应该如何注入呢,其实spring提供了自动装配的功能,如下图

我们可以看到default-autowire有5个可供选择

单独的bean标签里也有5个供选择,这里截个官网的图

下面我们来测试一下,首先是byName,我们把手动装配注释掉,添加上default-autowire="byName"  如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName" > <!--必须有空构造-->
<bean class="com.lusaisai.service.UserService" id="userService" >
<property name="userName" value="lusai"></property>
<!--这里的ref指定下方bean标签配置对象的id-->
<!--此处name的值与UserService的属性userDao的set方法名要一致-->
<!--<property name="userDao" ref="userDao"></property>-->
</bean> <bean class="com.lusaisai.dao.UserDaoImpl" id="userDao" ></bean> </beans>
UserService中的代码没有改动,测试
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getUserName());
UserDao userDao = userService.getUserDao();
System.out.println(userDao);

看下结果

好的,成功了,我们改一下set方法的名字

public void setUserDao1(UserDao userDao) {
this.userDao = userDao;
}

重新运行一次

可以看到,userDao没有注入进来, 我们把方法名复原再次修改属性名试一下

private String userName;

    private UserDao userDao2;

    public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao2;
} public void setUserDao(UserDao userDao) {
this.userDao2 = userDao;
}

重新运行

可以看到,注入成功,因此byName的set方法名要和依赖对象的bean标签的id相同

接下来看下byType,我们将default-autowire="byType" 再加上一个同一个类不同id的bean标签

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType" > <!--必须有空构造-->
<bean class="com.lusaisai.service.UserService" id="userService" >
<property name="userName" value="lusai"></property>
<!--这里的ref指定下方bean标签配置对象的id-->
<!--此处name的值与UserService的属性userDao的set方法名要一致-->
<!--<property name="userDao" ref="userDao"></property>-->
</bean> <bean class="com.lusaisai.dao.UserDaoImpl" id="userDao" ></bean> <bean class="com.lusaisai.dao.UserDaoImpl" id="userDao2" ></bean> </beans>

userService代码

private String userName;

    private UserDao userDao;

    public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
} public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

再运行一下结果

十月 17, 2019 11:53:15 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in class path resource [applicationContext.xml]: Unsatisfied dependency expressed through bean property 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lusaisai.dao.UserDao' available: expected single matching bean but found 2: userDao,userDao2
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in class path resource [applicationContext.xml]: Unsatisfied dependency expressed through bean property 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lusaisai.dao.UserDao' available: expected single matching bean but found 2: userDao,userDao2
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1499)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1379)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at com.lusaisai.test.Test.main(Test.java:16)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lusaisai.dao.UserDao' available: expected single matching bean but found 2: userDao,userDao2
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:221)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1225)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1484)
... 13 more

报错信息,显示只要一个,但找到2个,我们注释掉一个

<!--<bean class="com.lusaisai.dao.UserDaoImpl" id="userDao" ></bean>-->

重新运行,看下结果

注入成功了,说明byType的自动装配方式中如果存在多个相同类型不同id的bean标签,则抛出异常,如果没有匹配的bean,则不自动装配

最后看下构造方法注入

首先

default-autowire="constructor"

然后在UserService中注释掉set方法,添加userDao的构造方法

private String userName;

    private UserDao userDao;

    public UserService(UserDao userDao) {
this.userDao = userDao;
} public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
}

,运行,看结果

注入成功了

我们把userDao的构造方法注释了,再运行看下结果

没注入成功,但也不报错

好,到此为止,我们的xml配置将对象交给spring管理就讲完了总结一下

可以看出,no和Default是不自动装配的,byName和byType是通过set方法自动装配的,同时要确保有空构造存在,我猜底层是用newInstance()实现的具体源码后面再看

byName是根据set方法名自动装配的,set方法名要和bean标签的id相对应,否则,注入不成功,但不会报错

byType是根据类型装配的,如果存在多个该属性类型的bean标签,则抛出异常,如果没有匹配的bean,则不自动装配

constructor是根据构造方法来装配的,如果容器中没有一个构造函数参数类型的bean,则不自动装配

可以看到这种xml格式其实是非常麻烦的,实际项目中我们一般通过注解来将对象交给spring管理

只需要将spring的配置文件按以下配置即可,加入包扫描,就能将包下的所有对象通过注解方式来注入

<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
> <context:component-scan base-package="com.lusaisai"></context:component-scan> </beans>

我们来看例子,注意上面的spring配置文件中一个bean标签都没有了

这里加入了@Component注解,也可以用@Repository,@Service,@Controller注解,四个注解都可以将对象注入到spring容器进行管理,效果是一样的

@Component
public class UserDaoImpl implements UserDao {
@Override
public void test() {
System.out.println("UserDaoImpl");
}
}

这里在依赖的对象上加入了@autowire注解,我猜底层是通过filed.set调用的,所以不需要set方法,这个以后看源码了再讲

@Component
public class UserService { private String userName="lusai"; @Autowired
private UserDao userDao; public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
} }

当然也可以将注解加在set方法上,如下

@Component
public class UserService { private String userName="lusai"; private UserDao userDao; @Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
} public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
}

测试一下

public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService.getUserName());
UserDao userDao = userService.getUserDao();
System.out.println(userDao); }

看下结果

可以看到,注入成功了

如果我们再给UserDao接口的另一个实现类也交给spring管理,会不会报错呢?如下

@Component
public class UserDaoImpl2 implements UserDao{
@Override
public void test() {
System.out.println("UserDaoImpl2");
}
}

运行一下看结果

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lusaisai.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:221)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1225)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:668)
... 15 more

果然报错了,和上面的报错信息差不多,说明同一个接口,最好不要写多个实现类,那我非要写多个实现类怎么办呢

我们可以用@Resource,并指定它的name属性的别名为类名的首字母小写,如下

@Component
public class UserService { private String userName="lusai"; @Resource(name = "userDaoImpl2")
private UserDao userDao; public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
} }

运行一下,看看结果

成功了

总结:先来张网上的图

@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。两者如果都写在字段上,那么就不需要再写setter方法

@Autowired默认按类型装配(这个注解是属业spring的),需要导入包org.springframework.beans.factory.annotation.Autowired 默认按照类型来进行装配

@Resource(这个注解属于java的),需要导入包javax.annotation.Resource。默认按照名称进行装配,名称可以通过name属性进行指定

最后,现在流行用javaconfig而不是xml来配置spring,这里贴一下javaconfig的代码

第一种:相当于写bean标签,如下,这里使用@bean注解 将UserService 和UserDaoImpl对象交给spring管理

@Configuration
public class SpringConfig { @Bean
public UserService userService(){
return new UserService();
} @Bean
public UserDao userDao(){
return new UserDaoImpl();
} }

这里注入依赖

public class UserService  {

    private String userName="lusai";

    @Autowired
private UserDao userDao; public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
} }

这是测试demo

public static void main(String[] args) {

        AnnotationConfigApplicationContext annotationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = (UserService) annotationContext.getBean("userService");
System.out.println(userService.getUserName());
UserDao userDao = userService.getUserDao();
System.out.println(userDao); }

看下结果

第二种:相当于扫描包,把上面的两个@bean注解注释了,开启扫描

@Configuration
@ComponentScan("com.lusaisai")
public class SpringConfig { /*@Bean
public UserService userService(){
return new UserService();
} @Bean
public UserDao userDao(){
return new UserDaoImpl();
}*/ }

在UserService 和UserDaoImpl对象上加上@Component,将他们交给spring管理,如下

@Component
public class UserDaoImpl implements UserDao {
@Override
public void test() {
System.out.println("UserDaoImpl");
}
}
@Component
public class UserService { private String userName="lusai"; @Autowired
private UserDao userDao; public String getUserName() {
return userName;
} public void setUserName(String userName1) {
this.userName = userName1;
} public UserDao getUserDao() {
return userDao;
} }

重新测试

可以看到,注入成功了.

我们再提出一个问题 @Autowired与@Resource这两个注解 和xml配置中的 default-autowire="byName" 和byType是不是用的相同的原理的呢,这里就后面看源码的时候再来解释吧

最后说一下,我自己对IOC和DI的理解:以上文中的UserService和UserDao为例:

1.UserService和UserDao这两个对象原来我们是自己new出来的,现在交给spring管理,这就叫控制反转,即IOC

2.UserService中有个属性UserDao,那我们可以说:UserService要使用UserDao,那么UserService就对UserDao产生了依赖,也就是UserService依赖UserDao,而UserDao存在于spring容器中,spring将容器中的UserDao对象交给UserService使用,这就叫依赖注入,即DI,

3.DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结道:控制的什么被反转了?就是获得依赖对象的方式反转了(获得依赖对象的方式从我们自己new改成交给spring)

2. spring 应用之IOC的更多相关文章

  1. Spring学习之Ioc控制反转(1)

    开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...

  2. Spring学习之Ioc控制反转(2)

    开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...

  3. 比Spring简单的IoC容器

    比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...

  4. Spring学习笔记IOC与AOP实例

    Spring框架核心由两部分组成: 第一部分是反向控制(IOC),也叫依赖注入(DI); 控制反转(依赖注入)的主要内容是指:只描述程序中对象的被创建方式但不显示的创建对象.在以XML语言描述的配置文 ...

  5. Spring框架(3)---IOC装配Bean(注解方式)

    IOC装配Bean(注解方式) 上面一遍文章讲了通过xml来装配Bean,那么这篇来讲注解方式来讲装配Bean对象 注解方式需要在原先的基础上重新配置环境: (1)Component标签举例 1:导入 ...

  6. Spring框架之IOC(控制反转)

    [TOC] 第一章Spring框架简介 IOC(控制反转)和AOP(面向方面编程)作为Spring框架的两个核心,很好地实现了解耦合.所以,简单来说,Spring是一个轻量级的控制反转(IoC)和面向 ...

  7. 一) Spring 介绍、IOC控制反转思想与DI依赖注入

    一.spring介绍1.IOC反转控制思想(Inversion of Control)与DI依赖注入(Dependency Injection)2.AOP面向切面的编程思想与动态代理3.作用:项目的粘 ...

  8. Spring系列之IOC的原理及手动实现

    目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 导语 Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架.也是几乎所有J ...

  9. Spring框架[一]——spring概念和ioc入门(ioc操作xml配置文件)

    Spring概念 spring是开源的轻量级框架(即不需要依赖其他东西,可用直接使用) spring核心主要两部分 aop:面向切面编程,扩展功能不是修改源代码来实现: ioc:控制反转,比如:有一个 ...

  10. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

随机推荐

  1. Day004_Linux基础命令之特殊符号与正则表达式通配符

    特殊符号: . 点 cd . 表示当前目录 ' '' 单引号,所见即所得 原封不动输出 " ""双引号,里面的特殊符号会被解析运行 `` ====$( ) 先运行() 里 ...

  2. go 学习笔记之学习函数式编程前不要忘了函数基础

    在编程世界中向来就没有一家独大的编程风格,至少目前还是百家争鸣的春秋战国,除了众所周知的面向对象编程还有日渐流行的函数式编程,当然这也是本系列文章的重点. 越来越多的主流语言在设计的时候几乎无一例外都 ...

  3. 史上最详 Thymeleaf 使用教程

    前言 操作前建议先参考我的另一篇博客:玩转 SpringBoot 2 快速整合 | Thymeleaf 篇 查看如何在SpringBoot 中使用 Thymeleaf.还有一点需要注意的是:模版页面中 ...

  4. jquery插件之poshytip

    Poshy Tip 是一个强大的jQuery 工具提示插件,拥有不同的外观.作为 Form Tooltips使用时,可以自定义气泡出现的位置. 导入插件: <script type=" ...

  5. Android四大组件初识之Activity

    一.Activity的生命周期 Activity生命周期是一系列方法调用.熟悉各个方法调用时间,我们在创建activity就能根据具体实现选择合适的方法覆盖. 1.  覆盖Activity的生命周期方 ...

  6. LeetCode第七题

    Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 Have you ...

  7. <%@ include %>导入的文件乱码

    如: <% String ss = (String) session.getAttribute("username"); if (ss == null || ss == &q ...

  8. 配置树莓派3的openwrt中的网络

    在上一篇中讲到openwrt的编译安装: http://www.cnblogs.com/yeqluofwupheng/p/7296218.html 但是烧写进去,启动系统后发现它的默认配置是路由器,所 ...

  9. MybatisPlus报错Invalid bound statement (not found)的解决方案

    今天使用MybatisPlus,测试时报错Invalid bound statement (not found) 使用自定义的mapper接口中的方法可以执行,而调用MybatisPlus中baseM ...

  10. 链表-LinkList

    什么是链表 维基百科:链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必须按顺序存 ...