当前位置: 首页 > news >正文

IoC详解

共有两类注解类型可以实现:

1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.

2. 方法注解:@Bean.

类注解

@Controller(控制器存储)

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

    @Controller // 将对象存储到 Spring 中 public class UserController {public void sayHi(){System.out.println("hi,UserController...");}}

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

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

    @SpringBootApplicationpublic 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();}}

观察运⾏结果,发现成功从Spring中获取到Controller对象,并执⾏Controller的sayHi⽅法

如果把@Controller删掉,再观察运⾏结果

没有类型为“com.kdust.exercise”的合格bean。UserController”可用

获取bean对象的其他⽅式

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

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

public interface BeanFactory {//以上省略... // 1. 根据bean名称获取bean Object getBean(String var1) throws BeansException;// 2. 根据bean名称和类型获取bean <T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean Object getBean(String var1, Object... var2) throws BeansException;// 4. 根据类型获取bean <T> T getBean(Class<T> var1) throws BeansException;// 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的bean<T> T getBean(Class<T> var1, Object... var2) throws BeansException;//以下省略... 
}

常⽤的是上述1,2,4种,这三种⽅式,获取到的bean是⼀样的

其中1,2种都涉及到根据名称来获取对象.

代码演示:

@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对象,是⽗类BeanFactory提供的功能

• 继承关系和功能⽅⾯来说:Spring容器有两个顶级的接:BeanFactory和ApplicationContext。其中BeanFactory提供了基础的访问容器的能⼒,⽽ApplicationContext属于BeanFactory的⼦类,它除了继承了BeanFactory的所有功能之外, 它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.

• 从性能⽅⾯来说:ApplicationContext是⼀次性加载并初始化所有的Bean对象,⽽BeanFactory是需要那个才去加载那个,因此更加轻量.(空间换时间)

@Service(服务存储)

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

@Service
public class UserService {public void sayHi(String name) {System.out.println("Hi," + name);}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring中获取UserService对象UserService userService = context.getBean(UserService.class);//使⽤对象userService.sayHi("UserService");}

观察运⾏结果,发现成功从Spring中获取到UserService对象,并执⾏UserService的sayHi⽅法

同样的,把注解@Service删掉,再观察运⾏结果

没有类型为“com.kdust.exercise”的合格bean。UserService”可用

@Repository(仓库存储)

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

@Repository
public class UserRepository {public void sayHi() {System.out.println("Hi, UserRepository~");}
}
    public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象UserRepository userRepository = context.getBean(UserRepository.class);//使⽤对象userRepository.sayHi();}

观察运⾏结果,发现成功从Spring中获取到UserRepository对象,并执⾏UserRepository的sayHi⽅法

同样的,把注解@Repository删掉,再观察运⾏结果

没有类型为“com.kdust.exercise”的合格bean。UserRepository”可用

@Component(组件存储)

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


@Component
public class UserComponent {public void sayHi() {System.out.println("Hi, UserComponent~");}
}
public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象UserComponent userComponent = context.getBean(UserComponent.class);//使⽤对象userComponent.sayHi();}

观察运⾏结果,发现成功从Spring中获取到UserRepository对象,并执⾏UserRepository的sayHi⽅法

同样的,把注解@Component删掉,再观察运⾏结果

没有类型为“com.kdust.exercise”的合格bean。UserComponent”可用

@Configuration(配置存储)

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

@Configuration
public class UserConfiguration {public void sayHi() {System.out.println("Hi,UserConfiguration~");}
}
    public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);//使⽤对象userConfiguration.sayHi();}

观察运⾏结果,发现成功从Spring中获取到UserConfiguration对象,并执⾏UserConfiguration的 sayHi⽅法

同样的,把注解@Configuration删掉,再观察运⾏结果

没有类型为“com.kdust.exercise”的合格bean。UserConfiguration”可用

为什么要这么多类注解?

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

• @Controller:控制层,接收请求,对请求进⾏处理,并进⾏响应.

• @Servie:业务逻辑层,处理具体的业务逻辑.

• @Repository:数据访问层,也称为持久层.负责数据访问操作

• @Configuration:配置层.处理项⽬中的⼀些配置信息.

这和每个省/市都有⾃⼰的⻋牌号是⼀样的.

⻋牌号都是唯⼀的,标识⼀个⻋辆的.但是为什么还需要设置不同的⻋牌开头呢.

⽐如陕西的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,甚⾄⼀个省不同的县区也 是不同的,⽐如西安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样.

这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地.

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

类注解之间的关系

查看@Controller /@Service /@Repository /@Configuration 等注解的源码发现:

其实这些注解⾥⾯都有⼀个注解@Component ,说明它们本⾝就是属于@Component 的"⼦类".

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

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

⽐如杯⼦有喝⽔杯,刷⽛杯等,但是我们更倾向于在⽇常喝⽔时使⽤⽔杯,洗漱时使⽤刷⽛杯.

方法注解@Bean

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

1. 使⽤外部包⾥的类,没办法添加类注解

2. ⼀个类,需要多个对象,⽐如多个数据源

 这种场景,我们就需要使⽤⽅法注解@Bean

我们先来看看⽅法注解如何使⽤:

public class BeanConfig {@Beanpublic 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 {@Beanpublic User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}

再次执⾏以上代码,运⾏结果如下:

定义多个对象

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

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

我们看下@Bean的使⽤

@Component
public class BeanConfig {@Beanpublic User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2(){User user = new User();user.setName("lisi");user.setAge(19);return user;}
}
    public static void main(String[] args) {//获取Spring上下⽂对象ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象SecurityProperties.User user = context.getBean(SecurityProperties.User.class);//使⽤对象System.out.println(user);}

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

运⾏结果:

报错信息显⽰:期望只有⼀个匹配,结果发现了两个,user1,user2

从报错信息中,可以看出来,@Bean注解的bean,bean的名称就是它的⽅法名

接下来我们根据名称来获取bean对象

    @SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//根据bean名称, 从Spring上下⽂中获取对象 User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");System.out.println(user1);System.out.println(user2);}}

运⾏结果:

可以看到,@Bean可以针对同⼀个类,定义多个对象.

重命名Bean

    @Bean(name = {"u1","user1"})public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}

此时我们使⽤u1就可以获取到User对象了,如下代码所⽰:

    @SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User u1 = (User) context.getBean("u1");//使⽤对象 System.out.println(u1);}}

name={}可以省略,如下代码所⽰:

    @Bean({"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;}

扫描路径

Q:使⽤前⾯学习的四个注解声明的bean,⼀定会⽣效吗?

A:不⼀定(原因:bean想要⽣效,还需要被Spring扫描)

下⾯我们通过修改项⽬⼯程的⽬录结构,来测试bean对象是否⽣效:

再运⾏代码:

    @SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User u1 = (User) context.getBean("u1");//使⽤对象 System.out.println(u1);}}

运⾏结果:

解释:没有bean的名称为u1

为什么没有找到bean对象呢?

使⽤五⼤注解声明的bean,要想⽣效,还需要配置扫描路径,让Spring扫描到这些注解

也就是通过 @ComponentScan 来配置扫描路径.

    @ComponentScan({"com.example.demo"})@SpringBootApplicationpublic class SpringIocDemoApplication {public static void main(String[] args) {//获取Spring上下⽂对象 ApplicationContext context =SpringApplication.run(SpringIocDemoApplication.class, args);//从Spring上下⽂中获取对象 User u1 = (User) context.getBean("u1");//使⽤对象 System.out.println(u1);}}

{}⾥可以配置多个包路径

这种做法仅做了解,不做推荐使⽤

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

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

@SpringBootApplication中了

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

在配置类上添加@ComponentScan注解,该注解默认会扫描该类所在的包下所有的配置类

推荐做法:

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

http://www.lryc.cn/news/471372.html

相关文章:

  • 基于 ThinkPHP+Mysql 灵活用工_灵活用工系统_灵活用工平台
  • etcd之etcd分布式锁及事务(四)
  • 智慧旅游微信小程序平台
  • C++设计模式创建型模式———简单工厂模式、工厂方法模式、抽象工厂模式
  • C++ 类与对象(中) 默认成员函数
  • 中间人攻击(https降级攻击)和iptables命令分析
  • 开源生活-分布式管理
  • 华为OD机试真题- 关联子串
  • 云智慧完成华为原生鸿蒙系统的适配, 透视宝 APM 为用户体验保驾护航
  • QT 多语言转换 ts、qm
  • C++学习:类和对象(二)
  • 深度学习(五):语音处理领域的创新引擎(5/10)
  • 双曲函数(Hyperbolic functuons)公式
  • 【CSS/SCSS】@layer的介绍及使用方法
  • 我为什么投身于青少年AI编程?——打造生态圈(三)
  • 出海要深潜,中国手机闯关全球化有了新标杆
  • 百度SEO中的关键词密度与内容优化研究【百度SEO专家】
  • 如何用fastapi集成pdf.js 的viewer.html ,并支持 mjs
  • 文件相对路径与绝对路径
  • Linux 重启命令全解析:深入理解与应用指南
  • 【北京迅为】《STM32MP157开发板嵌入式开发指南》-第六十七章 Trusted Firmware-A 移植
  • `a = a + b` 与 `a += b` 的区别
  • mysqld.log文件过大,清理后不改变所属用户
  • v4.7+版本用户充值在交易统计中计算双倍的问题修复
  • [GXYCTF 2019]Ping Ping Ping 题解(多种解题方式)
  • MODSI EVI 数据的时间序列拟合一阶谐波模型
  • Java:String类(超详解!)
  • 【日志】力扣13.罗马数字转整数 || 解决泛型单例热加载失败问题
  • Mybatis高级
  • 【spark】spark structrued streaming读写kafka 使用kerberos认证