Spring Session:Redis序列化配置|Session事件监听

        Spring Session是可配置的。

Redis Configuration

JSON序列化配置

        Spring Session默认使用Java对象序列化机制对Session属性值进行序列化。

预定义类SysUser

        先来定义一个SysUser类,在下面的演示案例中会用到。

package com.example.demo.model.entity;import java.io.Serializable;public class SysUser implements Serializable {private static final long serialVersionUID = -1802561599630491376L;private Integer id;private String name;private Integer age;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public SysUser() {}public SysUser(Integer id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}@Overridepublic String toString() {return "SysUser{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}
}

Java对象序列化机制

        序列化是指把一个Java对象变成二进制内容,本质上是一个byte[]数组。

        通常,我们在SpringBoot Web应用中开发一个接口,配合@ResponseBody注解(将java对象转为json格式)返回一个Java对象给到前端时,无形中就使用到了SpringBoot内置的Jackson库进行对象的序列化操作。

        而Java序列化机制,需要实现Serializable接口,序列化的过程是将对象转换为字节流,可以通过ObjectOutputStream类来实现;反序列化的过程是将字节流转换为对象,可以通过ObjectInputStream类来实现。在进行序列化和反序列化时,需要注意一些事项,比如版本控制、字段的访问控制等。

对象序列化:ObjectOutputStream

        举个例子:将SysUser对象进行序列化,写入到test.txt文件中。        

        SysUser user = new SysUser(1, "TomLink", 15);ObjectOutputStream os = null;FileOutputStream fos = null;try {fos = new FileOutputStream("test.txt");os = new ObjectOutputStream(fos);os.writeObject(user);os.flush();} catch (Exception e) {e.printStackTrace();} finally {//关闭流if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}if (os != null) {try {os.close();} catch (IOException e) {e.printStackTrace();}}}

        输出文件内容为:

 sr %com.example.demo.model.entity.SysUser纥茍C? L aget Ljava/lang/Integer;L idq ~ L namet Ljava/lang/String;xpsr java.lang.Integer鉅亣8 I valuexr java.lang.Number啲?
斷?  xp   sq ~   t  TomLink

对象反序列化:ObjectInputStream

        那么如何将文件内容恢复成SysUser对象呢?这就是对象反序列化的过程:将二进制数据转换为Java对象。

    @Testpublic void deSerialize(){ObjectInputStream ois = null;FileInputStream fis = null;try {fis = new FileInputStream("test.txt");ois = new ObjectInputStream(fis);SysUser sysUser = (SysUser)ois.readObject();System.out.println(sysUser);}catch (Exception e){e.printStackTrace();}finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}if (ois != null) {try {ois.close();} catch (IOException e) {e.printStackTrace();}}}}

 存在的问题:序列化版本号

        这种序列化机制存在什么问题呢?

        假设我们将序列化版本号进行稍加改动:

     在执行反序列化操作,就会解析异常:

HttpSession的JSON序列化机制

        By default, Spring Session uses Java Serialization to serialize the session attributes. Sometimes it might be problematic, especially when you have multiple applications that use the same Redis instance but have different versions of the same class. 

        前面也提到,  Spring Session默认使用Java对象序列化机制对Session属性值进行序列化,那么,自然也存在同样的问题。

        如何进行避免呢?一个比较好的解决方案就是:可以提供一个RedisSerializer实例自定义Session属性值的序列化方式。

Spring Data Redis provides the GenericJackson2JsonRedisSerializer that serializes and deserializes objects using Jackson’s ObjectMapper.

        我们可以使用Spring Data Redis提供的GenericJackson2JsonRedisSerializer 类来进行配置,其底层使用Jackson库的ObjectMapper进行序列化和反序列化,和SpringBoot的底层序列化机制类似。

        配置方式如下,

package com.example.demo.config;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;@Configuration
public class RedisHttpSessionConfig {/*** 自定义Http Session-Data-Redis的序列化机制*/@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer(ObjectMapper objectMapper){return new GenericJackson2JsonRedisSerializer(objectMapper);}}

Namespace命名空间配置

        多个应用程序使用同一个 Redis 实例的情况并不少见。因此,Spring Session 会使用命名空间(默认为 spring:session),以便在需要时将会话数据分开。

        查看RedisSessionProperties源码,会发现默认Namespace命名空间的值,

        而实际存储到Redis中的Session信息,默认以namespace命名空间为前缀。

         为了避免多个应用的namespace发生冲突,我们可以自行配置,以便于区分。

方式1:配置文件方式

spring.session.redis.namespace=spring:session:myapplication

方式2:注解方式

        @EnableRedisHttpSession:该注解启用Spring Session Data Redis,默认导入了RedisHttpSessionConfiguration配置类,改配置类默认提供的值就是 spring:session。

        这里只需要重定义这个属性即可,例如:

@SpringBootApplication
@EnableRedisHttpSession(redisNamespace = "applicationA:session")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

SessionRepository配置

SessionRepository简介

        SessionRepository接口提供了对Session的增删改查操作。

        Spring Session内置的实现类如下,

         而我们接下来要考虑的,就是如何选择SessionRepository接口实现类的问题。

实现类1:RedisSessionRepository

        RedisSessionRepository:是在Redis中存储Session数据的一种基本实现,没有任何额外的索引。仅仅使用简单的key-value键值结构去存储session属性。每一个Session会话都有一个唯一的ID值,session数据存储在与该 ID 关联的 Redis键中。

  当需要获取Session数据时,RedisSessionRepository会使用SessionID在Redis中查询对应的Session数据。但是由于没有索引,根据SessionID 以外的属性名称查询Session数据可能会效率低下。

实现类2:RedisIndexedSessionRepository

  RedisIndexedSessionRepository:是Redis存储Session会话的一种拓展实现,并提供了索引能力。它为Redis引入了额外的数据结构,以便于高效的基于属性名查询sessions。除了RedisSessionRepository使用的key-value键值结构外,它还维护额外的索引,以实现快速查询。例如,它可以根据用户ID或最后访问时间等会话属性创建索引;通过这些索引,可以根据特定条件高效的查询Session数据,从而提升性能、实现高级会话管理功能。

  除此之外,RedisIndexedSessionRepository还支持会话过期和删除操作。

子接口:RedisIndexedSessionRepository

   RedisIndexedSessionRepository:该接口拓展了SessionRepository接口的功能,作为RedisIndexedSessionRepository类的父接口出现,它允许根据特定的索引名和索引值来查询sessions会话信息。

   通过检索特定用户的所有会话,可以跨设备或浏览器跟踪用户的活动会话。例如,可以将此信息用于会话管理目的,如允许用户从特定会话中失效或注销,或根据用户的会话活动执行某些操作。

        例如:如下案例,可以使用getSessions 方法根据特定用户查询所有的会话信息;也可以使用removeSession 方法去移除用户的特定会话信息。

@Autowired
public FindByIndexNameSessionRepository<? extends Session> sessions;public Collection<? extends Session> getSessions(Principal principal) {Collection<? extends Session> usersSessions = this.sessions.findByPrincipalName(principal.getName()).values();return usersSessions;
}public void removeSession(Principal principal, String sessionIdToDelete) {Set<String> usersSessionIds = this.sessions.findByPrincipalName(principal.getName()).keySet();if (usersSessionIds.contains(sessionIdToDelete)) {this.sessions.deleteById(sessionIdToDelete);}
}

@EnableRedisHttpSession注解与SessionRepository自动注入

        通过如下的方式查看默认注入的是哪一个实现子类,

@SpringBootApplication
@EnableRedisHttpSession/*(redisNamespace = "applicationA:session")*/
public class DemoApplication {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);SessionRepository bean = run.getBean(SessionRepository.class);System.out.println(bean.getClass().getName());}}

        至于为什么默认是RedisIndexedSessionRepository这个实现子类呢?其实和我们启用Http Session的方式有关。一开始我们是使用@EnableRedisHttpSession注解的方式有关。

        查看该注解的源码,它内部使用@Import注解导入了RedisHttpSessionConfiguration这个配置类,在该类的内容,通过@Bean注解自动向IOC容器中注入了RedisIndexedSessionRepository的实例。

@EnableRedisHttpSession:自动导入配置类
@Bean注入RedisIndexedSessionRepository实例

自定义SessionRepository Bean实例

        那么,如何自定义SessionRepository实例呢?最简单的方法就是不使用注解,而是主动通过@Bean注解自己创建一个实现子类,例如:

@Configuration
public class RedisHttpSessionConfig {@Beanpublic SessionRepository springSessionRepository(RedisTemplate redisTemplate){//这里面没有做namespace等的配置,可自行补充return new RedisSessionRepository(redisTemplate);}
}

         再次启动,查看类类型如下,

监听Session Event

        很多时候,对session操作做处响应的处理是有价值的。例如:根据session会话生命周期进行某种处理(前提是必须使用RedisIndexedSessionRepository)。在Servlet应用开发阶段,通常是通过HttpSessionListener来实现session会话创建和会话注销的监听和事件处理的。

/*** Implementations of this interface are notified of changes to the list of* active sessions in a web application. To receive notification events, the* implementation class must be configured in the deployment descriptor for the* web application.** @see HttpSessionEvent* @since Servlet 2.3*/
public interface HttpSessionListener extends EventListener {/*** Notification that a session was created.* The default implementation is a NO-OP.** @param se*            the notification event*/public default void sessionCreated(HttpSessionEvent se) {}/*** Notification that a session is about to be invalidated.* The default implementation is a NO-OP.** @param se*            the notification event*/public default void sessionDestroyed(HttpSessionEvent se) {}
}

        Spring Session也提供了相应的接口用来处理Session会话事件,本质上是基于Java的事件模型机制来实现的。

Java事件模型机制

        Java中事件模型机制的参与者分为3种角色:

  1. Event Source:事件源(source),表示是由谁触发的事件,例如:jframe编程中,定义一个按钮JButton,当你点击了这个按钮,就会有对应的点击事件被触发,那么这个按钮就作为事件源存在,通常将其作为事件对象的构造参数;
  2. Event Object:事件对象(java.util.EventObject),用于描述事件对象本身,每一个事件对象都和对应的事件源相绑定。

     3. Event Listener:事件监听器(java.util.EventListener),用于监听事件对象的状态变化,并进行相应的处理。

        举个例子,如上图所示,我们建立一个消息发布和消息监听与回调处理的逻辑,并用Java代码进行实现,

/*** 消息事件对象*/
class MessageEvent extends EventObject {//定义事件对象的状态private boolean status;/*** Constructs a prototypical Event.** @param source The object on which the Event initially occurred.* @throws IllegalArgumentException if source is null.*/public MessageEvent(Object source) {super(source);}//methods//修改状态public void setStatus(boolean status) {this.status = status;}//获取状态public boolean getStatus() {return status;}
}/*** 消息事件监听器*/
@FunctionalInterface
interface MessageEventListener extends EventListener{void handleMessage(MessageEvent event);
}/*** 管理器*/
class MessageEventManager{//事件监听器列表private static List<MessageEventListener> listeners = new ArrayList<>();//注册监听器public static void registListener(MessageEventListener eventListener){listeners.add(eventListener);}//触发监听器public static void notifyListener(MessageEvent messageEvent){for (MessageEventListener listener : listeners) {listener.handleMessage(messageEvent);}}//工具方法:发送消息public static void sendMessage(MessageEvent messageEvent){notifyListener(messageEvent);}}public class EventMechanismDemo {public static void main(String[] args) {//提供事件监听器MessageEventManager.registListener(event -> {System.out.println("MessageListener A::status["+event.getStatus()+"]");});//提供事件对象MessageEvent messageEvent = new MessageEvent(true);//获取与之绑定的事件源Object source = messageEvent.getSource();System.out.println("Event Srouce:"+source);//触发事件MessageEventManager.sendMessage(messageEvent);}
}

SpringBoot的事件模型机制

ApplicationEvent|事件对象

        SpringBoot框架提供了ApplicationEvent抽象类,继承了java.util.EventObject,作用就是被其它具体的应用事件所继承,换言之,它并不能用于表示具体的事件对象。

        ApplicationEvent拥有许多子类,例如:用于监听SpringBoot应用上下文的ApplicationContextEvent、监听Web服务器初始化的WebServerInitializedEvent、用于监听Spring应用的SpringApplicationEvent。

         ApplicationEvent抽象类源码如下:

package org.springframework.context;import java.time.Clock;
import java.util.EventObject;/*** Class to be extended by all application events. Abstract as it* doesn't make sense for generic events to be published directly.*/
public abstract class ApplicationEvent extends EventObject {/** use serialVersionUID from Spring 1.2 for interoperability. */private static final long serialVersionUID = 7099057708183571937L;/** System time when the event happened. */private final long timestamp;/*** Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}* set to {@link System#currentTimeMillis()}.* @param source the object on which the event initially occurred or with* which the event is associated (never {@code null})* @see #ApplicationEvent(Object, Clock)*/public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}/*** Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}* set to the value returned by {@link Clock#millis()} in the provided {@link Clock}.* <p>This constructor is typically used in testing scenarios.* @param source the object on which the event initially occurred or with* which the event is associated (never {@code null})* @param clock a clock which will provide the timestamp* @since 5.3.8* @see #ApplicationEvent(Object)*/public ApplicationEvent(Object source, Clock clock) {super(source);this.timestamp = clock.millis();}/*** Return the time in milliseconds when the event occurred.* @see #ApplicationEvent(Object)* @see #ApplicationEvent(Object, Clock)*/public final long getTimestamp() {return this.timestamp;}}
ApplicationListener|事件监听器

        SpringBoot框架提供了ApplicationListener函数式接口,用于被具体的事件监听器实现。它也提供了很多子类/子接口,

        ApplicationListener函数式接口源码, 

package org.springframework.context;import java.util.EventListener;
import java.util.function.Consumer;/*** Interface to be implemented by application event listeners.** <p>Based on the standard {@code java.util.EventListener} interface* for the Observer design pattern.** <p>As of Spring 3.0, an {@code ApplicationListener} can generically declare* the event type that it is interested in. When registered with a Spring* {@code ApplicationContext}, events will be filtered accordingly, with the* listener getting invoked for matching event objects only.*/
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {/*** Handle an application event.* @param event the event to respond to*/void onApplicationEvent(E event);/*** Create a new {@code ApplicationListener} for the given payload consumer.* @param consumer the event payload consumer* @param <T> the type of the event payload* @return a corresponding {@code ApplicationListener} instance* @since 5.3* @see PayloadApplicationEvent*/static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {return event -> consumer.accept(event.getPayload());}}
监听SpringBoot应用内置的事件

        接下来我们尝试自定义ApplicationListener实现子类,来监听SpringBoot应用启动过程中所触发的一些内置事件。

package com.example.demo.listener;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;import java.util.Date;@Slf4j
@Component
public class CustomApplicationListener implements ApplicationListener<ApplicationEvent> {@Overridepublic void onApplicationEvent(ApplicationEvent event) {log.info("当前时间:{}---->事件对象:{}",new Date(event.getTimestamp()),event.getClass().getName());}
}

        启动日志信息如下,

 @EventListener注解

        @EventListener注解相当于是ApplicationListener实现子类的替代品,可以用于监听指定的事件。被该注解修饰的方法,将在内部由EventListenerMethodProcessor类进行处理。

        该注解可用于监听的事件类型可以是一种,也可以是多种,相应的,方法的参数个数也随之发生变化。

        该注解也可以配合 @Async 和 @Order 来实现异步监听器、顺序监听器。

package com.example.demo.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;import java.util.Date;/*** 定义配置类*/
@Slf4j
@Configuration
public class CustomApplicationListenerConfig {//监听springboot应用启动时触发的事件@EventListener(value = ApplicationEvent.class)public void handler(ApplicationEvent event){log.info("当前时间:{}---->事件对象:{}",new Date(event.getTimestamp()),event.getClass().getName());}
}

Spring Session事件模型机制

事件对象|AbstractSessionEvent抽象类

        我们回归主题,Spring Session内置了AbstractSessionEvent抽象类,该类继承了SpringBoot内置的ApplicationEvent,每当Session会话被更新时,都会被触发。

package org.springframework.session.events;import org.springframework.context.ApplicationEvent;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;/*** For {@link SessionRepository} implementations that support it, this event is fired when* a {@link Session} is updated.** @author Rob Winch* @since 1.1*/
@SuppressWarnings("serial")
public abstract class AbstractSessionEvent extends ApplicationEvent {private final String sessionId;private final Session session;AbstractSessionEvent(Object source, Session session) {super(source);this.session = session;this.sessionId = session.getId();}/*** Gets the {@link Session} that was destroyed. For some {@link SessionRepository}* implementations it may not be possible to get the original session in which case* this may be null.* @param <S> the type of Session* @return the expired {@link Session} or null if the data store does not support* obtaining it*/@SuppressWarnings("unchecked")public <S extends Session> S getSession() {return (S) this.session;}public String getSessionId() {return this.sessionId;}}

        而AbstractSessionEvent抽象类也拥有如下几个子类,主要用于:

①SessionCreatedEvent:Session会话创建

②SessionDestroyedEvent:Session会话销毁,分为两种情况:Session会话过期和Session会话断开/删除,分别对应SessionExpiredEvent和SessionDeletedEvent两个类。

事件监听器

         至于如何建听呢?那就换汤不换药了,直接使用@EventListener即可。

Session事件监听

        折腾了以上这么多内容,言归正传,以下为监听Spring Session会话事件的示例代码,

package com.example.demo.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.session.events.SessionExpiredEvent;@Slf4j
@Configuration
public class SessionEventListener {@EventListener(value = SessionCreatedEvent.class)public void processSessionCreatedEvent(SessionCreatedEvent event) {// do the necessary worklog.info("SessionCreatedEvent::{}",event.toString());}@EventListener(SessionDeletedEvent.class)public void processSessionDeletedEvent(SessionDeletedEvent event) {// do the necessary worklog.info("SessionDeletedEvent::{}",event.toString());}@EventListener(SessionDestroyedEvent.class)public void processSessionDestroyedEvent(SessionDestroyedEvent event) {// do the necessary worklog.info("SessionDestroyedEvent::{}",event.toString());}@EventListener(SessionExpiredEvent.class)public void processSessionExpiredEvent(SessionExpiredEvent event) {// do the necessary worklog.info("SessionExpiredEvent::{}",event.toString());}
}

验证Session事件监听

        以下新建两个接口,分别用于触发Session会话创建和Session会话注销/删除事件。

  • /app/addUser?id=1:创建Session会话
  • /app/delUser?id=1:删除Session会话

        示例代码如下,

package com.example.demo.web;import com.example.demo.model.entity.SysUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.SessionIdGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;
import java.util.Map;@Slf4j
@RestController
@RequestMapping(value = "/app")
@RequiredArgsConstructor
public class AppController {private final RedisIndexedSessionRepository redisIndexedSessionRepository;//接收一个id,创建user并返回@GetMapping("/addUser")public SysUser addUser(HttpSession session,@RequestParam(value = "id")Integer id){SysUser user = new SysUser(id, "张三", 18);//设置user信息到session中session.setAttribute(String.valueOf(id),user);log.info("create SysUser:{}",user);
//        SessionIdGeneratorreturn user;}//接收一个id,删除Session中存储的user信息@GetMapping("/delUser")public SysUser delUser(HttpSession session,@RequestParam(value = "id")Integer id){session.removeAttribute(String.valueOf(id));log.info("del SysUser:{}",id);//Returns a string containing the unique identifier assigned to this session.String sessionId = session.getId();//删除redisIndexedSessionRepository.deleteById(sessionId);return null;}}

        web服务打印日志信息如下,

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

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

相关文章

Sqli-labs靶场第11关详解[Sqli-labs-less-11]

Sqli-labs-Less-11 前言&#xff1a; SQL注入的三个条件&#xff1a; ①参数可控&#xff1b;&#xff08;从参数输入就知道参数可控&#xff09; ②参数过滤不彻底导致恶意代码被执行&#xff1b;&#xff08;需要在测试过程中判断&#xff09; ③参数带入数据库执行。&…

Linux环境安装ffmpeg6.x

1.官网ffmpeg下载源码 https://ffmpeg.org/download.html#build-windows 2.未安装x264库则先安装配置 可以先查询x264库: whereis libx264 安装编译工具和依赖库&#xff1a; sudo yum install gcc make cmake mercurial git yasm pkgconfig autoconf automake libtool sudo…

docker搭建zookeeper集群

文章目录 1. 集群搭建2. Leader选举3. Zookeeper集群角色 1. 集群搭建 这里我们使用docker-compose 搭建伪集群 version: 3.1 services:zoo1:image: zookeeperrestart: alwayscontainer_name: zoo1ports:- 2181:2181volumes:- /home/zk/zoo1/data:/data- /home/zk/zoo1/datal…

算法沉淀——动态规划之简单多状态 dp 问题(上)(leetcode真题剖析)

算法沉淀——动态规划之简单多状态 dp 问题上 01.按摩师02.打家劫舍 II03.删除并获得点数04.粉刷房子 01.按摩师 题目链接&#xff1a;https://leetcode.cn/problems/the-masseuse-lcci/ 一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在…

选择排序-第15届蓝桥第4次STEMA测评Scratch真题精选

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第172讲。 第15届蓝桥杯第4次STEMA测评已于2024年1月28日落下帷幕&#xff0c;编程题一共有6题&#xff0c;分别如下&a…

吴恩达deeplearning.ai:Tensorflow训练一个神经网络

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai 在之前的博客中。我们陆续学习了各个方面的有关深度学习的内容&#xff0c;今天可以从头开始训练一个神经网络了。 Tensorflow训练神经网络模型 我们使用之前用过的例子&#xff1a; 这个神经…

SSM项目集成Spring Security 4.X版本 之 加入DWZ,J-UI框架实现登录和主页菜单显示

目录 前言 一、加入DWZ J-UI框架 二、实现登录页面 三、实现主页面菜单显示 前言 大家好&#xff01;写文章之前先列出几篇相关文章。本文内容也在其项目中接续实现。 一. SSM项目集成Spring Security 4.X版本&#xff08;使用spring-security.xml 配置文件方式&#xff…

HDL FPGA 学习 - Quartus II 工程搭建,ModelSim 仿真,时序分析,IP 核使用,Nios II 软核使用,更多技巧和规范总结

目录 工程搭建、仿真与时钟约束 一点技巧 ModelSim 仿真 Timing Analyzer 时钟信号约束 SignalTap II 使用 In-System Memory Content Editor 使用 记录 QII 的 IP 核使用 记录 Qsys/Nios II 相关 记录 Qsys 的 IP 核使用 封装 Avalon IP 更多小技巧教程文章 更多好…

TF-IDF,textRank,LSI_LDA 关键词提取

目录 任务 代码 keywordExtract.py TF_IDF.py LSI_LDA.py 结果 任务 用这三种方法提取关键词&#xff0c;代码目录如下&#xff0c; keywordExtract.py 为运行主程序 corpus.txt 为现有数据文档 其他文件&#xff0c;停用词&#xff0c;方法文件 corpus.txt 可以自己…

手把手教你,设置IDEA实现SSH远程连接Linux服务器

前言 工作中&#xff0c;偶尔会遇到需要连接远程Linux环境进行开发。这篇文章就介绍一下如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xff0c;实现远程开发。 IDEA的远程开发功能&#xff0c;可以将本地的编译、构建、…

神经网络系列---常用梯度下降算法

文章目录 常用梯度下降算法随机梯度下降&#xff08;Stochastic Gradient Descent&#xff0c;SGD&#xff09;&#xff1a;随机梯度下降数学公式&#xff1a;代码演示 批量梯度下降&#xff08;Batch Gradient Descent&#xff09;批量梯度下降数学公式&#xff1a;代码演示 小…

【监督学习之逻辑回归】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱1.什么是逻辑回归&#xff1f;2.逻辑回归有哪些应用&#xff1f;3.回归分析如何工作&#xff1f;4.逻辑回归模型如何工作&#xff1f;5.逻辑回归分析有哪些类型&#xff1f;6.逻辑回归与其他机器学习技术相…

网络编程、UDP、TCP

计算机网络 就是将地理位置不同的具有独立功能的多台计算及外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统、网络管理软件以及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统 目的 传播交流信息、数据交换、通信 如何做…

算法分析-面试1-字符串

文章目录 前言一、分类&#xff1a;看看就行了二、字符串API&#xff1a;创建和初始化&#xff1a;查询操作&#xff1a;比较操作&#xff1a;修改操作&#xff1a;截取操作&#xff1a;分割操作&#xff1a;格式化操作&#xff1a;连接操作&#xff08;Java 8 及以后&#xff…

给大家分享一款小程序:AI一秒修图

AI一秒修图 照片修复的AI助手特点&#xff1a;Demo&#xff08;1.选择图片 2.涂抹遮罩 3.消除&#xff09;Product Roadmap (版本演进)Contact-联系我们Reference 照片修复的AI助手 照片修复小小助手是一款快速P图微信小程序&#xff0c;用来消除图片中指定的人和物&#xff…

人工智能绘画的时代下到底是谁在主导,是人类的想象力,还是AI的创造力?

#ai作画 目录 一.AI绘画的概念 1. 数据集准备&#xff1a; 2. 模型训练&#xff1a; 3. 生成绘画&#xff1a; 二.AI绘画的应用领域 三.AI绘画的发展 四.AI绘画背后的技术剖析 1.AI绘画的底层原理 2.主流模型的发展趋势 2.1VAE — 伊始之门 2.2GAN 2.2.1GAN相较于…

软考43-上午题-【数据库】-关系代数转SQL语言

一、投影转SQL语言-select 示例&#xff1a; 二、选择转SQL语言-where 示例&#xff1a; 【注意】&#xff1a; 关系代数公式的写法&#xff0c;可以写属性名&#xff0c;也可以写列的序号&#xff0c;如&#xff1a; 但是&#xff0c;SQL语言不支持&#xff01;&#xff01;&a…

软件设计师软考题目解析05 --每日五题

想说的话&#xff1a;要准备软考了。0.0&#xff0c;其实我是不想考的&#xff0c;但是吧&#xff0c;由于本人已经学完所有知识了&#xff0c;只是被学校的课程给锁在那里了&#xff0c;不然早找工作去了。寻思着反正也无聊&#xff0c;就考个证玩玩。 本人github地址&#xf…

H5多用途的产品介绍展示单页HTML5静态网页模板

H5多用途的产品介绍展示单页HTML5静态网页模板 源码介绍&#xff1a;一款H5自适应多用途的产品介绍展示单页HTML静态网页模板&#xff0c;可用于团队官网、产品官网。 下载地址&#xff1a; https://www.changyouzuhao.cn/13534.html

作业 找单身狗2

方法一&#xff1a; 思路&#xff1a; 我们可以先创建一个新的数组&#xff0c;初始化为0&#xff0c;然后让原来的数组里面的元素作为新数组的下标 如果该下标对应的值为0&#xff0c;说明没有出现过该数&#xff0c;赋值为1作为标记&#xff0c;表示出现过1次 如果该下标…