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

后端:Spring(IOC、AOP)

文章目录

  • 1. Spring
  • 2. IOC 控制反转
    • 2-1. 通过配置文件定义Bean
      • 2-1-1. 通过set方法来注入Bean
      • 2-1-2. 通过构造方法来注入Bean
      • 2-1-3. 自动装配
      • 2-1-4. 集合注入
      • 2-1-5. 数据源对象管理(第三方Bean)
      • 2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)
      • 2-1-7. 加载容器的其他方式
      • 2-1-8. p命名空间
      • 2-1-9. c命名空间
      • 2-1-10. util命名空间
    • 2-2. 通过注解加载Bean
      • 2-2-1. 选择性实例化Bean
      • 2-2-2. 纯注解加载Bean
      • 2-2-3. 自动装配
      • 2-2-4. 简单类型的注入
      • 2-2-5. 管理第三方Bean
    • 2-3. Bean的实例化方式
      • 2-3-1. 通过构造方法来实例化Bean
      • 2-3-2. 通过简单工厂模式实例化
      • 2-3-3. 通过工厂方法模式来实例化
      • 2-3-4. 通过FactoryBean接口实例化
      • 2-3-5. BeanFactory 和 FactoryBean 的区别
      • 2-3-6. FactoryBean 的应用(以自定义Date类型举例)
    • 2-4. Bean的生命周期(5步)
      • 2-4-1. Bean 的生命周期之7步
      • 2-4-2. Bean 的生命周期之10步
      • 2-4-3. Bean的作用域
      • 2-4-4. 自己实例化的对象让spring容器去管理
    • 2-5. Bean 循环依赖问题
      • 2-5-1. set注入 + 单例模式之循环依赖
      • 2-5-2. 构造器注入 + 单例模式之循环依赖
      • 2-5-3. Spring 解决循环依赖的机理
    • 2-6. 自定义 spring 框架
  • 3. Spring之JdbcTemplate
  • 4. Spring 代理模式
    • 4-1. jdk 之动态代理
    • 4-2. cglib 之动态代理
    • 4-3. jdk 与 cglib 动态代理的区别
  • 5. 面向切面编程 AOP
  • 6. Spring 事务
    • 6-1. 事务的传播特性
    • 6-2. 事务的隔离级别
    • 6-3. 事务的超时时间
    • 6-4. 设置事务只读(readOnly)

1. Spring

主要包括两种功能,分别为IOC(Inverse Of Control,意为着控制反转,用于反转创建Bean的控制权。通过使用ioc,可以降低代码的耦合度,耦合度指的是类与类之间的依赖关系,如果耦合度高表明类与类之间的依赖关系越紧密,此时如果修改其中的一些代码,可能会造成其他类出错的情况,对于后期的维护及其不便。使用ioc容器管理的Bean,推荐使用实现接口的实现类)、AOP(Aspect Oriented Programming,意为面向切面编程)。

2. IOC 控制反转

IOC意为控制反转,也就是反转创建Bean的控制权。在这之前我们需要调用一个类下的某个方法时,通常做法是首先对这个类进行实例化,然后再调用其实例化对象的方法。通过IOC,把这个创建类交给一个容器去管理,我们需要用到时,只需要从容器中去拿即可。当然前提是我们需要定义配置文件,当然,随着版本的迭代,后期发展到我们只需要添加一些注解即可。

2-1. 通过配置文件定义Bean

这个前提是需要有spring-context的依赖(我的这个是SpringBoot项目哈),导入这个依赖之后,在resources这个目录下鼠标右键,找到新建xml配置文件就有对应的配置文件格式了。
在这里插入图片描述
具体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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/></beans>

我在这个配置文件里边定义了一个Bean,并且这个Bean的名字为userDao,此时我们使用java代码就可以从这个容器中去获取这个Bean了。当然首先需要先加载到这个配置文件,这里使用 ClassPathXmlApplicationContext去加载,加载完之后可以得到一个ioc容器对象,此时,只需要通过这个ioc容器对象通过getBean即可获取对应的Bean对象。

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.support.ClassPathXmlApplicationContext;@SpringBootTest(classes = Test2.class)
public class Test2 {@Testpublic void test1(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");UserDao userDao = (UserDao) ctx.getBean("userDao");System.out.println(userDao);}
}

2-1-1. 通过set方法来注入Bean

在一个类下如果需要引入另外一个类的方法,前提是需要对这个类实例化。如果使用ioc,添加set方法即可(还有其他)。

package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;
public class UserService {private UserDao ud;public void setUd(UserDao ud) {this.ud = ud;}public void print(){System.out.println(ud);}
}

对应的配置文件中需要做的配置如下:

<?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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/><bean id="userService" class="com.lz.demo_spring_24_1.service.UserService"><property name="ud" ref="userDao"/></bean>
</beans>

测试代码如下:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.support.ClassPathXmlApplicationContext;@SpringBootTest(classes = Test2.class)
public class Test2 {@Testpublic void test1(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");UserService us = (UserService) ctx.getBean("userService");System.out.println(us);us.print();}
}

注意:需要提醒一下是,set注入的配置文件Bean的property属性name值是根据对应类的set方法名来的,而不是根据对应Bean的变量名。
在这里插入图片描述
在这里插入图片描述
上述是错误写法,其中name属性值应该修改为abcSdi才对。
另外,关于xml配置文件中的其他一些属性。
name属性用于给这个Bean起别名,多个别名之间用逗号隔开。
在这里插入图片描述
scope属性用于设置对应的Bean是单例,还是原型,默认情况下是单例的
在这里插入图片描述
可以修改为原型的。对于一个简单类型,想在配置文件中注入值,只需要设置其value属性即可。
在这里插入图片描述
如果想了解哪些类型是简单类型,可以去BeanUtils类下找到isSimpleValueType方法,查看对应的源码就可以知道。
在这里插入图片描述
常见的简单类型有八种基本数据类型、字符串、枚举类型、Class类型、日期类型(不过日期类型在配置文件需要写入特定的格式才支持,因此通常情况下会把日期类型当作是简单类型来注入)。

2-1-2. 通过构造方法来注入Bean

需要添加对应的构造方法即可,然后在配置文件中添加对应的构造参数即可。

package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;public class UserService {private UserDao ud;private Integer val;public void setVal(Integer val) {this.val = val;}public void setUd(UserDao ud) {this.ud = ud;}public UserService(){}public UserService(UserDao ud){this.ud = ud;}public UserService(UserDao ud,Integer val){this.ud = ud;this.val = val;}public void print(){System.out.println(ud+" "+val);}
}
<?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="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/><bean id="userService" class="com.lz.demo_spring_24_1.service.UserService"><constructor-arg name="ud" ref="userDao"/><constructor-arg name="val" value="1000"/></bean></beans>

上述这样配置存在一个问题,那就是耦合度比较高(因为这是通过构造方法的变量名来进行注入的)比如如果我在类文件里边修改ud为ud1,那么此时就需要在配置文件中做对应的修改。此时可以通过设置类型type属性从而解决这个耦合度问题,如下:
在这里插入图片描述
但是如果构造器方法中存在很多相同类型,上述解决办法就不行了,此时可以通过设置构造器方法中的参数位置index属性来解决。
在这里插入图片描述

2-1-3. 自动装配

在xml配置文件中的配置autowire属性即可,本质依旧是set注入,因此在类文件下依旧需要添加对应的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"><bean id="userDao" class="com.lz.demo_spring_24_1.dao.UserDao"/><bean id="userService" class="com.lz.demo_spring_24_1.service.UserService" autowire="byType"/>
</beans>

属性autowire的值也可以修改为byName,此时根据的是通过Bean的名字进行注入,因此在配置文件中要注入的Bean的id值不能随便取,这里需要额外注意一下。上述是根据Bean的类型进行注入的,只不过在xml配置文件中要注入的Bean只能为一个,否则会报错,因为它不知道到底需要注入哪一个。
对应的java类如下:

package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;
public class UserService {private UserDao ud;public void setUd(UserDao ud) {this.ud = ud;}public void print(){System.out.println(ud+" ");}
}

既然是自动装配,那么装配的那个Bean肯定是需要在配置文件中进行定义的。
在这里插入图片描述

2-1-4. 集合注入

对应java类参考代码如下:

package com.lz.demo_spring_24_1.other;import java.util.*;public class Datas {private int[] arr1;private List<String> list1;private Set<String> set;private Map<String,Object> map;private Properties properties;public void setArr1(int[] arr1) {this.arr1 = arr1;}public void setList1(List<String> list1) {this.list1 = list1;}public void setSet(Set<String> set) {this.set = set;}public void setMap(Map<String, Object> map) {this.map = map;}public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic String toString() {return "Datas{" +"arr1=" + Arrays.toString(arr1) +", list1=" + list1 +", set=" + set +", map=" + map +", properties=" + properties +'}';}
}

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="datas" class="com.lz.demo_spring_24_1.other.Datas"><property name="arr1"><array><value>123</value><value>456</value></array></property><property name="list1"><list><value>123</value><value>234</value></list></property><property name="set"><set><value>123</value><value>234</value></set></property><property name="map"><map><entry key="country" value="china"/><entry key="age" value="100"/></map></property><property name="properties"><props><prop key="country">china</prop><prop key="age">100</prop></props></property></bean>
</beans>

如果在数组、List、Set中注入非简单类型,只需要把value标签修改为ref标签,且在ref标签的bean属性中写入对应的Bean的名称即可。
在这里插入图片描述
对于map数据类型,如果key、value值是非简单类型,直接使用key-ref、value-ref即可。
在这里插入图片描述

2-1-5. 数据源对象管理(第三方Bean)

这里以druid数据源为例,需要导入druid的依赖。

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version>
</dependency>

在对应的xml配置文件中定义的Bean如下(这里使用的是set注入,由于其并没有对应设置对应配置的构造方法,所以使用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"><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mytest1"/><property name="username" value="root"/><property name="password" value="root"/></bean></beans>

运行结果:
在这里插入图片描述

2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)

前提是需要在xml中开启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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
</beans>

之后需要在xml配置文件中加载properties文件,最后修改其中Bean的一些配置即可。propertis文件配置如下:

spring.application.name=demo_spring_24_1jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mytest1
jdbc.username=root
jdbc.password=root

此时在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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="application.properties"/>
<!--    使用context空间加载properties文件--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean>
</beans>

如果想要加载多个properties文件,可以在location属性中用逗号隔开,写上其他properties文件即可。

<context:property-placeholder location="application.properties,application2.properties"/>

上述这种写法并不怎么规范,规范写法应该是这样,如下:

<context:property-placeholder location="classpath:*.properties"/>

如果想不加载系统属性,可以在上面context的属性system-properties-mode设置为NEVER即可。

<context:property-placeholder location="application.properties" system-properties-mode="NEVER"/>

2-1-7. 加载容器的其他方式

上述方式都是通过加载类路径下的配置文件来进行的,其实还可以通过加载绝对路径来进行。
在这里插入图片描述

2-1-8. p命名空间

这种方式本质上是set注入,因此依旧需要在对应的类文件中添加set方法,但是在配置文件中可以简化一些操作而已。(是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"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"></beans>

在这里插入图片描述

package com.lz.demo_spring_24_1.entity;import java.util.Date;public class User {private String name;private Date birthDay;public void setName(String name) {this.name = name;}public void setBirthDay(Date birthDay) {this.birthDay = birthDay;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", birthDay=" + birthDay +'}';}
}

在这里插入图片描述
只需要在配置文件以p:变量名后添加对应的值即可,因为变量birthDay是日期类型,可以使用简单类型,也可以使用非简单类型。
在这里插入图片描述
运行结果:
在这里插入图片描述

2-1-9. c命名空间

本质上是构造方法注入,因此需要添加对应的构造方法。(只是构造器注入的一种简化而已)。和p命名空间一样,都需要对xml配置文件添加一些配置,用以开启c命名空间。

<?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"><bean id="date" class="java.util.Date"/><bean id="user" class="com.lz.demo_spring_24_1.entity.User" c:_0="张三" c:birthDay-ref="date"/></beans>

有两种方式可以进行注入,一种是通过参数的位置,另外一种是通过参数名,参考如上,运行结果和p命名空间一样。

2-1-10. util命名空间

和上面两种命名空间一样,需要在xml配置文件中添加util命名空间的配置。配置如下:

<?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">
</beans>

现在假设我想要连接数据库,但是可以使用连接池可以用druid、c3p0,它们都需要添加一些连接数据库的配置。
在这里插入图片描述
在这里插入图片描述

并且连接的配置信息都相同,此时可以采用util命名空间(实现配置复用而已),如下:

<?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:properties id="pro"><prop key="driver">com.mysql.cj.jdbc.driver</prop><prop key="url">jdbc:mysql://localhost:3306/mytest1</prop><prop key="username">root</prop><prop key="password">root</prop></util:properties><bean id="ds1" class="com.lz.demo_spring_24_1.entity.MyDataSource1"><property name="properties" ref="pro"/></bean><bean id="ds2" class="com.lz.demo_spring_24_1.entity.MyDataSource2"><property name="properties" ref="pro"/></bean>
</beans>

运行结果如下:
在这里插入图片描述

2-2. 通过注解加载Bean

这种方式最初的版本依旧需要写xml配置文件,同时需要在对应的类上加上注解@Component,如下:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="com.lz.demo_spring_24_1.dao"/></beans>

只要是在com.lz.demo_spring_24_1.dao这个目录下所有的添加了@Component注解的类都会被容器进行管理。如果需要添加其他包下Bean被spring容器进行管理,可以在上述配置文件的包后用逗号隔开,之后再添加其他包路径即可。当然也可以写两个包的父包即可。

package com.lz.demo_spring_24_1.dao;import org.springframework.stereotype.Component;@Component
public class UserDao {
}

从@Component又衍生出其他三种注解,分别为@Repository(数据层)、@Service(业务层)、@Controller(表现层)。它们的功能都相同,只是为了便于分辨而已。

2-2-1. 选择性实例化Bean

对于一个包下所定义的Bean(添加了对应注解的),如何在xml配置文件中选择性去选择哪些Bean可以被实例化。比如现在我定义了两个Bean,其中一个为a,另一个为b,a上添加了注解@Service,b上添加了注解@Controller,现在使用context命名空间扫描这两个bean的父包。然后进行过滤,只把有注解@Service Bean a添加到spring容器中进行管理,参考代码如下:

package com.lz.demo_spring_24_1.beans;import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;@Service
public class A {public A(){System.out.println("A........");}
}@Controller
class B {public B(){System.out.println("B........");}
}

xml配置文件写法1

<?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"><context:component-scan base-package="com.lz.demo_spring_24_1.beans" use-default-filters="true"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan></beans>

xml配置文件写法2

<?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"><context:component-scan base-package="com.lz.demo_spring_24_1.beans" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/></context:component-scan></beans>

运行结果如下:
在这里插入图片描述

2-2-2. 纯注解加载Bean

这种模式不需要编写xml配置文件,在上述代码不变的基础上,新建一个配置类,当然需要添加@Configuration注解。写这个配置类相当于是代替上面那个xml配置文件。

package com.lz.demo_spring_24_1.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan(value = "com.lz.demo_spring_24_1.dao")
public class SpringConfig {
}

之前是加载那个配置文件xml,此时如果想要运行成功,需要加载这个配置类。

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.config.SpringConfig;
import com.lz.demo_spring_24_1.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.sql.SQLException;@SpringBootTest(classes = Test2.class)
public class Test2 {@Testpublic void test1() {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);UserDao userDao = (UserDao) ctx.getBean("userDao");System.out.println(userDao);}
}

2-2-3. 自动装配

现在我在数据访问层定义了一个类UserDao,还在业务层定义了一个类UserService,其中需要在UserService中引用UserDao类下的某个方法。为此,需要在UserService中new UserDao,在前面知识中了解到,可以通过set、构造器、自动装配这三种方式注入UserDao类对象,但是,上述讲述的是xml配置文件来进行注入的,现在如何使用配置类来实现上述那种自动装配的效果呢?参考代码如下:

package com.lz.demo_spring_24_1.dao;import org.springframework.stereotype.Repository;@Repository
public class UserDao {
}
package com.lz.demo_spring_24_1.service;import com.lz.demo_spring_24_1.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserDao ud;public void print(){System.out.println(ud);}}

我们只需要在UserService类中使用注解@AutoWired这种注入方式即可,当然也可以使用注解@Resource。它们两者的区别是前者首先是根据类型去对应包下去查找是否存在UserDao这个Bean,如果没有,再通过Bean名去查找,如果两者都没有找到或者出现歧义,最终会报错;而后者正好相反。@Resource注解可以通过name属性指定Bean名,如果name值没有指定,那么会把变量名当作Bean名来使用。。(也就是说如果此时变量名和对应的Bean名不一致,此时再根据类型来进行装配,如果没有找到,会报错。。。)

如果所注入的Bean有多个(比如有多个类都实现了某个接口,而且注入的Bean类型使用了泛型),此时可以在注解@AutoWired下添加注解@Qualifier,并在@Qualifier内写上对应的Bean名,当然这个Bean名需要在对应的类上写上才行。如下,有一个接口UserDao,它有一个方法printUserInfo,它有两个实现类UserDaoImpl1、UserDaoImpl2,这个类上都添加了注解@Repository。然后有一个类UserService,添加了注解@Service,在这个类下需要UserDao的依赖,这里直接使用泛型。如下:

package com.lz.demo_spring_24_1.beans;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserDao ud;public void printUserInfo(){ud.printUserInfo();}
}

此时运行结果如下:
在这里插入图片描述
因为UserDao有两个实现类,此时不知道使用哪个,因此报错,此时就可以添加注解@Qualifier添加对应的Bean名(不设置对应Bean名,Bean名默认为类名首字母小写。。),如下:
在这里插入图片描述
当然也可以直接使用@Resource注解,写上对应的Bean名。
在这里插入图片描述
另外,注解@AutoWired也可以放在set方法上及构造方法上,如果所注入的Bean只有一个,@AutoWired可以省略,但是需要添加对应的构造方法,需要注意的是如果存在默认无参构造方法,这样是不行的,如下:
在这里插入图片描述

2-2-4. 简单类型的注入

通过使用注解@Value可以注入简单类型,如下:

package com.lz.demo_spring_24_1.dao;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;@Repository
public class UserDao {@Value("lize")private String name;@Overridepublic String toString() {return "UserDao{" +"name='" + name + '\'' +'}';}
}

不过,上述这样写基本上没有任何意义,直接在name后面添加等于号并写上对应的值不也是一样吗?@Value真正意义在于可以注入配置文件properties中的变量,如下:
首先和上面xml配置一样,首先需要在配置类上指明所对应的配置文件,需要用到注解@PropertySource。
在这里插入图片描述
使用的话只需要在@Value注解内用${}指明引用哪个变量的值。
在这里插入图片描述
运行结果如下:
在这里插入图片描述
另外@Value还可以使用在set方法上,以及构造方法上的对应参数上。。

2-2-5. 管理第三方Bean

首先需要定义一个配置类,在这个配置类下定义对应Bean的方法。参考代码如下:

package com.lz.demo_spring_24_1.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;@Configuration
public class SpringConfig2 {@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.driver");ds.setUrl("jdbc:mysql://localhost:3306/mytest1");ds.setUsername("root");ds.setPassword("root");return ds;}
}

但是上述方式存在一个问题,就是如果我想修改上述一些配置信息,此时还需要找到这个类,然后再进行修改,为此,我们可以把上述信息放到配置文件properties中去,需要用到时只需要通过@Value注解注入即可。

2-3. Bean的实例化方式

这部分有一些内容和前面有重复,但是这里相当于是总结了吧!

2-3-1. 通过构造方法来实例化Bean

在xml配置文件中定义对应的Bean,然后直接通过容器.getBean方法来获取对应的Bean。默认情况下,是调用Bean的无参构造方法。。

2-3-2. 通过简单工厂模式实例化

本质上依旧是通过构造方法来实例化Bean,参考代码如下:
工厂类

 package com.lz.demo_spring_24_1.entity.factory;public class Factory1 {public static User getUser(){return new User();}}

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="user" class="com.lz.demo_spring_24_1.entity.factory.Factory1" factory-method="getUser"/></beans>

运行结果:
在这里插入图片描述

2-3-3. 通过工厂方法模式来实例化

本质上依旧是通过构造方法来实例化Bean,参考代码如下:
工厂类

package com.lz.demo_spring_24_1.entity.factory;public class UserFactory {public User get(){return new User();}
}

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="user2Factory" class="com.lz.demo_spring_24_1.entity.factory.UserFactory"/><bean id="user2" factory-bean="user2Factory" factory-method="get"/>
</beans>

运行结果:
在这里插入图片描述

2-3-4. 通过FactoryBean接口实例化

可以说是第三种方式的一种简化。参考代码如下:
实现了FactoryBean接口的类

package com.lz.demo_spring_24_1.entity.factory;import org.springframework.beans.factory.FactoryBean;public class UserFactoryBean implements FactoryBean<User> {@Overridepublic boolean isSingleton() {
//        return FactoryBean.super.isSingleton();return true;}// 默认是单例的@Overridepublic User getObject() throws Exception {return new User();}@Overridepublic Class<?> getObjectType() {return null;}
}

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="user3" class="com.lz.demo_spring_24_1.entity.factory.UserFactoryBean"/>
</beans>

运行结果:
在这里插入图片描述

2-3-5. BeanFactory 和 FactoryBean 的区别

BeanFactory 是spring ioc容器的最顶层对象,意为 “Bean工厂”,负责创建Bean对象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
FactoryBean 是一个Bean,是一个能够辅助 spring 实例化其他Bean对象的一个Bean。在spring中,Bean可以分为两类,一种为普通Bean,另外一种是工厂Bean。

2-3-6. FactoryBean 的应用(以自定义Date类型举例)

从前面可以知道,Date这种类型既可以当作是简单类型,也可以当作非简单类型。当作简单类型时,在xml配置文件中定义时,需要输入特定格式,否则会报错。而 FactoryBean 是Spring中一种用于辅助实例化其他Bean的Bean,为此,可以使用 FactoryBean 的形式,使在xml文件定义Date 类型的Bean支持自定义格式输入,参考代码如下:
继承了FactoryBean 接口的类

package com.lz.demo_spring_24_1.entity.factory;import org.springframework.beans.factory.FactoryBean;import java.text.SimpleDateFormat;
import java.util.Date;public class DateFactoryBean implements FactoryBean<Date> {private String date_str;public DateFactoryBean(String date_str) {this.date_str = date_str;}@Overridepublic boolean isSingleton() {return true;}@Overridepublic Date getObject() throws Exception {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 自定义输入的日期格式return sdf.parse(date_str);}@Overridepublic Class<?> getObjectType() {return null;}
}

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="date" class="com.lz.demo_spring_24_1.entity.factory.DateFactoryBean"><constructor-arg name="date_str" value="2021-01-01"/></bean></beans>

运行结果:
在这里插入图片描述

2-4. Bean的生命周期(5步)

就是Bean从被创建到销毁的过程。Bean的生命周期可以被划分为5个过程,分别是实例化Bean、Bean属性赋值、初始化Bean、使用Bean、销毁Bean

  • 实例化Bean,调用无参数构造方法;
  • Bean属性赋值,调用set方法;
  • 初始化Bean,调用 Bean 的 init方法,需要自己编写代码,并进行配置;
  • 使用Bean;
  • 销毁Bean,调用 Bean 的destory方法,需要自己编写代码,并进行配置;

Bean的声明周期代码演示如下:

package com.lz.demo_spring_24_1.entity;// Bean 的生命周期
public class User3 {private String name;public User3() {System.out.println("1. 实例化Bean。。。");}public void setName(String name) {this.name = name;System.out.println("2. Bean 参数赋值...");}// 初始化Beanpublic void init(){System.out.println("3. 初始化Bean。。。");}// 销毁 Beanpublic void destory(){System.out.println("5. 销毁Bean。。。");}
}
<?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="user" class="com.lz.demo_spring_24_1.entity.User3" init-method="init" destroy-method="destory"><property name="name" value="张三"/></bean></beans>
package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.entity.User3;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test2025_1 {@Testpublic void test3(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-2025-3.xml");User3 user3 = ctx.getBean("user", User3.class);System.out.println("4. 使用Bean"+user3);// 使用Beanctx.close();// 关闭容器,只能applicationContext实现类才有close方法。。。}
}

运行结果:
在这里插入图片描述

2-4-1. Bean 的生命周期之7步

在上面说到Bean的生命周期只有5步,7步的说法是在前面5步的基础上添加了2步,就是在5步的第4步 初始化Bean 前面加上 执行”Bean后处理器“的before方法,在后面加上 执行”Bean后处理器“的fater方法。。。
需要在前面基本上添加一个类,这个类需要实现BeanPostProcessor接口。。。,并重写其下面的2个方法。

package com.lz.demo_spring_24_1.entity.interfaces;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class LogBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行 Bean后处理器 的before方法");return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("执行 Bean后处理器 的after方法");return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}

另外需要在xml配置文件中配置这个Bean。
在这里插入图片描述
之后就可以看到运行结果了。。。
在这里插入图片描述
需要注意的是,上述添加这个Bean对所有的Bean都会生效。。。也就是说 当前容器对象 getBean 之后获取到Bean都会执行上述两个函数。。。

2-4-2. Bean 的生命周期之10步

在前面7步的基础之上,再额外添加3步,添加的位置分别为:

  • 在 执行”Bean后处理器“的before方法 前面添加 检查 Bean 是否实现了Aware的相关接口,并设置相关依赖;
  • 在 执行”Bean后处理器“的before方法 后面添加 检查 Bean 是否实现了InitializingBean接口,并调用接口方法;
  • 在使用Bean 之后添加了 检查 Bean 是否实现了DispossableBean接口,并调用接口方法。

其中Aware的相关接口有:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。

  • 当Bean实现了BeanNameAware,spring会将Bean的名字传递给Bean;
  • 当Bean实现了BeanClassLoaderAware,spring会将加载该Bean的类加载器传递给Bean;
  • 当Bean实现了BeanFactoryAware,spring会将Bean工厂对象传递给Bean。

总结而言:如果测试生命周期10步,需要让对应的类实现5个接口,分别为BeanNameAware、BeanClassLoaderAware、BeanFactoryAware、InitializingBean、DispossableBean。

演示代码如下:

package com.lz.demo_spring_24_1.entity;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;// Bean 的生命周期
public class User3 implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {private String name;public User3() {System.out.println("1. 实例化Bean。。。");}public void setName(String name) {this.name = name;System.out.println("2. Bean 参数赋值...");}// 初始化Beanpublic void init(){System.out.println("4. 初始化Bean。。。");}// 销毁 Beanpublic void destory1(){System.out.println("7. 销毁Bean。。。");}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("Bean的类加载器:"+classLoader);}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("Bean的 工厂对象是:"+beanFactory);}@Overridepublic void setBeanName(String s) {System.out.println("Bean的名字是:"+s);}@Overridepublic void destroy() throws Exception {System.out.println("实现了DisposableBean接口。。。");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("实现了InitializingBean接口。。。");}
}
<?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 class="com.lz.demo_spring_24_1.entity.interfaces.LogBeanPostProcessor"/><bean id="user" class="com.lz.demo_spring_24_1.entity.User3" init-method="init" destroy-method="destory1"><property name="name" value="张三"/></bean></beans>

运行结果:
在这里插入图片描述

2-4-3. Bean的作用域

spring 容器只对 单例的 Bean进行完整的生命周期管理。如果是原型的Bean,spring容器只负责将Bean初始化完毕,等客户端一旦获取到该Bean之后,spring容器就不在管理该对象的生命周期了。如果需要测试的话,只需要在xml配置文件的Bean添加属性scope,并设置值为原型,运行结果如下:
在这里插入图片描述

2-4-4. 自己实例化的对象让spring容器去管理

直接复制类代码,导包让软件去导入。。。

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.entity.User3;
import com.lz.demo_spring_24_1.entity.User4;
import com.lz.demo_spring_24_1.entity.factory.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;public class Test2025_1 {@Testpublic void test4(){User4 user4 = new User4();System.out.println(user4);DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();beanFactory.registerSingleton("user",user4);User4 user1 = beanFactory.getBean(User4.class);System.out.println(user1);}
}

运行结果:
在这里插入图片描述

2-5. Bean 循环依赖问题

其实就是在一个Bean a中需要Bean b的依赖,而在Bean b中又需要Bean a的依赖。

2-5-1. set注入 + 单例模式之循环依赖

比如如下代码:

package com.lz.demo_spring_24_1.entity.xunhuan;public class UserA {private String name;private UserB userB;public void setName(String name) {this.name = name;}public void setUserB(UserB userB) {this.userB = userB;}@Overridepublic String toString() {return "UserA{" +"name='" + name + '\'' +", userB=" + userB.getName() +'}';}public String getName() {return name;}
}
package com.lz.demo_spring_24_1.entity.xunhuan;public class UserB {private String name;private UserA userA;public void setName(String name) {this.name = name;}public void setUserA(UserA userA) {this.userA = userA;}@Overridepublic String toString() {return "UserB{" +"name='" + name + '\'' +", userA=" + userA.getName() +'}';}public String getName() {return name;}
}

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="usera" class="com.lz.demo_spring_24_1.entity.xunhuan.UserA"><property name="name" value="张三"/><property name="userB" ref="userb"/></bean><bean id="userb" class="com.lz.demo_spring_24_1.entity.xunhuan.UserB"><property name="name" value="李四"/><property name="userA" ref="usera"/></bean>
</beans>

运行结果:
在这里插入图片描述
如果上述代码UserA的toString方法中参数直接是UserB,并且在UserB的toString方法中参数直接是UserA。此时的结果会报错,因为输出UserA这个对象时,实际上调用的是重写toString方法,而在toString方法中又会输出UserB,而在UserB的toString方法下又有UserA,此时会陷入si循环。。最终导致内存溢出从而导致报错。
上面是 单例模式 + set注入(原型模式下不可以) 的模式下的运行结果,Spring容器在加载的时候,进行实例化,只要任意一个Bean实例化后,马上进行“曝光”,不等属性赋值;Bean被“曝光”之后,再进行属性赋值。(在spring中为什么可以解决循环依赖的问题。。。)需要注意的是,在spring中只有当两个Bean都是原型下,才会出现异常,但是如果其中有一个是单例的,就不会出现异常。。

2-5-2. 构造器注入 + 单例模式之循环依赖

这种方式是存在问题,因为这是直接在Bean a构造方法里面给属性赋值,但是其中一个参数Bean b还没有进行实例化,而Bean b里边又有一个参数Bean a也没有进行实例化。参考代码如下:

package com.lz.demo_spring_24_1.entity.xunhuan;public class UserA {private String name;private UserB userB;public UserA(String name, UserB userB) {this.name = name;this.userB = userB;}/*public void setName(String name) {this.name = name;}public void setUserB(UserB userB) {this.userB = userB;}*/@Overridepublic String toString() {return "UserA{" +"name='" + name + '\'' +", userB=" + userB.getName() +'}';}public String getName() {return name;}
}
package com.lz.demo_spring_24_1.entity.xunhuan;public class UserB {private String name;private UserA userA;public UserB(String name, UserA userA) {this.name = name;this.userA = userA;}/*public void setName(String name) {this.name = name;}public void setUserA(UserA userA) {this.userA = userA;}*/@Overridepublic String toString() {return "UserB{" +"name='" + name + '\'' +", userA=" + userA.getName() +'}';}public String getName() {return name;}
}

在这里插入图片描述
也就是说 构造器注入 + 单例模式 这种方式下spring是无法解决循环依赖问题的。

2-5-3. Spring 解决循环依赖的机理

set注入+单例模式下为什么能解决循环依赖问题?
根本原因在于:这种方式可以将 实例化Bean 和 给Bean属性赋值 这两个动作分开去完成。实例化Bean的时候,调用无参构造方法来完成,**此刻可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。**给Bean属性赋值的时候,调用setter方法来完成。两个过程是完全分开去完成的,并且两个过程不要求在同一个时间点上完成。

2-6. 自定义 spring 框架

这里的spring框架只有基本ioc功能,且还是通过配置文件的形式。。参考代码如下:
myspring核心代码

package com.lz.demo_spring_24_1.myspring.utils;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MyBeanFactory {private Map<String,Object> beanMap = new HashMap<>();// 用来存储bean的哈希表// 在构造方法这里的读取xml配置文件的数据,// 然后通过反射机制进行实例化对象,之后把实例化后的对象存储到哈希表中进行存储public MyBeanFactory(String configPath) {try{SAXReader reader = new SAXReader();InputStream stream = ClassLoader.getSystemClassLoader().getResourceAsStream(configPath);Document document = reader.read(stream);List<Node> nodes = document.selectNodes("//bean");// 获取所有的bean标签for (Node node : nodes) {Element ele = (Element) node;// 转换成Element类型String id = ele.attributeValue("id");String clazz = ele.attributeValue("class");// bean 的名称,class 字符串Class<?> aClass = Class.forName(clazz);Constructor<?> constructor = aClass.getDeclaredConstructor();Object o = constructor.newInstance();beanMap.put(id,o);// 通过反射对bean进行无参实例化}setBeanField(nodes,beanMap);}catch (Exception e){e.printStackTrace();}}/*** 获取bean的方法* beanName : Bean的名称* */public Object getBean(String beanName){return beanMap.get(beanName);}// 给对象属性赋值// 相当于set注入private void setBeanField(List<Node> nodes,Map<String,Object> beanMap){for (Node node : nodes) {try{Element ele = (Element) node;String id = ele.attributeValue("id");String clazz = ele.attributeValue("class");// bean 的名称,class 字符串Class<?> aClass = Class.forName(clazz);List<Element> properties = ele.elements();// 所有的属性标签properties.forEach(property->{try{String name = property.attributeValue("name");String value = property.attributeValue("value");String ref = property.attributeValue("ref");String setName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);Field field = aClass.getDeclaredField(name);
//                        field.setAccessible(true);Class<?> type1 = field.getType();Method setMethod = aClass.getDeclaredMethod(setName, type1);Object v = value;if(value != null){// 这个变量是私有的// 简单类型String typeSimpleName = type1.getSimpleName();// 属性类型名switch (typeSimpleName){case "byte":v = Byte.parseByte(value);break;case "short":v = Short.parseShort(value);break;case "int":v = Integer.parseInt(value);break;case "long":v = Long.parseLong(value);break;case "boolean":v = Boolean.parseBoolean(value);break;case "float":v = Float.parseFloat(value);break;case "double":v = Double.parseDouble(value);break;case "char":v = value.charAt(0);break;case "Byte":v = Byte.valueOf(value);break;case "Short":v = Short.valueOf(value);break;case "Integer":v = Integer.valueOf(value);break;case "Long":v = Long.valueOf(value);break;case "Boolean":v = Boolean.valueOf(value);break;case "Float":v = Float.valueOf(value);break;case "Double":v = Double.valueOf(value);break;case "Character":v = Character.valueOf(value.charAt(0));break;}setMethod.invoke(beanMap.get(id),v);}if(ref != null){// 非简单类型setMethod.invoke(beanMap.get(id),beanMap.get(ref));}}catch (Exception e){e.printStackTrace();}});}catch (Exception e){e.printStackTrace();}}}
}

测试类

package com.lz.demo_spring_24_1.myspring;public class User {private String name;private Integer age;private User2 user2;public void setAge(Integer age) {this.age = age;}public void setName(String name) {this.name = name;}public void setUser2(User2 user2) {this.user2 = user2;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", user2=" + user2 +'}';}
}
package com.lz.demo_spring_24_1.myspring;public class User2 {
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans><bean id="user" class="com.lz.demo_spring_24_1.myspring.User"><property name="name" value="张三"/><property name="age" value="20"/><property name="user2" ref="user2"/></bean><bean id="user2" class="com.lz.demo_spring_24_1.myspring.User2"/></beans>

运行结果:
在这里插入图片描述
需要注意的是,因为需要解析xml文件数据,需要导入对应依赖,如下:

<!--        用于解析xml文件的包--><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.2.0</version></dependency>

3. Spring之JdbcTemplate

JdbcTemplate是Spring提供的一个jdbc模板类,是对jdbc的封装。当然,现在大多数用的都是MyBatis等。首先需要导入的依赖为:

        <!-- MySQL JDBC 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency>
<!--        spring-jdbc--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId></dependency>

另外还需要spring-context的依赖哈。因为我这边使用的mysql 数据库版本为5.xxx的版本,因此使用mysql的驱动为5.xxx,在对应的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="myDataSource" class="com.lz.demo_spring_24_1.jdbcTemplate.MyDataSource"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mytest1?characterEncoding=utf8"/><property name="username" value="root"/><property name="password" value="root"/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="myDataSource"/></bean></beans>

往数据库中插入一条数据,如下:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.jdbcTemplate.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;import java.util.List;public class MyJdbcTemplate {@Testpublic void test(){ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-jdbcTemplate.xml");JdbcTemplate jdbcTemplate = ctx.getBean("jdbcTemplate",JdbcTemplate.class);String sql = "insert into user values(?,?)";jdbcTemplate.update(sql,2,"王五");}
}

插入是可以成功的。
如果想查询数据,并且查询出的数据字段都需要映射到对应实体类上对应变量上去,可以使用如下代码:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.jdbcTemplate.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;import java.util.List;public class MyJdbcTemplate {@Testpublic void test(){ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-jdbcTemplate.xml");JdbcTemplate jdbcTemplate = ctx.getBean("jdbcTemplate",JdbcTemplate.class);String sql = "select * from user";List<User> users = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(User.class));for (User user : users) {System.out.println(user);}}
}

如果想要更换成其他的DataSource,只需要在xml配置文件中修改对应DataSource的配置即可,class属性值修改为druid的,driver、url、username、password这四个属性名可能有所不同。。

4. Spring 代理模式

关于Spring aop的功能实现本质上就是动态代理,参考文章链接为:Spring AOP原理–动态代理。。。关于上述文章的静态代理,这里有更加详细的参考,代理类和被代理类都需要实现公共的接口,如下:
公共的接口类

package com.lz.demo_spring_24_1.proxy;
// 这是一个接口
public interface IUser {void play();// 方法 play
}

被代理的类

package com.lz.demo_spring_24_1.proxy.impl;import com.lz.demo_spring_24_1.proxy.IUser;public class IUserImpl implements IUser {@Overridepublic void play() {System.out.println("学习编程技术。。。");}
}

代理类

package com.lz.demo_spring_24_1.proxy;// 代理类
public class UserProxy implements IUser{// 这里应用泛型,可以降低代码的耦合度private IUser iUser = null;// 通过构造方法来把对应变量赋值public UserProxy(IUser iUser) {this.iUser = iUser;}@Overridepublic void play() {System.out.println("这里可以做一些前置操作。。。");iUser.play();System.out.println("这里可以做一些后置操作。。。");}
}

测试运行

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.IUser;
import com.lz.demo_spring_24_1.proxy.UserProxy;
import com.lz.demo_spring_24_1.proxy.impl.IUserImpl;
import org.junit.jupiter.api.Test;public class ProxyTest {@Testpublic void test(){IUser iUser = new IUserImpl();IUser userProxy = new UserProxy(iUser);userProxy.play();}
}

在这里插入图片描述
但是上述代理存在一个很大的问题,那就是接口下面的方法如果很多的话,并且在代理类上上的每个方法都需要增强,那么被代理类就需要写很多可能较为重复的增强代码;而且每个被代理类的都需要编写对应的代理类。因此,有了动态代理。。。

4-1. jdk 之动态代理

使用动态代理,代理类可以不用编写了,但是接口必须要有。。在上述代码基础之上进行操作。。。接口类和被代理类和上面一样。。jdk动态代理不需要额外添加依赖。

Proxy.newProxyInstance(arg1,arg2,arg3)

通过上述代码实现一个代理对象,其中参数分别表示的意思为:

  • arg1:被代理类的类加载器;
  • arg2:被代理类实现的接口;
  • arg3:最为关键,实现InvocationHandler的对象a,且对象a传入参数为被代理的那个对象(这样才能实现增强代码);这个只需要写一个即可,就可以解决上述静态代理存在的那两个问题。

实现InvocationHandler接口的类

package com.lz.demo_spring_24_1.proxy.jdkProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MyHandler implements InvocationHandler {private Object target;public MyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// method 为被代理类方法// args 方法参数// 反射。。System.out.println("这里做一些前置操作。。。");Object ans = method.invoke(target,args);System.out.println("这里做一些后置操作。。。");return ans;}
}

运行代码:

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.IUser;
import com.lz.demo_spring_24_1.proxy.UserProxy;
import com.lz.demo_spring_24_1.proxy.impl.IUserImpl;
import com.lz.demo_spring_24_1.proxy.jdkProxy.MyHandler;
import org.junit.jupiter.api.Test;import java.lang.reflect.Proxy;public class ProxyTest {@Testpublic void test2(){IUser iUser = new IUserImpl();IUser iUserProxy = (IUser) Proxy.newProxyInstance(iUser.getClass().getClassLoader(),iUser.getClass().getInterfaces(),new MyHandler(iUser));iUserProxy.play();}
}

运行结果和上述一致。。。

4-2. cglib 之动态代理

如果是maven项目,需要额外导入cglib的依赖才行。参考代码如下:

package com.lz.demo_spring_24_1.proxy.cglibProxy;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class MyCallback implements MethodInterceptor {@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("这里可以做一些前置操作。。。");Object ans = methodProxy.invokeSuper(target,args);System.out.println("这里可以做一些后置操作。。。");return ans;}
}
package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.cglibProxy.MyCallback;
import com.lz.demo_spring_24_1.proxy.impl.IUserImpl;
import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;public class ProxyTest {@Testpublic void test3(){Enhancer enhancer = new Enhancer();// 设置被代理的类enhancer.setSuperclass(IUserImpl.class);enhancer.setCallback(new MyCallback());IUserImpl iUser = (IUserImpl) enhancer.create();// 代理的类iUser.play();}
}

运行结果和上面一致。。

4-3. jdk 与 cglib 动态代理的区别

在这里插入图片描述
参考链接在这:jdk 与 cglib 动态代理的区别

5. 面向切面编程 AOP

详细请看这篇博文:Aop 面向切面编程

Spring 的AOP底层实现本质是动态代理,jdk、cglib动态代理两者都有,Spring在这两种动态代理中可以根据实际应用场景实现切换,如果是代理接口,会默认使用jdk动态代理;如果要代理某个类,这个类没有实现接口,那么就会切换到cglib。当然,也可以通过配置强制让Spring来使用两者动态代理中的一种。

在这里插入图片描述
首先需要导入aspect的依赖

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

之后编写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"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="com.lz.demo_spring_24_1.proxy.aspect"/>
<!--    自动扫描--><aop:aspectj-autoproxy/>
<!--    让 @Aspect 起作用-->
</beans>

编写Aspect的切面类

package com.lz.demo_spring_24_1.proxy.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MyAspect {@Before("execution(* com.lz.demo_spring_24_1.proxy.aspect.UserService.*(..))")public void fun1(){System.out.println("前置通知。。。");}}

测试代码

package com.lz.demo_spring_24_1;import com.lz.demo_spring_24_1.proxy.aspect.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyAspectTest {@Testpublic void test(){ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-2025-aspect.xml");UserService userService = ctx.getBean("userService",UserService.class);userService.selectAll();}
}

运行结果:
在这里插入图片描述
在这里可以打印一下UserService的Class值,可以发现它是属于cglib动态代理生成的。
在这里插入图片描述
因为UserService类并不是通过实现某某接口的。
如果想纯注解实现上述效果,只需要把上述xml配置文件用一个配置类来代替即可,配置类参考如下:

package com.lz.demo_spring_24_1.proxy.aspect;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan(value = "com.lz.demo_spring_24_1.proxy.aspect")
@EnableAspectJAutoProxy
public class Config {
}

当然测试代码的加载容器需要修改一下:
在这里插入图片描述

6. Spring 事务

具体详细请看这篇文章:Spring 事务。在一个业务中,可能涉及到多条sql数据的执行,这些sql数据要么全部执行成功,要么全部执行失败,为此,应用到了事务 Transaction。
事务的四个处理过程,包括开启事务、执行核心业务代码、提交事务、回滚事务。事务的四个特性为:原子性、一致性、隔离性、持久性,也就是常说的ACID,其中原子性表示事务不可以再分;一致性表示事务要么同时成功,要么同时失败;隔离性表示事务和事务之间互不干扰;持久性表示一旦事务提交,它对数据库的修改就是永久性的,即使系统发生故障,数据也不会丢失。

6-1. 事务的传播特性

关于事务的传播特性,总共有7种,下述只是给出常见的四种。
在这里插入图片描述
测试结果:如果外部事物存在,并且内部事务也存在,且两个事务的传播行为都为REQUIRED。此时内部事务有抛异常的代码,在外部事务里边进行了try/catch捕获,事务会进行回滚。
如果上述内部事务为REQUIRES_NEW,外部事务不变,此时外部事务会正常执行,内部事务会进行回滚。

6-2. 事务的隔离级别

事务的隔离级别包括读未提交、读已提交、可以重复读、串行化。在上面那篇文章里边只介绍了后3种,因为通过设置后面3种隔离级别,可以解决对应的问题,比如脏读、不可重复读、幻读。
实际测试:关于脏读,如果其中一个事务a执行查询操作,另外一个事务b执行插入操作。如果事务a设置的隔离级别为读未提交,b事务没有设置隔离级别(数据库是MYSQL,也就是隔离级别为读已提交)。事务b先执行,但是没有结束;事务a后执行并已结束,此时事务a读取到数据是脏数据,也就是脏读。如果事务a设置的隔离级别为读已提交,依旧按照上述执行流程来,此时事务a的运行结果会报错。

6-3. 事务的超时时间

如果事务设置了超时时间a,那么表示超过a秒如果该事务种所有的DML语句还没有执行完毕的话,最终结果会选择回滚事务的超时时间指的事务开始到最后一条DML语句执行完的时间(只要不超过这个时间,就不会进行回滚操作)。如果最后一条DML语句后面还有很多业务逻辑,这些业务逻辑执行的时间不计入超时时间。

6-4. 设置事务只读(readOnly)

之所以设置事务为只读,是为了提高select语句的执行效率(这里启动了Spring的优化策略)。在这种事务下,只能执行查询操作,执行插入、删除、修改操作都会报错。

相关文章:

后端:Spring(IOC、AOP)

文章目录 1. Spring2. IOC 控制反转2-1. 通过配置文件定义Bean2-1-1. 通过set方法来注入Bean2-1-2. 通过构造方法来注入Bean2-1-3. 自动装配2-1-4. 集合注入2-1-5. 数据源对象管理(第三方Bean)2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)2-1-7. 加载容器…...

排序:插入、选择、交换、归并排序

排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性 &#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&#xff0c;…...

认识+安装ElasticSearch

1. 为什么要学习ElasticSearch? 一般的来说,项目中的搜索功能尤其是电商项目,商品的搜索肯定是访问频率最高的页面之一。目前搜索功能是基于数据库的模糊搜索来实现的&#xff0c;存在很多问题。 1.1 数据库搜索所存在的问题 1.1.1 查询效率较低 由于数据库模糊查询不走索引&…...

一个模块实现期货分钟 K 线计算、主连行情合成

由于不同期货品种的交易时间存在差异&#xff0c;且不同期货合约的活跃度各不相同&#xff0c;因此基于期货快照行情数据合成分钟K线的计算方法在时间对齐上需要进行不同的处理。 本教程旨在提升 DolphinDB 在具体业务场景中的应用效率&#xff0c;并降低其在实际业务中的开发…...

PyTorch:.max(1)和.max(0)的使用

目录 1&#xff09;.max(1)的使用&#xff1a; 2&#xff09;.max(0)的使用&#xff1a; 1&#xff09;.max(1)的使用&#xff1a; 假设有一个形状为 ( m , n ) 的 Tensor x &#xff0c;其中m表示行数&#xff0c;n表示列数。 x.max(1) &#xff0c;相当于x.max(dim1) 。作…...

ASP.NET Core 中使用 Cookie 身份验证

在 ASP.NET Core 中使用 Cookie 身份验证&#xff0c;通常是为了实现用户的登录和授权。以下是配置 Cookie 身份验证的步骤。 1. 安装必要的 NuGet 包 首先&#xff0c;确保项目中包含 Microsoft.AspNetCore.Authentication.Cookies 包。你可以通过 NuGet 包管理器或命令行安…...

Ollama私有化部署大语言模型LLM

目录 一、Ollama介绍 二、安装Ollama 1、标准安装 2、国内加速 三、升级Ollama版本 四、使用Ollama 1、启动ollama服务 systemctl start ollama.service ollama serve 2、使用ollama命令 ollama run 运行模型 ollama ps 查看正在运行的模型 ollama list 查看(本地)…...

安卓app抓包总结(精)

前言 这里简单记录一下相关抓包工具证书的安装 burp证书安装 安装证书到移动设备(安卓7以后必须上传到设备系统根证书上) 导出证书 openssl x509 -inform DER -in cacert.der -out cacert.pem 转换格式 openssl x509 -inform PEM -subject_hash_old -in cacert.pem …...

Three.js 性能优化:打造流畅高效的3D应用

文章目录 前言一、减少几何体复杂度&#xff08;Reduce Geometry Complexity&#xff09;二、合并几何体&#xff08;Merge Geometries&#xff09;三、使用缓冲区几何体&#xff08;Use BufferGeometries&#xff09;四、纹理压缩与管理&#xff08;Texture Compression and M…...

PHP 在 2025 年的现状与展望

PHP 在 2025 年依然强劲&#xff0c;继续为超过 77% 使用已知服务器端编程语言的网站提供动力。这并非仅仅依靠遗留代码&#xff0c;像 WordPress、Shopify 和 Laravel 这样的主流平台持续推动 PHP 的发展&#xff0c;使其保持着 актуальность 并不断进化。 为什么…...

力扣经典二分题:4. 寻找两个正序数组的中位数

题目链接&#xff1a;4. 寻找两个正序数组的中位数 - 力扣&#xff08;LeetCode&#xff09; 一、题目分析 这道题目是让我们在 两个正序的数组中寻找中位数已知两个数组的大小分别是&#xff1a;int m nums1.size(),n nums2.size();中位数性质1&#xff1a;中位数左侧元素 …...

解决WordPress出现Fatal error: Uncaught TypeError: ftp_nlist()致命问题

错误背景 WordPress版本&#xff1a;wordpress-6.6.2-zh_CN WooCommerce版本&#xff1a;woocommerce.9.5.1 WordPress在安装了WooCommerce插件后&#xff0c;安装的过程中没有问题&#xff0c;在安装完成后提示&#xff1a; 此站点遇到了致命错误&#xff0c;请查看您站点管理…...

Excel 技巧07 - 如何计算到两个日期之间的工作日数?(★)如何排除节假日计算两个日期之间的工作日数?

本文讲了如何在Excel中计算两个日期之间的工作日数&#xff0c;以及如何排除节假日计算两个日期之间的工作日数。 1&#xff0c;如何计算到两个日期之间的工作日数&#xff1f; 其实就是利用 NETWORKDAYS.INTL 函数 - weekend: 1 - 星期六&#xff0c;星期日 2&#xff0c;如…...

快速实现一个快递物流管理系统:实时更新与状态追踪

物流管理是电商、仓储和配送等行业的重要组成部分。随着电子商务的快速发展&#xff0c;快递物流的高效管理和实时状态更新变得尤为关键。本文将演示如何使用Node.js、Express、MongoDB等技术快速构建一个简单的快递物流管理系统&#xff0c;该系统支持快递订单的实时更新和追踪…...

kvm 解决 安装windows 虚拟机cpu 核数问题

通过lscpu命令查到我本机的cpu信息如下 CPU(s): 12 —— 系统的总逻辑处理单元数量&#xff08;包括所有核心和逻辑处理器&#xff09;。Thread(s) per core: 2 —— 每个物理核心支持 2 个线程&#xff08;表示启用了超线程技术&#xff09;。Core(s) per socket: 6 —— 每个…...

Ansys Fluent Aeroacoustics 应用

探索 Ansys Fluent 在气动声学领域的前沿功能&#xff0c;彻底改变各行各业解决降噪和提高音质的方式。 了解气动声学 气动声学是声学的一个分支&#xff0c;它处理湍流流体运动产生的噪声以及这些声音通过流体介质&#xff08;如空气&#xff09;的传播。这个领域在工程中至…...

119.使用AI Agent解决问题:Jenkins build Pipeline时,提示npm ERR! errno FETCH_ERROR

目录 1.Jenkins Build时的错误 2.百度文心快码AI智能体帮我解决 提问1&#xff1a;jenkins中如何配置npm的源 提问2&#xff1a;jenkins pipeline 类型为pipeline script from SCM时&#xff0c;如何配置npm源 3.最终解决方法-Jenkinsfile的修改 4.感触 1.Jenkins Build时…...

istio-proxy内存指标

在 Istio 环境中&#xff0c;istio-proxy 是 Envoy 的边车代理容器。通过运行命令 curl localhost:15000/memory&#xff0c;或者curl localhost:15000/stats 可以查询 Envoy 的内存统计信息。以下是典型返回结果的结构和意义&#xff1a; 返回结果单位是bytes&#xff0c;需/…...

List详解 - 双向链表的操作

在C中&#xff0c;std::list是标准模板库&#xff08;STL&#xff09;中的一个容器&#xff0c;它实现了双向链表的数据结构。与数组或向量&#xff08;std::vector&#xff09;不同&#xff0c;std::list允许在常数时间内进行插入和删除操作&#xff0c;尤其是在链表的任意位置…...

多目标优化算法之一:基于分解的方法

在多目标优化算法中,“基于分解的方法”通常指的是将多目标优化问题(MOP)分解为多个单目标优化子问题,并同时优化这些子问题。这种方法的核心思想是通过引入权重向量或参考点,将多目标问题转化为多个标量优化问题,每个子问题都关注于原始问题的一个特定方面或视角。这样可…...

conntrack iptables 安全组

centos 安装yum install conntrack-tools 1. conntrack状态 NEW: 新建连接&#xff08;第一次包&#xff09;。 ESTABLISHED: 已建立连接&#xff0c;正在传输数据。 RELATED: 与已有连接相关的连接&#xff0c;如 FTP 数据连接。 INVALID: 无效连接&#xff0c;无法识别或不…...

stringRedisTemplate.execute执行lua脚本

stringRedisTemplate.execute执行lua脚本 1. 引入必要依赖 确保项目中已经引入了Spring Data Redis相关依赖&#xff0c;例如在 Maven 项目中&#xff0c;一般会有如下依赖&#xff08;版本号根据实际情况调整&#xff09;&#xff1a; <dependency><groupId>or…...

HDFS异构存储和存储策略

一、HDFS异构存储类型 1.1 冷、热、温、冻数据 通常&#xff0c;公司或者组织总是有相当多的历史数据占用昂贵的存储空间。典型的数据使用模式是新传入的数据被应用程序大量使用&#xff0c;从而该数据被标记为"热"数据。随着时间的推移&#xff0c;存储的数据每周…...

生成idea ui风格界面代码

创建一个类似 IntelliJ IDEA 的用户界面&#xff08;UI&#xff09;涉及多个组件和复杂的布局设计。为了简化这个过程&#xff0c;我们可以使用 **Java Swing** 或 **JavaFX** 来实现一个基本的 IDE 界面&#xff0c;模仿 IntelliJ IDEA 的主要布局元素&#xff0c;如菜单栏、工…...

嵌入式C语言:二维数组

目录 一、二维数组的定义 二、内存布局 2.1. 内存布局特点 2.2. 内存布局示例 2.2.1. 数组元素地址 2.2.2. 内存布局图&#xff08;简化表示&#xff09; 2.3. 初始化对内存布局的影响 三、访问二维数组元素 3.1. 常规下标访问方式 3.2. 通过指针访问 3.2.1. 指向数…...

【机器学习:四、多输入变量的回归问题】

多输入变量的回归问题 1. 多元线性回归概述 1.1 单变量线性回归与多变量线性回归的概念区分 单变量线性回归&#xff1a;用于预测一个因变量&#xff08;输出变量&#xff09;与单一自变量&#xff08;输入变量&#xff09;之间的线性关系。模型形式为&#xff1a; y θ 0 …...

JVM实战—OOM的定位和解决

1.如何对系统的OOM异常进行监控和报警 (1)最佳的解决方案 最佳的OOM监控方案就是&#xff1a;建立一套监控平台&#xff0c;比如搭建Zabbix、Open-Falcon之类的监控平台。如果有监控平台&#xff0c;就可以接入系统异常的监控和报警&#xff0c;可以设置当系统出现OOM异常&…...

iOS 本地新项目上传git仓库,并使用sourceTree管理

此文记录的场景描述&#xff1a; iOS前期开发时&#xff0c;在本地创建项目&#xff0c;直至开发一段时间&#xff0c;初期编码及框架已完善后&#xff0c;才拿到git仓库的地址。此时需要将本地代码上传到git仓库。 上传至git仓库&#xff0c;可以使用终端&#xff0c;键入命令…...

mysql之基本select语句 运算符 排序分页

1.SQL的分类 DDL:数据定义语言. CREATE ALTER DROP RENAME TRUNCATE DML: 数据操作语言. INSERT DELETE UPDATE SELECT 重中之重 DCL: 数据控制语言. COMMIT ROLLBACK SAVEPOINT GRANT REVOKE 2.SQL语言的规则与规范 1.基本规则 SQL可以在一行或多行,为了提高可…...

如何在 Ubuntu 22.04 上安装 Nagios 服务器教程

简介 在本教程中&#xff0c;我们将解释如何在 Ubuntu 22.04 上安装和配置 Nagios&#xff0c;使用 Apache 作为 Web 服务器&#xff0c;并通过 Let’s Encrypt Certbot 使用 SSL 证书进行保护。 Nagios 是一个强大的监控系统&#xff0c;它可以帮助组织在 IT 基础设施问题影…...

石家庄网站开发费用/网络营销外包收费

骨牌铺方格 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 747 Accepted Submission(s): 479 Problem Description在2n的一个长方形方格中,用一个1 2的骨牌铺满方格,输入n ,输出铺放方案的总数.例如n3时,为2 3方格&…...

网站建设文化事业建设费/站长工具怎么关掉

CVS很酷&#xff0c;但Subversion更酷。然而&#xff0c;如果你在使用Eclipse进行开发&#xff0c;那么你可能直到近来才能利用Subversion带来的优点摘要 CVS很酷&#xff0c;但Subversion更酷。然而&#xff0c;如果你在使用Eclipse进行开发&#xff0c;那么你可能直到近来才能…...

做动态网站的总结/恢复正常百度

Linux服务器具有低成本、性能卓越、代码开放等特性。越来越多的企业正在准备或已经采用Linux担起了企业应用服务器的重任。本文要介绍的是笔者在实际工作中&#xff0c;采用Linux和其它开放套件共同部署高可靠性LDAP认证服务的实例。系统所要用到的软件包括&#xff1a;◆ Red …...

做个网站多少钱/百度关键词统计

更好的阅读体验点击原文链接 大家好&#xff0c;计算机视觉life经过几个月打磨&#xff0c;推出了激光SLAM逐行源码解析课程《Cartographer从入门到精通: 原理深剖源码逐行详解》&#xff0c;已经购买过我们的课程学员及知识星球用户均有优惠券&#xff08;见文末&#xff09;…...

网站建设方案浩森宇特/长沙网站优化推广方案

【案例2-5】输入圆的半径计算面积和周长 一、案例描述 考核知识点 toFixed()、isNaN、window.document对象 练习目标 掌握toFixed()方法。掌握数据类型检测。了解windoe.document对象。 需求分析 用JavaScript代码来计算圆的周长和面积&#xff0c;用户自己输入正确的r半径&…...

专门做狗猫配套网站有什么意思/关联词有哪些四年级

socket io.connect(http://192.168.1.200:9043?uuid333); 执行上面的语句时&#xff0c;产生下面的错误&#xff1a; 后来经过排查&#xff0c;是由于项目的jdk版本过低引起的&#xff0c;升级为1.7后就正常了。...