SpringIoc&DI
SpringIoc&DI
一。SpringIOC
Spring是一个包含众多工具的IOC容器
Spring的两个核心思想:IOC和AOP
Spring容器装的是对象
IOC:控制反转(控制权反转),其主要目的就是解耦合,就是把创建对象的控制权交给了Spring
就例如一下代码:完成一个汽车的制造(车->车身->底盘->轮子)
//main方法
public static void main(String[] args){
Car car=new Car();
car.run();
}
//Car方法
public class Car{
private FrameWork frameWork;
public Car(){
frameWork=new FrameWork();
System.out.println("car init....");
}
public void run{
System.out.println("car run....");
}
}
//车身(FrameWork)
public class FrameWork{
private Bottom bottom;
public FrameWork(){
bottom=new Bottom();
System.out.println("bottom init...")
}
}
//底盘(Bottom)
public class Bottom{
private Tire tire;
public Bottom(){
tire=new Tire();
System.out.println("bottom init...");
}
}
//轮子(Tire)
public class Tire{
private int size=32;
public Tire(){
System.out,println("Tire init");
}
}
这种是没用IOC思想的写法,它有很大的缺陷,假设这时候有一个新需求,要20中不同size的车轮,以这种写法就无法实现代码的复用,要创建20个这样的代码,然后手动更改size的值,当然也有办法让它实现复用
//main方法
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int size=sc.nextInt();
Car car=new Car(size);
car.run();
}
//Car方法
public class Car{
private FrameWork frameWork;
public Car(int size){
frameWork=new FrameWork(size);
System.out.println("car init....");
}
public void run{
System.out.println("car run....");
}
}
//车身(FrameWork)
public class FrameWork{
private Bottom bottom;
public FrameWork(int size){
bottom=new Bottom(size);
System.out.println("bottom init...")
}
}
//底盘(Bottom)
public class Bottom{
private Tire tire;
public Bottom(int size){
tire=new Tire(int size);
System.out.println("bottom init...");
}
}
//轮子(Tire)
public class Tire{
private int size;
public Tire(int size){
System.out,println("Tire init");
}
}
这个代码就比第一个好了很多,但是我们会发现,随着size这个参数加入到Tire这个类中的构造方法之中,那么其他的调用Tire类的方法就也要加上size这个参数,这样耦合性就会非常的高, 假如后面要加更多的参数,那么这个改动的地方就会非常多,因此有了IOC思想就可以降低耦合性
分析上面的代码,size起其实只作用在Tire(轮子)那个方法中,那么是否有办法让其他方法不变,只改变Tire这一个方法呢
就相当于这么一个逻辑,车能否不创建车身了,而是车身自己创建好,提供给车,同理车身能否不创建底盘了,而是底盘自己创建好提供给车,以此类推
//Car方法
public class Car{
private FrameWork frameWork;
public Car(FrameWork frameWork){
this.frameWork=frameWork;
System.out.println("car init....");
}
public void run{
System.out.println("car run....");
}
}
//车身(FrameWork)
public class FrameWork{
private Bottom bottom;
public FrameWork(Bottom bottom){
this.bottom=bottom;
System.out.println("bottom init...")
}
}
//底盘(Bottom)
public class Bottom{
private Tire tire;
public Bottom(Tire tire){
this.tire=tire;
System.out.println("bottom init...");
}
}
//轮子(Tire)
public class Tire{
private int size;
private String color;
public Tire(int size,String color){
this.size=size;
this.color=color;
System.out,println("Tire init...size:"+size+",color:"+color);
}
}
//main方法
public class Main{
public static void main(String[] args){
Tire tire=new Tire(17);
Bottom bottom=new Bottom(tire);
FrameWork frameWork=new FrameWork(bottom);
Car car=new Car(frameWork);
car.run();
}
}
这个代码就相当于站在Bottom的角度上,不管Tire类发生什么变化,我只要Tire给我就行,你给我什么我用什么,相当于间接的封装
这样写的话如果哪个方法想加新元素,只需要改变main方法和目标方法,耦合性大大降低
Main方法就是被Spring管理,其他的方法我们自己写,告诉Spring帮我们创建哪些对象
Spring帮我们管理对象:
(1)告诉Spring,帮我们管理哪些对象(存)
(2)知道这些对象是怎么取出来的(取)
二。DI
IOC是一种思想, DI是一种实现方式
(1)取对象
@RequestMappint("/book")
@RestController
public class BookController{
@Autowired
private BookService bookservice;
@RequestMapping("/getBookList")
public List<BookInfo> getBookList(){
//获取图书的数据
//对图书的数据进行处理
//返回数据
List<BookInfo> bookInfo=bookService.getBookList();
return bookInfo;
}
}
@Autowired
private BookService bookservice;
告诉Spring,从容器中取出这个对象,赋值给当前对象的属性
@Autowired
private BookService bookservice;
//其实上面这个注解是和下面这串代码是等价的
private BookService bookservice;
public BookService(){
bookeService=new BookService();
}
(2)存对象
@Component
public class BookService{
private BookDao bookdao=new BookDao();
public List<BookInfo> getBookList(){
List<BookInfo> bookInfos=bookdao.mockData();
//......
}
}
@Component注解就是存对象的写法
//同样上述代码中的private BookDao bookdao=new BookDao();也可写成
@Autowired
private BookDao bookDao;
并且在BookDao类前加上@Component注解
三。IOC详解
前面提到IOC控制反转,就是将对象的控制权交给了Spring的IOC容器,由IOC容器创建及管理对象。也就是bean的存储
1.Bean存储:
在之前的案例中,要把某个对象交给IOC容器管理,要加上一个注解:@Component,而Spring框架为了更好的服务Web应用程序,提供了更丰富的注解
共有两类注解可以实现:
类注解:@Controller,@Service,@Repository,@Compont,@Configuration
方法注解:@Bean
1.1类注解:
(1)@Controller
DemoApplication类:
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserController bean=context.getBean(UserController.class);
bean.doController();
}
}
这里的getBean就是从context中拿到bean(对象)
UserController类:
@Controller
public class UserController {
public void doController(){
System.out.println("do controller");
}
}
(2)@Service(服务存储)
UserService类:
@Service
public class UserService {
public void doService(){
System.out.println("do Service");
}
}
DemoApplication类:
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doService();
}
}
这只是一种获取bean的方式,其实还有很多种
//根据名称获取bean
UserService userService2=(UserService)context.getBean("userService");
getBean括号中的内容就是bean的名称,一般bean名称都是小驼峰的形式(首字母小写,中间拼接的单词大写),把类的大驼峰形式的名称改为小驼峰就成了bean的名称
//根据名称和类型获取bean
UserService userService3=context.getBean("userService",UserService.class);
userService3.doService();
注意:特殊情况:如果类名前两位都是大写,那么bean的名称为类名
无论使用哪种方法获取Bean,最底层调用的都是BeanFactory
(3)@Repository(仓库存储)
DemoApplication类:
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doRepository();
}
}
UserService类:
@Repository
public class UserService {
public void doRepository(){
System.out.println("do Repository");
}
}
(4)@Component(组件存储)
DemoApplication类:
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doComponent();
}
}
UserService类:
@Component
public class UserService {
public void doComponent(){
System.out.println("do Component");
}
}
(5)Configuration(配置存储)
DemoApplication类:
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserService bean=context.getBean(UserService.class);
bean.doConfiguration();
}
}
UserService类:
@Configuration
public class UserService {
public void doConfiguration(){
System.out.println("do Configuration");
}
}
其实观察代码可以发现,这五个注解实际上写法都是一样的,无非就是换一个注解名,然后方法名换一下;实际上这些注解就相当于不同地区的车牌,用来区分不同地区的车辆;映射到代码层面,就是来区分这串代码是数据层还是控制层还是其他层,可以让程序员们清楚的知道这串代码是干什么的
五大注解只能加在类上,并且只能加在自己的代码上,如果引入第三方jar包,也希望交给Spring管理,是没法加五大注解的;想要解决这个问题就要引入方法注解:@Bean
1.2方法注解:
类注解是添加到某个类上的,但是存在两个问题:
(1)使用外部包里的类,没办法添加类注解
(2)一个类需要多个对象,比如多个数据源
这种场景就要用到方法注解:@Bean
1.一个类中有多个对象
创建UserInfo类:
public class UserInfo {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
创建BeanConfig类:
@Component
public class BeanConfig {
@Bean
public UserInfo userInfo(){
UserInfo userInfo=new UserInfo();
userInfo.setId(1);
userInfo.setName("zhangsan");
userInfo.setAge(12);
return userInfo;
}
@Bean
public UserInfo userInfo2(){
UserInfo userInfo2=new UserInfo();
userInfo2.setId(2);
userInfo2.setName("lisi");
userInfo2.setAge(20);
return userInfo2;
}
}
注意:@Bean必须和五大注解搭配使用才行
当一个类中存在多个Bean时,就不能使用类型来获取对象了
DemoApplication类:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserInfo userInfo= (UserInfo) context.getBean("userInfo2");
System.out.println(userInfo);
}
}
还一种获取对象的方法:
UserInfo userInfo=context.getBean("userInfo2",UserInfo.class);
2.@Bean传递参数
@Bean
public String name(){
return "zhangsan";
}
@Bean
public String name2(){
return "lisi";
}
@Bean
public UserInfo userInfo(String name){
UserInfo userInfo=new UserInfo();
userInfo.setId(1);
userInfo.setName(name);
userInfo.setAge(12);
return userInfo;
}
如果需要Bean的类型对应的对象只有一个时,就直接赋值,如果有多个时,通过名称去匹配
userInfo方法中括号的内容是从上面那个name方法拿的,规则就是根据名称来拿
四。DI详解
依赖注入是一个过程,是指IOC容器在创建Bean时,去提供运行中所依赖的资源,而资源就指的是对象,在前面DI部分,我们使用@Autowired这个注解完成依赖注入的操作
简单的来说就是把对象取出来,放到某个类的属性之中
这里的“依赖注入”也可被称为“对象注入”或者“属性装配”
对于依赖注入,Spring提供了三种方法:
(1)属性注入 (2)构造方法注入 (3)Setter注入
1.属性注入:
创建一个UserController类
@Controller
public class UserController {
@Autowired
private UserService userService;
public void doController(){
userService.doRepository();
System.out.println("do Controller");
}
}
这里的@Autowired注解就是对线main这个UserService属性进行注入
UserService叫类型,userService叫名称
如果UserService这个类型中存在多个对象时,那么优先匹配名称与对象相同的,如果名称都对不上,就报错
UserService类
@Repository
public class UserService {
public void doRepository(){
System.out.println("do Repository");
}
}
DemoApplication类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(DemoApplication.class, args);
UserController userController= (UserController) context.getBean("userController");
userController.doController();
}
}
2.构造方法注入:
private UserService userService;
@Autowired
public UserController(UserService us){
this.us=us;
}
如果存在多个构造函数的时候,要加@Autowired,注明调用的哪个构造函数,如果只有一个构造函数,@Autowired可以省略掉
3.setter方法注入:
private UserService us;
@Autowired
public void setUs(UserService us){
this.us=us;
}
4.@Autowired存在问题和解决方法:
就像上面属性注入的时候描述的那样,当程序中同一个类型有多个对象,使用@Autowired有可能会报错
解决方法:
(1)属性名和你需要的对象名保持一致
这种办法虽然事最简单的,但是实际上不好完成,因为两者耦合性太高,如果忘记了就会使整个程序报错
(2)使用@Primary来标识默认对象
字面上Primary是优先的意思,其实就是给对象进行优先修饰,如果名称不匹配,那么默认调这个对象
@Primary
@Bean
public UserInfo userInfo2(){
UserInfo userInfo2=new UserInfo();
userInfo2.setId(2);
userInfo2.setName("lisi");
userInfo2.setAge(20);
return userInfo2;
}
(3)使用@Qualifier
UserController类:
@Controller
public class UserController {
@Qualifier("userInfo1")
@Autowired
private UserInfo userInfos;
public void doController(){
System.out.println(userInfos.toString());
}
}
UserInfo类:
public class UserInfo {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "UserInfo{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
BeanConfig类:
package com.example.demo;
import org.apache.catalina.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
public class BeanConfig {
@Bean
public String name(){
return "zhangsan";
}
@Bean
public String name2(){
return "lisi";
}
@Bean
public UserInfo userInfo1(){
UserInfo userInfo=new UserInfo();
userInfo.setId(1);
userInfo.setName("zhangsan");
userInfo.setAge(12);
return userInfo;
}
@Bean
public UserInfo userInfo2(){
UserInfo userInfo2=new UserInfo();
userInfo2.setId(2);
userInfo2.setName("lisi");
userInfo2.setAge(20);
return userInfo2;
}
}
这里的@Qualifier就是在指定使用,下面那行private UserInfo userInfos;实际上调用的是BeanConfig类中的userInfo1
这种@Qualifier原理就是指定使用对象,并且一定要和@Autowired一起使用,缺一个都会报错
(4)使用@Resource
UserController类:
@Controller
public class UserController {
@Resource(name="userInfo2")
private UserInfo userInfos;
public void doController(){
System.out.println(userInfos.toString());
}
}
其他类不变,代码和上面的一样
这个@Resource注解实际上和@Qualifier一样,都是指定对象,只是它括号内是name=“xxx”的形式,而且不于@Autowired一起使用
补充:@Autowired和@Resource的区别:
(1)@Autowired是Spring框架的注解,而@Resource是JDK提供的注解
(2)@Autowired默认是按照类型注入的,而@Resource是按照名称注入的,相比较于@Autowired来说,@Resource支持更多的参数设置,例如name设置,根据名称来获取Bean
总结一句:上面IOC详解就是Bean(对象)的存,DI详解就是Bean(对象)的取
原文地址:https://blog.csdn.net/Eliaukqwe/article/details/143661028
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!