文章目录

@Controller(控制器存储)如何从Spring容器中获取对象(ApplicationContext)获取bean对象的其他方式(BeanFactory)Bean 命名约定Bean面试题

@Service、@Repository、@Component、@Configuration.为什么要这么多类注解?类注解之间的关系方法注解 @Bean方法注解要配合类注解使用定义多个对象重命名 Bean扫描路径

既然 Spring 是⼀个 IoC(控制反转)容器,作为容器, 那么它就具备两个最基础的功能:

存取

Spring 容器 管理的主要是对象, 这些对象, 我们称之为"Bean". 我们把这些对象交由Spring管理, 由 Spring来负责对象的创建和销毁. 我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出 对象

前⾯我们提到IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对 象。 也就是bean的存储.

把某个对象交给IOC容器管理,Spring框架为了更好的服务web应⽤程序, 提供了更丰富的注解.

共有两类注解类型可以实现bean的存储:

类注解:@Controller、@Service、@Repository、@Component、@Configuration.⽅法注解:@Bean.、

@Controller(控制器存储)

使⽤ @Controller 存储 bean 的代码如下所⽰:

@Controller // 将对象存储到 Spring 中

public class UserController {

public void sayHi(){

System.out.println("hi,UserController...");

}

}

如何观察这个对象已经存在Spring容器当中了呢?

如何从Spring容器中获取对象(ApplicationContext)

接下来我们学习如何从Spring容器中获取对象

@SpringBootApplication

public class SpringIocDemoApplication {

public static void main(String[] args) {

//获取Spring上下⽂对象

ApplicationContext context =

SpringApplication.run(SpringIocDemoApplication.class, args);

//从Spring上下⽂中获取对象

UserController userController = context.getBean(UserController.class);

//使⽤对象

userController.sayHi();

}

}

ApplicationContext 翻译过来就是: Spring 上下⽂ 因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下 ⽂

关于上下⽂的概念 上学时, 阅读理解经常会这样问: 根据上下⽂, 说⼀下你对XX的理解 在计算机领域, 上下⽂这个概念, 咱们最早是在学习线程时了解到过, ⽐如我们应⽤进⾏线程切换的时 候,切换前都会把线程的状态信息暂时储存起来,这⾥的上下⽂就包括了当前线程的信息,等下次该 线程⼜得到CPU时间的时候, 从上下⽂中拿到线程上次运⾏的信息 这个上下⽂, 就是指当前的运⾏环境, 也可以看作是⼀个容器, 容器⾥存了很多内容, 这些内容是当前 运⾏的环境

观察运⾏结果, 发现成功从Spring中获取到Controller对象, 并执⾏Controller的sayHi⽅法 如果把@Controller删掉, 再观察运⾏结果 报错信息显⽰: 找不到类型是: com.example.demo.controller.UserController的bean

获取bean对象的其他方式(BeanFactory)

上述代码是根据类型来查找对象, 如果Spring容器中, 同⼀个类型存在多个bean的话, 怎么来获取呢?

ApplicationContext 也提供了其他获取bean的方式, ApplicationContext 获取bean对象的功能, 是父类BeanFactory提供的功能.

public interface BeanFactory {

//以上省略...

// 1. 根据bean名称获取bean

Object getBean(String var1) throws BeansException;

// 2. 根据bean名称和类型获取bean

T getBean(String var1, Class var2) throws BeansException;

// 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean

Object getBean(String var1, Object... var2) throws BeansException;

// 4. 根据类型获取bean

T getBean(Class var1) throws BeansException;

// 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的

bean

T getBean(Class var1, Object... var2) throws BeansException;

//以下省略...

}

常⽤的是上述1,2,4种, 这三种⽅式,获取到的bean是⼀样的 其中1,2种都涉及到根据名称来获取对象. bean的名称是什么呢?

Spring bean是Spring框架在运⾏时管理的对象, Spring会给管理的对象起⼀个名字. ⽐如学校管理学⽣, 会给每个学⽣分配⼀个学号, 根据学号, 就可以找到对应的学⽣. Spring也是如此, 给每个对象起⼀个名字, 根据Bean的名称(BeanId)就可以获取到对应的对象.

Bean 命名约定

程序开发⼈员不需要为bean指定名称(BeanId), 如果没有显式的提供名称(BeanId),Spring容器将为该bean⽣成唯⼀的名称.

命名约定使⽤Java标准约定作为实例字段名. 也就是说,bean名称以⼩写字⺟开头,然后使⽤驼峰式⼤⼩写.

⽐如 类名: UserController, Bean的名称为: userController 类名: AccountManager, Bean的名称为: accountManager 类名: AccountService, Bean的名称为: accountService

也有⼀些特殊情况, 当有多个字符并且第⼀个和第⼆个字符都是⼤写时, 将保留原始的⼤⼩写

⽐如 类名: UController, Bean的名称为: UController 类名: AManager, Bean的名称为: AManager

根据这个命名规则, 我们来获取Bean

@SpringBootApplication

public class SpringIocDemoApplication {

public static void main(String[] args) {

//获取Spring上下⽂对象

ApplicationContext context =

SpringApplication.run(SpringIocDemoApplication.class, args);

//从Spring上下⽂中获取对象

//根据bean类型, 从Spring上下⽂中获取对象

UserController userController1 = context.getBean(UserController.class);

//根据bean名称, 从Spring上下⽂中获取对象

UserController userController2 = (UserController)

context.getBean("userController");

//根据bean类型+名称, 从Spring上下⽂中获取对象

UserController userController3 =

context.getBean("userController",UserController.class);

System.out.println(userController1);

System.out.println(userController2);

System.out.println(userController3);

}

}

运⾏结果: 地址⼀样, 说明对象是⼀个

Bean面试题

获取bean对象, 是⽗类BeanFactory提供的功能

ApplicationContext VS BeanFactory(常⻅⾯试题)

继承关系和功能⽅⾯来说:Spring 容器有两个顶级的接⼝:BeanFactory 和 ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能⼒,⽽ ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了 BeanFactory 的所有功能之外, 它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,⽽ BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间)

@Service、@Repository、@Component、@Configuration.

使用与Controller一致,这里不做演示

为什么要这么多类注解?

这个也是和咱们前⾯讲的应⽤分层是呼应的. 让程序员看到类注解之后,就能直接了解当前类的⽤途.

• @Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应. • @Servie:业务逻辑层, 处理具体的业务逻辑. • @Repository:数据访问层,也称为持久层. 负责数据访问操作 • @Configuration:配置层. 处理项⽬中的⼀些配置信息.

程序的应⽤分层,调⽤流程如下:

类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发 现: 其实这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".

@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service , @Repository 等. 这些注解被称为 @Component 的衍⽣注解.

@Controller , @Service 和 @Repository ⽤于更具体的⽤例(分别在控制层, 业务逻辑层, 持 久化层), 在开发过程中, 如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更 好的选择

方法注解 @Bean

类注解是添加到某个类上的, 但是存在两个问题:

使⽤外部包⾥的类, 没办法添加类注解⼀个类, 需要多个对象, ⽐如多个数据源

这种场景, 我们就需要使⽤⽅法注解 @Bean 我们先来看看⽅法注解如何使⽤:

public class BeanConfig {

@Bean

public User user(){

User user = new User();

user.setName("zhangsan");

user.setAge(18);

return user;

}

}

然⽽,当我们写完以上代码,尝试获取 bean 对象中的 user 时却发现,根本获取不到:

@SpringBootApplication

public class SpringIocDemoApplication {

public static void main(String[] args) {

//获取Spring上下⽂对象

ApplicationContext context =

SpringApplication.run(SpringIocDemoApplication.class, args);

//从Spring上下⽂中获取对象

User user = context.getBean(User.class);

//使⽤对象

System.out.println(user);

}

}

以上程序的执⾏结果如下:

方法注解要配合类注解使用

在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中, 如下代码所⽰:

定义多个对象

对于同⼀个类, 如何定义多个对象呢?

⽐如多数据源的场景, 类是同⼀个, 但是配置不同, 指向不同的数据源

@Component

public class BeanConfig {

@Bean

public User user1(){

User user = new User();

user.setName("zhangsan");

user.setAge(18);

return user;

}

@Bean

public User user2(){

User user = new User();

user.setName("lisi");

user.setAge(19);

return user;

}

}

定义了多个对象的话, 我们根据类型获取对象, 获取的是哪个对象呢?

@Bean 注解的bean, bean的名称就是它的⽅法名

重命名 Bean

可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所⽰

@Bean(name = {"u1","user1"})

public User user1(){

User user = new User();

user.setName("zhangsan");

user.setAge(18);

return user;

}

或者是

@Bean("u1")

public User user1(){

User user = new User();

user.setName("zhangsan");

user.setAge(18);

return user;

}

扫描路径

bean想要⽣效,还需要被Spring扫描)

使⽤五⼤注解声明的bean,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解 也就是通过 @ComponentScan 来配置扫描路径

@ComponentScan({"com.example.demo"})

那为什么前⾯没有配置 @ComponentScan注解也可以呢?

@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中了

默认扫描的范围是SpringBoot启动类所在包及其⼦包

推荐做法:

把启动类放在我们希望扫描的包的路径下, 这样我们定义的bean就都可以被扫描到

精彩链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。