当前位置: 首页 > 图灵资讯 > 技术篇> spring对loC的实现

spring对loC的实现

来源:图灵教育
时间:2023-06-01 09:45:56

1.IOC控制反转16

●控制反转是一种思想。

●控制反转是为了降低程序耦合度,提高程序扩展性,实现OCP原则和DIP原则。

●什么是控制反转?

○将对象的创建权交给第三方容器负责。

○交出对象与对象关系的维护权,交给第三方容器负责。

●如何实现控制反转的思想?

○DI(DependencyInjection):依赖注入

2.依赖注入16

依靠注入来控制逆转的想法。

Spring依靠注入来完成Bean管理。

Bean管理说:Bean对象的创建,以及Bean对象中属性的赋值(或Bean对象之间关系的维护)。

依赖注入:

●依赖是指对象与对象之间的关系。

●注入是指通过注入使对象与对象产生关系的数据传输行为。

依赖注入有两种常见的实现方法:

●第一种:set注入

●二是结构注入

2.1set注入177

基于set方法实现的set注入,底层会通过反射机制调用属性对应的set方法,然后赋值属性。这种方法要求属性必须向外界提供set方法。

package com.powernode.spring6.dao;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * Bean  17 **/public class UserDao {    private static final Logger logger = LoggerFactory.getLogger(UserDao.class);    public void insert(){        //System.out.println(”数据库正在保存用户信息。");        // 使用log4j2日志框架        logger.info(”数据库正在保存用户信息。");        // 使用log4j2日志框架        logger.info(数据库正在保存用户信息。);    }}

package com.powernode.spring6.service;import com.powernode.spring6.dao.UserDao;/** * Bean  17 **/public class UserService {    private UserDao userDao;//    // 如果set注入,必须提供set方法。    // Spring容器将调用此set方法赋值userdao属性。    // Spring容器将调用此set方法赋值userdao属性。    // 在不使用IDEA工具的情况下,我自己写一个set方法。不符合javabean规范。    // 至少这种方法是从set单词开始的。前三个字母不能随便写,必须是“set"   /* public void setMySQLUserDao(UserDao xyz){        this.userDao = xyz;    }*/    // 这种set方法是由符合javabean规范的IDEA工具生成的。    public void setUserDao(UserDao userDao) {        this.userDao = userDao;    }    public void saveUser(){        // 将用户信息保存到数据库        userDao.insert();        //vipDao.insert();    }}

@Test    public void testSetDI(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");        UserService userService = applicationContext.getBean("userServiceBean", UserService.class);        userService.saveUser();    }

spring.xml

<!--配置dao-->    <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>    <!--配置service-->    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">        <!-- 要使Spring调用相应的set方法,需要配置property标签    17-->        <!-- name属性如何指定值:set方法的方法名,去掉set,然后将剩余的单词首字母变成小写,写在这里。-->        <!-- ref翻译为引用。英语单词:references。ref后面指定的是要注入的beanid。--><!--        <property name="mySQLUserDao" ref="userDaoBean"/>-->        <!--在命名set方法时,不要为难自己,按照规范来。所以一般来说,在name位置写属性名是可以的。-->        <property name="userDao" ref="userDaoBean"/>        <!--<property name="vipDao" ref="vipDaoBean"/>--><!--        <property name="abc" ref="vipDaoBean"/>-->    </bean>

spring对loC的实现_lo控制反转

关键内容是什么原则:

<?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">    <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">        <property name="userDao" ref="userDaoBean"/>    </bean></beans>

2.1.1实现原理:17

属性名通过property标签获得:userDao

set方法名通过属性名推断出来:setUserDao

通过反射机制调用setuserdao()给属性赋值

name是property标签的属性名。

property标签的ref是要注入bean对象的id。(bean最简单的组装方式就是通过ref属性完成bean的组装。组装是指创建系统组件之间关联的动作)

我们再写一个VipDao,故意把set方法写成setabc,然后在spring.property标签在xml文件中的name是属性名故意写成vipDao

再次测试:

package com.powernode.spring6.dao;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/*** dao  17 **/public class VipDao {    private static final Logger logger = LoggerFactory.getLogger(VipDao.class);    public void insert(){        logger.info(”Vip信息正在保存!!!!!");    }}

private VipDao vipDao;    public void setAbc(VipDao vipDao){        this.vipDao = vipDao;    }

 <bean id="vipDaoBean" class="com.powernode.spring6.dao.VipDao"/>  <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">   <property name="vipDao" ref="vipDaoBean"/>   </bean>

spring对loC的实现_xml_02

2.1.2结论:

<property name=" " ref=""/>

property标签的name是set方法后面的字母名(首字母小写后)

例如

property标签的name是:setUserDao()方法名的演变。演变的规律是:

●setUsername()演变为username

●setPassword()演变为pasword

●setUserDao()演变为userDaoo

●setUserService()演变为userservice

property标签的ref是注入bean对象的id,通常是clas=“标签中的值”

set注入的核心实现原理:通过反射机制调用set法赋值属性,使两个对象之间产生关系。

2.2构造注入18

核心原理:通过调用结构法赋值属性。

beans.xml

<?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">    <bean id="xxxx" class="com.powernode.spring6.dao.UserDao"/>    <bean id="yyyy" class="com.powernode.spring6.dao.VipDao"/>    <bean id=“csbean3” class="com.powernode.spring6.service.CustomerService">        <!--不指定下标,也不指定参数名,让spring自己做类型匹配。--不指定下标,也不指定参数名,让spring自己做类型匹配。-->        <!--这种方法实际上是根据类型注入的。spring将根据类型自动判断将ref注入哪个参数。-->        <constructor-arg ref="yyyy"/>        <constructor-arg ref="xxxx"/>    </bean>    <bean id=“csbean2” class="com.powernode.spring6.service.CustomerService">        <!--根据结构方法参数的名称注入。-->        <constructor-arg name="vipDao" ref="yyyy"/>        <constructor-arg name="userDao" ref="xxxx"/>    </bean>    <!--<bean id="csBean" class="com.powernode.spring6.service.CustomerService"><!--<bean id="csBean" class="com.powernode.spring6.service.CustomerService"><!–        构造输入  18–>        <!–           Index属性指定参数下标,第一个参数为0,第二个参数为1,第三个参数为2,以此类推。           ref属性用于指定注入bean的id       –><!–        指定结构方法的第一个参数为0–>        <constructor-arg index="0" ref="xxxx"/><!–        指定结构方法的第二个参数为1–>        <constructor-arg index="1" ref="yyyy"/>    </bean>--></beans>

com.powernode.spring6.service

CustomerService

package com.powernode.spring6.service;import com.powernode.spring6.dao.UserDao;import com.powernode.spring6.dao.VipDao;/** * Bean  构造注入  18 **/public class CustomerService {    private UserDao userDao;    private VipDao vipDao;    public CustomerService(UserDao userDao, VipDao vipDao) {        this.userDao = userDao;        this.vipDao = vipDao;    }    /*public void setUserDao(UserDao userDao) {        this.userDao = userDao;    }    public void setVipDao(VipDao vipDao) {        this.vipDao = vipDao;    }*/    public void save(){        userDao.insert();        vipDao.insert();    }}

///结构注入  18    @Test    public void testConstructorDI(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");//        CustomerService customerService = applicationContext.getBean("csBean", CustomerService.class);//        customerService.save();//        CustomerService csbean2 = applicationContext.getBean("csbean2", CustomerService.class);//        csbean2.save();        CustomerService csbean3 = applicationContext.getBean("csbean3", CustomerService.class);        csbean3.save();    }

spring对loC的实现_xml_03

2.2.1结论18

通过测试得知,注入结构方法时:

●可通过下标

●可通过参数名

●也可以自动推断类型,无需指定下标和参数名。

Spring在装配上还是比较强壮的。

3.set注入专题1193.19注入外部Bean

以前使用的案例是注入外部Bean的方式。

OrderDao

package com.powernode.spring6.dao;import org.slf4j.Logger;import org.slf4j.LoggerFactory;///set注入专题,订单dao  19public class OrderDao {    private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);    public void insert(){        logger.info(”订单正在生成。。。。。");    }}

OrderService

package com.powernode.spring6.service;import com.powernode.spring6.dao.OrderDao;///set注入专题  19public class OrderService {    private OrderDao orderDao;    ////通过set赋值属性  19    public void setOrderDao(OrderDao orderDao) {        this.orderDao = orderDao;    }    /////生成订单的业务方法  19    public void generate(){        orderDao.insert();    }}

set-di.xml

<!--    声明定义外部beann   19-->    <bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"></bean>    <bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService"><!--        使用ref属性进行介绍,这就是注入外部beann  19-->        <property name="orderDao" ref="orderDaoBean"/>    </bean>    <bean id=“orderserviceBean2” class="com.powernode.spring6.service.OrderService">        <property name="orderDao"><!--            嵌套bean标签用于property标签,这就是内部bean  19-->            <bean class="com.powernode.spring6.dao.OrderDao"></bean>        </property>    </bean>

测试

///set注入专题,之 外部bean和内部bean  19    @Test    public void testsetDI2(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");        OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);        orderServiceBean.generate();        OrderService orderserviceBean = applicationContext.getBean("orderserviceBean", OrderService.class);        orderserviceBean.generate();    }

spring对loC的实现_spring_04

3.2set注入专题简单类型20

需要特别注意的是,如果给简单类型赋值,则使用value属性或value标签。而不是ref。

set-di.xml

<!--    set注入专题的简单类型  20-->    <bean id="userBean" class="com.powernode.spring6.bean.User">        <!--重点:如果给简单类型赋值,就不能使用ref。value需要使用。-->        <property name="username" value="张三"/>        <property name="password" value="123"/>        <property name="age" value="20"/>    </bean>

package com.powernode.spring6.bean;///简单类型的普通Java///set注入专题  20public class User {    private String username;    private String password;    private int age;    public void setUsername(String username) {        this.username = username;    }    public void setPassword(String password) {        this.password = password;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "User{" +                "username='" + username + '\'' +                ", password='" + password + '\'' +                ", age=" + age +                '}';    }}

///set注入专题的简单类型  20    @Test    public void testSimpleTypeSet(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");        User user = applicationContext.getBean("userBean", User.class);        System.out.println(user);//        SimpleValueType svt = applicationContext.getBean("svt", SimpleValueType.class);//        System.out.println(svt);    }

spring对loC的实现_spring_05

3.2.哪种类型是简单类型21

可以看到源码阅读

/** * Check if the given type represents a "simple" value type: a primitive or * primitive wrapper, an enum, a String or other CharSequence, a Number, a * Date, a Temporal, a URI, a URL, a Locale, or a Class. * {@code Void} and {@code void} are not considered simple value types. * @param type the type to check * @return whether the given type represents a "simple" value type * @see #isSimpleProperty(Class) */public static boolean isSimpleValueType(Class type) {return (Void.class != type && void.class != type &&(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||charSequencece.class.isAssignableFrom(type) ||Number.class.isAssignableFrom(type) |||Date.class.isAssignableFrom(type) ||Temporal.class.isAssignableFrom(type) ||URI.class == type ||URL.class == type |||Locale.class == type ||Class.class == type));}

3.2.1.1根据源码分析,简单类型包括:22

需要注意的是:

●如果将Date视为简单类型,则不能随意编写日期字符串格式。格式必须符合Date的tostring()方法格式。显然,这是鸡肋。如果我们提供这样一个日期字符串:2010-10-11,我们不能在这里赋予Date类型的属性。

●Spring6后,当注入URL时,将检测URL字符串的有效性。如果它是一个存在的URL,那就没问题了。如果没有,请报告错误。

●基本数据类型

●基本数据类型对应的包装类型

●String或其他CharSequence子类

●Number子类

●Date子类23

如果你坚持把Date作为一个简单的类型,使用value赋值,这个日期字符串格式需要在实际开发中,我们通常不把Date作为一个简单的类型,尽管它是一个简单的类型。ref通常用于赋值Date类型的属性。

●Enum子类

●URI

●URL

●Temporal子类

●Locale

●Class

●还包括上述简单值类型对应的数组类型。

package com.powernode.spring6.bean;/** * 这是一枚枚举  22 **/public enum Season {    SPRING, SUMMER, AUTUMN, WINTER}

com.powernode.spring6.bean

SimpleValueType

package com.powernode.spring6.bean;import java.util.Date;/** * 简单类型的测试  22 **/public class SimpleValueType {    /*    包括以下简单类型  22    public static boolean isSimpleValueType(Class type) {return (Void.class != type && void.class != type &&(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||charSequencece.class.isAssignableFrom(type) ||Number.class.isAssignableFrom(type) |||Date.class.isAssignableFrom(type) ||            java.util.简单类型的Temporalate.class.isAssignableFrom(type) ||        Temporal是Java8提供的时间和时区类型URI.class == type ||URL.class == type |||Locale.class == type ||                         Locale是语言类的,也是一种简单的类型。Class.class == type));}     */    private int age;    private Integer age2;    private boolean flag;    private Boolean flag2;    private char c;    private Character c2;    private Season season;    private String username;    private Class clazz;    // 生日    private Date birth;    public void setBirth(Date birth) {        this.birth = birth;    }    @Override    public String toString() {        return "SimpleValueType{" +                "age=" + age +                ", age2=" + age2 +                ", flag=" + flag +                ", flag2=“” + flag2 +                ", c=" + c +                ", c2=" + c2 +                ", season=" + season +                ", username='" + username + '\'' +                ", clazz=" + clazz +                ", birth=" + birth +                '}';    }    public void setAge(int age) {        this.age = age;    }    public void setAge2(Integer age2) {        this.age2 = age2;    }    public void setFlag(boolean flag) {        this.flag = flag;    }    public void setFlag2(Boolean flag2) {        this.flag2 = flag2;    }    public void setC(char c) {        this.c = c;    }    public void setC2(Character c2) {        this.c2 = c2;    }    public void setSeason(Season season) {        this.season = season;    }    public void setUsername(String username) {        this.username = username;    }    public void setClazz(Class clazz) {        this.clazz = clazz;    }}

<!--测试哪些类型的简单类型?  22-->    <bean id="svt" class="com.powernode.spring6.bean.SimpleValueType">        <property name="age" value="20"/>        <property name="age2" value="20"/>        <property name="username" value="zhangsan"/>        <property name="season" value="SPRING"/>        <property name="flag" value="false"/>        <property name=“flag2” value="true"/>        <property name="c" value="男"/>        <property name="c2" value="女"/>        <property name="clazz" value="java.lang.String"/>        <!--报错了,说字符串1970-10-11不能转换成java.util.Date类型。--报错了,说字符串1970-10-11不能转换成java.util.Date类型。  23-->        <!--<property name="birth" value="1970-10-11"/>-->        <!--如果您坚持使用value赋值Date作为一种简单的类型,则需要此日期字符串格式-->        <!--尽管Date是一种简单的类型,但在实际开发中,我们通常不会将Date视为一种简单的类型。        ref通常用于赋值Date类型的属性。-->        <property name="birth" value="Wed Oct 19 16:28:13 CST 2022"/>    </bean>

///set注入专题的简单类型  20    @Test    public void testSimpleTypeSet(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");//        User user = applicationContext.getBean("userBean", User.class);//        System.out.println(user);        //测试简单类型2     22        SimpleValueType svt = applicationContext.getBean("svt", SimpleValueType.class);        System.out.println(svt);    }

spring对loC的实现_spring_06

3.2.24简单类型在实际开发中的应用

经典案例:数据源属性注入值:

假设我们现在必须自己写一个数据源,我们都知道所有的数据源都必须实现javax.sql.DataSource接口,数据源中应该有连接数据库的信息,例如:driver、url、username、password等。

com.powernode.spring6.jdbc

MyDataSource

package com.powernode.spring6.jdbc;import javax.sql.DataSource;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;// 在实际开发中应用简单类型   Java规范应实现24///所有数据源:javax.sql.DataSource///什么是数据源:可以为您提供Conection对象,都是数据源。public class MyDataSource implements DataSource {    // Spring容器可以管理数据源。    private String driver;    private String url;    private String username;    private String password;    public void setDriver(String driver) {        this.driver = driver;    }    public void setUrl(String url) {        this.url = url;    }    public void setUsername(String username) {        this.username = username;    }    public void setPassword(String password) {        this.password = password;    }    @Override    public String toString() {        return "MyDataSource{" +                "driver='" + driver + '\'' +                ", url='" + url + '\'' +                ", username='" + username + '\'' +                ", password='" + password + '\'' +                '}';    }    @Override    public Connection getConnection() throws SQLException {        // 在获取数据库连接对象时,需要四个信息:driver url username password    24        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 unwrap(Class iface) throws SQLException {        return null;    }    @Override    public boolean isWrapperFor(Class iface) throws SQLException {        return false;    }}

<!--    在实际开发中应用简单类型   让spring管理我们的数据源  24-->    <bean id="myDataSource" class="com.powernode.spring6.jdbc.MyDataSource">        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>        <property name="url" value="jdbc:mysql://localhost:3306/spring6>        <property name="username" value="root"/>        <property name="password" value="lzl"/>    </bean>

///简单类型在实际开发中的应用   24    @Test    public void testMyDataSource(){        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");        MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);        System.out.println(myDataSource);    }

spring对loC的实现_spring_07

上一篇:

使用容器

下一篇:

第一个spring程序