Spring 05: DI(依赖注入)优化Spring三层项目架构 + xml引用类型自动注入

2年前 (2022) 程序员胖胖胖虎阿
240 0 0

背景

  • 用注解改造前面Spring博客集里(指 Spring 02)基于xml的Spring接管下的三层项目架构
  • 对前面Spring博客集里(指 Spring 04)@Controller + @Service + @Repository 3个注解的用法进行演示

实现类

数据访问层

  • 数据访问层的实现类:添加@Repository注解,专门用来创建数据访问层对象
package com.example.dao;

import com.example.pojo.User;
import org.springframework.stereotype.Repository;

/**
 * 数据访问层的实现类
 */
@Repository
public class UserMapperImpl implements UserMapper{

    //模拟用户信息导入
    @Override
    public int insertUser(User user) {
        System.out.println("用户: " + user.getName() + ", 导入成功!");
        return 1;
    }
}

业务逻辑层

  • 业务逻辑层的实现类:添加@Service注解,专门用来创建业务逻辑层对象
  • 同时,由于持有数据访问层的接口类型的成员变量,需要添加@Autowired注解
  • 注意:对于@Autowired,这里是同源类型注入的情况3:存在接口和实现类关系的注入。上面注册了数据访问层的实现类对象,这里注入接口变量中,面向接口编程
package com.example.Service.impl;

import com.example.Service.UserService;
import com.example.dao.UserMapper;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 业务逻辑层实现类
 */
@Service
public class UserServiceImpl implements UserService {

    //数据访问层接口指向数据访问层实现类
    @Autowired
    UserMapper userMapper;
    
    @Override
    public int insertUser(User user) {
        return userMapper.insertUser(user);
    }
}

界面层

  • 界面层:添加@Controller注解,专门用来创建界面层对象

  • 同时,由于持有业务逻辑层的接口类型的成员变量,需要添加@Autowired注解,也是同源类型注入的情况3:存在接口和实现类关系的注入

package com.example.controller;

import com.example.Service.UserService;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 * 界面层
 */
@Controller
public class UserController {
    
    //业务逻辑层接口指向业务逻辑层实现类
    @Autowired
    UserService userService;
    
    public int insertUser(User user){
        return userService.insertUser(user);
    }
}

applicationContext.xml

  • 项目结构

Spring 05: DI(依赖注入)优化Spring三层项目架构 + xml引用类型自动注入

  • 添加包扫描:由上面的项目结构可知,这里的包扫描可行但不是最优做法,因为pojo包根本不会交给容器创建对象但也要扫描,而且要进入impl包才能扫描到UserServiceImpl类。扫描做法以后会改进
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 添加包扫描 -->
    <context:component-scan base-package="com.example"/>
    
</beans>

包扫描的方式

  • 分单个包进行扫描(推荐):对需要将实体类交给Spring容器进行对象创建的包分别进行扫描
    <context:component-scan base-package="com.example.controller"/>
    <context:component-scan base-package="com.example.Service"/>
    <context:component-scan base-package="com.example.dao"/>
  • 一次扫描多个包,各包路径之间可用空格或逗号隔开(不直观)
    <context:component-scan base-package="com.example.dao com.example.Service com.example.controller"/>
  • 从根包开始扫描(不推荐):可能需要扫描许多根本不用交给容器创建对象的包,使得Spring容器的创建速度变慢,做很多无用功
    <context:component-scan base-package="com"/>

测试

package com.example.test;

import com.example.controller.UserController;
import com.example.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestInsert {
    
    //测试:注解改造后的Spring接管下的简单三层架构
    @Test
    public void testInsertUser(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从容器中获取UserController对象
        UserController uc = (UserController) ac.getBean("userController");
        //完成用户数据的导入
        uc.insertUser(new User("荷包蛋", 20, "黑河"));
    }
}

测试输出

用户: 荷包蛋, 导入成功!

Process finished with exit code 0

补充了解

  • 我们在前面Spring博客集中简单分析了基于xml的IOC(setter注入 + 构造方法注入)以及基于注解的IOC(DI依赖注入),其实基于xml的IOC也可以完成引用类型的自动注入,可以简单了解一下,不常用

xml引用类型自动注入

手动注入

  • 当Student实体类持有School实体类对象的引用时,原先applicationContext.xml这样注册
<?xml version="1.0" encoding="UTF-8"?>

<!-- bean工厂 -->
<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">


    <!-- 定义School实体类的实例对象-->
    <bean id="school" class="com.example.pojo02.School">
        <property name="name" value="nefu"/>
        <property name="address" value="哈尔滨"/>
    </bean>

    <!-- 定义Student实体类的实例对象 -->
    <bean id="stu" class="com.example.pojo02.Student">
        <property name="name" value="荷包蛋"/>
        <property name="age" value="20"/>
        <!-- 根据bean工厂中注册过的对象,进行依赖注入 -->
        <property name="school" ref="school"/>
    </bean>

</beans>

标签自动注入

其实,还可以使用标签属性,实现基于xml的引用类型的自动注入。

可以使用autowire="byType"根据已经注册的引用类型自动注入。

也可以使用autowire="byName"根据已经注册的引用类型的名称进行自动注入。

  • autowire="byType",不用再手动指定Student实体类里的引用类型的属性,Spring会将Bean工厂中已经注册的引用类型的对象,自动注入给对应类型的Student实体类对象中的属性
    <!-- 定义School实体类的实例对象-->
    <bean id="school" class="com.example.pojo02.School">
        <property name="name" value="nefu"/>
        <property name="address" value="哈尔滨"/>
    </bean>

    <!-- 定义Student实体类的实例对象 -->
    <bean id="stu" class="com.example.pojo02.Student" autowire="byType">
        <property name="name" value="荷包蛋"/>
        <property name="age" value="20"/>
        <!-- 根据bean工厂中注册过的对象,进行依赖注入 -->
        <!-- <property name="school" ref="school"/> -->
    </bean>

  • autowire="byName",不用再手动指定Student实体类里的引用类型的属性,Spring会根据Bean工厂中已经注册的引用类型的对象的名称,自动注入给Student实体类中名称相同的属性
    <!-- 定义School实体类的实例对象-->
    <bean id="school" class="com.example.pojo02.School">
        <property name="name" value="nefu"/>
        <property name="address" value="哈尔滨"/>
    </bean>

    <!-- 定义Student实体类的实例对象 -->
    <bean id="stu" class="com.example.pojo02.Student" autowire="byName">
        <property name="name" value="荷包蛋"/>
        <property name="age" value="20"/>
        <!-- 根据bean工厂中注册过的对象,进行依赖注入 -->
        <!-- <property name="school" ref="school"/> -->
    </bean>

测试

package com.example.test;

import com.example.pojo02.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestStudent02 {
    //测试:xml引用类型自动注入
    @Test
    public void testStudent(){
        //创建Spring容器,同时生成bean工厂中注册的对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("source02/applicationContext.xml");
        //获取对象
        Student stu = (Student) applicationContext.getBean("stu");
        System.out.println(stu);
    }
}

测试输出

School类的构造方法被执行,实体对象被创建.....
Student类的构造方法执行,实体对象被创建....
Student{name='荷包蛋', age=20, school=School{name='nefu', address='哈尔滨'}}

Process finished with exit code 0

相关文章

暂无评论

暂无评论...