Bootstrap

javaweb--分层解耦

目录

分层解耦

三层架构

分层解耦

IoC控制反转

bean的声明

bean组件扫描

案例

DI依赖注入

setter注入

引用类型

简单类型

构造器注入

引用类型

简单类型

方式选择

依赖自动装配

集合注入

在Spring框架

@Resource和@Autowired


分层解耦

三层架构

controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据

service:业务逻辑层,处理具体的业务逻辑

dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括数据地址增,删,改,查

分层解耦

内聚:软件中各个功能模块内部的功能联系

耦合:衡量软件中各个层/模块之间的依赖,关联程度

软件设计原则:高内聚低耦合

控制反转:对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转

依赖注入:容器为应用程序提供运行时,所依赖的资源,称为依赖注入

Bean对象:IOC容器创建、管理的对象,称之为bean

IoC控制反转

由service或者dao使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转

Spring技术对Ioc思想进行了实现

Spring提供了一个容器称作IoC容器,用来充当IoC思想中的外部

IoC容器负责对象的创建,初始化等一系列工作,被创建或 被管理的对象在IoC容器中统称为Bean

目标是充分解耦

最终目标是使用对象时不仅可以直接从IoC容器中获取,并且获取到Bean已经绑定了所有的依赖关系

bean的声明

要把某个对象交给IOC容器管理,需要在对应的类上加如下注解之一:

@Component表示将当前类交给IOC容器管理,成为IOC容器中的bean

声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写

使用以上四个注解都可以声明bean,但在springboot集成web开发中,声明控制器bean只能用@controller

bean组件扫描

前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponsentScan扫描

@ComponentScan注解虽然没有显示配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描范围是启动类所包含在包及其子包

案例

1.导入spring坐标

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.8</version>
    </dependency>
</dependencies>

2,定义spring管理的类(接口)

public interface BookService {
    public void save();
}
​
public class BookServiceImpl implements BookService {
    private BookDao bookDao=new BookDaoImpl();
​
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    //6.提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

3.创建spring配置文件,配置对应类作为spring管理的bean

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--在pom中导入spring坐标,版本是5点多-->
    <!--<groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.8</version>-->
    <!--配置bean-->
    <!--bean标签表示配置bean
    id属性表示给bean起名字
    class属性表示给bean定义类型-->
​
<bean id="bookDao" class="org.example.impl.BookDaoImpl"/>
    <bean id="bookService" class="org.example.service.impl.BookServiceImpl"/>
</beans>

bean定义时id属性在同一个上下文中不能重复

4.初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean

public class App2 {
    public static void main(String[] args) {
        //获取IoC容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //获取bean对象
       BookDao bookDao=(BookDao)ctx.getBean("bookDao");
       bookDao.save();
        BookService bc = (BookService) ctx.getBean("bookService");
        bc.save(); //book dao save ...
                   //book service save ...
                   //book dao save ...
    }
}

(加上DI以后的使用)

5.删除使用new的形式创建对象的代码,提供依赖对象对应的setter方法

public class BookServiceImpl implements BookService {
    //5.删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao;
​
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    //6.提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

6.配置service和dao之间的关系

<bean id="bookDao" class="org.example.impl.BookDaoImpl"/>
    <bean id="bookService" class="org.example.service.impl.BookServiceImpl">
    <!--配置server和dao的关系
    prooerty标签表示配置当前bean属性
    name属性表示配置哪一个具体的属性
    ref属性表示参照哪一个bean-->
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>

DI依赖注入

setter注入

引用类型

案例中的5,配置中使用property标签ref属性注入引用类型对象

简单类型

在bean中定义引用类型属性并提供可访问的set方法

public class BookDaoImpl implements BookDao {
    private int connectionNumber;
    public void setConnectionNumber(int connectionNumber) {
        this.connectionNumber = connectionNumber;
    }
}

配置中使用property标签value属性注入简单类型数据

<bean id="bookDao" class="org.example.impl.BookDaoImpl" >
    <property name="connectionNumber" value="100"/>
</bean>

构造器注入

引用类型

在bean中定义引用类型属性并提供可访问的构造方法

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    private UserDao userDao;
​
    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }
}

配置中使用constructor-args标签ref属性注入引用类型的对象

<bean id="bookService" class="org.example.service.impl.BookServiceImpl">
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>
 <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>
简单类型
在bean中定义引用类型属性并提供可访问的set方法

public class BookDaoImpl {
    private String databaseName;
    private int connectionNum;
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
}

配置中使用constructor-arg标签value属性注入简单类型数据

<bean id="bookDao" class="org.example.dao.impl.BookDaoImpl">
        <constructor-arg name="connectionNumber" value="10"/>
</bean>
<!-- 
    标准书写 这个存在的问题是如果前面的名称修改了,这个也得一直修改
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数名称注入
        <constructor-arg name="connectionNum" value="10"/>
        <constructor-arg name="databaseName" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
​
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
 -->
<!-- 
    解决形参名称的问题,与形参名不耦合  这个的问题是如果两个的类型一样,又无法区分了
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数类型注入
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
​
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean> -->
<!-- 解决参数类型重复问题,使用位置解决参数匹配 -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!-- 根据构造方法参数位置注入 -->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>

方式选择

强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现

可选依赖使用setter注入进行,灵活性强

Spring框架倡导使用构造器,第三框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨

如果有必要可以两者同时使用,使用构造器注入完成强制依赖注入,使用setter注入完成可选依赖的注入

实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入

自己开发的模块推荐使用setter注入

依赖自动装配

IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配,其方式有四种,分别是按类型按名称构造器(不推荐)还有一个没有

<bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="org.example.service.impl.BookServiceImpl" autowire="byType"/>

注意:

自动装配用于引用类型依赖注入,不能对简单类型进行操作

使用时按照类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用

使用时按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用(就是前面改了名称我后面配置那里没改自动配置就会报错)

自动装配优先级低于setter注入和构造器注入,同时出现时自动装配配置失效

集合注入

注入对象

<bean id="bookDao" class="org.example.dao.impl.BookDaoImpl">
    <property name="array">
        <array>
            <value>100</value>
            <value>200</value>
            <value>300</value>
        </array>
    </property>
    <property name="list">
        <list>
            <value>xixi</value>
            <value>haha</value>
            <value>zz</value>
        </list>
    </property>
    <property name="set">
        <set>
            <value>huhu</value>
            <value>hiha</value>
            <value>zza</value>
        </set>
    </property>
    <property name="map">
        <map>
          <entry key="aa" value="xx"/>
            <entry key="cc" value="ee"/>
            <entry key="ff" value="gg"/>
        </map>
    </property>
    <property name="properties">
        <props>
           <prop key="aa">xx</prop>
            <prop key="cc">ee</prop>
            <prop key="ff">gg</prop>
        </props>
    </property>
        </bean>
</beans>
public class AppForSiCollection {
    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("ApplicationContext.xml");
        BookDao bookDao=(BookDao) ctx.getBean("bookDao");
        bookDao.save();//book dao saving。。。 遍历数组[100, 200, 300] 遍历list[xixi, haha, zz] 遍历set[huhu, hiha, zza] 遍历map{aa=xx, cc=ee, ff=gg} 遍历properties{aa=xx, cc=ee, ff=gg}
    }
}

在Spring框架

不用管那么多坐标引入了

@Autowired注解(运行时,IOC容器会提供该类型的bean对象,并且赋值给该变量--依赖注入),默认是按照类型进行,如果存在多个相同类型的bean,将会报错,可以通过

@Primary(用来设置bean的优先级,想让谁生效就在她的class上面再添加上这个注解)

@Primary
@Service
public class EmployeeServiceB inmplments EmployService{}//表示的是我要执行这个。

@Qualifier(配合@Autowired直接指定要注入哪个bean)(默认按照类型注入)

@RestController
publicclassEmpController{
@Autowired
@Qualifier("empServiceA")//引号里面的是类名首字母小写@Qualifier("bean的名称)
private EmpService empService;
}

@Resource(默认按照名称注入)

在容器中建立Bean与Bean之间的依赖关系的整个过程,成为依赖注入,注入有两种方法

@RestController
public class EmpController{
@Resource(name="empServiceB")//name="bean的名称"
private EmpService empService;
}

@Resource和@Autowired

他们的区别在于:

@Autowired是Spring框架提供的注解,而@Resource是JDK提供的注解

@Resource默认是按照类型注入 ,而@Resource默认按照名称注入

;