Spring Bean 的装配方式

装配 Bean 的三种方式

一个程序中,许多功能模块都是由多个为了实现相同业务而相互协作的组件构成的。而代码之间的相互联系又势必会带来耦合。耦合是个具有两面性的概念,高度的耦合会导致代码难以复用,难以测试,难以理解;但同时耦合又是必须的,不耦合的代码什么也做不了。

Spring 中,容器负责了把需要相互协作的对象引用赋予各个对象,对象无需自己查找或创建与其相关联的对象。而创建应用对象之间的协作称之为装配(wiring),也就是依赖注入(DI)的本质。

Spring 中装配 Bean 有以下三种常见的方式:

  • 在 XML 中进行显示配置;
  • 在 Java 代码中显示配置;
  • 隐式的 Bean 发现机制和自动装配;

自动化装配 Bean

三种装配方式中,最常用最高效的就是自动化装配了,Spring 从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的 Bean。
  • 自动装配(autowiring):Spring 自动满足 Bean 之间的依赖。

以我们平时代码中的三层架构举例,首先创建一个 Dao 层的接口类和实现类

接口类

@Repository
public interface UserDao { /**
* 根据 Username 和 password 查找 User 对象
* @param usernmae 账号名称
* @param password 账户密码
* @return User
*/
public User getUser(String Username, String password); }

实现类

public class UserDaoImpl implements UserDao {

    public User getUser(String email, String password) {

        // do something...

        return user;
} }

然后当我们在 Service 层需要调用到上面 Dao 层的实例化对象时,我们只需要简单声明一个对应的类,并上标注解即可

public class UserServiceImpl implements UserService {

    @Autowired
UserDao userDao; @Override
public User login(String email, String password) {
return userDao.getUser(email, password);
} }

之后,你还需要在配置文件中启动组件扫描。在 spring-context.xml 文件中(或者这个文件在你项目中叫其他名字,无所谓)配置 <context:component-scen> 元素。

<context:component-scan base-package="yourpackage" />

当然,这里你也不一定只能通过 xml 文件形式来配置,也可以通过基于 Java 的配置。

@Configuration
@CompomentScan(basePackages = "yourpackage")
public class BeanConfig {}

现在这段代码应该就能发挥它的实际用途了。

回顾刚刚的代码,可以发现有 @Repository@Autowired 这两个注解。当你声明一个 Bean 的时候,你需要在类上使用相应的注解来让 Spring 的上下文找到这个类,类似于 Repository, Spring 中还提供了 3 个功能基本等效的注解:

  • @Repository:用于对 DAO 的实现类进行注解;

  • @Service:用于对 Service 的实现类进行注解;

  • @Controller:用于对 Controller 的实现类进行注解;

  • @Component:用于注解一些工具类或其他不归与上述类别的组件。

而当你需要引入一个 Bean 时,@AutoWired 就可以将 Bean 注入到代码中。

不过 @AutoWired 注解不仅能够用在构造器上,还能用在属性的 Setter 方法上,如下面代码所示

@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

不过实际上 Setter 方法没有任何特殊之处,@Autowried 注解可以用在类的任何方法上。

但是不管是构造器,Setter 方法还是其他的方法,Spring 都会尝试满足方法参数上所声明的依赖。假如有且只有一个 Bean 匹配依赖需求的话,那么这个 Bean 将会被装配进来。

如果没有匹配的 Bean,那么在应用上下文创建的时候,Spring 就会抛出异常,为了避免异常的出现,你可以将 @Autowiredrequired 属性设置为 false

@Autowired(required = false)
UserDao userDao;

required 属性设置为 false 时, Spring 依旧会尝试进行自动装配,但是如果没有匹配到 Bean 的话,Spring 会将这个处于未装配状态。不过我们在实际开发中,毕竟我们不大可能引入一个后面代码没有引用到的对象,而这可能让你的程序报空指针异常。

@Auowried 注解是通过 byType 方式注入的,如果环境中不止匹配到两个 Bean 时,或许你该给你的 Bean 进行命名,然后在 @Qualifier 的属性中指向该 Bean 的名称。

@Component("beanName")
public class Bean {
// write something
}
@Autowried
@Qualifier("beanName")
Bean bean;

通过 Java 代码配置 Bean

通过 Java 代码配置 Bean 的方式在理解起来要简单明了许多,而且这种方式也有其特定的应用场景,比如说你要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加 @Component 或者其他注解的,因此就不能使用自动化装配的方案了。所以在这种情况下,显示装配就成了新的选择,下面我们先来了解下如何通过 Java 代码配置 Bean。

创建配置类

@Configuration
public class BeanConfig {
}

@Configuration 注解表明这个类是一个配置类,之后我们将在该类中配置 Spring 应用上下文中如何创建类的细节。

声明 Bean

然后在配置类中声明要配置 Bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加 @Bean 注解,同时也可以通过 name 属性指定一个不同的名字,当然不写也没问题。例如下面代码

@Bean(name = "beanName")
public Bean bean {
return new Bean();
}

假如你需要另外一个 Bean 作为你创建这个 Bean 的参数的话,你也可以通过下面的方式进行配置

public Bean bean(AnotherBean anotherBean) {
return new Bean(anotherBean);
}

之后的事情就交给 Spring 去完成吧,你依旧可以像前面提及的方式去进行依赖注入。

通过 XML 装配 Bean

在我们项目开发中,我们可以发现,通过注解和 Java 配置文件装配 Bean,最大的好处就是配置方便,直观等等,但是其弊端也显而易见,以硬编码的方式写入到 Java 代码中,当修改其中的代码时,是需要重新编译的。

XML 相对起前面两种方式,也有其相应的利弊。最大的好处莫过于对其做修改,无需编译代码,只需要重启程序即可加载新的配置。下面就来介绍下通过 XML 配置装配 Bean。

创建 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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- congifuration here ... --> </beans>

声明 <bean>

<bean id="beanName" class="package.Bean">

这里声明了一个很简单的 Bean,创建这个 Bean 的类通过 class 属性来指定的,并且要使用权限定的类名。同时也要给 bean 声明一个 ID,当然你在这里不声明也没什么问题,bean 本身会自动得到一个 ID 名 package.Bean#0。不过你之后也可能会引用到这个 Bean,所以就加个 ID 咯。

到这里,其实 XML 的配置已经完成了,你可以在你的代码中进行依赖注入了。但是我们的对象初始化的时候,我们可能还要注入一些属性。在 Spring 中,当发现到 <bean> 元素时,会调用这个元素中指向的类的默认构造函数来创建 bean。在 XML 的配置中,没法像 Java Config 那样灵活地配置。接下来就介绍如何在初始化 Bean 时注入属性

借助构造器注入初始化 Bean

当你采用构造器注入时,有两种基本的配置方案可供选择:

  • <constructor-arg>元素
  • 使用 c- 命名空间(只适用 Spring 3.0 后的版本)

下面举例采用 <constructor-arg> 元素:

<bean id="beanName" class="package.Bean">
<constructor-arg ref="beanName2" />
</bean>

这样,当 Spring 遇到这个 元素时,他会创建一个 Bean 实例。<constructor-arg> 元素会告知 Spring 将一个 ID 为 beanName2 的 bean 引用传递到 Bean 的构造器中。

装配字面量以及集合

我们也可以将字面量注入构造器中,我们先创建一个 Bean3 类

public class Bean3 {
String str1;
String str2; public Bean3(String str1, String str2) {
this.str1 = str1;
this.str2 = str2;
} public void log() {
sout(str1 + " " + str2);
}
}

然后再在 xml 文件中进行以下配置

<bean id="beanName3" class="package.Bean3">
<constructor-arg value="hello" />
<constructor-arg value="world" />
</bean>

接下来介绍下如何装配集合,其实只需要简单在 <constructor-arg> 中声明集合即可,如下所示:

<constructor-arg>
<list>
<value>str1</value>
<value>str2</value>
</list>
</constructor-arg>
-----------------------------------------------
<constructor-arg>
<list>
<ref bean="beanName" />
<ref bean="beanName" />
<ref bean="beanName" />
</list>
</constructor-arg>

设置属性值

假设我们现在有这么一个类,没有任何的构造器(除了隐式的构造器),它没有任何的强依赖。

public void TestBean {

    private String str1;
private List<String> strList; // set()... public void log() {
sout(str1);
fore strList {
sout(strList + " ");
}
} }

就如上面类所示,它并不强制我们装配任何的属性,但是当你不设置属性属性去调用 log() 方法的时候,毫无疑问会报空指针异常。所以,我们要设置这些属性,可以借助 property 元素的 value 属性实现该功能

<bean id="testBean" class="package.TestBean">
<property name="str1" value="hello" />
<property name="strList">
<list>
<value>world</value>
<value>and</value>
<value>spring</value>
</list>
</property>
</bean>

这样就可以实现将量注入 bean 中了。

小结

Spring 有多种方案来配置 Bean,这给我们提供了许多解决问题的思路,Spring 的配置也是可以相互搭配的,就像我们热衷与使用自动装配,但是有时 xml 装配或者 Java Config 装配才是最优的选择,这取决我们工作中遇见的情况。

顺带一提,若注解与 XML 同时使用,XML 的优先级要高于注解。这样做的好处是,需要对某个 Bean 做修改,只需要修改配置文件即可。

Spring Bean 的装配方式的更多相关文章

  1. Spring 中Bean的装配方式

    最近又买了一本介绍SSM框架的书,是由黑马程序员编写的,书上讲的很好理解,边看边总结一下.主要总结一下bean的装配方式. Bean的装配可以理解为依赖系统注入,Bean的装配方式即Bean依赖注入的 ...

  2. 【Spring】Spring中的Bean - 5、Bean的装配方式(XML、注解(Annotation)、自动装配)

    Bean的装配方式 简单记录-Java EE企业级应用开发教程(Spring+Spring MVC+MyBatis)-Spring中的Bean 文章目录 Bean的装配方式 基于XML的装配 基于注解 ...

  3. Bean的装配方式

    (一) 知识点:Spring容器支持多种形式的Bean的装配方式,比如基于XML的装配,基于注解的装配和自动装配(最常用的就是基于注解的装配) Spring提供了两种基于xml的装配方式:设值注入(S ...

  4. bean的装配方式(注入方式,构造注入,setter属性注入)

    bean的装配方式有两种,构造注入和setter属性注入. public class User { private String username; private String password; ...

  5. Spring -bean的装配和注解的使用

    一,bean的装配 bean是依赖注入的,通过spring容器取对象的. 装配方法有: 前面两种没什么好讲的,就改改参数就好了. 这里重要讲注解. 注解的主要类型见图,其中component是bean ...

  6. Spring———bean的创建方式,注入方式,复杂类型注入 概括

    Spring相关概念和类    1.IOC             inverse of control    控制反转   反转了创建对象的方式            以前:new 对象,管理和维护 ...

  7. Spring Bean的装配

    Bean 的装配,即Bean对象的创建.容器根据代码要求创建Bean对象后再传递给代码的过程,称为Bean的装配. 一.默认分的装配方式 默认的装配的方式调用Bean类的构造方法 二.动态工厂Bean ...

  8. Spring bean实例化的方式

    实例化过程如图,方式如图. 甩代码. 方式一:构造方法 搞一个bean,修改一下xml配置 package com.itheima.instance.constructor; public class ...

  9. spring——bean自动装配

    注意:自动装配功能和手动装配要是同时使用,那么自动装配就不起作用. beans.xml <?xml version="1.0" encoding="UTF-8&qu ...

随机推荐

  1. 零元学Expression Blend 4 - Chapter 32 简单轻松的学会如何使用Visual States(上)

    原文:零元学Expression Blend 4 - Chapter 32 简单轻松的学会如何使用Visual States(上) Visual State Manager中文翻译为视觉状态管理器,这 ...

  2. shell条件测试结构

    条件测试结构 if/then结构用来判断命令列表的退出状态码是否为0(因为在UNIX惯例, 0表示"成功"), 如果成功的话, 那么就执行接下来的一个或多个命令. 有一个专有命令[ ...

  3. How to setup Assigned Access in Windows 10 (Kiosk Mode) 设置分配的访问权限(Kiosk模式)

    Let’s say you’re building some sort of ingenious mechanical contraption to be displayed in public th ...

  4. WPF 设置只能运行一个实例

    codereview上的帖子 https://codereview.stackexchange.com/questions/20871/single-instance-wpf-application ...

  5. clipboard.js在剪切中的使用

    目前只是对文字的复制. 下面的代码解释: 1.html 需要添加data-clipboard="copy"和data-clipboard-target="对象选择器&qu ...

  6. .NET重思(三)-数组列表与数组的区别,栈集合和队列结合的区别

    数组列表和数组十分相似,区别在于数组列表的容量是可以动态变化的,而数组的容量是固定的.数组即Array类,数组列表即ArrayList类,两者十分相似.不过,Array类在System命名空间下,Ar ...

  7. delphi 实现微信开发(1) (使用kbmmw web server)

    原文地址:delphi 实现微信开发(1)作者:红鱼儿 大体思路: 1.用户向服务号发消息,(这里可以是个菜单项,也可以是一个关键词,如:注册会员.) 2.kbmmw web server收到消息,生 ...

  8. isHiden和isVisible的区别(可是有nativeEvent进行设置)

    之前一直对isHiden和isVisible的区别比较模糊,都是乱用的.今天因需要仔细看了一下. 1.isHiden只是返回部件的隐藏属性,并不能表示部件当前的真实状态.比如A部件有个子部件B,而A处 ...

  9. Qt 5.8 for Device Creation(好多内容,包括虚拟机安装,静态编译)

    http://doc.qt.io/QtEnterpriseEmbedded/qt-configuration-tool.html http://doc.qt.io/QtEnterpriseEmbedd ...

  10. Linux运维工程师成长路线及应实现的目标

    作为一名运维工程师,需要学习的东西非常多,在学习的过程中也没有任何捷径可言,必须一步一个脚印地学习.积累才能把个人技能提升到相应的高度.根据目前流行的发行版及国际流行的Linux认证,红帽认证和LPI ...