Clang之词法分析Lex
Clang是LLVM编译器框架的前端(Frontend)编译器,可编译链接C、C++、Objective-C和Objective-C++四种语言的项目代码。Clang 的开发目标是提供一个可以替代 GCC 的前端编译器,与GCC相比,节省时间和内存空间;拥有更人性化的代码诊断输出;基于库的框架,使编译和链接过程模块化;方便集成进IDE等等(具体参见calng源码目录clang/www/comparison.html, clang都指源码目录,以下都同此)。从开发角度,GCC或G++代码庞大,代码耦合度高,“是一个纯粹的编译系统”,非常不利于二次开发。LLVM+Clang则不同,可谓开发者的编译器。
1、何为前端(Frontend)
早期,我们将编译器描述为一个简单的盒子,能够将源程序转化为目标程序即可…单盒模型指出,编译器必须理解源程序并将功能映射到目标机。这两项任务截然不同的性质暗示着一种可能的任务划分,并最终导致了一种将编译分解为两个主要部分的设计:前端和后端。(摘自《编译器设计》,Keith D.Cooper Linda Torczon著)。LLVM就是如上所说之后端,Clang就是前端。以下论述中llvm的版本为3.6.0。
aaarticlea/png;base64,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" alt="" />
2、三步演绎
编译过程一般分为词法分析、语法分析和语义分析,clang也不例外:
clang通过Preprocessor词法分析出一个一个 Token;
语法分析可得AST(Abstract Syntax Tree,抽象语法树),clang提供了访问者Visistor和各种回调函数供用户使用;
Clang基本支持C系列的标准,在语义分析之上——静态代码分析层面,通过注册机制,只需实现ExplodedGraph提供Checker 和 CheckerVisitor接口就可实现高级语义检查。
3、 词法分析(Lex)
Clang词法分析的核心数据结构是预编译器Preprocessor,循环调用Preprocessor.Lex(Token)可解析出一个一个Token。使用Proprocessor需要一大堆的初始化函数,首先从构造函数着手。
3.1 构造函数说明
Preprocessor(IntrusiveRefCntPtr<PreprocessorOptions> PPOpts,
DiagnosticsEngine &diags, LangOptions &opts,
SourceManager &SM, HeaderSearch &Headers,
ModuleLoader &TheModuleLoader,
IdentifierInfoLookup *IILookup, bool OwnsHeaders,
TranslationUnitKind TUKind)
(1)PreprocessorOptions
为Preprocess初始化做准备,主要设置预处理选项,如提供宏参数(-Dbug)、重映射文件说明(Remappedfile)等;
(2)DiagnosticsEngine
Diagnostics存放于源代码clang/lib/Basic中,足说明它是一个很基础的类,贯穿整个clang。DiagnosticsEngine是供前端报告错误、警告、提示等消息的具体类,它需要一个翻译单元和位置管理器(DiagnosticsEngine is tied to one translation unit and one SourceManager,因为一般编译打印的诊断信息主要就是错误的位置、错误类型)。
它有一个成员函数Report成员函数,可以触发各种Diagnositc信息,还有一个回调的类DiagnosticConsumer处理触发的各种Diagnositc。
inline DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID); inline DiagnosticBuilder Report(unsigned DiagID);
其构造函数如下,下面一一说明。
DiagnosticsEngine(const IntrusiveRefCntPtr<DiagnosticIDs> &Diags,
DiagnosticOptions *DiagOpts,
DiagnosticConsumer *client = nullptr,
bool ShouldOwnClient = true);
(2-1)DiagnosticIDs
所有代码的诊断(Diagnostic)信息的种类和输出方式等都在这里实现。Clang/include/clang/Basic下 DiagnosticCommonKinds.inc, DiagnosticDriverKinds.inc, DiagnosticFrontendKinds.inc, DiagnosticSerializationKinds.inc, DiagnosticLexKinds.inc, DiagnosticParseKinds.inc, DiagnosticASTKinds.inc, DiagnosticCommentKinds.inc, DiagnosticSemaKinds.inc, DiagnosticAnalysisKinds.inc列举了各种诊断信息。
(2-2)DiagnosticOptions
Diagnostic的选项设置。
(2-3)DiagnosticConsumer
该类是一个回调类,对已经得到的诊断(Diagnostic)信息,做再次处理,主要作用是以定制的方式呈现给外界。Clang中实现了DiagnosticConsumer的继承类,如IgnoringDiagConsumer, LogDiagnosticPrinter,TextDiagnosticPrinter,ForwardingDiagnosticConsumer等,如下简单打印是第几个warnings:
class clientConsumer:public DiagnosticConsumer{
void HandleDiagnostic(DiagnosticsEngine::Level Level,
const Diagnostic &Info) {
DiagnosticConsumer::HandleDiagnostic(Level, Info);
std::cout<<getNumWarnings()<<"th warnings occurs!"<<std::endl;//当warning出现时,打印输出。}};
Clang中的诊断Diagnostic等级有六类,如下枚举:
enum Level {
Ignored = DiagnosticIDs::Ignored,
Note = DiagnosticIDs::Note,
Remark = DiagnosticIDs::Remark,
Warning = DiagnosticIDs::Warning,
Error = DiagnosticIDs::Error,
Fatal = DiagnosticIDs::Fatal
};
(2-4)ShouldOwnClient
当设置为True时,可以转移DiagnosticsEngine 的控制权(具体可以参考DiagnosticsEngine的takeClient()成员函数)。
(3)LangOptions
主要是关于C/C++/Objective-c/objective-c++语言的一些选项。
(4)SourceManager
SourceManager也位于clang/lib/Basic中,是资源管理的工具箱(Track and cache source files)。除cache、buffer管理之外,还管理SourceLocation。
(4-1)SourceLocation
SourceLocation:Encodes a location in the source. The SourceManager can decode this to get at the full include stack, line and column information.在编译器中需要用到的三个“重要”Location:行号,列号,声明和调用文件路径,都与SourceManager有关,其中行号和列号用SourceLocation表示。SourceLocation是一个偏移,整个type大小为四个字节(4==sizeof(SourceLocation))。SourceRange是两个SourceLocation组成的区间。
(4-2)Location种类
Location有三种类型:spelling Location,expansion Location,presumed location。
当遇到宏展开的时候,expansion和presumed解析方式一样,行的结果可能不能一样(因为presumed遇到#line会重新计算行号),列结果一样都是call的列;而spelling是其原始定义(#define)的行和列。
当遇到#line指定行号之后的代码,spelling和expansion结果一样(非宏定义的地方),而presumed会重新计算行号。如下简单说明,第六行的数字2的位置spelling是第一行,而expansion则是其展开的位置第六行,presumed因为前面有#line 4则表示第四行。
:#define min(x,y) (x)>(y)?(y):(x)
:
:Void main(int argc,char **argv){
:
:#line 4
:int a=min(,);
//解析数字2的位置:|spelling |expansion|presumed|
// | 1行 | 6行 | 4行 |
:}
调用SourceManager的getSpellingLineNumber(SourceLocation)获得行号,getSpellingColumNumber(SourceLocation)获得列号。其他两种Location类似。
(4-3)Token所在的文件
Preprocessor不仅处理cpp文件中的Token,而且还处理#include的Token。如果此Token属于#include文件,可以使用sourceManager的getBufferName成员函数。如下示例:
if(!SourceMgr_->isInMainFile(tok.getLocation())) std::cout<<SourceMgr_->getBufferName(tok.getLocation())<<std::endl;
如果是在MainFile中(即translation unit中的CPP文件),可以通过SourceManager的getPresumedLoc(SourceLocation)获取PresumedLoc,该类中有相关filename。
FileID fd=SourceMgr_->getMainFileID();
if (!fd.isInvalid()){
std::cout<<"main file:";
const FileEntry * FE=SourceMgr_->getFileEntryForID(fd);
if (FE && FE->isValid())
std::cout<<FE->getName()<<std::endl;
}
(5)HeaderSearch
提供头文件的搜寻位置,其AddSearchPath成员函数可以为头文件搜索提供新的路径,当AddSearchPath第二个参数设置为True,则会覆盖原有路径;如果为false,则为添加。
const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
if(DE){
DirectoryLookup DL(DE, SrcMgr::C_User, false);
HeaderInfo->AddSearchPath(DL, false);
}
(6)ModuleLoader
主要预处理Objective-C语言代码中@import指令。在clang/doc/Modules.rst中,大篇幅谈及了import加载模块的好处,将向C++委员会建议加入此功能。
(7)IdentifierInfoLookup
词法解析(Lex)每个Token,都有与之对应TokenKinds,Identifier是TokenKinds的其中一种(include/clang/Basic/TokenKinds.def有说明),主要是指函数或者变量名等。IdentifierInfoLookup 是个抽象接口【virtual IdentifierInfo* get(StringRef Name) 必须实现】,在Preprocessor构造函数中,如果该项不为NULL,预处理器在查询IdentiferInfo表(hash表)时,将优先调用IdentifierInfoLookup的get成员函数,获取IdentifierInfo,这样就可以达到修改Token的IdentifierInfo属性的目的。
(8)OwnsHeaders
如果前面的HeaderSearch是动态分配的,该项设置为true,则Preprocessor会回收该空间。无需用户调用delete。
(9)TranslationUnitKind
每一个Cpp及include文件组成一个翻译单元(Translation unit),在Preprocessor中默认为TU_Complete,表示是一个完整的翻译单元,也没有使用该参数。
enum TranslationUnitKind {
/// \brief The translation unit is a complete translation unit.
TU_Complete,
/// \brief The translation unit is a prefix to a translation unit, and is
/// not complete.
TU_Prefix,
/// \brief The translation unit is a module.
TU_Module
};
至此,Preprocessor的构造函数说明,在使用之前只需要做些繁琐的初始化工作。
3.2 Preprocessor钩子
在预处理translation unit完每一部分(如#include、#if)的时候,还可以往Preprocessor中添加“钩子”( 继承PPCallbacks,实现某些接口函数,然后addPPCallbacks),就可以将用户的“意图”加入到Preprocessor的处理过程中。这些可以接口函数参考clang/Lex/PPCallbacks.h文件。如下示例实现了InclusionDirective接口,打印#include文件的搜索路径。
/*该回调函数打印#include文件的搜索路径*/
class InclusionDirectiveCallbacks : public PPCallbacks {
public:
void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
StringRef FileName,
bool IsAngled,
CharSourceRange FilenameRange,
const FileEntry *File,
StringRef SearchPath,
StringRef RelativePath,
const Module *Imported) {
std::cout<< FileName.str()<<std::endl;
std::cout<<SearchPath.str()<<std::endl; }};
在clang中源代码中有PPConditionalDirectiveRecord和PreprocessingRecord——两个Preprocess的Hooks,以PPConditionalDirectiveRecord为例,监听Preprocess处理#If,#Ifdef,#Ifndef,#Elif,#Else,#Endif。
PPConditionalDirectiveRecord *callbacks2=new PPConditionalDirectiveRecord(*SourceMgr_);
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(callbacks2));
….
//测试一个代码位置区间是否与#If,#Ifdef,#Ifndef,#Elif,#Else,#Endif等模块有交集
bool ret=callbacks2->rangeIntersectsConditionalDirective(SourceRange(innerdefiner,outdefiner));
3.3整个代码
(1)makefile
LLVM基础库众多,而且找对应库非常麻烦。借助llvm-config工具,虽然降低了编译速度,但使用简单。以下用clang++编译LexerTest.cpp。
CXX := clang++
LLVMCOMPONENTS := cppbackend
RTTIFLAG := -fno-rtti
LLVMCONFIG := llvm-config
CXXFLAGS := -I. -I/usr/local/include -I/usr/include -I$(shell $(LLVMCONFIG) --src-root)/tools/clang/include -I$(shell $(LLVMCONFIG) --obj-root)/tools/clang/include -g $(shell $(LLVMCONFIG) --cxxflags) $(RTTIFLAG)
LLVMLDFLAGS := $(shell $(LLVMCONFIG) --ldflags --libs $(LLVMCOMPONENTS))
SOURCES = LexerTest.cpp
OBJECTS = $(SOURCES:.cpp=.o)
EXES = $(OBJECTS:.o=)
CLANGLIBS = \
-lclangTooling\
-lclangFrontendTool\
-lclangFrontend\
-lclangDriver\
-lclangSerialization\
-lclangCodeGen\
-lclangParse\
-lclangSema\
-lclangStaticAnalyzerFrontend\
-lclangStaticAnalyzerCheckers\
-lclangStaticAnalyzerCore\
-lclangAnalysis\
-lclangARCMigrate\
-lclangRewriteFrontend\
-lclangRewrite\
-lclangEdit\
-lclangAST\
-lclangLex\
-lclangBasic\
$(shell $(LLVMCONFIG) --libs)\
$(shell $(LLVMCONFIG) --system-libs)
#all: $(OBJECTS)
#$(EXES)
%.o:%.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
#%: %.o
LexerTest: LexerTest.o
$(CXX) -o $@ *.o $(CLANGLIBS) $(LLVMLDFLAGS)
#FrontendAction:FrontendAction.o
clean:
-rm -f $(EXES) $(OBJECTS)
(2)源代码
#include "clang/Lex/Lexer.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/ModuleLoader.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/PPConditionalDirectiveRecord.h"
#include "llvm/Support/Path.h"
#include<iostream>
using namespace llvm;
using namespace clang;
class clientConsumer:public DiagnosticConsumer{
void HandleDiagnostic(DiagnosticsEngine::Level Level,
const Diagnostic &Info) {
DiagnosticConsumer::HandleDiagnostic(Level, Info);
std::cout<<getNumWarnings()<<"th warnings occurs!"<<std::endl;
}
};
std::string getSourceText_(Token Begin, Token End, SourceManager * SourceMgr_,
LangOptions & LangOpts);
class InclusionDirectiveCallbacks : public PPCallbacks {
public:
void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
StringRef FileName,
bool IsAngled,
CharSourceRange FilenameRange,
const FileEntry *File,
StringRef SearchPath,
StringRef RelativePath,
const Module *Imported) {
std::cout<< “include file:”<<FileName.str()<<”::”;
std::cout<<SearchPath.str()<<std::endl;
}
};
class IDLookup :public IdentifierInfoLookup{
IdentifierInfo* get(StringRef Name){
return NULL;
}
};
class moduleImportCallback:public PPCallbacks{
public:
void moduleImport(SourceLocation ImportLoc,
ModuleIdPath Path,
const Module *Imported) {
if(Imported)
std::cout<<"import:"<<Imported->Name<<std::endl;
}
};
class VoidModuleLoader : public ModuleLoader {
ModuleLoadResult loadModule(SourceLocation ImportLoc,
ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
std::cout<<"load Module:"<<std::endl;
return ModuleLoadResult();
}
void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc,
bool Complain) override { std::cout<<Mod->Name<<std::endl;}
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
{
std::cout<<"loadGlobalModuleIndex"<<std::endl;
return nullptr; }
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
{
std::cout<<"lookupMissingImports"<<std::endl;
return ;}
};
bool CheckLex(StringRef Source) {
DiagnosticOptions diagnosticOptions;
TextDiagnosticPrinter *pTextDiagnosticPrinter =
new TextDiagnosticPrinter(
llvm::errs(),
&diagnosticOptions,
true);
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
clientConsumer cC;
DiagnosticsEngine *Diags =new DiagnosticsEngine(DiagID,
&diagnosticOptions,
//pTextDiagnosticPrinter);
//new IgnoringDiagConsumer());
&cC,false);
FileSystemOptions FileMgrOpts;
FileManager FileMgr(FileMgrOpts);
SourceManager * SourceMgr_= new SourceManager(*Diags,FileMgr);
LangOptions LangOpts;
clang::TargetOptions targetOptions;
targetOptions.Triple = llvm::sys::getDefaultTargetTriple();
IntrusiveRefCntPtr<TargetInfo> Target = TargetInfo::CreateTargetInfo(*Diags,
std::make_shared<clang::TargetOptions>(targetOptions));
std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(Source);
const FileEntry *File = FileMgr.getFile("./input.cpp");
if(File){
SourceMgr_->setMainFileID(SourceMgr_->createFileID(File,SourceLocation(),SrcMgr::C_User));
}
else
SourceMgr_->setMainFileID(SourceMgr_->createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
HeaderSearch *HeaderInfo=new HeaderSearch(new HeaderSearchOptions, *SourceMgr_, *Diags, LangOpts,
Target.get());
StringRef SearchPath = llvm::sys::path::parent_path("/home/usr/Desktop/Lex");
const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
const DirectoryEntry *DE1 = FileMgr.getDirectory("/home/usr/Desktop/Lex");
if(DE1&&DE){
DirectoryLookup DL(DE, SrcMgr::C_User, false);
DirectoryLookup DL1(DE1, SrcMgr::C_User, false);
HeaderInfo->AddSearchPath(DL, false);
HeaderInfo->AddSearchPath(DL1, true);}
IDLookup *idlookup=new IDLookup;
Preprocessor PP(new PreprocessorOptions(), *Diags, LangOpts, *SourceMgr_,
*HeaderInfo, ModLoader, /*IILookup =*/NULL,
/*OwnsHeaderSearch =*/true,TU_Prefix);
PP.Initialize(*Target);
moduleImportCallback *Callbacks1= new moduleImportCallback;
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks1));
InclusionDirectiveCallbacks *Callbacks=new InclusionDirectiveCallbacks;
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
PPConditionalDirectiveRecord *callbacks2=new PPConditionalDirectiveRecord(*SourceMgr_);
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(callbacks2));
PP.EnterMainSourceFile();
bool Invalid;
bool islong=false;
SourceLocation innerdefiner;
SourceLocation outdefiner;
while () {
Token tok;
PP.Lex(tok);
if (tok.is(tok::eof))
break;
std::string str =getSourceText_(tok,tok,
SourceMgr_, LangOpts);
if(str=="printf")
Diags->Report(diag::warn_mt_message) << str;
if(str=="innerDefiner")
innerdefiner=tok.getLocation();
if(str=="outDefiner")
outdefiner=tok.getLocation();
//std::cout<<str<<":"<<tok.getName()<<std::endl;
//std::cout<<str<<std::endl;
//std::cout<<Str.data()<<std::endl;
/* std::cout<<str<<"::" \
<<SourceMgr_->getExpansionLineNumber(tok.getLocation())<<":" \
<<SourceMgr_->getExpansionColumnNumber(tok.getLocation())<<std::endl;*/
std::cout<<str<<"----";
if(!SourceMgr_->isInMainFile(tok.getLocation()))
std::cout<<SourceMgr_->getBufferName(tok.getLocation())<<std::endl;
}
FileID fd=SourceMgr_->getMainFileID();
if (!fd.isInvalid()){
std::cout<<"main file:";
const FileEntry * FE=SourceMgr_->getFileEntryForID(fd);
if (FE && FE->isValid())
std::cout<<FE->getName()<<std::endl;
}
bool ret=callbacks2->rangeIntersectsConditionalDirective(SourceRange(innerdefiner,outdefiner));
if(ret)
std::cout<<"Total Memory:"<<callbacks2->getTotalMemory()<<std::endl;
return true;
}
//}//end namespace clangT
std::string getSourceText_(Token Begin, Token End, SourceManager * SourceMgr_,
LangOptions & LangOpts) {
bool Invalid;
StringRef Str =
Lexer::getSourceText(CharSourceRange::getTokenRange(SourceRange( \
Begin.getLocation(), End.getLocation())),
*SourceMgr_, LangOpts, &Invalid);
if (Invalid)
return "<INVALID>";
// std::cout<<Str.str()<<std::endl;
return Str;
}
int main(int argc,char **argv)
{
FileSystemOptions fileSystemOptions;
FileManager file(fileSystemOptions);
auto FileBuffer=file.getBufferForFile("./input.cpp");
std::string code= (*FileBuffer)->getBuffer();
CheckLex(code);
}
全部代码
Clang之词法分析Lex的更多相关文章
- GCC编译器原理(三)------编译原理三:编译过程(2-1)---编译之词法分析
二.编译 引用文档:https://blog.csdn.net/chdhust/article/details/9040647 编译过程就是把预处理完的文件进行一系列词法分析.语法分析.语义分析及优化 ...
- 词法分析程序 LEX和VC6整合使用的一个简单例子
词法分析的理论知识不少,包括了正规式.正规文法.它们之间的转换以及确定的有穷自动机和不确定的有穷自动机等等... 要自己写一个词法分析器也不会很难,只要给出了最简的有穷自动机,就能很方便实现了,用if ...
- Yacc 与 Lex 快速入门(词法分析和语法分析)
我们知道,高级语言,一般的如c,Java等是不能直接运行的,它们需要经过编译成机器认识的语言.即编译器的工作. 编译器工作流程:词法分析.语法分析.语义分析.IR(中间代码,intermediate ...
- 深入研究Clang(五) Clang Lexer代码阅读笔记之Lexer
作者:史宁宁(snsn1984) Clang的Lexer(词法分析器)的源代码的主要位置例如以下: clang/lib/Lex 这里是基本的Lexer的代码: clang/include/cla ...
- Clang比 gcc/g++更人性化代码出错提示的C/C++编译器
编译器方面的几个命令 gcc/g++ 一. 常用编译命令选项 常用用法 gcc -Wall test.c -o test gcc编译过程 .c ->(-E)-> .i[中间文件] -> ...
- windows平台下基于VisualStudio的Clang安装和配置
LLVM 是一个开源的编译器架构,它已经被成功应用到多个应用领域.Clang是 LLVM 的一个编译器前端,它目前支持 C, C++, Objective-C 以及 Objective-C++ 等编程 ...
- 转:GCC,LLVM,Clang编译器对比
GCC,LLVM,Clang编译器对比 转自: http://www.cnblogs.com/qoakzmxncb/archive/2013/04/18/3029105.html 在XCode中, ...
- C# 词法分析器(一)词法分析介绍 update 2014.1.8
系列导航 (一)词法分析介绍 (二)输入缓冲和代码定位 (三)正则表达式 (四)构造 NFA (五)转换 DFA (六)构造词法分析器 (七)总结 虽然文章的标题是词法分析,但首先还是要从编译原理说开 ...
- 【译】Python Lex Yacc手册
本文是PLY (Python Lex-Yacc)的中文翻译版.转载请注明出处.这里有更好的阅读体验. 如果你从事编译器或解析器的开发工作,你可能对lex和yacc不会陌生,PLY是David Beaz ...
随机推荐
- Unity3D文件读取
Resources: 是作为一个Unity3D的保留文件夹出现的,也就是如果你新建的文件夹的名字叫Resources,那么里面的内容在打包时都会被无条件的打到发布包中.它的特点简单总结一下就是: 只读 ...
- 在ASP.NET MVC中利用Aspose.cells 将查询出的数据导出为excel,并在浏览器中下载。
正题前的唠叨 本人是才出来工作不久的小白菜一颗,技术很一般,总是会有遇到一些很简单的问题却不知道怎么做,这些问题可能是之前解决过的.发现这个问题,想着提升一下自己的技术水平,将一些学的新的'好'东西记 ...
- 父(Spring)子(SpringMVC)容器之初解篇
Spring和SpringMVC作为Bean管理容器和MVC层的默认框架,已被众多WEB应用采用,而在实际开发中,由于有了强大的注解功能,很多基于XML的配置方式已经被替代,但在实际项目中,我们经常会 ...
- Hibernate学习之一对多关联
注意事项: 1.单向一对多 只需在“一”放进行配置2.双向一对多 需要在关联双方都加以配置,而且需要在一的一方设置inverse=true 首先是实体类: TAddress.java(多的一方) ...
- (转)log4j(六)——log4j.properties简单配置样例说明
一:测试环境与log4j(一)——为什么要使用log4j?一样,这里不再重述 1 老规矩,先来个栗子,然后再聊聊感受 (1)使用配文件的方式,是不是感觉非常的清爽,如果不在程序中读取配置文件就更加的清 ...
- 【samba】samba 用户权限配置(转)
首先要保证你的samba安装并配置好,关于安装和配置samba请参考此文章 http://blog.csdn.net/linglongwunv/archive/2010/01/19/5212875.a ...
- Yii框架用ajax提交表单时候报错Bad Request (#400): Unable to verify your data submission.
提交表单报400错误,提示 "您提交的数据无法验证"原来是csrf验证的问题,因为表单是自己写的,在Yii框架中,为了防止csrf攻击,对post的表单数据封装了CSRF令牌验证. ...
- TypeScript02 方法特性【参数种类、参数个数】、generate方法、析构表达式、箭头表达式、循环
1 方法的参数 1.1 必选参数 调用方法时实参的个数必须和定义方法时形参在数量和类型上匹配 /** * Created by Administrator on 2017/8/2 0002. */ f ...
- MySQL buffer pool中的三种链
三种page.三种list.LRU控制调优 一.innodb buffer pool中的三种页 1.free page:从未用过的页 2.clean page:干净的页,数据页的数据和磁盘一致 3.d ...
- Python网络数据采集4-POST提交与Cookie的处理
Python网络数据采集4-POST提交与Cookie的处理 POST提交 之前访问页面都是用的get提交方式,有些网页需要登录才能访问,此时需要提交参数.虽然在一些网页,get方式也能提交参.比如h ...