这段时间在学习 Spring, 依赖注入 DI 和面向切面编程 AOP 是 Spring 框架最核心的部分. 这次主要是总结依赖注入的 bean 的装配方式.
什么是依赖注入呢? 也可以称为控制反转, 简单的来说, 一般完成稍微复杂的业务逻辑, 可能需要多个类, 会出现有些类要引用其他类的实例, 也可以称为依赖其他类. 传统的方法就是直接引用那个类对象作为自己的一个属性, 但如果我们每次创建这个类的对象时, 都会创建依赖的类的对象, 还有如果那个类将来可能不用了, 还需要到这个类去删除这个对象, 那破坏了代码的复用性和导致高度耦合!
依赖注入的出现可以很好地解决这个问题, 依赖注入就是由系统负责协调类的依赖对象的创建, 我们无需自己去显示的创建依赖对象, 而是由系统给我们注入这个对象, 系统控制了这个对象的创建, 也称为控制反转.
Spring 给我们注入对象有三种方式:
隐式的 bean 扫描发现机制和自动装配
在 java 中进行显示配置
在 xml 中进行显示配置
第一种:
spring 从两个角度实现自动化装配: 组件扫描和自动装配.
当对一个类标注 @Component 注解时, 表明该类会作为组件类, spring 将为这个类创建 bean. 当在应用文中引用这个 bean,spring 会自动扫描事先指定的包查找这个 bean. 但 spring 默认是不启用组件扫描的, 可以在 xml 中配置加上 < context:component-scan base-package="xx"/>. 还有一种方法: 在新建一个配置类, 类中可以什么不用写, 在配置类上加上 @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 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. 在类上使用 @ imortResource("classpath:spring-dao.xml") 组合其他 xml 注解
3. 在类上可以使用 @ContenxtConfiguration 包含 class 或者 xml
4. 在 xml 中可以用 < import resource="spring-dao.xml"> 引入 xml 注解, 也可以使用 < bean class="com.bbs.dao.Userdao"> 引入 java 注解
来源: https://www.cnblogs.com/maxshare/p/10467761.html