四、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个特殊字符对应的转义字符分别是:
特殊字符 | 转义字符 |
---|---|
> | > |
< | < |
’ | ' |
" | " |
& | & |
<bean id="mathBean" class="com.powernode.spring6.bean.MathBean"><!--第一种方案:使用实体符号代替特殊符号--><!--<property name="result" value="2 < 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 控制反转 控制反转是一种思想。控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。控制反转,反转的是什么? 将对象的创建权利交出去,交给第三方容器负责。将对象和对象之间关系…...

Java语言如何求平方根
问题 在编程时,会遇到求平方根的问题,本次问题讲到如何使用Java来求解平方根。 方法 使用java.lang.Math类的sqrt(double)方法求平方根。Math是java.lang包中的类,所以就可以直接使用这个类。Double为对象中的基本类型。例如求正整数16的平方…...
C++20中的span容器
一.span容器 span 是 C20 中引入的一个新的标准容器,它用于表示连续的一段内存区间,类似于一个轻量级的只读数组容器。 span 是一个轻量级的非拥有式容器,它提供了对连续内存的引用。 span 的主要用途是作为函数参数,可以避免不…...

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

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

2023 工业互联网平台:智慧制硅厂 Web SCADA 生产线
我国目前是全球最大的工业硅生产国、消费国和贸易国,且未来该产业的主要增量也将来源于我国。绿色低碳发展已成为全球大趋势和国际社会的共识,随着我国“双碳”目标的推进,光伏产业链快速发展,在光伏装机需求的带动下,…...

6-2 SpringCloud快速开发入门:声明式服务消费 Feign实现消费者
声明式服务消费 Feign实现消费者 使用 Feign实现消费者,我们通过下面步骤进行: 第一步:创建普通 Spring Boot工程 第二步:添加依赖 <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】控制自己的手机拍照,并自动发送到邮箱
前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 今天这个案例,就是控制自己的摄像头拍照, 并且把拍下来的照片,通过邮件发到自己的邮箱里。 想完成今天的这个案例,只要记住一个重点:你需要一个摄像头 思路…...

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

在CANoe/CANalyzer中观察CAN Message报文的周期Cycle
案例背景: 该篇博文将告诉您,如何直观的,图示化的,查看CAN网络中各CAN Message报文的周期变化。 优质博文推荐阅读(单击下方链接,即可跳转): Vector工具链 CAN Matrix DBC CAN M…...
Linux命令·ls
ls命令是linux下最常用的命令。ls命令就是list的缩写缺省下ls用来打印出当前目录的清单如果ls指定其他目录那么就会显示指定目录里的文件及文件夹清单。 通过ls 命令不仅可以查看linux文件夹包含的文件而且可以查看文件权限(包括目录、文件夹、文件权限)查看目录信息…...

Mysql InnoDB 存储引擎笔记
1 存储引擎 简介 Mysql 存储引擎有多种:包括 MyISAM、InnoDB 和 Memory。 其中MyISAM 和 INNODB 的区别: 事务安全(MyISAM不支持事务,INNODB支持事务);外键 MyISAM 不支持外键, INNODB支持外…...

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

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

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

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

【数据结构】链表:看我如何顺藤摸瓜
👑专栏内容:数据结构⛪个人主页:子夜的星的主页💕座右铭:日拱一卒,功不唐捐 文章目录一、前言二、链表1、定义2、单链表Ⅰ、新建一个节点Ⅱ、内存泄漏Ⅲ、插入一个节点Ⅳ、销毁所有节点Ⅴ、反转一个链表3、…...

linux shell 入门学习笔记18 函数开发
概念 函数就是将你需要执行的shell命令组合起来,组成一个函数体。一个完整的函数包括函数头和函数体,其中函数名就是函数的名字。 优点 将相同的程序,定义,封装为一个函数,能减少程序的代码数量,提高开发…...
如何最巧妙回答HR面试“送命题”:你为什么离开上家公司?
一 HR面试存在“送命题”? 一个资深HR朋友聊到,他最近pass掉一个名校高材生。 其实洽谈过程还比较愉悦,小姑娘名校毕业,落落大方,薪酬要求比较合理,各方面都比较符合,最后就在决定要录用时,HR朋友随口问了句 “你为什么离开上家公司?”,小姑娘也是随口说了句“我不喜…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...