当前位置: 首页 > news >正文

四、Spring对IoC的实现

1.IoC 控制反转

  • 控制反转是一种思想。
  • 控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
  • 控制反转,反转的是什么?
    • 将对象的创建权利交出去,交给第三方容器负责。
    • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
  • 控制反转这种思想如何实现呢?
    • DI(Dependency Injection):依赖注入

2.依赖注入

依赖注入实现了控制反转的思想。
Spring通过依赖注入的方式来完成Bean管理的。
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。

依赖注入:

  • 依赖指的是对象和对象之间的关联关系。
  • 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。

依赖注入常见的实现方式包括两种:

  • 第一种:set注入
  • 第二种:构造注入

set注入和构造注入的时机是不同的。set注入是调用set方法,此时对象早已创建出来了。而构造注入调用的是构造方法,是在对象创建的时刻进行注入。

2.1 set注入

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

通过UserService调用UserDao里面的方法

package com.powernode.spring6.dao;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*** bean*/
public class UserDao {// 记录日志private static final Logger logger = LoggerFactory.getLogger(UserDao.class);public void insert(){logger.info("数据库正在保存用户信息");}
}
package com.powernode.spring6.service;import com.powernode.spring6.dao.UserDao;/*** bean*/
public class UserService {private UserDao userDao;// set注入,必须要提供一个set方法// Spring容器会调用这个set方法,来给userDao属性赋值public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void savaUser(){// 调用userDao的insert()方法userDao.insert();}
}

spring配置文件中配置

<?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"><!--配置dao--><bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/><!--配置service--><bean id="userServiceBean" class="com.powernode.spring6.service.UserService"><!--Spring调用对应的set方法,需要配置property标签--><!--name属性指定值:set方法的方法名去掉set,剩下的单词首字母小写--><!--ref:引用,ref后面指定的是要注入的bean的id--><property name="userDao" ref="userDaoBean"/></bean>
</beans>

测试程序

package com.powernode.spring6.test;import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringDITest {@Testpublic void testSetDI(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");UserService userService = applicationContext.getBean("userServiceBean", UserService.class);userService.savaUser();}
}

实现原理:
通过property标签获取到属性名:userDao
通过属性名推断出set方法名:setUserDao
通过反射机制调用setUserDao()方法给属性赋值

property标签的name是属性名,set方法的方法名去掉set,剩下的单词首字母小写。
property标签的ref是要注入的bean对象的id。(通过ref属性来完成bean的装配,这是bean最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)

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

2.2 构造注入

核心原理:通过调用构造方法来给属性赋值。

package com.powernode.spring6.service;import com.powernode.spring6.dao.UserDao;/*** bean*/
public class UserService {private UserDao userDao;// 通过反射机制调用构造方法给属性赋值public UserService(UserDao userDao) {this.userDao = userDao;}public void savaUser(){// 调用userDao的insert()方法userDao.insert();}
}

spring配置文件中配置

<?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"><!--配置dao--><bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/><!--配置service--><bean id="userServiceBean" class="com.powernode.spring6.service.UserService"><!--构造注入--><!--index属性指定参数下标,第一个参数是0,第二个参数是1,...ref属性用来指定要注入的bean的id--><!--指定构造方法的第一个参数,下标是0--><constructor-arg index="0" ref="userDaoBean"/></bean><bean id="userServiceBean2" class="com.powernode.spring6.service.UserService"><!--根据参数构造方法的名字注入--><constructor-arg name="userDao" ref="userDaoBean"/></bean><bean id="userServiceBean3" class="com.powernode.spring6.service.UserService"><!--不指定下标,也不指定参数名,spring自己做类型匹配--><!--这种方法实际上是根据类型进行注入的,spring会自动根据类型来判断把ref注入给哪个参数--><constructor-arg ref="userDaoBean"/></bean>
</beans>

通过构造方法注入的时候:

  • 可以通过下标
  • 可以通过参数名
  • 也可以不指定下标和参数名,可以类型自动推断。

3 set注入专题

3.1 注入外部Bean

<!--声明Bean-->
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService"><!--使用ref引入,注入外部Bean--><property name="orderDao" ref="orderDaoBean"/>
</bean>

外部Bean的特点:bean定义到外面,在property标签中使用ref属性进行注入。通常这种方式是常用。

3.2 注入内部Bean

内部Bean的方式:在bean标签中嵌套bean标签。

<bean id="orderServiceBean2" class="com.powernode.spring6.service.OrderService"><property name="orderDao"><!--property标签使用嵌套的bean标签,内部bean--><bean class="com.powernode.spring6.dao.OrderDao"/></property>
</bean>

这种方式很少用

3.3 注入简单类型

一个普通的java类

package com.powernode.spring6.bean;public class User {private String username;private String password;private int age;// 省略set,toString方法
}
<!--注入简单类型-->
<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>

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

Spring的BeanUtils类源码

	public static boolean isSimpleProperty(Class<?> type) {Assert.notNull(type, "'type' must not be null");return isSimpleValueType(type) || type.isArray() && isSimpleValueType(type.getComponentType());}public static boolean isSimpleValueType(Class<?> type) {return (Void.class != type && void.class != type &&(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||CharSequence.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));}

通过源码分析得知,简单类型包括:

  • 基本数据类型
  • 基本数据类型对应的包装类
  • String或其他的CharSequence子类
  • Number子类
  • Date子类
  • Enum子类
  • URI
  • URL
  • Temporal子类
  • Locale
  • Class
  • 另外还包括以上简单值类型对应的数组类型。

需要注意的是:

  • 如果把Date当做简单类型的话,日期字符串格式不能随便写。格式必须符合Date的toString()方法格式Thu Mar 02 18:06:45 HKT 2023。显然这就比较鸡肋了。如果我们提供一个这样的日期字符串:2010-10-11,在这里是无法赋值给Date类型的属性的。一般不会把Date当作简单类型,虽然它是简单类型,一般会采用ref给Date类型的属性赋值
  • spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的。如果是一个存在的url,那就没问题。如果不存在则报错。

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

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

package com.powernode.spring6.jdbc;/*** 所有的数据源都要实现java规范:javax.sql.DataSource* 什么是数据源:能够提供Connection对象的,都是数据源*/
public class MyDataSource implements DataSource {// 把数据源交给Spring容器来管理private String driver;private String url;private String username;private String password;@Overridepublic Connection getConnection() throws SQLException {// 获取数据库连接对象的时候需要4个信息:driver url username passwordreturn null;}// 省略接口的其他方法// 省略driver url username password的set方法
}
<?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"><!--Spring管理数据源--><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="root"/></bean>
</beans>

测试

package com.powernode.spring6.test;
public class SpringSetDITest {@Testpublic void testMyDataSource(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);System.out.println(myDataSource);}
}

输出

MyDataSource{driver='com.mysql.cj.jdbc.Driver', url='jdbc:mysql://localhost:3306/spring6', username='root', password='root'}

3.4 级联属性赋值(了解)

    <bean id="clazzBean" class="com.powernode.spring6.beans.Clazz"/><bean id="student" class="com.powernode.spring6.beans.Student"><property name="name" value="张三"/><!--要点1:以下两行配置的顺序不能颠倒--><property name="clazz" ref="clazzBean"/><!--要点2:clazz属性必须还要有getter方法--><property name="clazz.name" value="高三一班"/></bean>
</beans>

3.5 注入数组

当数组中的元素是简单类型:
private String[] username;

<bean id="user" class="com.powernode.spring6.beans.User"><!--数组属性中的元素类型是String简单类型--><property name="username"><array><value>张三</value><value>李四</value><value>王五</value></array></property></bean>

当数组中的元素是非简单类型:

<bean id="goods1" class="com.powernode.spring6.beans.Goods"><property name="name" value="西瓜"/>
</bean><bean id="goods2" class="com.powernode.spring6.beans.Goods"><property name="name" value="苹果"/>
</bean><bean id="order" class="com.powernode.spring6.beans.Order"><property name="goods"><array><!--这里使用ref标签即可--><ref bean="goods1"/><ref bean="goods2"/></array></property>
</bean>

要点:

  • 如果数组中是简单类型,使用value标签。
  • 如果数组中是非简单类型,使用ref标签

3.6 注入List集合和Set集合

List集合:有序可重复
Set集合:无序不可重复

<bean id="personBean" class="com.powernode.spring6.bean.Person"><property name="names"><list><value>张三</value><value>李四</value><value>李四</value></list></property><property name="adds"><set><value>北京</value><value>深圳</value></set></property>
</bean>

注入List集合的时候使用list标签,如果List集合中是简单类型使用value标签,反之使用ref标签。
注入Set集合的时候使用set标签 , 如果Set集合中是简单类型使用value标签,反之使用ref标签

3.7 注入Map集合

<bean id="personBean" class="com.powernode.spring6.bean.Person"><property name="phones"><!--注入Map集合--><map><!--如果key和value是简单类型用key,value--><entry key="1" value="110"/><entry key="2" value="120"/><!--如果key和value不是简单类型用key-ref,value-ref--><!--<entry key-ref="" value-ref=""/>--></map></property>
</bean>

使用<map>标签
如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
如果value是简单类型,使用 value 属性,反之使用 value-ref 属性

3.8 注入Properties

Properties本质上也是一个Map集合
java.util.Properties继承java.util.Hashtable,Hashtable实现了Map接口
虽然Properties也是一个Map集合,和Map的注入方式有点像,但是不同
Properties的key和value只能是String类型

<bean id="personBean" class="com.powernode.spring6.bean.Person"><!--注入Properties属性类对象--><property name="properties"><props><prop key="driver">com.mysql.cj.jdbc.Driver</prop><prop key="url">jdbc:mysql://localhost:3306/spring6</prop><prop key="username">root</prop><prop key="password">root</prop></props></property>
</bean>

使用<props>标签嵌套<prop>标签完成。

3.9 注入null和空字符串

<!--注入null和空字符串-->
<bean id="userBean2" class="com.powernode.spring6.bean.User"><!--不给属性注入,默认值就是null--><!--<property name="username" value="张三"/>--><!--这不是注入null,只是注入了"null"字符串--><!--<property name="username" value="null"/>--><!--手动注入null--><!--<property name="username"><null/></property>--><!--注入空字符串--><!--<property name="username" value=""/>--><!--注入空字符串--><property name="username"><value/></property><property name="password" value="123"/><property name="age" value="20"/>
</bean>

注入空字符串使用:<value/> 或者 value=“”
注入null使用:<null/> 或者 不为该属性赋值

3.10 注入的值中含有特殊符号

XML中有5个特殊字符,分别是:<、>、'、"、&
以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。

解决方案包括两种:
第一种:特殊符号使用转义字符代替。
第二种:将含有特殊符号的字符串放到:<![CDATA[]]> 当中。因为放在CDATA区中的数据不会被XML文件解析器解析。

5个特殊字符对应的转义字符分别是:

特殊字符转义字符
>&gt;
<&lt;
&apos;
"&quot;
&&amp;
<bean id="mathBean" class="com.powernode.spring6.bean.MathBean"><!--第一种方案:使用实体符号代替特殊符号--><!--<property name="result" value="2 &lt; 3"/>--><!--第二种方案:使用<![CDATA[]]>--><property name="result"><!--只能使用value标签--><value><![CDATA[2 < 3]]></value></property>
</bean>

使用CDATA时,不能使用value属性,只能使用value标签。

4 p命名空间注入

目的:简化配置。
使用p命名空间注入的前提条件包括两个:
第一:在XML头部信息中添加p命名空间的配置信息:xmlns:p=“http://www.springframework.org/schema/p”
第二:p命名空间注入是基于setter方法的,所以需要对应的属性提供setter方法。

package com.powernode.spring6.bean;import java.util.Date;public class Dog {// 简单类型private String name;private int age;// 非简单类型,虽然Date是简单类型,当作非简单类型看待private Date birth;// 省略set、toString方法
<?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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--在spring的配置文件头部添加p命名空间 xmlns:p="http://www.springframework.org/schema/p"--><!--使用 p:属性名 = "属性值"--><bean id="dogBean" class="com.powernode.spring6.bean.Dog" p:name="张三" p:age="3" p:birth-ref="birthBean"/><!--获取系统当前时间--><bean id="birthBean" class="java.util.Date"/>
</beans>

测试

@Test
public void testP(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");Dog dogBean = applicationContext.getBean("dogBean", Dog.class);System.out.println(dogBean);
}

p命名空间实际上是对set注入的简化。

5.c命名空间注入

c命名空间是简化构造方法注入的。
使用c命名空间的两个前提条件:
第一:需要在xml配置文件头部添加信息:xmlns:c=“http://www.springframework.org/schema/c”
第二:需要提供构造方法。

Dog类添加构造方式

<?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:c="http://www.springframework.org/schema/c"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--在spring的配置文件头部添加c命名空间 xmlns:c="http://www.springframework.org/schema/c"--><!--使用 下标方式 c:_0 = "属性值"--><!--使用 参数名方式 c:name = "属性值"--><bean id="dogBean" class="com.powernode.spring6.bean.Dog" c:name="张三" c:age="3" c:birth-ref="birthBean"/><!--获取系统当前时间--><bean id="birthBean" class="java.util.Date"/>
</beans>

c命名空间是依靠构造方法的。
不管是p命名空间还是c命名空间,注入的时候都可以注入简单类型以及非简单类型。

6 util命名空间

使用util命名空间可以让配置复用。
使用util命名空间的前提是:在spring配置文件头部添加配置信息。
xmlns:util=“http://www.springframework.org/schema/util”
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd

假设有两个自己定义的数据源MyDataSource1和MyDataSource2

package com.powernode.spring6.jdbc;
public class MyDataSource1 implements DataSource {// 把数据源交给Spring容器来管理// Properties属性类对象,这是一个Map集合,key和value都是String类型private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}// 省略接口方法和toString方法
package com.powernode.spring6.jdbc;
public class MyDataSource2 implements DataSource {private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}// 省略接口方法和toString方法
<?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:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"><!--引入util命名空间在spring的配置文件头部添加xmlns:util="http://www.springframework.org/schema/util"http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd--><util:properties id="prop"><prop key="driver">com.mysql.cj.jdbc.Driver</prop><prop key="url">jdbc:mysql://localhost:3306/spring6</prop><prop key="username">root</prop><prop key="password">root</prop></util:properties><!--数据源1--><bean id="myDataSource1" class="com.powernode.spring6.jdbc.MyDataSource1"><property name="properties" ref="prop"/></bean><!--数据源2--><bean id="myDataSource2" class="com.powernode.spring6.jdbc.MyDataSource2"><property name="properties" ref="prop"/></bean>
</beans>

测试

@Testpublic void testUtil(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");MyDataSource1 myDataSource1 = applicationContext.getBean("myDataSource1", MyDataSource1.class);MyDataSource2 myDataSource2 = applicationContext.getBean("myDataSource2", MyDataSource2.class);System.out.println(myDataSource1);System.out.println(myDataSource2);}

输出

MyDataSource1{properties={password=root, driver=com.mysql.cj.jdbc.Driver, url=jdbc:mysql://localhost:3306/spring6, username=root}}
MyDataSource2{properties={password=root, driver=com.mysql.cj.jdbc.Driver, url=jdbc:mysql://localhost:3306/spring6, username=root}}

7 基于XML的自动装配

Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配。

7.1 根据名称自动装配

package com.powernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderDao {private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);public void insert(){logger.info("订单正在生成...");}
}
package com.powernode.spring6.service;
import com.powernode.spring6.dao.OrderDao;
public class OrderService {private OrderDao orderDao;// 通过set方法给属性赋值public void setOrderDao(OrderDao orderDao) {this.orderDao = orderDao;}/*** 生成订单的业务方法*/public void generate(){orderDao.insert();}
}
<?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"><!--根据名字进行自动装配--><!--自动装配也是基于set方法实现的--><bean id="orderService" class="com.powernode.spring6.service.OrderService" autowire="byName"/><!--id也叫做bean的名称--><!--根据名字进行自动装配的时候,被注入的对象的bean的id不能随便写,set方法的方法名去掉set,剩下单词首字母小写--><bean id="orderDao" class="com.powernode.spring6.dao.OrderDao"/>
</beans>

如果根据名称装配(byName),底层会调用set方法进行注入。
例如:setOrderDao() 对应的名字是orderDao,setPassword()对应的名字是password

7.2 根据类型自动装配

<!--根据类型进行自动装配-->
<!--自动装配是基于set方法实现的-->
<bean id="orderService" class="com.powernode.spring6.service.OrderService" autowire="byType"/>
<!--根据类型进行自动装配的时候,在有效得到配置文件中,某种类型的实例只能有一个-->
<bean class="com.powernode.spring6.dao.OrderDao"/>

无论是byName还是byType,在装配的时候都是基于set方法的。所以set方法是必须要提供的。提供构造方法是不行的

8 Spring引入外部属性配置文件

在类路径下新建jdbc.properties文件,并配置信息。

jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=root

写一个数据源类,提供相关属性。

package com.powernode.spring6.jdbc;
public class MyDataSource implements DataSource {// 把数据源交给Spring容器来管理private String driver;private String url;private String username;private String password;// 省略set、toString方法和接口方法

在spring配置文件中引入context命名空间。

<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"></beans>
    <!--引入外部properties文件引入context命名空间使用context:property-placeholder标签的location属性来指定属性配置文件的路径location默认从类的根路径下开始加载资源--><context:property-placeholder location="jdbc.properties"/><!--配置数据源--><bean id="myDataSource" class="com.powernode.spring6.jdbc.MyDataSource"><!--取值:使用${key}--><property name="driver" value="${jdbc.driverClass}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>

测试程序

@Test
public void testProperties(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);System.out.println(myDataSource);
}

输出

MyDataSource{driver='com.mysql.cj.jdbc.Driver', url='jdbc:mysql://localhost:3306/spring6', username='root', password='root'}

相关文章:

四、Spring对IoC的实现

1.IoC 控制反转 控制反转是一种思想。控制反转是为了降低程序耦合度&#xff0c;提高程序扩展力&#xff0c;达到OCP原则&#xff0c;达到DIP原则。控制反转&#xff0c;反转的是什么&#xff1f; 将对象的创建权利交出去&#xff0c;交给第三方容器负责。将对象和对象之间关系…...

Java语言如何求平方根

问题 在编程时&#xff0c;会遇到求平方根的问题&#xff0c;本次问题讲到如何使用Java来求解平方根。 方法 使用java.lang.Math类的sqrt(double)方法求平方根。Math是java.lang包中的类&#xff0c;所以就可以直接使用这个类。Double为对象中的基本类型。例如求正整数16的平方…...

C++20中的span容器

一.span容器 span 是 C20 中引入的一个新的标准容器&#xff0c;它用于表示连续的一段内存区间&#xff0c;类似于一个轻量级的只读数组容器。 span 是一个轻量级的非拥有式容器&#xff0c;它提供了对连续内存的引用。 span 的主要用途是作为函数参数&#xff0c;可以避免不…...

codeforces周赛div3#855记录

目录 总结 一&#xff0c;A. Is It a Cat? 二&#xff0c;B. Count the Number of Pairs 三&#xff0c;C1. Powering the Hero (easy version) 四&#xff0c;C2. Powering the Hero (hard version) 总结 真羡慕ACM校队的同学&#xff0c;能AC七八题&#xff0c;甚至ak …...

2022年考研结果已出,你上岸了吗?

官方公布&#xff1a;2022年考研人数为457万。 2月20号左右&#xff0c;全国考研分数已经陆续公布&#xff0c;现在已经过去一周左右的时间了&#xff0c;你上岸了吗&#xff0c;还是在等调剂&#xff0c;或者已经知道落榜不知道何去何从&#xff1f; 考研的热潮在近几年席卷…...

2023 工业互联网平台:智慧制硅厂 Web SCADA 生产线

我国目前是全球最大的工业硅生产国、消费国和贸易国&#xff0c;且未来该产业的主要增量也将来源于我国。绿色低碳发展已成为全球大趋势和国际社会的共识&#xff0c;随着我国“双碳”目标的推进&#xff0c;光伏产业链快速发展&#xff0c;在光伏装机需求的带动下&#xff0c;…...

6-2 SpringCloud快速开发入门:声明式服务消费 Feign实现消费者

声明式服务消费 Feign实现消费者 使用 Feign实现消费者&#xff0c;我们通过下面步骤进行&#xff1a; 第一步&#xff1a;创建普通 Spring Boot工程 第二步&#xff1a;添加依赖 <dependencies><!--SpringCloud 集成 eureka 客户端的起步依赖--><dependency>…...

Git-学习笔记01【Git简介及安装使用】

Java后端 学习路线 笔记汇总表【黑马-传智播客】Git-学习笔记01【Git简介及安装使用】Git-学习笔记02【Git连接远程仓库】Git-学习笔记03【Git分支】目录 01-git的历史 02-git和svn的对比 03-git的安装 04-向本地仓库中添加文件 05-修改文件内容并提交 06-删除本地仓库中…...

【Python】控制自己的手机拍照,并自动发送到邮箱

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 今天这个案例&#xff0c;就是控制自己的摄像头拍照&#xff0c; 并且把拍下来的照片&#xff0c;通过邮件发到自己的邮箱里。 想完成今天的这个案例&#xff0c;只要记住一个重点&#xff1a;你需要一个摄像头 思路…...

八股文(二)

一、 实现深拷贝和浅拷贝 1.深拷贝 function checkType(any) {return Object.prototype.toString.call(any).slice(8, -1) }//判断拷贝的要进行深拷贝的是数组还是对象&#xff0c;是数组的话进行数组拷贝&#xff0c;对象的话进行对象拷贝 //如果获得的数据是可遍历的&#…...

在CANoe/CANalyzer中观察CAN Message报文的周期Cycle

案例背景&#xff1a; 该篇博文将告诉您&#xff0c;如何直观的&#xff0c;图示化的&#xff0c;查看CAN网络中各CAN Message报文的周期变化。 优质博文推荐阅读&#xff08;单击下方链接&#xff0c;即可跳转&#xff09;&#xff1a; Vector工具链 CAN Matrix DBC CAN M…...

Linux命令·ls

ls命令是linux下最常用的命令。ls命令就是list的缩写缺省下ls用来打印出当前目录的清单如果ls指定其他目录那么就会显示指定目录里的文件及文件夹清单。 通过ls 命令不仅可以查看linux文件夹包含的文件而且可以查看文件权限(包括目录、文件夹、文件权限)查看目录信息…...

Mysql InnoDB 存储引擎笔记

1 存储引擎 简介 Mysql 存储引擎有多种&#xff1a;包括 MyISAM、InnoDB 和 Memory。 其中MyISAM 和 INNODB 的区别&#xff1a; 事务安全&#xff08;MyISAM不支持事务&#xff0c;INNODB支持事务&#xff09;&#xff1b;外键 MyISAM 不支持外键&#xff0c; INNODB支持外…...

智慧工地AI视频分析系统 opencv

智慧工地AI视频分析系统通过pythonopencv网络模型图像识别技术&#xff0c;智慧工地AI视频分析算法自动识别现场人员穿戴是否合规。本算法模型中用到opencv技术&#xff0c;OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV的Pyth…...

小红书「高效达人筛选攻略」

三八女神节降临&#xff0c;诸多品牌纷纷开启铺垫预热&#xff0c;在各大平台借势宣传。而聚集庞大年轻女性消费群体的小红书&#xff0c;对“她营销”的重要性不言而喻。节点序幕拉开&#xff0c;面对海量达人信息&#xff0c;如何提前积草屯粮、高效备战&#xff1f; 本期千瓜…...

大话数据结构-线性表

1 定义 线性表是零个或多个数据元素的有限序列。 2 抽象数据类型 ADT 线性表(List)Data&#xff1a;线性表的数据对象集合为{al,a2,a3,....an}&#xff0c;每个元素的类型均为DataType。其中&#xff0c;除第一个元素a1外&#xff0c;每一个元素有且只有一个直接前驱元素&…...

分布式缓存 Memcached Linux 系统安装

1.Memcached简介 Memcached是一个开源、高性能&#xff0c;将数据分布于内存中并使用key-value存储结构的缓存系统。它通过在内存中缓存数据来减少向数据库的频繁访问连接的次数&#xff0c;可以提高动态、数据库驱动之类网站的运行速度。 Memcached在使用是比较简单的&#…...

【数据结构】链表:看我如何顺藤摸瓜

&#x1f451;专栏内容&#xff1a;数据结构⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐 文章目录一、前言二、链表1、定义2、单链表Ⅰ、新建一个节点Ⅱ、内存泄漏Ⅲ、插入一个节点Ⅳ、销毁所有节点Ⅴ、反转一个链表3、…...

linux shell 入门学习笔记18 函数开发

概念 函数就是将你需要执行的shell命令组合起来&#xff0c;组成一个函数体。一个完整的函数包括函数头和函数体&#xff0c;其中函数名就是函数的名字。 优点 将相同的程序&#xff0c;定义&#xff0c;封装为一个函数&#xff0c;能减少程序的代码数量&#xff0c;提高开发…...

如何最巧妙回答HR面试“送命题”:你为什么离开上家公司?

一 HR面试存在“送命题”? 一个资深HR朋友聊到,他最近pass掉一个名校高材生。 其实洽谈过程还比较愉悦,小姑娘名校毕业,落落大方,薪酬要求比较合理,各方面都比较符合,最后就在决定要录用时,HR朋友随口问了句 “你为什么离开上家公司?”,小姑娘也是随口说了句“我不喜…...

注意力机制详解系列(五):分支与时间注意力机制

&#x1f468;‍&#x1f4bb;作者简介&#xff1a; 大数据专业硕士在读&#xff0c;CSDN人工智能领域博客专家&#xff0c;阿里云专家博主&#xff0c;专注大数据与人工智能知识分享&#xff0c;公众号&#xff1a;GoAI的学习小屋&#xff0c;免费分享书籍、简历、导图等资料&…...

创宇盾重保经验分享,看政府、央企如何防护?

三月重保已经迫近&#xff0c;留给我们的准备时间越来越少&#xff0c;综合近两年三月重保经验及数据总结&#xff0c;知道创宇用实际案例的防护效果说话&#xff0c;深入解析为何创宇盾可以在历次重保中保持“零事故”成绩&#xff0c;受到众多部委、政府、央企/国企客户的青睐…...

软件测试面试汇总

在浏览器中输入 URL&#xff0c;回车后发生了什么&#xff1f; 在浏览器中输入URL并按下回车键后&#xff0c;大致流程如下&#xff1a; 1、浏览器解析 URL&#xff0c;提取出协议&#xff08;例如HTTP、HTTPS)、主机名和路径等信息。 2、浏览器查找该URL的缓存记录&#xff0…...

空指针,野指针

空指针在C/C中&#xff0c;空指针&#xff08;null pointer&#xff09;是指向内存地址0的指针变量。NULL在C/C中的定义为&#xff1a;#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif #endif从上面的代码定义中&#xff0c;我们可以发现在C…...

Mysql Nested-Loop Join算法和MRR

MySQL8之前仅支持一种join 算法—— nested loop&#xff0c;在 MySQL8 中推出了一种新的算法 hash join&#xff0c;比 nested loop 更加高效。&#xff08;后面有时间介绍这种join算法&#xff09; 1、mysql驱动表与被驱动表及join优化 先了解在join连接时哪个表是驱动表&a…...

Spark 广播/累加

Spark 广播/累加广播变量普通变量广播分布式数据集广播克制 Shuffle强制广播配置项Join Hintsbroadcast累加器Spark 提供了两类共享变量&#xff1a;广播变量&#xff08;Broadcast variables&#xff09;/累加器&#xff08;Accumulators&#xff09; 广播变量 创建广播变量…...

飞天云动,站在下一个商业时代的门口

ChatGPT的爆火让AIGC再度成为热词&#xff0c;随之而来的是对其商业化的畅想——不是ChatGPT自身如何盈利&#xff0c;而是它乃至整个AIGC能给现在的商业环境带来多大改变。 这不由得令人想起另一个同样旨在改变世界的概念&#xff0c;元宇宙。不同的是&#xff0c;元宇宙更侧…...

上海分时电价机制调整对储能项目的影响分析

安科瑞 耿敏花 2022年12月16日&#xff0c;上海市发改委发布《关于进一步完善我市分时电价机制有关事项的通知》(沪发改价管〔2022〕50号)。通知明确上海分时电价机制&#xff0c;一般工商业及其他两部制、大工业两部制用电夏季&#xff08;7、8、9月&#xff09;和冬季&#x…...

产品新人如何快速上手工作

三百六十行&#xff0c;行行出产品经理&#xff1a;上至封神的乔布斯&#xff0c;下至卖鸡蛋罐饼的阿姨&#xff0c;他们对如何打造自己的产品都会有一套完整的产品思路&#xff0c;这也是为什么说“人人都是产品经理”。这个看似光鲜的“经理”有时也会被戏称产品汪&#xff0…...

Linux: ARM GIC仅中断CPU 0问题分析

文章目录1. 前言2. 分析背景3. 问题4. 分析4.1 ARM GIC 中断芯片简介4.1.1 中断类型和分布4.1.2 拓扑结构4.2 问题根因4.2.1 设置GIC SPI中断的CPU亲和性4.2.2 GIC初始化&#xff1a;缺省的CPU亲和性4.2.2.1 boot CPU亲和性初始化流程4.2.2.1 其它非 boot CPU亲和性初始化流程5…...

华为网站建设方案模板/宁德seo公司

将正则表达式直接写在mock里 Mock拦截Ajax的函数如下&#xff1a; Mock.mock(rurl, method, function) 已知 rurl 可以直接使用正则表达式&#xff0c;那么不妨碍直接把正则表达式写在 rurl 里&#xff0c;即如下&#xff1a; Mock.mock(/http:\/\/localhost:3000\/ywcklb\/g…...

网站制作优化济南/长春网站优化流程

写在前面 笔记内容大多出自于拉勾教育大前端高薪训练营的教程&#xff0c;因此也许会和其他文章雷同较多&#xff0c;请担待。 Yeoman $ npm i yo -g / $ yarn global add yo $ npm i generator-node -g / $ yarn global add generator-node // 基于node的开发脚手架&#x…...

单位做网站备案用身份证有啥用/百度云登录入口官网

创建一个新的项目IPHONE 的。 项目名&#xff1a;prog3 点击到 main.storyboard 中&#xff0c;再点击 view ,就可以看到手机界面了。 从对象库 object library 中拖控件到界面中即可。 上图指的就是对象库。...

wordpress 动态特效/产品宣传

1 ICMP Internet 控制消息2 IGMP Internet 组管理3 GGP 网关对网关4 IP IP 中的 IP&#xff08;封装&#xff09; 5 ST 流6 TCP 传输控制8 EGP 外部网关协议9 IGP 任何专用内部网关&#xff08;Cisco 将其用于 IGRP&#xff09;17 UDP 用户数据报18 MUX 多路复用 29 ISO-TP4 IS…...

17网站一起做网店池尾商圈/站长收录平台

先看效果&#xff1a;你有没有想到王者荣耀&#xff1f;当然&#xff0c;这个效果不是我首创的&#xff0c;改编于 PEP 官方的 demo。如果在手机上查看它那个的话&#xff0c;可以用左手控制飞船飞行&#xff0c;右手点击页面发射子弹。它已经有了游戏的雏形。(另外插一句&…...

B2B网站做不出排名跟流量/指数基金是什么意思

文章目录一、关于 Mirantis二、Fuel是什么&#xff1f;1.Fuel介绍1.1openstack对Fuel的说明1.2 在线测试2. Fuel的优势3.Fuel架构4.部分名词说明三、Fuel OpenStack 安装1.安装虚拟机2.安装虚拟机2.1安装虚拟机软件3.安装OpenStack3.1 网卡配置3.2安装master节点3.3 安装contro…...