扫码关注公众号:芋道源码

发送: 百事可乐
获取永久解锁本站全部文章的链接

《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://www.cnblogs.com/maxshare/p/10467761.html 「max-先生」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

ps:拿笔记一下,面试可能会考。依赖注入DI和面向切面编程AOP是Spring框架最核心的部分。这次主要是总结依赖注入的bean的装配方式。

前言

什么是依赖注入呢?也可以称为控制反转,简单的来说,一般完成稍微复杂的业务逻辑,可能需要多个类,会出现有些类要引用其他类的实例,也可以称为依赖其他类。传统的方法就是直接引用那个类对象作为自己的一个属性,但如果我们每次创建这个类的对象时,都会创建依赖的类的对象,还有如果那个类将来可能不用了,还需要到这个类去删除这个对象,那破坏了代码的复用性和导致高度耦合!

依赖注入的出现可以很好地解决这个问题,依赖注入就是由系统负责协调类的依赖对象的创建,我们无需自己去显示的创建依赖对象,而是由系统给我们注入这个对象,系统控制了这个对象的创建,也称为控制反转。

Spring给我们注入对象有三种方式

  • 隐式的bean扫描发现机制和自动装配
  • 在java中进行显示配置
  • 在XML中进行显示配置

第一种:spring从两个角度实现自动化装配:组件扫描和自动装配。

当对一个类标注@Component注解时,表明该类会作为组件类,spring将为这个类创建bean。当在应用文中引用这个bean,spring会自动扫描事先指定的包查找这个 bean。但spring默认是不启用组件扫描的,可以在XML中配置加上。还有一种方法:在新建一个配置类,类中可以什么不用写,在配置类上加上@ComponentScan注解,spring会自动扫描改配置类所在的包,一般应该倾向xml配置。下面是一个bbs论坛系统用户发帖的功能小例子:

package bbs.dao;
@Component
public interface Postdao {
/*
*用户发帖 ,post表添加帖子信息
*/
public int addpost(@Param("title") String title,@Param("content") String content,@Param("userid") int userid);
}

package bbs.dao;
@Component
public interface Userdao {
/*
* 用户发帖后,user表将用户发帖数加一
*/
public int addpost(int userid);
}

再在bbs.service包中创建一个postservice接口及其实现类,依赖Postdao和Userdao。

package bbs.service;
public interface PostService {
/*
用户发帖后,先添加帖子信息再更新用户发帖数量
*/
public void addpost(String title,String content,int userid);
}

package bbs.service;
@Component
public class PostserviceImpl implements PostService {

private Postdao postdao;
private Userdao userdao;

// @Autowired
// public void setPostdao(Postdao postdao)
// {
// this.postdao=postdao;
// }
//
// @Autowired
// public void setUserdao(Userdao userdao)
// {
// this.userdao=userdao;
// }

@Autowired
public PostserviceImpl(Postdao postdao,Userdao userdao)
{
this.userdao=userdao;
this.postdao=postdao;
}

public void addpost(String title, String content, int userid) {
int i=postdao.addpost(title, content, userid);
int j=userdao.addpost(userid);
if(i==1&j==1)
System.out.println("发帖成功");
else
System.out.println("发帖失败");
}
}

@Component在接口实现上注解就可以,但发现在userdao、postdao接口也加上了,其实可以去掉,因为我采用mybatis在xml中配置数据库的操作,动态实现dao接口。等下会提到。上面代码出现的@Autowired注解实现bean自动装配,会在spring应用上下文中的组件类寻找需求的bean。一般有两种装配方式:构造器和Setter方法(其他方法名也行,只要能够使注入的bean成为这个类的属性就行)

也可能出现spring没有查找到匹配的bean会抛出异常,在@Autowired加上required=false,如果没有匹配的bean时,spring会使这个bean处于未装配的状态,没有装配成功。还有可能会出现相同名字的bean有很多个,会产生歧义,一般在组件类上添加注解@Qualifier()括号写这个bean的id,在注入时也加上@Qualifier(),写上bean的id。像下面:

@Component
@Qualifier("postdao")
public interface Postdao{
. . . .
}

@Component
@Qualifier("userdao")
public interface Userdao{
. . . .
}

@Autowired
@Qualifier("usedao")
public void setUserdao(Userdao userdao)
{. . .
}

@Autowired
@Qualifier("postdao")
public void setUserdao(Postdao postdao)
{. . .
}

由于java不允许在同一个条目上重复出现相同类型的多个注解,所有注入采用set方式。但是其实可以创建自定义的限定符注解。这里就不介绍啦。

第二种:通过java代码装配bean

一般通过组件扫描和自动装配方式就比较方便了,但如果由于需求我们要使用第三方的库的类,在这种情况没有办法到第三方库中去给类加注解,就不能使用第一种方法了。这时得采用显示装配,可以采用java代码或xml显示装配bean。使用java代码,先新建一个配置类JavaConfig,里面都是配置所需的bean,不应该有业务逻辑代码,所以单独建一个类。

@Configuration
@ContextConfiguration(locations = {"classpath:spring/spring-dao.xml","classpath:scan.xml"})
public class bbsConfig{
  private Postdao postdao;
  private Userdao userdao;
  @Bean(name="postservice")
public PostService getPost()
  {
  return new PostserviceImpl(postdao,userdao);
  }
}

在对PostService的bean注入时,同时又依赖了两个bean,postdao和userdao。直接引用beanID就可以,spring会自动地从容器中获取这些bean,只要他们的配置是正确的就行。这个例子中userdao、postdao是Mybatis配置自动扫描将dao接口生成代理注入到spring的,其实也算是xml装配bean。可参考这篇文章,写的挺清楚的。 https://bijian1013.iteye.com/blog/2318860

这里如果再声明一个bean,返回的仍是postserviceImpl对象,和之前的那个bean完全一样,是同一个实例。一般spring@bean如果是同一个beanID,默认返回的是一个单例bean,注入的是同一个实例。如果修改其中一个会都改变的。

不过在这里要注意进行测试时,由于spring的单元测试和springIoc容器是完全独立的,postdao和userdao注入检测时是使用locations加载xml文件,而postservice使用classes加载config类的,但是两个不能同时混用在@ContextConfiguration中。所以非要都测试的话,就分开测试吧。

第三种:在XML中装配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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">
<import resource="spring/spring-dao.xml"/>

<bean id="postservice" class="com.bbs.service.impl.PostserviceImpl">
<constructor-arg ref="postdao"/>
<constructor-arg ref="userdao"/>
</bean>
</beans>

配置postservice的bean时需要引入两个bean,postdao和userdao,放到constructor-arg的标签中,ref指的是依赖的bean的ID。如果是在javaConfig中配置的,就写@Bean的内容。如果是@Component就写@Qualifier的内容。这里是引入的是动态实现的dao接口的bean,是在spring-dao.xml中配置的,引入这个配置文件就可以自动获得beanID。

混合使用三种装配

  1. 在类上可以使用 @import(bbsConfig.class)组合其他java注解
  2. 在类上使用 @importResource("classpath:spring-dao.xml")组合其他xml注解
  3. 在类上可以使用@ContenxtConfiguration包含class或者xml
  4. 在xml中可以用引入xml注解,也可以使用引入java注解
文章目录
  1. 1. 前言
  2. 2. 第一种:spring从两个角度实现自动化装配:组件扫描和自动装配。
  3. 3. 第二种:通过java代码装配bean
  4. 4. 第三种:在XML中装配bean
  5. 5. 混合使用三种装配