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


    背景

    • 用注解改造前面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

    • 项目结构

    image

    • 添加包扫描:由上面的项目结构可知,这里的包扫描可行但不是最优做法,因为pojo包根本不会交给容器创建对象但也要扫描,而且要进入impl包才能扫描到UserServiceImpl类。扫描做法以后会改进
    
    <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这样注册
    
    
    
    <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">
    
    
        
        <bean id="school" class="com.example.pojo02.School">
            <property name="name" value="nefu"/>
            <property name="address" value="哈尔滨"/>
        bean>
    
        
        <bean id="stu" class="com.example.pojo02.Student">
            <property name="name" value="荷包蛋"/>
            <property name="age" value="20"/>
            
            <property name="school" ref="school"/>
        bean>
    
    beans>
    

    标签自动注入

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

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

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

    • autowire="byType",不用再手动指定Student实体类里的引用类型的属性,Spring会将Bean工厂中已经注册的引用类型的对象,自动注入给对应类型的Student实体类对象中的属性
        
        <bean id="school" class="com.example.pojo02.School">
            <property name="name" value="nefu"/>
            <property name="address" value="哈尔滨"/>
        bean>
    
        
        <bean id="stu" class="com.example.pojo02.Student" autowire="byType">
            <property name="name" value="荷包蛋"/>
            <property name="age" value="20"/>
            
            
        bean>
    
    
    • autowire="byName",不用再手动指定Student实体类里的引用类型的属性,Spring会根据Bean工厂中已经注册的引用类型的对象的名称,自动注入给Student实体类中名称相同的属性
        
        <bean id="school" class="com.example.pojo02.School">
            <property name="name" value="nefu"/>
            <property name="address" value="哈尔滨"/>
        bean>
    
        
        <bean id="stu" class="com.example.pojo02.Student" autowire="byName">
            <property name="name" value="荷包蛋"/>
            <property name="age" value="20"/>
            
            
        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
    
  • 相关阅读:
    react-组件间的通讯
    虚拟 DOM 和 diff 算法
    Rust学习笔记:基础工具和基本名词
    电容屏物体识别手工制作
    null 不好,我真的推荐你使用 Optional
    linux系统离线安装docker(分步法&一键法)
    Pod详解
    品牌低价的形式有哪些
    OVER(PARTITION BY ***)用法,力扣mysql算法题,每日一题
    数学建模(四):分类
  • 原文地址:https://www.cnblogs.com/nefu-wangxun/p/16609882.html