frp将配置写在代码中重新打包

news2024/11/28 16:25:55

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。在有些情况下我们需要隐藏配置信息,尤其是客户端(比如我们要在第三方电脑上使用,又不想让别人知道我们的配置信息),而frp的配置信息默认是明文写在.ini文件中,要隐藏配置信息,可以将配置信息写在代码中。

拉取代码

git clone https://github.com/fatedier/frp.git
#拉取完毕后进入给目录
cd frp/

修改客户端配置

修改./pkg/config/client.go
在文件开始处加入我们的配置信息:

var myClientBytesWithFull = []byte(`
		# [common] is integral section
		[common]
		server_addr = 117.189.6.139
		server_port = 7200
		token = 123456
		
		tls_enable = true
		
		[website-demo]
		type = http
		local_port = 8080
		subdomain = demo
	`)

在UnmarshalClientConfFromIni方法和LoadAllProxyConfsFromIni方法的开始加入以下内容:

if source != nil {
		//	这里可以不要if,直接赋值
		source = cyClientBytesWithFull
	}

注意:这样在frpc.ini配置文件中不用写任何信息,直接空着就行
client.go完整代码如下:

// Copyright 2020 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/ini.v1"

	"github.com/fatedier/frp/pkg/auth"
	"github.com/fatedier/frp/pkg/util/util"
)

// ClientCommonConf contains information for a client service. It is
// recommended to use GetDefaultClientConf instead of creating this object
// directly, so that all unspecified fields have reasonable default values.
type ClientCommonConf struct {
	auth.ClientConfig `ini:",extends"`

	// ServerAddr specifies the address of the server to connect to. By
	// default, this value is "0.0.0.0".
	ServerAddr string `ini:"server_addr" json:"server_addr"`
	// Specify another address of the server to connect for nat hole. By default, it's same with
	// ServerAddr.
	NatHoleServerAddr string `ini:"nat_hole_server_addr" json:"nat_hole_server_addr"`
	// ServerPort specifies the port to connect to the server on. By default,
	// this value is 7000.
	ServerPort int `ini:"server_port" json:"server_port"`
	// ServerUDPPort specifies the server port to help penetrate NAT hole. By default, this value is 0.
	// This parameter is only used when executing "nathole discover" in the command line.
	ServerUDPPort int `ini:"server_udp_port" json:"server_udp_port"`
	// STUN server to help penetrate NAT hole.
	NatHoleSTUNServer string `ini:"nat_hole_stun_server" json:"nat_hole_stun_server"`
	// The maximum amount of time a dial to server will wait for a connect to complete.
	DialServerTimeout int64 `ini:"dial_server_timeout" json:"dial_server_timeout"`
	// DialServerKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
	// If negative, keep-alive probes are disabled.
	DialServerKeepAlive int64 `ini:"dial_server_keepalive" json:"dial_server_keepalive"`
	// ConnectServerLocalIP specifies the address of the client bind when it connect to server.
	// By default, this value is empty.
	// this value only use in TCP/Websocket protocol. Not support in KCP protocol.
	ConnectServerLocalIP string `ini:"connect_server_local_ip" json:"connect_server_local_ip"`
	// HTTPProxy specifies a proxy address to connect to the server through. If
	// this value is "", the server will be connected to directly. By default,
	// this value is read from the "http_proxy" environment variable.
	HTTPProxy string `ini:"http_proxy" json:"http_proxy"`
	// LogFile specifies a file where logs will be written to. This value will
	// only be used if LogWay is set appropriately. By default, this value is
	// "console".
	LogFile string `ini:"log_file" json:"log_file"`
	// LogWay specifies the way logging is managed. Valid values are "console"
	// or "file". If "console" is used, logs will be printed to stdout. If
	// "file" is used, logs will be printed to LogFile. By default, this value
	// is "console".
	LogWay string `ini:"log_way" json:"log_way"`
	// LogLevel specifies the minimum log level. Valid values are "trace",
	// "debug", "info", "warn", and "error". By default, this value is "info".
	LogLevel string `ini:"log_level" json:"log_level"`
	// LogMaxDays specifies the maximum number of days to store log information
	// before deletion. This is only used if LogWay == "file". By default, this
	// value is 0.
	LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"`
	// DisableLogColor disables log colors when LogWay == "console" when set to
	// true. By default, this value is false.
	DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"`
	// AdminAddr specifies the address that the admin server binds to. By
	// default, this value is "127.0.0.1".
	AdminAddr string `ini:"admin_addr" json:"admin_addr"`
	// AdminPort specifies the port for the admin server to listen on. If this
	// value is 0, the admin server will not be started. By default, this value
	// is 0.
	AdminPort int `ini:"admin_port" json:"admin_port"`
	// AdminUser specifies the username that the admin server will use for
	// login.
	AdminUser string `ini:"admin_user" json:"admin_user"`
	// AdminPwd specifies the password that the admin server will use for
	// login.
	AdminPwd string `ini:"admin_pwd" json:"admin_pwd"`
	// AssetsDir specifies the local directory that the admin server will load
	// resources from. If this value is "", assets will be loaded from the
	// bundled executable using statik. By default, this value is "".
	AssetsDir string `ini:"assets_dir" json:"assets_dir"`
	// PoolCount specifies the number of connections the client will make to
	// the server in advance. By default, this value is 0.
	PoolCount int `ini:"pool_count" json:"pool_count"`
	// TCPMux toggles TCP stream multiplexing. This allows multiple requests
	// from a client to share a single TCP connection. If this value is true,
	// the server must have TCP multiplexing enabled as well. By default, this
	// value is true.
	TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
	// TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler.
	// If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux.
	TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
	// User specifies a prefix for proxy names to distinguish them from other
	// clients. If this value is not "", proxy names will automatically be
	// changed to "{user}.{proxy_name}". By default, this value is "".
	User string `ini:"user" json:"user"`
	// DNSServer specifies a DNS server address for FRPC to use. If this value
	// is "", the default DNS will be used. By default, this value is "".
	DNSServer string `ini:"dns_server" json:"dns_server"`
	// LoginFailExit controls whether or not the client should exit after a
	// failed login attempt. If false, the client will retry until a login
	// attempt succeeds. By default, this value is true.
	LoginFailExit bool `ini:"login_fail_exit" json:"login_fail_exit"`
	// Start specifies a set of enabled proxies by name. If this set is empty,
	// all supplied proxies are enabled. By default, this value is an empty
	// set.
	Start []string `ini:"start" json:"start"`
	// Start map[string]struct{} `json:"start"`
	// Protocol specifies the protocol to use when interacting with the server.
	// Valid values are "tcp", "kcp", "quic" and "websocket". By default, this value
	// is "tcp".
	Protocol string `ini:"protocol" json:"protocol"`
	// QUIC protocol options
	QUICKeepalivePeriod    int `ini:"quic_keepalive_period" json:"quic_keepalive_period" validate:"gte=0"`
	QUICMaxIdleTimeout     int `ini:"quic_max_idle_timeout" json:"quic_max_idle_timeout" validate:"gte=0"`
	QUICMaxIncomingStreams int `ini:"quic_max_incoming_streams" json:"quic_max_incoming_streams" validate:"gte=0"`
	// TLSEnable specifies whether or not TLS should be used when communicating
	// with the server. If "tls_cert_file" and "tls_key_file" are valid,
	// client will load the supplied tls configuration.
	TLSEnable bool `ini:"tls_enable" json:"tls_enable"`
	// TLSCertPath specifies the path of the cert file that client will
	// load. It only works when "tls_enable" is true and "tls_key_file" is valid.
	TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"`
	// TLSKeyPath specifies the path of the secret key file that client
	// will load. It only works when "tls_enable" is true and "tls_cert_file"
	// are valid.
	TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"`
	// TLSTrustedCaFile specifies the path of the trusted ca file that will load.
	// It only works when "tls_enable" is valid and tls configuration of server
	// has been specified.
	TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
	// TLSServerName specifies the custom server name of tls certificate. By
	// default, server name if same to ServerAddr.
	TLSServerName string `ini:"tls_server_name" json:"tls_server_name"`
	// By default, frpc will connect frps with first custom byte if tls is enabled.
	// If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
	DisableCustomTLSFirstByte bool `ini:"disable_custom_tls_first_byte" json:"disable_custom_tls_first_byte"`
	// HeartBeatInterval specifies at what interval heartbeats are sent to the
	// server, in seconds. It is not recommended to change this value. By
	// default, this value is 30. Set negative value to disable it.
	HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"`
	// HeartBeatTimeout specifies the maximum allowed heartbeat response delay
	// before the connection is terminated, in seconds. It is not recommended
	// to change this value. By default, this value is 90. Set negative value to disable it.
	HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
	// Client meta info
	Metas map[string]string `ini:"-" json:"metas"`
	// UDPPacketSize specifies the udp packet size
	// By default, this value is 1500
	UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
	// Include other config files for proxies.
	IncludeConfigFiles []string `ini:"includes" json:"includes"`
	// Enable golang pprof handlers in admin listener.
	// Admin port must be set first.
	PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
}

var myClientBytesWithFull = []byte(`
		# [common] is integral section
		[common]
		server_addr = 117.189.6.139
		server_port = 7200
		token = 123456
		
		tls_enable = true
		
		[website-demo]
		type = http
		local_port = 8080
		subdomain = demo
	`)

// GetDefaultClientConf returns a client configuration with default values.
func GetDefaultClientConf() ClientCommonConf {
	return ClientCommonConf{
		ClientConfig:            auth.GetDefaultClientConf(),
		ServerAddr:              "0.0.0.0",
		ServerPort:              7000,
		NatHoleSTUNServer:       "stun.easyvoip.com:3478",
		DialServerTimeout:       10,
		DialServerKeepAlive:     7200,
		HTTPProxy:               os.Getenv("http_proxy"),
		LogFile:                 "console",
		LogWay:                  "console",
		LogLevel:                "info",
		LogMaxDays:              3,
		AdminAddr:               "127.0.0.1",
		PoolCount:               1,
		TCPMux:                  true,
		TCPMuxKeepaliveInterval: 60,
		LoginFailExit:           true,
		Start:                   make([]string, 0),
		Protocol:                "tcp",
		QUICKeepalivePeriod:     10,
		QUICMaxIdleTimeout:      30,
		QUICMaxIncomingStreams:  100000,
		HeartbeatInterval:       30,
		HeartbeatTimeout:        90,
		Metas:                   make(map[string]string),
		UDPPacketSize:           1500,
		IncludeConfigFiles:      make([]string, 0),
	}
}

func (cfg *ClientCommonConf) Complete() {
	if cfg.LogFile == "console" {
		cfg.LogWay = "console"
	} else {
		cfg.LogWay = "file"
	}
}

func (cfg *ClientCommonConf) Validate() error {
	if cfg.HeartbeatTimeout > 0 && cfg.HeartbeatInterval > 0 {
		if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
			return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
		}
	}

	if !cfg.TLSEnable {
		if cfg.TLSCertFile != "" {
			fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
		}

		if cfg.TLSKeyFile != "" {
			fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
		}

		if cfg.TLSTrustedCaFile != "" {
			fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
		}
	}

	if cfg.Protocol != "tcp" && cfg.Protocol != "kcp" && cfg.Protocol != "websocket" && cfg.Protocol != "quic" {
		return fmt.Errorf("invalid protocol")
	}

	for _, f := range cfg.IncludeConfigFiles {
		absDir, err := filepath.Abs(filepath.Dir(f))
		if err != nil {
			return fmt.Errorf("include: parse directory of %s failed: %v", f, absDir)
		}
		if _, err := os.Stat(absDir); os.IsNotExist(err) {
			return fmt.Errorf("include: directory of %s not exist", f)
		}
	}
	return nil
}

// Supported sources including: string(file path), []byte, Reader interface.
func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
	if source != nil {
	//	这里可以不要if,直接赋值
		source = myClientBytesWithFull
	}
	f, err := ini.LoadSources(ini.LoadOptions{
		Insensitive:         false,
		InsensitiveSections: false,
		InsensitiveKeys:     false,
		IgnoreInlineComment: true,
		AllowBooleanKeys:    true,
	}, source)
	if err != nil {
		return ClientCommonConf{}, err
	}

	s, err := f.GetSection("common")
	if err != nil {
		return ClientCommonConf{}, fmt.Errorf("invalid configuration file, not found [common] section")
	}

	common := GetDefaultClientConf()
	err = s.MapTo(&common)
	if err != nil {
		return ClientCommonConf{}, err
	}

	common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
	common.ClientConfig.OidcAdditionalEndpointParams = GetMapWithoutPrefix(s.KeysHash(), "oidc_additional_")

	return common, nil
}

// if len(startProxy) is 0, start all
// otherwise just start proxies in startProxy map
func LoadAllProxyConfsFromIni(
	prefix string,
	source interface{},
	start []string,
) (map[string]ProxyConf, map[string]VisitorConf, error) {
	// 这里可以不要if,直接赋值
	if source != nil {
		source = myClientBytesWithFull
	}
	f, err := ini.LoadSources(ini.LoadOptions{
		Insensitive:         false,
		InsensitiveSections: false,
		InsensitiveKeys:     false,
		IgnoreInlineComment: true,
		AllowBooleanKeys:    true,
	}, source)
	if err != nil {
		return nil, nil, err
	}

	proxyConfs := make(map[string]ProxyConf)
	visitorConfs := make(map[string]VisitorConf)

	if prefix != "" {
		prefix += "."
	}

	startProxy := make(map[string]struct{})
	for _, s := range start {
		startProxy[s] = struct{}{}
	}

	startAll := true
	if len(startProxy) > 0 {
		startAll = false
	}

	// Build template sections from range section And append to ini.File.
	rangeSections := make([]*ini.Section, 0)
	for _, section := range f.Sections() {

		if !strings.HasPrefix(section.Name(), "range:") {
			continue
		}

		rangeSections = append(rangeSections, section)
	}

	for _, section := range rangeSections {
		err = renderRangeProxyTemplates(f, section)
		if err != nil {
			return nil, nil, fmt.Errorf("failed to render template for proxy %s: %v", section.Name(), err)
		}
	}

	for _, section := range f.Sections() {
		name := section.Name()

		if name == ini.DefaultSection || name == "common" || strings.HasPrefix(name, "range:") {
			continue
		}

		_, shouldStart := startProxy[name]
		if !startAll && !shouldStart {
			continue
		}

		roleType := section.Key("role").String()
		if roleType == "" {
			roleType = "server"
		}

		switch roleType {
		case "server":
			newConf, newErr := NewProxyConfFromIni(prefix, name, section)
			if newErr != nil {
				return nil, nil, fmt.Errorf("failed to parse proxy %s, err: %v", name, newErr)
			}
			proxyConfs[prefix+name] = newConf
		case "visitor":
			newConf, newErr := NewVisitorConfFromIni(prefix, name, section)
			if newErr != nil {
				return nil, nil, newErr
			}
			visitorConfs[prefix+name] = newConf
		default:
			return nil, nil, fmt.Errorf("proxy %s role should be 'server' or 'visitor'", name)
		}
	}
	return proxyConfs, visitorConfs, nil
}

func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
	// Validation
	localPortStr := section.Key("local_port").String()
	remotePortStr := section.Key("remote_port").String()
	if localPortStr == "" || remotePortStr == "" {
		return fmt.Errorf("local_port or remote_port is empty")
	}

	localPorts, err := util.ParseRangeNumbers(localPortStr)
	if err != nil {
		return err
	}

	remotePorts, err := util.ParseRangeNumbers(remotePortStr)
	if err != nil {
		return err
	}

	if len(localPorts) != len(remotePorts) {
		return fmt.Errorf("local ports number should be same with remote ports number")
	}

	if len(localPorts) == 0 {
		return fmt.Errorf("local_port and remote_port is necessary")
	}

	// Templates
	prefix := strings.TrimSpace(strings.TrimPrefix(section.Name(), "range:"))

	for i := range localPorts {
		tmpname := fmt.Sprintf("%s_%d", prefix, i)

		tmpsection, err := f.NewSection(tmpname)
		if err != nil {
			return err
		}

		copySection(section, tmpsection)
		if _, err := tmpsection.NewKey("local_port", fmt.Sprintf("%d", localPorts[i])); err != nil {
			return fmt.Errorf("local_port new key in section error: %v", err)
		}
		if _, err := tmpsection.NewKey("remote_port", fmt.Sprintf("%d", remotePorts[i])); err != nil {
			return fmt.Errorf("remote_port new key in section error: %v", err)
		}
	}

	return nil
}

func copySection(source, target *ini.Section) {
	for key, value := range source.KeysHash() {
		_, _ = target.NewKey(key, value)
	}
}

修改服务端配置(如需)

服务端配置一般不需要做隐藏配置信息处理,如果需要也可以用同样的方式修改。

重新打包

修改完毕后重新打包:

./package.sh

在这里插入图片描述

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

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

相关文章

第五章 总结及作业(123)【编译原理】

第五章 作业【编译原理】 前言推荐第五章 总结5.1自下而上分析基本问题 5.1.1归约5.1.2规范归约简述5.1.3 符号栈的使用与语法树的表示 5.2 算符优先分析5.2.1算符优先文法及优先表构造算法&#xff1a;构造FIRSTVT集算法&#xff1a;构造LASTVT集算法&#xff1a;构造优先表5.…

Google I/O 2023 - 一文快速总结 Flutter Dart 的现状和未来

随着 Google I/O 2023 的发布&#xff0c; Flutter 3.10 和 Dart 3.0 也都正式发&#xff0c;不得不说如今 Dart 的版本号终于快追上 Flutter 得版本号了&#xff0c;特别随着 Dart 3 的发布&#xff0c; Flutter 在 records 和 patterns 的加持下&#xff0c;开发体验终于开始…

LlamaIndex :面向QA 系统的全新文档摘要索引

在这篇博文中&#xff0c;我们介绍了一种全新的 LlamaIndex 数据结构&#xff1a;文档摘要索引。我们描述了与传统语义搜索相比&#xff0c;它如何帮助提供更好的检索性能&#xff0c;并通过一个示例进行了介绍。 背景 大型语言模型 (LLM) 的核心场景之一是对用户自己的数据进…

MapReduce框架

TextInputFormat 1&#xff09;FileInputFormat实现类 思考&#xff1a;在运行MapReduce程序时&#xff0c;输入的文件格式包括&#xff1a;基于行的日志文件、二进制格式文件、数据库表等。那么&#xff0c;针对不同的数据类型&#xff0c;MapReduce是如何读取这些数据的呢&…

Postman 如何关联接口测试并设置全局变量(带有token鉴权)

一、登陆接口 创建一个request请求 在Tests中添加JavaScript代码&#xff0c;用来获取鉴权&#xff1a; var jsonData JSON.parse(responseBody); var Authorization jsonData.data.access_token; console.log(Authorization) pm.globals.set(‘Authorization’,Authorizatio…

solr快速上手:solr简介及安装(一)

0. 引言 虽然现在主流的搜索引擎组件已经es主导&#xff0c;但不乏有部分“老”项目依旧在采用solr&#xff0c;当遇到这类项目时&#xff0c;如何快速上手solr组件&#xff0c;以及后续如何拓展深入研究solr的途径成为问题&#xff0c;本期我们的目的就是带大家来快速上手sol…

2023 年第三届长三角高校数学建模竞赛赛题浅析

为了更好地让大家本次长三角比赛选题&#xff0c;我将对本次比赛的题目进行简要浅析。数模模型通常分为优化、预测、评价三类&#xff0c;而本次数学题目就正好对应着A、B、C分别为优化、预测、评价。整体难度不大&#xff0c;主要难点在于A题的优化以及B、C的数据收集。稍后&a…

QT5.12.6 + mysql5.5.9 出现 Driver not loaded Driver not loaded

由于我重装了电脑系统&#xff0c;qt 和mysql均进行了软件版本的升级&#xff0c; 在使用数据库模块时&#xff0c;出现了如下问题&#xff1a; Driver not loaded Driver not loaded 排除问题一&#xff1a; pro文件中是否加载了sql模块 查看pro文件&#xff0c;发现 有此模…

React的两种组件创建方式(二)

react是面向组件编程的一种模式&#xff0c;它包含两种组件类型&#xff1a;函数式组件及类式组件 函数式组件 一个基本的函数组件长这个样子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>hell…

java报错-->java.lang.IllegalAccessError

1、前言 在gradle中运行main方法突然出现如下错误 Exception in thread "main" java.lang.IllegalAccessError: class XXX.util.ImageBorderUtils (in unnamed module 0x4bd4bcd4) cannot access class sun.font.FontDesignMetrics (in module java.desktop) becaus…

使用宝塔在Linux面板搭建网站,并实现公网远程访问「内网穿透」

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 转载自远程内网穿透的文章&#xff1a;Linux使用宝塔面板搭建网站&#xff0c;并内网穿透实现公网访问 前言 宝塔面板作为简单好用的服务器运维管理面板&#…

数据结构与算法(Java版) | 数组模拟队列的思路分析与代码实现

思路分析 上一讲我们讲过&#xff0c;队列既可以用数组来实现&#xff0c;也可以用链表来实现&#xff0c;但由于我们比较熟悉数组这种结构&#xff0c;所以这里我会先给大家讲一下数组这种实现方式&#xff0c;至于链表这种实现方式&#xff0c;那就以后有机会再说吧&#xf…

探索人工智能新纪元:Pre-Training 快速指南,轻松上手

theme: orange 预训练 Pre-Training 已被证明是当前人工智能范式中最重要的方面之一&#xff0c;大型语言模型&#xff0c;要转变为通用引擎&#xff0c;需要预训练。 什么是预训练模型 人工智能中的预训练至少部分受到人类学习方式的启发。我们不需要从零开始学习一个主题&…

肝一肝设计模式【七】-- 代理模式

系列文章目录 肝一肝设计模式【一】-- 单例模式 传送门 肝一肝设计模式【二】-- 工厂模式 传送门 肝一肝设计模式【三】-- 原型模式 传送门 肝一肝设计模式【四】-- 建造者模式 传送门 肝一肝设计模式【五】-- 适配器模式 传送门 肝一肝设计模式【六】-- 装饰器模式 传送门 文…

JavaWeb:Cookie、Session、JSP、JavaBean、MVC 三层架构

文章目录 JavaWeb - 03一、Cookie1. Cookie 应用2. 注意点 二、Session三、JSP1. 概述2. JSP 基础语法和指令&#xff08;了解&#xff09;3. 内置对象及作用域4. JSP 标签、JSTL 标签 四、JavaBean五、MVC 三层架构1. 之前的架构2. 现在的 MVC 三层架构 注意&#xff1a; Java…

Feign和OpenFeign

1.Feign和OpenFeign的关系 Feign Feign是一个声明式的Web服务客户端&#xff08;Web服务客户端就是Http客户端&#xff09;&#xff0c;让编写Web服务客户端变得非常容易&#xff0c;只需创建一个接口并在接口上添加注解即可。 Feign是Spring Cloud组件中一个轻量级RESTful的H…

MySQL基础学习---7、子查询

子查询 子查询指的是一个查询语句嵌套在另一个查询语句内部的查询&#xff08;从MySQL4.1开始引入&#xff09;。1、子查询的基本使用 语法格式&#xff1a;select select_listfrom tablewhere expr operator(select select_listfrom table); 说明&#xff1a;1、子查询&…

【终极解决方案】IDEA maven 项目修改代码不生效。

【终极解决方案】IDEA maven 项目修改代码不生效。 文章目录 【终极解决方案】IDEA maven 项目修改代码不生效。1、项目问题描述2、可能的解决方案3、分析原因4、解决方案5、参考文献 1、项目问题描述 遇到一个非常奇怪的问题&#xff0c;修改了一个基于maven搭建的SSM项目&am…

learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景、volatile 的基本理解与实验证明)

目录 return 关键字 const 关键字 const 修饰的只读变量 - - - 不可直接被修改&#xff01; const修饰的变量&#xff0c;可以作为数组定义的一部分吗&#xff1f; const只能在定义的时候直接初始化&#xff0c;不能二次赋值。为什么&#xff1f; const修饰指针 volatil…

opencv remap 像素重映射

remap()函数的输入是一个源图像和一个映射矩阵。映射矩阵包含了每个像素的新坐标&#xff0c;用于指定每个像素在输出图像中的位置。 假设原始图像中的一个像素的坐标为 ( x , y ) (x,y) (x,y)&#xff0c;它在输出图像中的新坐标为 ( x ′ , y ′ ) (x,y) (x′,y′)。为了计算…