Spring IoC与bean

A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application.

bean是由Spring IoC容器实例化、装配和管理的对象,否则,bean只是应用程序中的众多对象之一。

bean及其之间的依赖关系反映在容器使用的配置元数据中。

我们已经了解,Spring IoC容器能够帮我们操作bean,但是前提是我们需要配置元数据以告知Spring容器,它才能够通过读取这些配置,来实例化,装配和管理bean对象。

而配置元数据的方式,就是我们今天要总结的三种,分别是XML,Java注解以及Java代码。我们通过这几种方式,向Spring容器传达这些对象之间丰富的相互依赖关系。

该图是Spring如何工作的高级视图。可以看到,应用程序类与配置元数据相结合,在创建并初始化ApplicationContext之后,就可以获得一个完全配置和可执行的系统或应用程序。

基于XML的显式装配

xml配置的基本结构

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="..." class="...">
  7. <!-- collaborators and configuration for this bean go here -->
  8. </bean>
  9. <bean id="..." class="...">
  10. <!-- collaborators and configuration for this bean go here -->
  11. </bean>
  12. <!-- more bean definitions go here -->
  13. </beans>

id属性表示bean的唯一标识。

class属性定义bean的类型并使用完全限定的类名。

bean实例的三种创建方式

  1. <!-- 一、使用默认构造函数创建,如果没有该默认构造函数,则创建失败。 -->
  2. <bean id="userService" class="com.smday.service.impl.UserServiceImpl"></bean>
  3. <!-- 二、使用普通公章中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器 -->
  4. <bean id="instanceFactory" class="com.smday.factory.InstanceFactory"></bean>
  5. <bean id="userService" factory-bean="instanceFactory" factory-method="getUserService"></bean>
  6. <!-- 三、使用工厂中的静态方法创建对象 -->
  7. <bean id="userService" class="com.smday.factory.StaticFactory" factory-method="getUserService"></bean>

依赖注入的两种方式

构造器注入方式

<bean>标签的内部定义<constructor-arg>标签。

  1. public class ExampleBean {
  2. private AnotherBean beanOne;
  3. private YetAnotherBean beanTwo;
  4. private int i;
  5. public ExampleBean(
  6. AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
  7. this.beanOne = anotherBean;
  8. this.beanTwo = yetAnotherBean;
  9. this.i = i;
  10. }
  11. }
  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <!-- constructor injection using the nested ref element -->
  3. <constructor-arg>
  4. <ref bean="anotherExampleBean"/>
  5. </constructor-arg>
  6. <!-- constructor injection using the neater ref attribute -->
  7. <constructor-arg ref="yetAnotherBean"/>
  8. <constructor-arg type="int" value="1"/>
  9. </bean>
  10. <bean id="anotherExampleBean" class="examples.AnotherBean"/>
  11. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

value:用于提供基本类型和String类型的数据。

ref:用于提供其他的bean类型数据,在spring的ioc核心容器中出现过的bean对象。

在创建对象时,如果没有提供构造器中的这些参数,将无法创建该对象。

setter方法注入方式

<bean>标签的内部定义<property>标签。

  1. public class ExampleBean {
  2. private AnotherBean beanOne;
  3. private YetAnotherBean beanTwo;
  4. private int i;
  5. public void setBeanOne(AnotherBean beanOne) {
  6. this.beanOne = beanOne;
  7. }
  8. public void setBeanTwo(YetAnotherBean beanTwo) {
  9. this.beanTwo = beanTwo;
  10. }
  11. public void setIntegerProperty(int i) {
  12. this.i = i;
  13. }
  14. }
  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <!-- setter injection using the nested ref element -->
  3. <property name="beanOne">
  4. <ref bean="anotherExampleBean"/>
  5. </property>
  6. <!-- setter injection using the neater ref attribute -->
  7. <property name="beanTwo" ref="yetAnotherBean"/>
  8. <property name="integerProperty" value="1"/>
  9. </bean>
  10. <bean id="anotherExampleBean" class="examples.AnotherBean"/>
  11. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

name:指定注入时调用的set方法的属性名称。

value:提供基本类型和String类型的数据。

ref:提供其他的bean类型数据,在spring的ioc核心容器中出现过的bean对象。

如果某个成员必须有值,但并没有提供相应的setter方法,将会出错。

【集合类型的注入】:分为list和map两类结构

  1. <bean id="userService" class="com.smday.service.impl.UserServiceImpl">
  2. <property name="myStrs">
  3. <array>
  4. <value>AAA</value>
  5. <value>BBB</value>
  6. <value>BBB</value>
  7. </array>
  8. </property>
  9. <property name="myList">
  10. <list>
  11. <value>AAA</value>
  12. <value>BBB</value>
  13. <value>BBB</value>
  14. </list>
  15. </property>
  16. <property name="mySet">
  17. <set>
  18. <value>AAA</value>
  19. <value>BBB</value>
  20. <value>BBB</value>
  21. </set>
  22. </property>
  23. <property name="myMap">
  24. <map>
  25. <entry key="testA" value="AAA"></entry>
  26. <entry key="testB" >
  27. <value>BBB</value>
  28. </entry>
  29. </map>
  30. </property>
  31. <property name="myProp">
  32. <props>
  33. <prop key="testC">CCC</prop>
  34. <prop key="testD">DDD</prop>
  35. </props>
  36. </property>
  37. </bean>

list结构可以使用list、array和set标签。

map结构可以使用map和props标签。

利用命名空间简化xml

一、p-namespace使用bean元素的属性来提供属性值和协作bean,而不是使用嵌套的<property/>元素,下面两段bean的配置效果相同。

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:p="http://www.springframework.org/schema/p"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!-- 传统的xml声明 -->
  7. <bean name="classic" class="com.example.ExampleBean">
  8. <property name="email" value="foo@bar.com"/>
  9. </bean>
  10. <!-- p-namespace 声明 -->
  11. <bean name="p-namespace" class="com.example.ExampleBean"
  12. p:email="foo@bar.com"/>
  13. </beans>

二、Spring 3.1中新引入的c-namespace允许使用内联属性来配置构造函数参数,而不是使用嵌套的<constructor-arg>

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:c="http://www.springframework.org/schema/c"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="bar" class="x.y.Bar"/>
  7. <bean id="baz" class="x.y.Baz"/>
  8. <!-- 传统的xml声明 -->
  9. <bean id="foo" class="x.y.Foo">
  10. <constructor-arg ref="bar"/>
  11. <constructor-arg ref="baz"/>
  12. <constructor-arg value="foo@bar.com"/>
  13. </bean>
  14. <!-- c-namespace 声明 -->
  15. <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>
  16. </beans>

基于Java的显式装配

@Bean 和 @Configuration

这两个注解类是Spring's new java-configuration的核心构件。

@Bean注解用于指示方法实例化、配置和初始化要由Spring IoC容器管理的新对象,@Bean注解的作用与<bean/>标签相同。简单的理解就是这个注解可以告知spring,这个方法上面未来希望注册一个应用上下文的bean对象,因此用@Bean注解的方法需要利用Java代码,定义返回一个bean实例的逻辑。

@Configuration注解一个类表明这个类的主要目的是作为bean定义的源,@Configuration类允许通过简单地调用同一类中的其他@Bean方法来定义bean之间的依赖关系。简单的理解就是一个配置类,自此之后,你可以在该配置类中完成在xml中完成的事,但形式会有所不同。

下面这个例子是一个最简单的配置类的定义:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public MyService myService() {
  5. return new MyServiceImpl();
  6. }
  7. }

它的作用和下面这段xml配置的方式等价:

  1. <beans>
  2. <bean id="myService" class="com.acme.services.MyServiceImpl"/>
  3. </beans>

Bean的依赖

一个@Bean注释的方法可以有任意数量的参数来描述构建该bean所需的依赖关系。例如,如果我们的TransferService需要一个AccountRepository,我们可以通过一个方法参数来实现这个依赖:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public TransferService transferService(AccountRepository accountRepository) {
  5. return new TransferServiceImpl(accountRepository);
  6. }
  7. }

当spring调用transferService方法创建bean时,会自动装配accountRepository到配置方法中,再次印证了那句话,带有@Bean注解的方法可以编写任何必要的Java代码来产生Bean的实例,例如构造器,setter方法,以及任何可以产生实例的方法。

初始化Spring容器

AnnotationConfigApplicationContext是Spring 3.0中新增的。它不仅可以接受@Configuration配置类作为输入,还可以接受普通的@Component类和使用JSR-330元数据注释的类。

初始化spring容器,获取Myservice对象,调用对象的方法。

  1. public static void main(String[] args) {
  2. ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  3. MyService myService = ctx.getBean(MyService.class);
  4. myService.doStuff();
  5. }

定制bean的命名

默认情况下,配置类将会使用@Bean注解的方法的名称作为bean的名称,这一点可以通过name属性修改。

  1. @Configuration
  2. public class AppConfig {
  3. @Bean(name = "myFoo")
  4. public Foo foo() {
  5. return new Foo();
  6. }
  7. }

如上:如果没有指定name属性,该bean的名称为foo,如果指定了name属性,这里的名称就是myFoo。

基于注解的自动装配

Spring从以下两个角度实现自动装配:

  • 组件扫描:Spring自动发现应用上下文中所创建的bean。
  • 自动装配:Spring自动满足bean之间的依赖。

首先还是来看一段简单的例子:

  1. //定义一个UserService接口
  2. public interface UserService {
  3. void add();
  4. }
  1. //定义实现类,注意加上@Component注解,告知spring创建这个bean
  2. @Component
  3. public class NormalUserServiceImpl implements UserService {
  4. @Override
  5. public void add() {
  6. System.out.println("添加用户");
  7. }
  8. }
  1. //controller层,注意@Autowired注解,自动按类型注入Userservice
  2. @Component
  3. public class UserController {
  4. @Autowired
  5. private UserService userservice;
  6. public void add(){
  7. userservice.add();
  8. }
  9. }
  1. //定义配置类,注意@ComponentScan("com.my.demo")注解开启组件扫描
  2. @Configuration
  3. @ComponentScan("com.my.demo")
  4. public class Appconfig {
  5. }
  1. //整合junit测试类进行测试
  2. @RunWith(SpringJUnit4ClassRunner.class)
  3. @ContextConfiguration(classes = Appconfig.class)
  4. public class UserServiceTest {
  5. @Autowired
  6. private UserService userservice;
  7. @Test
  8. public void testMethod(){
  9. userservice.add();
  10. }
  11. }

以上就是一套可以正常运行的简单案例,当然其中有不少可能出现的问题或者是其他可以实现相同功能的方案,我们都暂且不提。其中出现了许多自动化装配bean的注解,我们一一来看:

自动装配的常用注解

【@Component】

  • 作用:将当前类对象存入spring容器中。
  • 属性:value,用于指定bean的id,不指定value时,默认值为当前类名首字母小写。

值得一提的是,在三层架构中,Spring框架提供了明确的三层注释,作用与@Component相同,但语义更加清晰明了,分别是:

Controller:表现层、Service:业务层、Respository:持久层

【@Autowired】

  • 作用:自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
  • 如果ioc容器中没有任何bean类型和要注入的变量类型匹配,则报错(解决方法是,设置required属性的值为false,如果没找到对应类型的bean,则会出于未装配状态),如果ioc容器中有多个类型匹配时,出现歧义性,也会报错。
  • 出现位置:既可以是构造器,也可以是setter方法,甚至任何其他的方法,Spring都会尝试满足方法参数上声明的依赖。
  • 细节:在使用注解注入时,set方法就不是必须的了。

当出现歧义性时,满足类型要求的bean不是唯一时,可以考虑使用@Qualifier和@Resource注解,参考:Spring解决自动装配歧义性的几种方案

【@Configuration】

  • 作用:指定当前类是一个配置类
  • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。

【@ComponentScan】

  • 作用:开启组件扫描,用于通过注解指定spring在创建容器时要扫描的包。
  • 属性:value,和basePackages的作用相同,指定创建容器时要扫描的包。
  • 如果不指定value或者basePackages的值,将会默认扫描与配置类相同的包

设置spring组件扫描的基础包的几种方案:

  • @ComponentScan("com.my.demo")

  • @ComponentScan(basePackages = {"com.my.demo.web","com.my.demo.service"})

  • @ComponentScan(basePackageClasses = {UserController.class, UserService.class, UserDao.class}),相较于第二种,较为安全。

需要注意的是,组件扫描默认是不开启的,我们需要通过该注解显式通知Spring,告诉它去寻找带有@Component注解的类,去创建该类的bean对象。

开启组件扫描的xml方式:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
  6. <!--配置扫描,相当于@ComponentScan("com.my.demo")-->
  7. <context:component-scan base-package="com.my.demo"/>
  8. </beans>

既然使用xml方式开启组件扫描,那么测试的时候需要谨慎,要读取该xml文件:@ContextConfiguration("classpath:applicationContext.xml")

导入和混合配置

直接以例子呈现:

  1. #jdbcConfig.properties
  2. jdbc.driver=com.mysql.jdbc.Driver
  3. jdbc.url=jdbc:mysql://localhost:3306/spring
  4. jdbc.username=root
  5. jdbc.password=123456
  1. /**
  2. * @author Summerday
  3. * <p>
  4. * 和spring连接数据库相关的配置类
  5. */
  6. public class JdbcConfig {
  7. @Value("${jdbc.driver}")
  8. private String driver;
  9. @Value("${jdbc.url}")
  10. private String url;
  11. @Value("${jdbc.username}")
  12. private String username;
  13. @Value("${jdbc.password}")
  14. private String password;
  15. /**
  16. * 创建queryRunner对象
  17. *
  18. * @param dataSource
  19. * @return
  20. */
  21. @Bean(name = "runner")
  22. @Scope("prototype")
  23. public QueryRunner createQueryRunner(DataSource dataSource) {
  24. return new QueryRunner(dataSource);
  25. }
  26. /**
  27. * 创建数据源对象
  28. *
  29. * @return
  30. */
  31. @Bean(name = "dataSource")
  32. public DataSource createDataSource() {
  33. try {
  34. ComboPooledDataSource ds = new ComboPooledDataSource();
  35. ds.setDriverClass(driver);
  36. ds.setJdbcUrl(url);
  37. ds.setUser(username);
  38. ds.setPassword(password);
  39. return ds;
  40. } catch (Exception e) {
  41. throw new RuntimeException(e);
  42. }
  43. }
  44. }

【@Value】

  • 作用:用于基本类型和string类型的数据。
  • 属性:value,指定数据的值,可以使用spring中的SpEL,spring的el表达式。
  • SpEL的写法:${表达式}。
  1. /**
  2. * 主配置类
  3. */
  4. @Configuration
  5. @ComponentScan(basePackages = "com.smday")
  6. @Import(JdbcConfig.class)
  7. @PropertySource("classpath:JdbcConfig.properties")
  8. public class SpringConfiguration {
  9. }

【@Import】

  • 作用:用于导入其他的配置类。
  • 属性:value,指定其他配置类的字节码,使用Import注解后,有该注解的类为父配置类,导入的都是子配置类。

【@PropertySource】

  • 作用:作用于指定properties文件的位置。
  • 属性:value,指定文件的名称和路径,关键字classpath表示类路径下。

最后的最后,引用Spring in Action中作者的话:自动化配置、基于Java的显式配置以及基于xml的显式配置都描述了Spring应用中组件以及这些组件之间的关系。作者建议尽可能使用自动化的配置,其次如果需要显式配置,希望优先选择基于Java的配置,类型安全且易懂。

Spring装配Bean的三种方式+导入和混合配置的更多相关文章

  1. spring装配bean的三种方式及其混合装配

    在spring容器中装配bean有三种基本方式和混合装配方式: 隐式的bean自动发现机制和自动装配 在java中进行显式配置 在xml中配置 混合装配(在多个java文件中配置.在JavaConfi ...

  2. spring 装配bean的三种方式

    这段时间在学习Spring,依赖注入DI和面向切面编程AOP是Spring框架最核心的部分.这次主要是总结依赖注入的bean的装配方式. 什么是依赖注入呢?也可以称为控制反转,简单的来说,一般完成稍微 ...

  3. spring创建bean的三种方式

    spring创建bean的三种方式: 1通过构造方法创建bean(最常用) 1.1 spring默认会通过无参构造方法来创建bean,如果xml文件是这样配置,则实体类中必须要有无参构造方法,无参构造 ...

  4. Spring容器装配Bean的三种方式

    欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...

  5. Spring实战(三)Spring中装配Bean的三种方式---XML、JavaConfig、AutoWire

    创建应用对象之间协作关系的行为称为装配(wiring),这也是依赖注入的本质. Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,而开发者需要告诉Spring需要创建哪些 ...

  6. 装配Bean的三种方式

    一.装配Bean就是在xml写一个Bean标签:装配完Bean,还需要读取xml配置文件创建Spring容器来创建对象: 1.new 实现类方式 正常的三种创建Bean容器的方法都可以根据装配的Bea ...

  7. Spring 实例化bean的三种方式

    第一种方法:直接配置Bean <bena id="所需要实例化的一个实例名称" class="包名.类名"/> 例如: 配置文件中的bean.XML ...

  8. Spring实例化Bean的三种方式及Bean的类型

    1.使用类构造器实例化  [默认的类构造器] <bean id=“orderService" class="cn.itcast.OrderServiceBean"/ ...

  9. spring实例化bean的三种方式

    公共使用的实体

随机推荐

  1. Tomcat 之startup.bat启动失败案例

    今天我在部署一个Tomcat环境时,各种变量都配置完了,最后启动Tomcat时,Tomcat一闪而过,当时我的内心是崩溃的~~ 然后我就开始百度.定位问题.进入cmd命令行窗口,cd进入到Tomcat ...

  2. JavaScript的数组系列

    数组 今天逆战班的学习主题关于Javascript的数组,主要有数组的概念.创建.分类.方法.遍历.经典算法...... 一.数组是什么呢?怎么写数组呢?数组有多少种呢? 数组的概念 对象是属性的无序 ...

  3. angular的开始历程

    开始写angular了,抑制不住的开心,比react差点开心,vue开始太虐 喜欢一个人要不要表个白?其实也没啥资格喜欢~!!考虑一段时间吧 9.29表白了,嗯,被拒绝的干脆利落 为他写了一首小诗歌, ...

  4. Markdown怎么使用制表符TAB键?为什么TAB失灵了?

    目录 问题描述 解决办法 问题描述  我们写文章(Markdown文章)的时候,经常想使用自然段标记划分段落,可是我们会发现,不管是任何编辑器,tab键都没有用,怎么办? 解决办法 语法:   文章- ...

  5. 2016 Multi-University Training Contest 4 T9

    http://acm.hdu.edu.cn/showproblem.php?pid=5772 最大权闭合子图. 得到价值w[i][j]的条件是选了i,j这两个位置的字符.选择位置的i字符花费为 第一次 ...

  6. 【Elasticsearch】查询并删除匹配文档之_delete_by_query

    思路:先查询确认,后精准删除 假设我想删除title是"小明今晚真的不加班"这条记录,先查看一下现有的记录: (不加班不好吗?为什么要删除呢?) tips:可以使用match_ph ...

  7. Spring Boot入门系列(八)整合定时任务Task,一秒搞定定时任务

    前面介绍了Spring Boot 中的整合Redis缓存已经如何实现数据缓存功能.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/categ ...

  8. 使用new Image()可以针对单单请求,不要返回数据的情况

    使用new Image()可以针对单单请求,不要返回数据的情况,比如我这里写了一个Demo,请求百度的Logo一个示例: <html> <head> </head> ...

  9. 6个出色的Kubernetes发行版,哪款最适合你?

    作者简介 Christopher Tozzi,自2008年来以自由职业者的身份对Linux.虚拟化.容器.数据存储及其相关主题进行报道. 本文来自Rancher Labs 时至今日,通过Kuberne ...

  10. MySQL笔记(4)-- 索引优化

    索引失效情况: 最佳左前缀法则:如果索引了多列,要遵循最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列:[覆盖索引有a,b,c,条件中使用了b或bc都导致该索引失效:如果条件使用了ac ...