【Spring总结】注解开发

news2024/11/17 7:36:14

本篇讲的内容主要是基于Spring v2.5的注解来完成bean的定义
之前都是使用纯配置的方式来定义的bean

文章目录

  • 前言
  • 1. Spring v2.5 注解开发定义bean
    • 第一步:在需要定义的类上写上注解`@Component`
    • 第二步:在Spring Config中定义扫描包
    • 第三步:主方法中测试
    • 基于@Component的衍生注解
  • 2. Spring v3.0 纯注解开发
    • 第一步:创建配置类
    • 第二步:导入配置类
  • 3. Bean的作用范围(单例/多例)与生命周期
    • Bean的作用范围
    • Bean的生命周期
  • 4.依赖注入
    • 引用类型注入
      • @Autowire属性默认按类型注入
      • 按类型注入反射结果不唯一,按名注入
      • 按名注入的标准写法(少用)
    • 简单类型注入
    • 引入外部的properties文件
  • 5. 第三方Bean
    • 管理
    • 依赖注入
      • 简单类型注入
      • 引用类型注入
  • 6. XML配置比对注解配置

前言

XML配置解耦,而注解是简化开发

1. Spring v2.5 注解开发定义bean

之前都是使用纯配置的方式,在Spring Config中通过<bean id="xxx" class="xxx" />来定义的bean,从此处开始将根据注解开发定义bean

第一步:在需要定义的类上写上注解@Component

这里有两种写的形式:
@Component
或者是给其定义名称
@Component("bookDao")
通过第一种形式,就通过在getBean()方法中写类型来获得bean
通过第二种形式,就通过在getBean()方法中写名称来获得bean

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.stereotype.Component;

@Component
// @Component("bookDao")
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("Book Dao Save...");
    }
}

第二步:在Spring Config中定义扫描包

注意,这里也要开启命名空间
然后通过<context:component-scan base-package="...." />进行定义扫描的包,这里就是扫描com.example.demo.dao下边的包,修改这个包为com.example.demo也是可以的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
        ">

    <context:component-scan base-package="com.example.demo.dao" />

</beans>

第三步:主方法中测试

package com.example.demo;

import com.example.demo.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoApplication {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

		// 第一种定义形式写法,定义@Component写
		BookDao bookDao = ctx.getBean(BookDao.class);
		System.out.println(bookDao);

		// 第二种定义形式写法,定义@Component("bookDao")写
 		BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
		System.out.println(bookDao1);
	}
}

结果均为

com.example.demo.dao.impl.BookDaoImpl@3a52dba3

基于@Component的衍生注解

@Controller:表现层定义bean
@Service:业务层定义bean
@Repository:数据层定义bean
比如,在BookServiceImpl类上就使用@Service进行定义;在BookDaoImpl类上就使用@Repository进行定义,加括号的规则@Component类似,调用的方式也和原本的类似


2. Spring v3.0 纯注解开发

既然是纯注解开发,就需要删除之前冗余的这个配置文件,纯注解开发将配置的xml文件转换为了一个配置类

第一步:创建配置类

配置类写法为,实际上就是用两个注解来替代了原本配置文件中的内容:

package com.example.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.example.demo")
public class SpringConfig {
}

配置文件中的内容分别被配置类中的内容替代,如下:
在这里插入图片描述
假如想扫描多个包,使用{}来写即可,如:@ComponentScan({"com.example.demo.dao", "com.example.demo.service"})

第二步:导入配置类

将原本的加载配置文件的ClassPathXmlApplicationContext换成了加载配置类AnnotationConfigApplicationContext,里面的参数是注解类名称.class,即读取这个注解类,剩下getBean的操作和原来一样。

package com.example.demo;

import com.example.demo.config.SpringConfig;
import com.example.demo.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

	public static void main(String[] args) {
		ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
		
		BookDao bookDao = ctx.getBean(BookDao.class);
		System.out.println(bookDao);

	}
}

3. Bean的作用范围(单例/多例)与生命周期

Bean的作用范围

之前配置Bean的作用范围是在配置文件里通过指定scope属性定义的:
scope="singleton/prototype"

<bean id="bookDao" name="dao" class="com.example.demo231116.dao.impl.BookDaoImpl" scope="prototype" />

因为Bean默认是单例模式,如果我们想要修改成多例模式,就在类上加@Scope注解,括号的内容写prototype为多例,singleton为单例

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Repository
@Scope("prototype")
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("Book Dao Save...");
    }
}

Bean的生命周期

之前提到,Bean的生命周期其实就是我们可以定义在Bean加载的时候初始化一些资源,在销毁前销毁一些资源,在配置文件中写初始化和销毁是通过init-methoddestroy-method实现的:

<bean id="bookDaoCycle" class="com.example.demo231116.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy" />

在注解开发中,我们只需要在类中写好初始化和销毁方法,并分别加上PostConstruct(注解后执行init方法)和PreDestroy(销毁前执行destroy方法)两个注解,注意,初始化和销毁方法的方法名是任意的:

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Repository
//@Scope("prototype")
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("Book Dao Save...");
    }

    @PostConstruct
    public void init(){
        System.out.println("init...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("destroy");
    }

}

注意,如果提示没找到注解的话需要在pom.xml中导入坐标:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.2</version>
</dependency>

然后再执行主方法:

package com.example.demo;

import com.example.demo.config.SpringConfig;
import com.example.demo.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

	public static void main(String[] args) {
		ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

		BookDao bookDao = ctx.getBean(BookDao.class);
		System.out.println(bookDao);

	}
}

执行的结果为:

init...
com.example.demo.dao.impl.BookDaoImpl@7eac9008

没有调用Destroy方法的原因是没有关闭IoC容器/注册关闭钩子,只需要像之前一样在程序结束前使用ctx.close()或通过ctx.registerShutdownHook()来注册关闭钩子
(该方法注意不能使用ApplicationContext类,因为没有close()方法和registerShutdownHook()方法)

package com.example.demo;

import com.example.demo.config.SpringConfig;
import com.example.demo.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
//		方法二
		ctx.registerShutdownHook();

		BookDao bookDao = ctx.getBean(BookDao.class);
		System.out.println(bookDao);

//		方法一
		ctx.close();

	}
}

注意:非单例模式不会执行destroy,因为多例模式下Spring不负责销毁。Spring默认是单例的,它只管单例的销毁而不管多例的销毁

4.依赖注入

引用类型注入

依赖注入使用自动装配的方式,这里阉割掉了配置文件中的Setter注入、构造注入等等一系列冗杂的方法,使用自动装配。

@Autowire属性默认按类型注入

这里注入很简单,只需要在属性上加上@Autowire注解,就能够实现自动装配,这里的自动装配是基于暴力反射实现的,也无需再BookDaoImpl中实现set方法

package com.example.demo.service.impl;

import com.example.demo.dao.BookDao;
import com.example.demo.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    BookDao bookDao;
    @Override
    public void save() {
        System.out.println("Book Service Save...");
        bookDao.save();
    }
}

假如我有两个类实现了BookDao.java这个类:BookDaoImplBookDaoImpl2,这时候再执行自动装配会报错,因为基于类型反射得到的类不唯一

按类型注入反射结果不唯一,按名注入

针对这种不唯一的情况,给两个类的@Repository注解后加名字,BookDaoImplBookDaoImpl2分别为@Repository("bookDao")@Repository("bookDao2")

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.stereotype.Repository;

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("Book Dao Save...");
    }
}
package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.stereotype.Repository;

@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao {
    @Override
    public void save() {
        System.out.println("Book Dao Save...2");
    }
}

此时,假如我们在Service中的属性定义是

@Autowired
BookDao bookDao2;

则会注入对应BookDaoImpl2这个类,如果写的是bookDao1则会注入对应BookDaoImpl这个类
以上其实就是说明,Spring注解开发中,如果按类型注入不成功,就会按名注入,这些都是自动的,无需人为配置按类型注入/按名注入

按名注入的标准写法(少用)

事实上,按名注入更标准的方法是使用@Qualifier注解,这种注解还是要基于给BookDaoImplBookDaoImpl2都在@Repository后指定了名称的情况下进行的,标准写法如下:

package com.example.demo.service.impl;

import com.example.demo.dao.BookDao;
import com.example.demo.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier("bookDao2")
    BookDao bookDao;
    @Override
    public void save() {
        System.out.println("Book Service Save...");
        bookDao.save();
    }
}

@Qualifier后指定你想让bookDao属性被哪个类注入,注意,该注解必须搭配@Autowired注解使用

简单类型注入

使用@Value属性完成注入,括号内写值

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository
public class BookDaoImpl implements BookDao {

    @Value("namenamename!!!")
    String name;
    
    @Override
    public void save() {
        System.out.println("Book Dao Save..." + this.name);
    }
}

就这样的方法,可能会有疑问,这样写和String name = "namenamename!!!"的区别是什么?
不一样的地方在于:你注解里面的这个字符串,可能来自于外部(比如来自于外部的properties文件)

引入外部的properties文件

假如在Resource下有jdbc.properties文件,有内容:

name=namenamename!!!

那么我们需要在配置类中加载这个文件,加上注解@PropertySource,括号内是文件名:

package com.example.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.example.demo")
@PropertySource("jdbc.properties")
public class SpringConfig {
}

然后在值名称上写使用${}引用即可:

package com.example.demo.dao.impl;

import com.example.demo.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository
public class BookDaoImpl implements BookDao {

    @Value("${name}")
    String name;
    @Override
    public void save() {
        System.out.println("Book Dao Save..." + this.name);
    }
}

假如想加载多个配置文件,使用{}来写即可,类似于@ComponentScan
但是注意:之前在配置文件里加载的时候支持使用通配符*但这里完全不支持使用通配符!
但是加上@PropertySource("classpath:jdbc.properties")是可以的,只是不能使用通配符


5. 第三方Bean

此处以数据库连接为例,需要在pom.xml中导入坐标:

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

管理

在配置类中写一个方法,一般方法名为Bean的ID。然后new一个第三方对象,并返回。
在该方法上加上@Bean注解,表明该方法返回的是一个Bean

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
@ComponentScan("com.example.demo")
public class SpringConfig {

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/mysql");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}

这样写就可以在主方法里像平常一样获得Bean:

DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);

但是将第三方的配置写在Spring的配置类中可能比较乱,所以我们给它新建一个文件JdbcConfig.java

package com.example.demo.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 JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/mysql");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}

想要让这个文件被扫描到
第一种方法是在这个配置文件上加上注解@Configuration,然后在配置类中用@ComponentScan()扫描这个包:
@ComponentScan("com.example.config")
第二种方法(推荐使用)无需在文件上加注解@Configuration,而是直接在配置类中加上@Import注解,并在括号内写这个新的配置文件名.class@Import(JdbcConfig.class)
完整代码如下:

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import javax.sql.DataSource;

@Configuration
//@ComponentScan("com.example.demo.config")
@Import(JdbcConfig.class)
public class SpringConfig {

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/mysql");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
}

多个数据也使用数组模式

依赖注入

简单类型注入

只需要在类里面定义简单类型的成员变量,并在方法中使用成员变量即可,同样通过@Value()注解赋值

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class JdbcConfig {
    
    @Value("com.mysql.jdbc.Driver")
    String driverClassName;
    
    @Value("jdbc:mysql://localhost:3306/mysql")
    String url;
    
    @Value("root")
    String usernmae;
    
    @Value("123456")
    String password;
    
    
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driverClassName);
        ds.setUrl(url);
        ds.setUsername(usernmae);
        ds.setPassword(password);
        return ds;
    }
}

引用类型注入

假如要注入引用类型,需要在方法的参数里面写引用类型

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.example.demo.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class JdbcConfig {

    @Value("com.mysql.jdbc.Driver")
    String driverClassName;

    @Value("jdbc:mysql://localhost:3306/mysql")
    String url;

    @Value("root")
    String usernmae;

    @Value("123456")
    String password;


    @Bean
    public DataSource dataSource(BookDao bookDao){
        bookDao.save();
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driverClassName);
        ds.setUrl(url);
        ds.setUsername(usernmae);
        ds.setPassword(password);
        return ds;
    }
}

因为我们已经注解了这个dataSource是个Bean,所以对于方法里面的形参,Spring会自动到IoC里面查找对应的类进行注入

6. XML配置比对注解配置

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1226818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java springboot 在测试类中声明临时Bean对象

上文 java springboot在当前测试类中添加临时属性 不影响application和其他范围 中 我们讲了怎么在测试类中设置临时属性 但是 如果我们想设置临时的Bean呢&#xff1f; 其实做过几个项目的人都会理解 我们很多功能 需要一些第三方bean才能完成 那么 我们可能存在需要用第三方b…

图书管理系统(图文详解,附源码)

前言&#xff1a;本文旨在用面向对象的思想编程实现图书管理系统&#xff0c;功能包括增删查找&#xff0c;完整源码放在文末&#xff0c;大家有需自取 目录 一.整体框架 二.书籍和书架 书籍(Book) 书架(BookRack) 三.对书籍的相关操作 操作接口(IOperation) 新增图书(A…

项目点使用Redis作为缓存技术-自用

在spring boot项目中&#xff0c;使用缓存技术只需在项目中导入相关缓存技术的依赖包&#xff0c;并在启动类上使用EnableCaching开启缓存支持即可。 例如&#xff0c;使用Redis作为缓存技术&#xff0c;只需要导入Spring data Redis的maven坐标即可。 描述 使用Redis缓存高频数…

WSA子系统(一)

WSA子系统安装教程 Windows Subsystem for Android (WSA) 是微软推出的一项功能&#xff0c;它允许用户在 Windows 11 上运行 Android 应用程序。通过在 Windows 11 上引入 WSA&#xff0c;用户可以在其 PC 上轻松运行 Android 应用程序&#xff0c;从而扩展了用户的应用程序选…

RobotFramework之用例执行时添加命令行参数(十三)

学习目录 引言 标签tag 设置变量 随机执行顺序 设置监听器 输出日志目录和文件 引言 Robot Framework 提供了许多命令行选项&#xff0c;可用于控制测试用例的执行方式以及生成的输出。本节介绍一些常用的选项语法。 标签tag 之前文章我们介绍过&#xff0c;在测试套件…

Leetcode经典题目之“双指针交换元素“类题目

1 LC 27. 移除元素 class Solution {public int removeElement(int[] nums, int val) {int nnums.length;int s0;for(int i0;i<n;i){// 只有不等于目标值的时候才会进行交换&#xff0c;然后移动s指针if(nums[i]!val){swap(nums,i,s);}}return s;}void swap(int[]nums, int…

(数据结构)算法的时间复杂度

注意语句频度和时间复杂度的区别&#xff0c;语句频度是指语句执行的次数&#xff0c;不可以像时间复杂度一样近似次数和省略常数项

在线识别二维码工具

具体请前往&#xff1a;在线二维码识别解码工具--在线识别并解码二维码网址等内容

FlinkCDC数据实时同步Mysql到ES

考大家一个问题&#xff0c;如果想要把数据库的数据同步到别的地方,比如es,mongodb,大家会采用哪些方案呢&#xff1f; ::: 定时扫描同步&#xff1f; 实时日志同步? 定时同步是一个很好的方案&#xff0c;比较简单&#xff0c;但是如果对实时要求比较高的话&#xff0c;定…

HFSS螺旋线圈的设计与仿真

HFSS螺旋线圈的设计与仿真 HFSS中设计螺旋线圈的方法&#xff1a;参考文献&#xff1a; HFSS中设计螺旋线圈的方法&#xff1a; 打开软件Ansys Eletronics Desktop 2022 R1&#xff0c; 建立工程&#xff0c; File/New 插入HFSS设计&#xff0c; Project/Insert HFSS Design …

【Spring】之初识

未来的几周时间&#xff0c;大概率我会更新一下Spring家族的一些简单知识。而什么是Spring家族&#xff0c;好多同学还不是很清楚&#xff0c;我先来简单介绍一下吧&#xff1a; 所谓Spring家族&#xff0c;它其实就是一个框架&#xff0c;是基于Servlet再次进行封装的内容。为…

大模型的交互能力

摘要&#xff1a; 基础大模型显示出明显的潜力&#xff0c;可以改变AI系统的开发人员和用户体验&#xff1a;基础模型降低了原型设计和构建AI应用程序的难度阈值&#xff0c;因为它们在适应方面的样本效率&#xff0c;并提高了新用户交互的上限&#xff0c;因为它们的多模式和生…

笔记55:长短期记忆网络 LSTM

本地笔记地址&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\3.循环神经网络\第9章&#xff1a;动手学深度学习~现代循环神经网络 a a a a a a a a a

jbase打印完善

上一篇实现了粗略的打印元素绘制协议&#xff0c;并且写了打印示例和导出示例&#xff0c;趁着空隙时间完善一下打印。 首先元素构造函数默认初始化每个字段值 package LIS.Core.Dto;/*** 打印约定元素*/ public class PrintElement {/*** 元素类型*/public String PrintType…

OpenCV快速入门:图像形态学操作

文章目录 前言一、图像形态学基础1.1 背景介绍1.2 像素距离1.2.1 什么是像素距离&#xff1f;1.2.2 常见的像素距离度量方法1.2.3 计算像素距离的代码实现 1.3 图像连通性1.3.1 什么是图像连通性&#xff1f;1.3.2 连通类型1.3.3 连通组件标记1.3.4 连通性在图像处理中的应用 1…

2023.11.18 每日一题(AI自生成应用)【C++】【Python】【Java】【Go】 动态时间序列分析

目录 一、编程挑战&#xff1a;动态时间序列分析 实际应用&#xff1a; 实现提示&#xff1a; 二、实现 1. C 2. Python 3. JAVA 4. Go 一、编程挑战&#xff1a;动态时间序列分析 问题描述&#xff1a; 假设你是一名软件工程师&#xff0c;需要开发一个应用来分析和预…

内容运营策略:个性化推荐

一、推荐系统流程 典型的推荐系统包括3个部分&#xff0c;即召回层&#xff08; Recall )、排序层&#xff08; Rank )和重排层&#xff08; ReRank )。 1&#xff0e;召回层&#xff08; Recall ) 召回层主要是从全量库中首先获取用户可能感兴趣的候选集&#xff0c;是推荐系…

「Tech初见」对epoll的理解

一、Motivation 通常&#xff0c;操作系统会为每个进程划分一个时间片的&#xff0c;在这个时间片内进程可以合法占有 cpu 进行一些计算任务。并当时间片结束后自动退回至就绪状态待命&#xff0c;等待下一次的调度 但是&#xff0c;有一种情况会使进程提前&#xff08;时间片…

cad提示由于找不到mfc140u.dll,无法继续执行代码怎么修复

在Windows操作系统中&#xff0c;mfc140u.dll是一个重要的文件&#xff0c;很多软件运行都需要它&#xff0c;它属于Microsoft Visual C库的一部分。许多基于C的开发项目都依赖于这个文件&#xff0c;如果在使用过程中出现丢失现象&#xff0c;可能导致相关软件或游戏无法正常运…

洛谷 P1064 [NOIP2006 提高组] 金明的预算方案 python解析

P1064 [NOIP2006 提高组] 金明的预算方案 时间&#xff1a;2023.11.19 题目地址&#xff1a;[NOIP2006 提高组] 金明的预算方案 题目分析 动态规划的0-1背包&#xff0c;采用动态数组。如果不了解的话&#xff0c;可以先看看这个背包DP。 这个是0-1背包的标准状态转移方程 f…