• 05set注入和构造注入的原理


    set注入复杂类型ref属性

    测试准备

    准备一个UserDao类和UserService类,其中UserService类关联UserDao类型的属性

    public class UserDao {
        public void insert(){
            System.out.println("正在保存用户数据");
        }
    }
    
    public class UserService {
        private UserDao userDao;
        // 使用set方式注入必须提供set方法,反射机制要调用这个方法给属性赋值的
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        public void save(){
            userDao.insert();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注入外部Bean(常用)

    注入外部Bean的方式: 在外部注册一个bean, 然后通过外部bean的id配合property标签的ref引用属性进行注入

    • 对于property标签来说还可以使用子标签ref的bean属性方式引入外部bean(实际开发中一般直接使用ref属性)
    
    <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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
        
        
        <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
            <property name="userDao" ref="userDaoBean"/>
        bean>
        
        <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
            <property name="userDao">
                <ref bean="userDaoBean"/>
            property>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    注入的bean相当于是从容器中拿的,我们修改从容器中获取的bean或者通过访问被注入对象的属性修改bean都会引起容器中的bean变化

    public void testFirst(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        // 从容器中获取要注入的UserDao然后修改属性值,此时注入到UserService的UserDao属性值也发生了变化
        UserDao userDaoBean = applicationContext.getBean("userDaoBean", UserDao.class);
        userDaoBean.setId(9);
        UserService userServiceBean =  (UserService)applicationContext.getBean("userServiceBean");
        System.out.println(userServiceBean.getUserDao().getId());
        
        // 通过UserService修改UserDao的属性值,此时从容器中获取的UserDao的属性值也发生改变
        userServiceBean.getUserDao().setId(6);
        System.out.println(userDaoBean.getId());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    注入内部Bean(了解)

    注入内部Bean的方式: 在bean标签中嵌套bean标签直接注入,注意通过内部bean的id并不能通过容器获取到bean对象,所以不用写id

    
    <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="userServiceBean" class="com.powernode.spring6.service.UserService">
            <property name="userDao">
                <bean class="com.powernode.spring6.dao.UserDao"/>
            property>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    set注入简单类型value属性

    简单类型的注入和分类

    Spring的源码来分析有哪些简单类型: BeanUtils类(按两次shift键搜索)

    • String类或其他的CharSequence子类,Number子类,Date子类,Enum子类,URI,URL,Temporal子类,Locale,Class,包括以上简单值类型对应的数组类型
    public class BeanUtils{
        //........
        //ctrl + F12搜索isSimpleValueType方法
        public static boolean isSimpleValueType(Class<?> type) {
            return (Void.class != type && void.class != type &&
                    (ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||
                     //String实现了CharSequence
                     CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) ||
                     Date.class.isAssignableFrom(type) ||
                     //java8提供的时间和时区类型
                     Temporal.class.isAssignableFrom(type) ||
                     URI.class == type || URL.class == type ||
                     //语言类型
                     Locale.class == type || Class.class == type));
        }
        //........
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注入部分简单类型: 实际开发中我们一般不会把Date当做简单类型而是采用ref属性给Date类型的属性赋值

    • 如果把Date当做简单类型的话,日期字符串格式必须符合Date的toString()方法格式Fri Sep 30 15:26:38 CST 2022,否则无法赋值给Date类型的属性
    // 实体类
    public class A {
        private String str;
        private Date date;
        // 枚举类型的属性
        private Season season;
        // spring6之后要求注入的URL必须存在有效,如果不存在则报错
        private URI uri;
        private URL url;
        private LocalDate localDate;
        private Locale locale;
        private Class clazz; 
        // 生成setter方法
        // 生成toString方法
    }
    // 枚举类
    enum Season {
        SPRING, SUMMER, AUTUMN, WINTER
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    
    <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="a" class="com.powernode.spring6.beans.A">
            <property name="str" value="zhangsan"/>
            
            
            <property name="date" value="Fri Sep 30 15:26:38 CST 2022"/>
            <property name="season" value="WINTER"/>
            <property name="uri" value="/save.do"/>
            
            <property name="url" value="http://www.baidu.com"/>
            <property name="localDate" value="EPOCH"/>
            
            <property name="locale" value="CHINESE"/>
            <property name="clazz" value="java.lang.String"/>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    手写数据源并自动注入属性值

    我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息例如driver、url、username、password

    // 手写一个数据源并交给Spring取管理
    public class MyDataSource implements DataSource {
        private String driver;
        private String url;
        private String username;
        private String password;
        //提供属性的set和toString方法....
        @Override
        public Connection getConnection() throws SQLException {
            return null;
        }
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return null;
        }
        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }
    
        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
    
        }
        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
    
        }
        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    使用Spring提供的依赖注入完成数据源对象的创建和driver,url,username,password属性的赋值(要求属性提供了setter方法)

    
    <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="dataSource" class="com.powernode.spring6.beans.MyDataSource">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/spring"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    构造注入

    测试准备

    准备一个OrderDao类和OrderService类 , 其中OrderService类关联OrderDao类型的属性

    public class OrderDao {
        public void deleteById(){
            System.out.println("正在删除订单");
        }
    }
    public class UserDao {
        public void insert(){
            System.out.println("正在添加用户");
        }
    }
    public class OrderService {
        private OrderDao orderDao;
        private UserDao userDao;
        // 通过反射机制调用构造方法给属性赋值(构造方法有两个参数)
        public OrderService(OrderDao orderDao, UserDao userDao) {
            this.orderDao = orderDao;
            this.userDao = userDao;
        }
        // 构造方法重载
        public OrderService(Integer a, UserDao userDao) {
            this.userDao = userDao;
        }
        public void delete(){
            orderDao.deleteById();
            userDao.insert();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    构造注入的核心原理

    通过调用构造方法来给属性赋值,每个constructor-arg标签对应构造器的一个形参,Spring先通过constructor-arg标签的个数确定调用哪个构造方法

    如果构造方法有多个参数,通过index和name属性指定为构造方法的哪个形参赋值, 对于没有赋值的形参遵循属性的默认赋值规范

    
    <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="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
        <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
        
        <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
            
            <constructor-arg index="0" ref="orderDaoBean"/>
            
            <constructor-arg index="1" ref="userDaoBean"/>
        bean>
        
        <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
            <constructor-arg name="orderDao" ref="orderDaoBean"/>
            <constructor-arg name="userDao" ref="userDaoBean"/>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    如果既不指定下标也不指定参数名字,Spring也会根据实参类型自动推断出要给哪个形参赋值,使用type属性指定形参的类型让Spring可以快速精确到具体形参

    
    <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
        <constructor-arg ref="orderDaoBean"/>
        <constructor-arg ref="userDaoBean"/>
    bean>
    
    
    <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
        <constructor-arg ref="userDaoBean"/>
        <constructor-arg ref="orderDaoBean"/>
    bean>
    
    
    <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
        <constructor-arg ref="userDaoBean" type="com.powernode.spring6.dao.UserDao"/>
        <constructor-arg ref="orderDaoBean" type="com.powernode.spring6.dao.OrderDao"/>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    set和构造注入的核心实现原理

    set注入是对象创建好后注入的,构造注入是对象实例化过程当中注入

    set注入

    set注入简单类型和复杂类型的常见方式

    • 利用property标签的value属性给对象的属性注入简单类型,ref属性给对象的属性注入复杂类型

    set注入是基于setter方法实现的,底层会通过反射机制调用属性对应的setter方法然后给属性赋值

    • set注入要求实体类的属性必须对外提供setter方法, 并且想让Spring调用对应的setter方法还需要设置property标签
    • Spring先拿到property标签中的name属性值 ,然后首字母大写前面加上set , 最后通过反射机制调用实体类中对应的setXxx方法为该属性赋值
    • set注入只会调用实体类属性对应的setter方法为属性赋值与属性名无关,如果所有getter/setter都自动生成,那么property标签的name属性的值就可以写属性名
    • 对于实体类中没有赋值的属性,遵循属性默认赋值规范

    构造注入

    构造注入简单类型和复杂类型的常见方式

    • 利用constructor-arg标签的value属性给形参注入简单类型,ref属性注入复杂类型

    构造注入是基于构造方法实现的,底层会通过反射机制调用对应的构造方法给属性赋值

    • 通过constructor-arg标签个数确定调用哪个构造方法,即使形参的个数相同但类型一定不都相同,Spring会根据实参类型自动推断出要给哪个构造方法的形参赋值
    • 如果构造方法有多个参数,通过index和name属性指定为构造方法的哪个形参赋值, 对于没有赋值的形参遵循属性的默认赋值规范
  • 相关阅读:
    binder通信实现
    MySQL常见面试题(一)
    DBeaver报错:can‘t load driver class ‘com.mysql.cj.jdbc.Driver‘
    golang开发:go并发的建议(完)
    三维点云转换为二维图像
    fft_傅里叶
    NLP - GIZA++ 实现词对齐
    webpack5 CssMinimizerPlugin css压缩
    虹科培训 | 虹科携手PLCopen开展IEC 61131-3国际工程师培训
    推荐系统-召回-概述(四):热门推荐
  • 原文地址:https://blog.csdn.net/qq_57005976/article/details/132940934