技术标签: spring+mybatis+springmvc spring 接口 java 后端 ioc
IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活IoC很好的体现了面向对象设计法则之一——
好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找
可是我们为什么要有这个思维,它的优点是什么?
我看到网上的一种解释特别好
要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Principle )。什么是依赖倒置原则?假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。
这样的设计看起来没问题,但是可维护性却很低。假设设计完工之后,上司却突然说根据市场需求的变动,要我们把车子的轮子设计都改大一码。这下我们就蛋疼了:因为我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改;同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽车设计也得改——整个设计几乎都得改!我们现在换一种思路。我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘, 底盘依赖车身, 车身依赖汽车。
这时候,上司再说要改动轮子的设计,我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车的设计了。这就是依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。
我结合自己的实际学习过程,会发现在我们学习javase基础知识时我们一般都会想的是需要什么就建立什么对象,假如在我们建立三个接口分别是dao接口和service接口还有test接口每个接口都有对应的实现类分别是daoImp和serviceImp还有testImp,我们在dao接口里面定义了一个 String show();方法,daoImp实现了这个方法public string show(){System.out.println(“我是dao的方法”)}
日常的Java项目开发都是由两个或多个类的彼此合作实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用(称为所依赖的对象),如果合作的对象的引用或依赖关系由具体的对象实现,这对复杂的面向对象系统的设计与开发是非常不利的,由此,如果能把这些依赖关系和对象的注人交给框架实现,让具体对象交出手中对于依赖对象的控制,就能很大程度上解耦代码,这显然是极有价值的,而这就是“依赖反转”,即反转对依赖的控制,把控制权从具体的对象中转交到平台或者框架。
依赖反转的实现有很多种,在Spring中,IoC容器就是实现这个模式的载体,它可以在对象生成或初始化的过程中,直接将数据或者依赖对象的引用注人对象的数据域中,从而实现方法调用的依赖。而这种依赖注人是递归的,依赖对象会被逐层注人,从而建立起一套有序的对象 依赖关系,简化了对象依赖关系的管理,把面向对象过程中需要执行的如对象的创建和对象引用赋值等操作交由容器统- 管理,极大程度上降低了面向对象编程的复杂性。
在Spring中Spring IoC提供了一个基本JavaBen容器,通过loC模式管理依赖关系,并通过依赖注人和AOP切面对类似POIO这样的对象提供了事务管理生命周期管理等功能,在应用开发中,设计组件往需要引人和调用其他组件的服务时,这种你关系如果固化在组件设计中,就会导致组件之间的耦合和维维护难度增大,这时如果使用IoC容器,把资源获取的方式反转,让IoC容器主动管理这些依赖关系,将依赖关系注人到组件中,那么这些依赖关系的适配和管理就会更加灵活。,能通过可视化的 文本完成应用管理依赖关系时,如果在IoC实现依赖反转的过程中那么肯定能能提高依赖关系配置,并且通过工具对这些配置信息进行可视化的管理和浏览,那代码,这符合在面向的管理水平,而且如果耦合关系变动,并不需要重新修改和编译Java对象过程中的开闭原则。Spring倡导的开发方式就是如此,所有的类都会在Spring容器中登记,告诉Spring你是一个什么组件,需要哪些组件,然后Spring会在系统运行到适当的时候,把你要的组件或对象主动给你,同时也把你交给其他需要你的组件或对象。所有对象的创建、销毁都由Spring控制,也就是说,控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,所以称控制反转。IoC的一个重点是在系统运行中,动态地向某个对象提供它所需要的其他对象。这- .点是通过DI( Dependency Injection, 依赖注人)实现的。例如,对象A需要操作数据库,以前我们总是要在A中自己编写代码获得一个Connection对象,有了Spring, 就只需要告诉Spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。系统运行时,Spring会在适当的时候制造一个Connection,然后像打针一样注射到A中,这样就完成了对各个对象之间关系的控制。A需要依赖Connection才能正常运行,而这个Connection是由Spring注人A中的,依赖注人的名字就是这么来的。
Spring的依赖注入
依赖注ADpeneney leioeiD与IoC的含义相同,只不过这两个称呼是从两个角度描述同一个概念。对于一个Spring初学者来说,这两个概念很难理解。下面对这两个概念进行简单的介绍。
当某个Java对角(调用对角)需要调用另一个Java对象(被调用者,即被依赖对象)时,在传统模式下,调用者通常会采用new运算符创建一个对象,这种方式会导致调用者与被调用者之间的耦合性增加,不利于后期项目的升级维护
在使用了spring框架之后对象的实例不再由调用者创建,而是用spring容器创建,spring容器负责控制对象之间的关系取代了由调用者控制对象之间的关系。这样,对象之间的依赖由程序转移到IoC容器,控制权发生了反转,这就是spring控制反转。
从Spring容器的角度看,Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于调用者注人了它依赖的实例,这就是Spring的依赖注入了
这样表述不是很清楚我们来看下代码
public interface Dao {
void show();
}
package dao;
/**
* @author gql
* @date 2021/10/26 - 8:29
*/
public class daoImp implements Dao{
public void show(){
System.out.println("我是dao的方法");
}
}
package service;
/**
* @author gql
* @date 2021/10/26 - 8:31
*/
public interface service {
void show();
}
package service;
import dao.Dao;
import dao.daoImp;
/**
* @author gql
* @date 2021/10/26 - 8:31
*/
public class serviceImp implements service{
Dao dao=new daoImp();
public void show(){
dao.show();
}
}
import service.service;
import service.serviceImp;
/**
* @author gql
* @date 2021/10/26 - 0:41
*/
public class test {
public static void main(String[] args) {
service s=new serviceImp();
s.show();
}
}
结果是
我是dao的方法
通过这种方式我们可以直观的看出来我们的test和dao没有接触过,假如我们先要再添加一个实现类MysqlImp去实现dao接口会怎么样
package dao;
/**
* @author gql
* @date 2021/10/26 - 8:45
*/
public class MysqlImp implements Dao {
public void show(){
System.out.println("我是mysql的实现类");
}
}
我们通过test去访问这个实现类的方法时就需要去修改service的方法
修改后为
package service;
import dao.Dao;
import dao.MysqlImp;
/**
* @author gql
* @date 2021/10/26 - 8:31
*/
public class serviceImp implements service{
Dao dao=new MysqlImp();
public void show(){
dao.show();
}
}
结果为
我是mysql的实现类
通过这次测试我们可以看出来,每次增加一种需求时都会修改原有的代码,这种方式代码耦合性比较高,目前代码量比较少,可能没有直观的感受,但是如果代码量比较大是修改起来耗费就有些昂贵了,我们其实可以修改为多态类型这样我们每次增加实现类都可以直接添加
修改后的serviceImp为
package service;
import dao.Dao;
import dao.MysqlImp;
/**
* @author gql
* @date 2021/10/26 - 8:31
*/
public class serviceImp implements service{
private Dao dao1;
public void setDao1(Dao dao1){
this.dao1=dao1;
}
Dao dao=new MysqlImp();
public void show(){
dao.show();
}
}
修改后的test为
import dao.MysqlImp;
import service.service;
import service.serviceImp;
/**
* @author gql
* @date 2021/10/26 - 0:41
*/
public class test {
public static void main(String[] args) {
service s = new serviceImp();
((serviceImp) s).setDao1(new MysqlImp());
s.show();
}
}
通过这种形式我们假如有个servletImp的类也同样实现了Dao接口那么我们去实现它时不用和之前一样去修改,serviceImp的代码了,我们只需要在test下改变为
import dao.MysqlImp;
import service.service;
import service.serviceImp;
/**
* @author gql
* @date 2021/10/26 - 0:41
*/
public class test {
public static void main(String[] args) {
service s = new serviceImp();
((serviceImp) s).setDao1(new servltImp());
s.show();
}
}
这样程序的耦合性就降低了,不会牵一发而动全身
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.10</version>
</dependency>
</dependencies>
package com.gql.test;
/**
* @author gql
* @date 2021/10/25 - 20:55
*/
public class Hello {
private String src;
private String name;
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
@Override
public String toString() {
return "Hello{" +
"src='" + src + '\'' +
'}';
}
public void show(){
System.out.println("name="+src);
}
public Hello(){
System.out.println("我是Spring");
}
public Hello(String src){
this.src=src;
}
}
3.然后在resource加载资源目录下建立了一个xml配置文件,里面放入了从spring文档里面找到基于 XML 的配置元数据的基本结构:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-constructor-injection
?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
4.然后在test文件的Java目录中建立测试类·Mytest
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gql.test.Hello;
/**
- @author gql
- @date 2021/10/25 - 21:38
*/
public class Mytest {
public static void main(String[] args) {
ApplicationContext context= new ClassPathXmlApplicationContext("beans.xml");
Hello user = (Hello)context.getBean("user");
user.show();
}
}
完成以上步骤然后我们先在xml里面加入几种创建对象的方式
<bean id="user" class="com.gql.test.Hello">
<property name="src" value="gql" ></property>
</bean>
这里的name就是值我们定义的变量,src是我们在Hello里面定义的是私有变量,这里的value就是给src赋值,程序运行的结果就是
我是Spring
name=gql
值得注意的是:之所以产生”我是spring“这句话是因为在加载的过程中其实在时已经调用了我们的无参构造代替了我们自己去new一个类对象;
2.可以使用属性明确指定构造参数的索引,如下示例所示:index
<bean id="user" class="com.gql.test.Hello">
<constructor-arg index="0" value="张浩楠"/>
这是给有产构造第一个参数赋值
</bean>
name=张浩楠
同样值得注意的是,这里的constructor从字面来看就是值构造的意思,这里的index应该是第一个有参构造的第一个参数的下标,value同样是给第一个参数进行赋值
3.第三种方式:
使用属性明确指定构造器参数的类型,则容器可以使用与简单类型匹配的类型,如下示例所示:type
<bean id="user" class="com.gql.test.Hello">
<constructor-arg type="java.lang.String" value="彭一名"></constructor-arg>
</bean>
name=彭一名
这个方法不建议使用当两个都是string类型就容易产生冲突了;如果是引用类型需要输入完整的string引用 但是基本类型可以直接用 ,同样的这个方法也有constructor的形式所以这里的type指的也是构造函数的参数类型,这个java,.lang.String就是有参的第一个类型,所以这就是为什么两个参数都是String类型时容易产生问题。
<!-- 4.直接通过参数名来设置-->
<!-- <bean id="user" class="com.gql.test.Hello">-->
<!-- <constructor-arg name="src" value="李佳怡">-->
<!-- </constructor-arg>-->
<!-- </bean>-->
name=李佳怡
这个方式也是我们用的最多的形式,只需要通过参数名来进行设置,如果仔细看和第一种形式比较相似**在这些方法中我们并没有通过new关键字创建对象,而是通过spring容器获取实现类对象,这就是spring Ioc容器的工作机制
@ConfigurationProperties 注解,可以方便的获取配置在application.properties 或 application.yml 文件中的参数值,以达到更灵活的配置,和更好的模块化整合例子:在 application.properties 文件中创建这些参数使用@ConfigurationProperties 来获取这些属性...
文章目录C语言简介基本编程知识数据类型运算符和表达式流程控制函数数组指针变量的作用域和存储方式扩展数据类型专题c语言概述C语言简介基本编程知识数据类型运算符和表达式流程控制函数数组指针变量的作用域和存储方式扩展数据类型专题c语言概述1.C语言的特点优点:代码量小,速度快,功能强大缺点:危险性高,开发周期长,可移植性不强2.C语言的关键字32个关键字:(有系统定...
Mybatis 查询某些字段值为Null的情况@[TOC](Mybatis 查询某些字段值为Null的情况)我又来分享我的傻逼操作了......今天使用mybatis进行查询操作时,发现两个字段的值映射出现了问题,保险信息的值被映射到了mobil字段,而保险信息字段的值为空,然后我进行了如下检测1.检查resultMap,仔细检查是否是字段和实体类映射出现问题,发现没问题。2.检查sql语句...
一、介绍:PHP-FPM 即 PHP FastCGI 进程管理器。FastCGI 顾名思义,是 CGI 的升级版本,为了提升 CGI 的性能而生,CGI 针对每个 HTTP 请求都会 fork 一个新进程来进行处理(解析配置文件、初始化执行环境、处理请求),然后把这个进程处理完的结果通过 Web 服务器转发给用户,刚刚 fork 的新进程也随之退出,如果下次用户再请求动态资源,那么 Web 服务器又再次 fork 一个新进程,如此周而复始循环往复。而 FastCGI 则会先 fork 一个 maste
C语言常用函数速查手册是一本非常全面系统的讲述了学习c语言相关的常用函数,包括常见的350多个常用函数,且每一个常用函数后面都会有一个经典的示例帮助更快掌握C语言,欢迎下载。图书简介:为了方便查找,所有函数都按照所在库进行分章讲解。这样既方便读者系统学习,也方便同类函数的对比和查找。本书所涉及的函数全面,适合所有想学习C语言的开发人员、爱好者和大中专院校学生使用。对于经常采用C语言进行开发的开发人...
项目中用到了多屏幕功能使用过程中经常会出现windows框或者切出就自动最小化的现象,最近花精力研究了下具体如何设置才能呈现完美的效果Fullscreen Mode可以设置4个模式,只实验了Fullscreen Window和Exclusive FullScreen模式Fullscreen Window1.设置为单屏时进入项目不会显示边框切出程序,程序不会最小化2.设置为多屏时第一次进入项目不会显示边框,但第二次进入时,当还没触发多屏激活时,会显示边框,触发多屏激活后,边框消失Exclus
文章目录前言一、东方财富人气top1001.需求说明2.数据爬取①首页数据② 实时趋势(排名)③历史趋势(排名)二、汉服荟小姐姐主页的视频爬取1.需求说明2. 数据爬取总结前言最近时间排不过来(在和大佬学习研究JS),所以本次更新内容较为简单,有两个站进行讲解示例。文章写的不好,py写的也不好,请大佬们看到的飘过~见笑了见笑了。本项目仅用于交流学习,若侵犯到贵公司权益请联系邮箱[email protected]第一时间删除。读者请切忌用于一切非法途径,否则后果自行承担!项目一(东方财富人气top10
题目描述对于一个任意的三位自然数X,编程计算其各个数位上的数字之和S。输入输入一行,只有一个整数x(100输出输出只有一行,包括1个整数样例输入123样例输出6 C++代码#inc ludeusing namespace std;int main(){ int a,s,d,f,sum; cin>>a;
Java泛型 Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构; 泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数; 泛型的好处 类型安全。泛型的主要目的就是提高Java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类...
并发编程之美读后感第一章1.1什么是进程:进程是代码在数据集合上的依次运行活动,是系统进行资源分配和调度的基本单位.什么是线程:线程是进程的一个执行路径,是cpu分配的基本单位,一个进程中可以有多个线程.每个线程都有自己的程序计数器和栈区域,在cpu中通过程序计数器用来记录线程当前要执行的地址,栈区域是存放局部变量和调用栈帧的堆和方法区是线程共享的,堆中存放new的对象...
C++产生不重复的随机数方法详解在编写考试题目时,要求在一定范围内随机出题,但是如果按如下的方法设计程序:void shuiji(int min,int max)//从min到max范围内随机出题{int num=max-min+1; //比如15到20,实际上是6个,因此要加1int i;for(i=0;i{a[ i ]=min+rand()%(num);
问题十:手机有很多软件会自启动,怎么能关闭呢 建议您使用360手机卫士和360超级root,先用360超级root获取root权限,再赋予360手机卫士root权限,并使用360手机卫士进行软件自启动的控制,就可以从很大程度上禁止软件的自启动,换手机一个清洁的世界,理论上,root是对手机没有害处的,只是会不保修,不过解决的办法也很简单,重新刷机即可。方法很简单,就是事先按著图示2秒,然后图示下方就有一个小锁的标志,当我们下划的时候,锁住了的程式不会跟着关闭,解锁的方法也很简单,再次按住图示2秒即可。