尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring IoCDI

Spring IoCDI 一、Spring 是什么Spring 是包含众多工具方法的 IoC 容器。1.1 什么是容器容器是用来容纳某种物品的装置。例如生活中的水杯、垃圾桶、冰箱等等这些都是容器。1.2 什么是 IoCIoC 是控制反转Bean 的控制权发生了反转。什么是控制反转也就是控制权反转。什么的控制权发生了反转获取依赖对象的过程发生了反转也就是说当需要某个对象时传统开发模式中需要自己通过 new 创建对象现在不需要再进行创建把创建对象的任务交给容器程序中只需要依赖注入就可以了。这个容器称为IoC 容器Spring 是 IoC 容器所以有时 Spring 也称为 Spring 容器。控制反转是一种思想在生活也是处处体现。比如自动驾驶传统驾驶方式车辆的横向和纵向驾驶权由驾驶员来控制现在交给了驾驶自动化系统控制。比如招聘企业的员工招聘、入职、解雇等控制权由老板交给 HR 来处理。1.3 IoC 介绍接下来我们通过案例来了解一下什么是 IoC。需求造一辆车1.3.1 传统开发我们的实现思路是这样的先设计轮子然后根据轮子的大小设计出底盘接着根据底盘设计车身最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系汽车依赖车身车身依赖底盘底盘依赖车轮子。public class Main { public static void main(String[] args) { Car car new Car(); car.run(); } } public class Car { private Framework framework; public Car() { framework new Framework(); System.out.println(car create...); } public void run() { System.out.println(car run...); } } public class Framework { private Bottom bottom; public Framework() { bottom new Bottom(); System.out.println(Framework create...); } } public class Bottom { private Tire tire; public Bottom() { tire new Tire(); System.out.println(Bottom create...); } } public class Tire { private int size 17; public Tire() { System.out.println(tire create...); System.out.println(size: size); } }1.3.2 问题分析这样设计看起来没问题但是可维护性却很低。接下来需求有了变更随着对车的需求量越来越大个性化需求也会越来越多我们需要加工多尺寸轮胎。public class Main { public static void main(String[] args) { Car car new Car(30); car.run(); } } public class Car { private Framework framework; public Car(int size) { framework new Framework(size); System.out.println(car create...); } public void run() { System.out.println(car run...); } } public class Framework { private Bottom bottom; public Framework(int size) { bottom new Bottom(size); System.out.println(Framework create...); } } public class Bottom { private Tire tire; public Bottom(int size) { tire new Tire(size); System.out.println(Bottom create...); } } public class Tire { private int size; public Tire(int size) { this.size size; System.out.println(tire create...); System.out.println(size: size); } }这是修改后的代码。修改的代码如图所示修改后代码会继续进行报错需要继续进行修改。从以上代码可以看出以上程序的问题是当最底层代码改动后整个调用链上的所有代码都需要修改。程序的耦合度非常高修改一处代码影响其他处的代码修改。1.3.3 解决方案我们尝试换一种思路,我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘.最后根据底盘来设计轮子.这时候,依赖就倒置过来了:轮子依赖底盘,底盘依赖车身,车身依赖汽车.这就类似我们打造一辆完整的汽车,如果所有的配件都是自己造,那么当客户需求发生改变的时候,比如轮胎的尺寸不再是原来的尺寸了,那我们要自己动来改了,但如果我们是把轮胎外包出去,那么即使是轮胎的尺寸发生改变了,我们就只需要想代理工厂下订单就行了,我们自身是不需要出力的.public class Main { public static void main(String[] args) { Tire tirenew Tire(30); Bottom bottomnew Bottom(tire); Framework frameworknew Framework(bottom); Car carnew Car(framework); car.run(); } } public class Car { private Framework framework; public Car(Framework framework) { this.frameworkframework; System.out.println(car create...); } public void run() { System.out.println(car run...); } } public class Framework { private Bottom bottom; public Framework(Bottom bottom) { this.bottombottom; System.out.println(Framework create...); } } public class Bottom { private Tire tire; public Bottom(Tire tire) { this.tiretire; System.out.println(Bottom create...); } } public class Tire { private int size; public Tire(int size) { this.sizesize; System.out.println(tire create...); System.out.println(size:size); } }代码经过以上调整,无论底层类如何变化,整个调用链是不用做任何改变的,这样就完成了代码之间的解耦,从而实现了更加灵活,通用的程序设计了.1.3.4 IoC优势在传统的代码中对象创建顺序是:Car-Framework-Bottom-Tire改进之后解耦的代码的对象创建顺序是:Tire-Bottom-Framework-Car我们发现一个规律,通常程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了Framework,Framework创建了Botom,依次往下,二改进之后的控制权发生的反转,不再是使用对象创建并控制依赖对象了,而是把依赖对象注入当前对象中,依赖对象的控制权不再由当前类控制了.这部分代码,就是IoC容器做的工作.从上面可以看出来,IoC容器具备以下优点:资源不有使用资源的双方管理,而又不使用的第三方管理,这可以带来很多好处.第一,资源集中管理,实现资源的配置和易管理.第二,降低了使用双方依赖程度,也就是我们说的耦合度.资源集中管理:IoC容器会帮我们管理一些资源,我们需要使用时,只需要从IoC容器中去获取就可以了我们在创建实例的时候不需要了解其中的细节,降低了使用资源双方的依赖程度,也就是耦合度.Spring就是一种IoC容器,帮助我们来做了这些资源管理.1.4DIDI:(依赖注入):容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入.Spring是一个IoC容器,作为容器,那么它就具备两个最基础的功能:存取二.IoC详解前面我们提到IoC控制反转,也就是将对象的控制权交给Spring的IoC容器,由IoC容器创建及管理对象,也就是bean的存储2.1Bean的存储想把某个对象交给IoC容器管理,需要在类上添加一个注解.Spring框架为了更好的服务Web应用程序,提供了更丰富的注解.共有两类注解类型可以实现:类注解:Controller,Service,Component,Configuration方法注解:Bean2.1.1Controller使用Controller存储bean的代码如下所示:Controller public class UserController { public void sayHi(){ System.out.println(hi,UserController); } }如何在Spring容器中获取对象SpringBootApplication public class SpringIoCCsdnDemoApplication { public static void main(String[] args) { ApplicationContext context SpringApplication.run(SpringIoCCsdnDemoApplication.class, args); UserController userController (UserController) context.getBean(userController); userController.sayHi(); UserController bean context.getBean(UserController.class); bean.sayHi(); // (context.getBean(userController, UserController.class)) UserController bean1 context.getBean(userController, UserController.class); bean1.sayHi(); } }2.1.2 Beam命名约定比如类名:UserController,Bean的名称:userController类名:AccountManager,Bean的名称:accountmanager类名:AccountService,Bean的名称:accountService类名:UController,Bean的名称:UController类名:AManager,Bean的名称:AManager2.1.3 ServiceService public class ServiceController { public void saiHi(){ System.out.println(hi,serviceController!); } }在Spring容器中获取ServiceController serviceController (ServiceController) context.getBean(serviceController); serviceController.saiHi(); ServiceController bean context.getBean(ServiceController.class); bean.saiHi(); ServiceController bean1 context.getBean(serviceController, ServiceController.class); bean1.saiHi(); System.out.println(serviceController); System.out.println(bean); System.out.println(bean1);2.1.4RepositoryRepository public class ReposityController { public void sayHi(){ System.out.println(hi,ReposityController); } }从Spring仓库中获取ReposityController bean context.getBean(ReposityController.class); System.out.println(bean); bean.sayHi(); ReposityController bean1 context.getBean(reposityController, ReposityController.class); System.out.println(bean1); bean1.sayHi(); ReposityController reposityController (ReposityController) context.getBean(reposityController); reposityController.sayHi(); System.out.println(reposityController);2.1.5 CompoentComponent public class CompoentController { public void sayHi(){ System.out.println(hi,CompoentController); } }从Spring仓库中获取CompoentController bean (CompoentController) context.getBean(compoentController); System.out.println(bean); bean.sayHi(); CompoentController bean1 context.getBean(CompoentController.class); bean1.sayHi(); System.out.println(bean1); CompoentController compoentController context.getBean(compoentController, CompoentController.class); compoentController.sayHi(); System.out.println(compoentController);3.1.6 ConfigurationConfiguration public class ConfigurationController { public void sayHi(){ System.out.println(hi,ConfigurationController); } }从Spring仓库中获取ConfigurationController bean context.getBean(ConfigurationController.class); System.out.println(bean); bean.sayHi(); ConfigurationController configurationController (ConfigurationController) context.getBean(configurationController); configurationController.sayHi(); System.out.println(configurationController); ConfigurationController bean1 context.getBean(configurationController, ConfigurationController.class); bean1.sayHi(); System.out.println(bean1);2.2为啥么要这么多注解?Controller:控制层,接收请求,对请求进行处理,并进行响应Service:业务逻辑层,处理具体的业务逻辑Repository:数据访问层,也称为持久层,负责数据访问操作Configuration:配置层,处理项目中的一些配置信息应用分层,调用流程如下:类注解之间的关系查看Controller/Service/Repository/Configuration等注解的源码大线:其实这些注解里面都有一个Compoent,说明它们本身就是属于Compoent的子类.Component是一个元注解,也就是说可以注解其他类注解,Controller,Service,Repository等.这些注解被称为Component的衍生注解,Controller,Service和Repository用于具体的用例分类(分别在控制层,业务逻辑层,持久化层),在开发过程中,如果你要在业务逻辑使用Component或Service,显然Srevice是更好的选择.2.3方法注解Bean类注解是添加到某个类上的,但是存在两个问题:使用外部包里的类,没办法添加类注解一个类,需要多个对象,比如多个数据源这种场景,我们就需要使用方法注解BeanComponent public class BeanController { Bean public User user(){ User usernew User(); user.setName(zhangsan); user.setAge(18); return user; } } User user context.getBean(BeanController.class).user(); System.out.println(user);定义多个对象Bean public User user1(){ User usernew User(); user.setName(zhangsan); user.setAge(18); return user; } Bean public User user2(){ User usernew User(); user.setName(lisi); user.setAge(18); return user; } User user context.getBean(User.class); System.out.println(user);运行结果报错信息显示:期望只有一个匹配,结果发现了两个user1,user2User user1 (User) context.getBean(user1); System.out.println(user1); User user2(User) context.getBean(user2); System.out.println(user2);name{}可以省略,如下代码所示:Bean({u1,user}) public User user(){ User usernew User(); user.setAge(19); user.setName(lisi); return user; }只有一个名称时,{}也可以省略Bean(u2) public User user1(){ User usernew User(); user.setAge(19); user.setName(lisi); return user; }2.4扫描路径使用前面学习的四个注解声明的bean,一定会生效吗?不一定(原因:bean想要生效,还需要被Spring扫描)三.DI详解关于依赖注入,Spring也给我们提供了三种方式:属性注入构造方法注入Setter注入3.1属性注入属性注入是使用Autowired实现的.将Service类注入到Controller类中.Service public class UserService { public void sayHi(){ System.out.println(hi,userService); } } Controller public class UserInjection { Autowired private UserService userService; public void sayHi1(){ userService.sayHi(); System.out.println(hi,userInjection); } }去掉Autowired,在运行一下程序看看结果3.2构造方法注入Controller public class UserInjection2 { // Autowired private UserService userService; Autowired public UserInjection2(UserService service) { this.userServiceservice; } public void sayhi2(){ userService.sayHi(); System.out.println(hi,userInjection2); } }注意事项:如果类只有一个构造方法,那么Autowired注解可以省略;如果类中有多个构造方法,那么需要添加上Autowired来明确指定到底使用哪个构造方法.3.3Setter注入Setter注入和属性的Setter方法实现类似,只不过在设置set方法的时候需要加上Autowired注解Controller public class UserInjection3 { private UserService userService; Autowired public void setUserService(UserService userService) { this.userService userService; } public void sayhi1(){ userService.sayHi(); System.out.println(hi,UserInjection3); } }3.4三种注入优缺点分析属性注入优点:简洁,方便;缺点:只能用于IoC容器,如果是非IoC容器不可用,并且只有在使用的时候才会出现NPE(空指针异常)不能注入一个Final修饰的属性构造函数注入优点:可以注入final修饰的属性注入的对象不会被修改依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,二构造方法是在类加载阶段就会执行的方法通用性好,构造方法是JDK支持的,所以更换任何框架,都是适用的缺点:注入多个对象时,代码会比较繁琐Setter注入优点:方便在类实例之后,重新对该对象进行配置或者注入缺点:不能注入一个FInal修饰的属性注入对象可能会改变,因为setter方法可能会被多次调用,就会被修改的风险
返回列表