💐个人主页:初晴~
📚相关专栏:程序猿的春天
一、IOC(Inversion of Control)
1、概念
IOC(Inversion of Control,控制反转)是一种设计原则,它将对象的控制权从程序代码中转移到外部容器中。比如在类上⾯添加 @RestController 和 @Controller 注解,就是把这个对象交给Spring管理,Spring 框架启动时就会加载该类。把对象交给Spring管理,就是IoC思想。
2、具体分析
比如我们有一个需求是:造一辆车
(1)传统开发过程
代码如下:
public class CarExample {public static void main(String[] args) {Car car = new Car();car.run();}/*** 汽⻋对象*/static class Car {private Framework framework;public Car() {framework = new Framework();System.out.println("Car init....");}public void run(){System.out.println("Car run...");}}/*** ⻋⾝类*/static class Framework {private Bottom bottom;public Framework() {bottom = new Bottom();System.out.println("Framework init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom() {this.tire = new Tire();System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {// 尺⼨private int size;public Tire(){this.size = 17;System.out.println("轮胎尺⼨:" + size);}}
}
这样设计确实可以实现我们的需求。但是可维护性非常低。当我们需要更改某一个需求时,就很有可能会“牵一发而动全身”。
比如当我们需要定制各种尺寸的轮胎时,轮胎的尺寸就需要修改为变量:
但是由于这种开发方式的耦合调用关系,只改轮胎的代码肯定会导致其它依赖的程序出现报错,就需要继续修改:
由以上例子我们不难看出,传统的开发方式各个类的耦合程度过高,各个类直接相互依赖,当某个类需要发生改变时,整个调⽤链上的所有代码都需要修改
(2)IOC开发方式
如图,改进之后的控制权发生了翻转,不再是使用方创建并控制对象,而是把依赖对象注入到当前的对象,依赖对象的控制权不再由当前类控制,而是交由一个统一的类进行管理:
public class Main {public static void main(String[] args) {Tire tire=new Tire(20);Bottom bottom=new Bottom(tire);Framework framework=new Framework(bottom);Car car = new Car(framework);car.run();}
}
public class Tire {// 尺⼨private int size;public Tire(int size){this.size = size;System.out.println("轮胎尺⼨:" + size);}
}
public class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("Bottom init...");}
}
public class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("Framework init...");}
}
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...");}
}
3、优势
1、降低耦合度:
- 通过将对象的创建和依赖关系的管理交给外部容器,减少了代码之间的直接依赖,使得系统更加模块化。
2、提高代码可维护性:
- 由于对象之间的依赖关系是通过配置文件或注解定义的,修改这些依赖关系时不需要修改代码,只需要调整配置,这使得代码更容易维护。
3、提高代码的可重用性:
- 由于对象的创建和依赖关系管理被外部化,相同的对象可以在不同的上下文中重用,而不需要修改代码。
4、Spring中运用IOC
- @Controller:控制层,接收请求,对请求进⾏处理,并进⾏响应
- @Servie:业务逻辑层,处理具体的业务逻辑
- @Repository:数据访问层,也称为持久层。负责数据访问操作
- @Configuration:配置层。处理项⽬中的⼀些配置信息
其实这几个注解的作用基本都是一致的,之所以要进行区分,主要是为了对类的用途进行分类,让代码结构更加清晰:
观察这几个注解的源码我们会发现:
它们底层都有一个 @Component 注解,这是一个元注解,是真正完成控制反转的注解。理论上,上述所有注解都可以用 @Component 直接代替。不过,为了让开发更加规范,还是建议运用对应的注解比较合适。
(2)方法注解
@Component
public class BeanConfig {@Bean()public User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}
运行结果:
- 根据类型获取bean
User user = context.getBean(User.class);
- 根据名称获取bean
User user1 = (User) context.getBean( "user1" );
@Component
public class BeanConfig {//@Bean(name={"u1","user1"}) //完整写法//@Bean({"u1","user1"}) //可省略“name=”@Bean("u1") //当重命名参数只有一个时,还可省略{}public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}
二、DI(Dependency Injection)
1、概念
DI,即依赖注入(Dependency Injection),是一种实现控制反转(IOC)原则的技术手段。
可以这样理解:IOC是一种思想,而DI则是具体实现
2、实现方式
(1)属性注入
@Controllerpublic class UserController {//注⼊⽅法1: 属性注⼊@Autowiredprivate UserService userService;public void sayHi(){System.out.println("hi,UserController...");userService.sayHi();}
}
@Controller
public class UserController2 {//注⼊⽅法2: 构造⽅法private UserService userService;@Autowiredpublic UserController2(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController2...");userService.sayHi();}
}
注意: 如果类只有⼀个构造⽅法,那么 @Autowired 注解可以省略;如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法。
(3)Setter 注⼊
Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注
@Controller
public class UserController3 {//注⼊⽅法3: Setter⽅法注⼊private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController3...");userService.sayHi();}
}
3、三种注⼊优缺点分析
(1)属性注⼊
- 简洁,使⽤⽅便;
缺点:
- 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指 针异常)
- 不能注⼊⼀个Final修饰的属性
优点:
- 可以注⼊final修饰的属性
- 注⼊的对象不会被修改
- 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.
- 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
- 注⼊多个对象时, 代码会⽐较繁琐
- ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
- 不能注⼊⼀个Final修饰的属性
- 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险
4、@Autowired存在问题
@Component
public class BeanConfig {@Bean("u1")public 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;}
}@Controller
public class UserController {//注入user@Autowiredprivate User user;public void sayHi(){System.out.println("hi,UserController...");System.out.println(user);}
}
@Component
public class BeanConfig {@Primary //指定该bean为默认bean的实现@Bean("u1")public 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;}
}
(2)使用@Qualifier注解
注意: @Qualifier注解不能单独使⽤,必须配合@Autowired使⽤
@Controller
public class UserController {//注⼊user@Qualifier("user2")@Autowiredprivate User user;public void sayHi(){System.out.println("hi,UserController...");System.out.println(user);}
}
(3)使⽤@Resource注解
@Controller
public class UserController {//注⼊user@Resource(name = "user2")private User user;public void sayHi(){System.out.println("hi,UserController...");System.out.println(user);}
}
5、@Autowird 与 @Resource的区别
- @Autowired是spring框架提供的,@Resource是JDK提供的注解
- @Autowired默认是按类型注入的,而@Resource优先是按照名称注入的,@Resource提供更多的参数设置
那么本篇文章就到此为止了,如果觉得这篇文章对你有帮助的话,可以点一下关注和点赞来支持作者哦。如果有什么讲的不对的地方欢迎在评论区指出,希望能够和你们一起进步✊