SpringBoot系列: 理解 Spring 的依赖注入(一)
==============================
Spring 的依赖注入
==============================
对于 Spring 程序, Spring 框架为我们提供一个 IoC 容器, 该容器负责创建对象和维护对象之间的依赖关系.
对于普通程序, 我们是通过对象本身来创建和解决自己的依赖问题.
ApplicationContext 即是 Spring 程序的 IoC 容器, 该容器负责创建 Bean, 并将功能类 Bean 注入到你需要的 Bean 中. 那么, Spring 是如何知道我们有哪些 Bean 类, 以及这些类的依赖关系是什么? 有三种配置方式告知 Spring 程序, 分别是 Xml 配置方式/注解配置方式/Java 配置方式.
ApplicationContext 是 Spring 程序的核心, 不管是 Spring 程序, 还是 Spring MVC 程序, 还是SpringBoot 程序, 其 main() 函数最主要的代码就是初始化了 ApplicationContext 容器, Spring 框架为我们提供了多种容器实现, 可以针对不同的应用场景选择.
1. AnnotationConfigApplicationContext: 该容器读取从一个或多个基于 java 的配置类, 适用于 Java 配置方式;
2. AnnotationConfigWebApplicationContext: 专门为 web 应用准备的, 适用于注解方式;
3. XmlWebApplicationContext: 该容器读取一个或多个 Xml 配置文件,使用于 Xml 配置方式;
4. ClassPathXmlApplicationContext, 该容器从 classpath 路径下读取 Xml 配置文件,使用于 Xml 配置方式.
//ApplicationContext context = new ClassPathXmlApplicationContext("resouces/applicationContext.xml");
//ApplicationContext context = new AnnotationConfigApplicationContext(ManConfig.class);
==============================
三种 Bean 配置方式
==============================
1. Xml 配置方式: 老的程序中经常见到, 比如将 Spring bean 声明放到 applicationContext.xml 中.
2. 注解配置方式: 在类定义时通过@Service, @Controller, @Repository, @Component 声明为 Spring Bean.
@Service, 用于业务服务层
@Controller, 用于展现层
@Repository, 用于 DAO 层
@Component, 通用组件, 它是上面 3 个注解的父注解, 没有明确的角色, 对于普通的组件最好使用@Component 注解
3. Java 配置方式: 该方式是通过@Configuration+@Bean 实现的
具体为, 该方式引入了一个 Config 类, 在类中通过方法函数声明 bean 对象, 而 Pojo 类定义不加@Component 之类的注解, Config 类需要加上@Configuration 注解, Config 类中的 bean 方法需要加上@Bean 注解.
@Configuration 等同于 xml 配置中的 <beans> </beans> 标签, 需说明的是@Component 其实也是@Component 的一个子注解,
@Bean 等同于 xml 配置中的 <bean> </bean>标签. @Bean 用来注解一个函数, 该函数应该一个 Bean 对象, Bean 对象的名称是方法名.
最佳实践: 注解配置方式和 Java 配置方式没有孰优孰劣, 理论上是可以相互替换的, 但可以总结一个最佳实践, 全局性的配置使用 Java 配置 (比如数据库相关配置, MVC 相关配置), 业务 Bean 配置使用注解配置, 尽量少用 Xml 配置方式.
==============================
示例程序的 pom.xml
==============================
开发基于 Spring 的命令行程序, 只需要引入 spring-context 即可.
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
===============================
基于注解配置示例
===============================
基于注解的配置, 主要在各个 Pojo Class 定义时使用@Component 来声明该类是一个 Spring 的 Bean, 比如 Car/Boss 类都使用的@Component 注解, 依赖注入可以使用@Autowired 或@Resource 注解, 比如在 Boss 类中, 使用了@Autowired 自动注入了一个 Car 对象.
//Car.java
//将 Car 加上注解 @Component, 表明它是一个 Spring bean
@Component
public class Car {
@Override
public String toString() {
return "Brand: Benz, price:1000";
}
}
//Boss.java
//将 Boss 加上注解 @Component, 表明它是一个 Spring bean
//Boss 类的 field Car 是通过 @Autowired 注入的.
@Component
public class Boss {
@Autowired
private Car car; @Override
public String toString() {
return "Boss has one car:("+car+")";
}
}
//SomeConfig.java
//这里创建 SomeConfig 类目的只有一个, 不用准备 Xml 配置文件.
//在 main() 函数中将使用这个 config 类初始化容器, 并加上@ComponentScan, 告知 IoC 容器需要扫描指定 package 来获取 bean 的定义 (包括各种注解了@Component/@Service/@Controller/@Repository 的类).
@Configuration
@ComponentScan("javaTestMaven.demo2")
public class SomeConfig {
}
//App.java
//入口函数,先初始化容器,然后通过容器获得 boss 对象
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SomeConfig.class);
context.refresh();
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
context.close();
}
}
===============================
基于 Java 配置示例
===============================
基于注解的配置, 主要在各个 Pojo Class 定义时使用@Component 来声明该类是一个 Spring 的 Bean; 但如果使用基于 Java 的配置, Pojo Class 上不需要加上任何 IoC 相关的注解, 而是需要在 Config 类中使用 @Bean 来注解那些需要 Spring 容器管理的对象, @Bean 注解往往加在一个函数体上, 该函数需要返回一个对象.
//Car.java
//Car 类就是一个普通的 Class, 没有加@Component 注解
public class Car {
@Override
public String toString() {
return "Brand: Benz, price:1000";
}
}
//Boss.java
//Boss 类就是一个普通的 Class, 没有加@Component 注解
//Boss 类的 field Car 是通过 @Autowired 注入的.
@Component
public class Boss {
@Autowired
private Car car; @Override
public String toString() {
return "Boss has one car:("+car+")";
}
}
//SomeConfig.java
//在上个示例中 SomeConfig 类目的只有一个, 不用准备 Xml 配置文件.
//这个示例中 SomeConfig 类是一个重点, 不仅加了@Configuration, 而且使用了@Bean 注解定义了多个 Bean 对象
//因为所有的 Bean 都在 SomeConfig 类定义了, 所以 ComponentScan 注解可有可无.
@Configuration
@ComponentScan("javaTestMaven.demo3") //可有可无
public class SomeConfig {
@Bean
public Car car() {
return new Car();
} @Bean
public Boss boss() {
return new Boss();
}
}
//App.java
//入口函数,先初始化容器,然后通过容器获得 boss 对象
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SomeConfig.class);
context.refresh();
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
context.close();
}
}
===============================
多个 Java 配置类的处理方式
===============================
有时候我们会在多个 Config 类中定义不同的 Bean, 如何告诉 Spring IoC 加在这些 Config 类呢? 有两个方法分别是: 1. 将 Config 类分为主从 Config 两个类别, 在主要 Config 导入从属 Config, 在初始化 Spring 容器的时候只要注册主要 Config 类即可. 2. 不区分中从 Config 类, 在初始化 Spring 容器的时候将这些 Config 类都注册进去.
推荐使用主从 Config 处理方式, 下面是两种处理方式的示例代码, 这个代码是修改自"基于 Java 配置示例", 主要改动是将原来 SomeConfig 分为两个类.
-------------------------------
区分主从 Config 的处理方式
-------------------------------
//SomeConfig1.java
//SomeConfig1 是主 Config, 使用@Import 导入从属的 Config
@Configuration
@Import(SomeConfig2.class)
public class SomeConfig1 {
@Bean
Boss boss() {
return new Boss();
}
}
//SomeConfig2.java
@Configuration
public class SomeConfig2 {
@Bean
public Car car() {
return new Car();
}
}
//App.java
//仅仅导入了主 Config 类
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SomeConfig1.class);
context.refresh();
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
context.close();
}
}
-------------------------------
不区分主从 Config 的处理方式
-------------------------------
@Configuration
public class SomeConfig1 {
@Bean
Boss boss() {
return new Boss();
}
}
@Configuration
public class SomeConfig2 {
@Bean
public Car car() {
return new Car();
}
}
//App.java
//导入了两个 Config 类
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SomeConfig1.class);
context.register(SomeConfig2.class);
context.refresh();
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
context.close();
}
}
===============================
Java 配置和注解配置类组合使用
===============================
最佳实践: 注解配置方式和 Java 配置方式没有孰优孰劣, 理论上是可以相互替换的, 但可以总结一个最佳实践, 全局性的配置使用 Java 配置 (比如数据库相关配置, MVC 相关配置), 业务 Bean 配置使用注解配置, 尽量少用 Xml 配置方式.
这本示例中, Boss bean 采用了 Java 配置方式, 而 Car bean 采用注解配置方式.
//Boss.java
//该 Java 没有加任何 IoC 注解
public class Boss {
@Autowired
private Car car; @Override
public String toString() {
return "Boss has one car:("+car+")";
}
}
//Car.java
//该 Java 加上了基于注解的配置说明
@Component
public class Car {
@Override
public String toString() {
return "Brand: Benz, price:1000";
}
}
//SomeConfig.java
//该配置类中仅定义了 boss Bean, 没有直接定义 car Bean, 但实际上 Boss 对象中仍能被成功地注入 Car.
//原因是我们组合使用了基于注解的配置方法, Config 类加上了@ComponentScan 后, IoC 容器能扫描到 Car 类 (因为其被注解为 Component).
@Configuration
@ComponentScan("javaTestMaven.demo3")
public class SomeConfig {
@Bean
Boss boss() {
return new Boss();
}
}
//App.java
//将 Config 类注册一下
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SomeConfig.class);
context.refresh();
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
context.close();
}
}
===============================
SpringBoot 程序的 @EnableAutoConfiguration
===============================
前面的示例都是基于经典的 Spring 框架, 在程序启动时需要我们将 Config 类主动注册容器上. 而对于 SpringBoot 程序, 因为已经加了 @EnableAutoConfiguration, 所以可以省去注册 Config 类的过程.
下面示例和上一个示例代码只有两处不同, 即 pom.xml 和 App.java 程序.
pom.xml, 引入 spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
//App.java
//基于 SpringBoot 的命令行程序
//无需注册 Config 类
@SpringBootApplication //same as @Configuration @EnableAutoConfiguration @ComponentScan
public class App implements CommandLineRunner {
@Autowired
private ApplicationContext context; public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication(App.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
} // Put your logic here.
@Override
public void run(String... args) throws Exception {
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
}
}
//Boss.java
//该 Java 没有加任何 IoC 注解
public class Boss {
@Autowired
private Car car; @Override
public String toString() {
return "Boss has one car:("+car+")";
}
}
//Car.java
//该 Java 加上了基于注解的配置说明
@Component
public class Car {
@Override
public String toString() {
return "Brand: Benz, price:1000";
}
}
//SomeConfig.java
//该配置类中仅定义了 boss Bean, 并加上了@ComponentScan 后, IoC 容器能扫描到 Car 类 (因为其被注解为 @Component)
@Configuration
@ComponentScan("javaTestMaven.demo3")
public class SomeConfig {
@Bean
Boss boss() {
return new Boss();
}
}
===============================
挖掘机技术到底哪家强?
===============================
摘自<<用小说的形式讲解Spring(3) —— xml、注解和Java Config到底选哪个>>
http://bridgeforyou.cn/2017/10/03/Spring-Novel-3-Annotaion-Based-Configuration-and-Java-Based-Configuration/
总结的非常好.

===============================
参考
===============================
http://bridgeforyou.cn/2017/10/03/Spring-Novel-3-Annotaion-Based-Configuration-and-Java-Based-Configuration/
https://www.ibm.com/developerworks/cn/java/j-lo-spring25-ioc/
https://www.ibm.com/developerworks/cn/java/j-lo-spring25-mvc/
https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-iocannt/index.html
SpringBoot系列: 理解 Spring 的依赖注入(一)的更多相关文章
- SpringBoot系列: 理解 Spring 的依赖注入(二)
==============================Spring 容器中 Bean 的名称==============================声明 bean 有两个方式, 一个是 @B ...
- Java 系列之spring学习--依赖注入(二)
一.依赖注入的三种方式 接口注入,set注入,构造函数注入 二.构造函数注入 2.1.测试类 package test; public class test01 { public String msg ...
- Spring学习-理解IOC和依赖注入
最近刚买了一本介绍ssm框架的书,里面主要对Mybatis.spring.springmvc和redis做了很多的讲解,个人觉得虽然有的内容我看不懂,但是整体上还是不错的.最近正在学习中,一边学习一边 ...
- (spring-第3回【IoC基础篇】)spring的依赖注入-属性、构造函数、工厂方法等的注入(基于XML)
Spring要把xml配置中bean的属性实例化为具体的bean,"依赖注入"是关卡.所谓的"依赖注入",就是把应用程序对bean的属性依赖都注入到spring ...
- Spring的依赖注入(DI)三种方式
Spring依赖注入(DI)的三种方式,分别为: 1. 接口注入 2. Setter方法注入 3. 构造方法注入 下面介绍一下这三种依赖注入在Spring中是怎么样实现的. 首先我们需要以下几个 ...
- spring的依赖注入是什么意思
最近学习spring框架,对依赖注入有些模糊,遂上网翻阅资料,做了下列总结,原博客为CSDN 南夏的 spring的依赖注入是什么意思,侵删! Spring 能有效地组织J2EE应用各层的对象.不管是 ...
- Spring.NET依赖注入框架学习--简介
Spring.NET依赖注入框架学习--Spring.NET简介 概述 Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入. ...
- Spring.NET依赖注入框架学习--入门
Spring.NET依赖注入框架学习--入门 在学些Spring.net框架之前,有必要先脑补一点知识,比如什么是依赖注入?IOC又是什么?控制反转又是什么意思?它们与Spring.net又有什么关系 ...
- Spring和依赖注入的价值
javaeye上看到有帖子,置疑spring和依赖注入的价值,回复内容整理如下: 依赖注入对设计有利,而spring则促进了依赖注入的使用. 如果业务处理类,它所使用的倚赖,都是依靠在这个类内部实现或 ...
随机推荐
- 【CF618F】Double Knapsack(构造)
[CF618F]Double Knapsack(构造) 题面 洛谷 Codeforces 题解 很妙的一道题. 发现找两个数集很不爽,我们强制加强限制,我们来找两个区间,使得他们的区间和相等. 把区间 ...
- QML学习笔记(三)-引入Font-awesome
作者: 狐狸家的鱼 Github: 八至 1.首先得在qml文件夹下建立字体文件,将font-awesome放入进去 2.然后在main.cpp中注册字体 引入中一定要写上 引用字体 引用字体得路径一 ...
- Django 子程序
在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该 ...
- Django(十九)Ajax全套
参考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html 提交: - Form - Ajax 一.Ajax,偷偷向后台发请求 - XMLHtt ...
- 安装Nginx配置常用参数含义
--prefix #nginx安装目录,默认在/usr/local/nginx--pid-path #pid问件位置,默认在logs目录--lock-path #lock问件位置,默认在logs目录- ...
- linux c 编程 ------ 通过设备节点调用驱动
驱动程序如下,加载驱动后,会在/dev文件夹下生成一个文件hello_device_node,是此驱动的设备节点 #include <linux/init.h> #include < ...
- C# WebApi过滤器(开发接口必备利器)
在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理.引入了这一机制可以更好地践行DRY(Don’t Repeat Yourself)思想 ...
- 关于web-root和web-inf的用处
web-inf下的页面不能直接反问,必须通过servlet页面进行跳转,安全性高. web-root下的页面能直接通过地址栏来访问,安全性低. 哪些页面放在web-root下,哪些放在web-inf下 ...
- SqlAlchenmy基本使用
#简单查询 print(session.query(User).all()) print(session.query(User.name, User.fullname).all()) print(se ...
- 使用mysql存放Ambari元数据的配置案例
使用mysql存放Ambari元数据的配置案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.环境准备 详情请参考我之前的笔记:离线方式部署Ambari2.6.0.0 中关 ...