昨天看了B站一个UP主面试JAVA的视频,感觉面试内容很基础但是有些东西看着眼熟(需要想好久或者很模糊),所以开个文章积累一些基础知识,不定时更新
20190813
1、string、stringbuffer、 stringbuilder之间的区别
参考博客:https://www.cnblogs.com/crazytomato/p/8359009.html
https://blog.csdn.net/weixin_39070579/article/details/83575448
更喜欢第二个的解释 下面摘抄自第二个的解释
1、 三者在执行速度方面的比较:StringBuilder >StringBuffer > String
String一旦赋值或实例化后就不可更改,如果赋予新值将会重新开辟内存地址进行存储。
而StringBuffer类使用append和insert等方法改变字符串值时只是在原有对象存储的内存地址上进行连续操作,减少了资源的开销。
所以我们在声明结果集,sql语句时,用StringBuilder。
因此:当需要进行频繁修改字符串的操作时先建立StringBuffer类对象进行操作,将最后结果转化成String类对象返回,这样效率会高很多。如: MySqlHelp.GetDataSet(connStr, CommandType.Text, sqlCmd.ToString();
StringBuffer(StringBuilder)其实可以看做“基本数据类型”String的包装类(Wrapper),就像int与之对应的Integer等关系。
StringBuffer有缓存的,如果你声明一个字符串只是接收传过来的参数,然后进行业务逻辑处理,那么假如你用很多个StringBuffer类型的对象,就比较浪费内存。这样用String就更好。
2、 在字符串拼接时,String 对象的速度并不会比 StringBuffer对象慢。
String 对象的字符串拼接其实是被 JVM 解释成了StringBuffer 对象的拼接,所以这些时候String 对象的速度并不会比StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快。
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“test”);
会发现,生成String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”;
其实就是:
String S1 = “This is only a simple test”;
所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做
3、在线程安全方面不同
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
三者使用总结:在操作少量变动的数据时,使用String;在单线程中操作大量数据的字符串时,使用StringBuilder;在多线程中操作大量数据的字符串时,使用StringBuffer。
2、HashTable、Dictionary、DataTable、List的比较
参考:
https://www.cnblogs.com/jilodream/p/4219840.html
Dictionary Hash原理https://blog.csdn.net/zhaoguanghui2012/article/details/88105715
下面摘抄自第一个
**在.Net 模仿java 的过程中 抛弃了 HashMap ,所以以后再去面试.Net的时候当别人问你HashTable 和HashMap 的区别的时候,请告诉他,C#.Net 中 没有HashMap
好接下来进入正题**
HashTable和Dic :
Hashtable和Dictionary从数据结构上来说都属于Hashtable(哈希表),都是对关键字(键值)进行散列操作,将关键字散列到Hashtable的某一个槽位中去,不同的是处理碰撞的方法。散列函数有可能将不同的关键字散列到Hashtable中的同一个槽中去,这个时候我们称发生了碰撞,为了将数据插入进去,我们需要另外的方法来解决这个问题。
采用链表法的是Dic 而采用开放寻址法(open addressing)-中 双重散列的方法的是 HashTable
至于这两种数据结构的使用方法 请自行阅读算法导论 或者参照网上博客
但从底层的数据结构可以发现
如果增删的动作很多的话 推荐使用Dic 因为解决碰撞的方式 是List.Add
如果改动的动作很少 查询的动作很多的话 则推荐 使用HashTable 因为映射查找之后 只需要跳跃查找到 碰撞后移动数据即可,另外当增加数据太多时,开放寻址的扩容很耗费性能(请阅读<算法导论>
1:单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分.
2:多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减.
3:Dictionary 有按插入顺序排列数据的特性 (注: 但当调用 Remove() 删除过节点后顺序被打乱), 因此在需要体现顺序的情境中使用 Dictionary 能获得一定方便. //Dic遍历时 会采用插入时的遍历,而hashTable 采用遍历时 则是打乱的
Hashtable 类和 Dictionary<TKey, TValue> 泛型类实现 IDictionary 接口
Dictionary<TKey, TValue> 泛型类还实现 IDictionary<TKey, TValue>泛型接口。
因此,这些集合中的每个元素都是一个键/值对。
Dictionary<TKey, TValue> 类与 Hashtable 类的功能相同
对于值类型,特定类型(不包括 Object)的 Dictionary<TKey, TValue> 的性能优于 Hashtable,这是因为 Hashtable 的元素属于 Object 类型,所以在存储或检索值类型时通常发生装箱和取消装箱操作
Dic 和 List<T>:
首先我们清楚List<T>是对数组做了一层包装,我们在数据结构上称之为线性表,而线性表的概念是,在内存中的连续区域,除了首节点和尾节点外,每个节点都有着其唯一的前驱结点和后续节点。我们在这里关注的是连续这个概念。
而HashTable或者Dictionary,他是根据Key而根据Hash算法分析产生的内存地址,因此在宏观上是不连续的,虽然微软对其算法也进行了很大的优化。
由于这样的不连续,在遍历时,Dictionary必然会产生大量的内存换页操作,而List只需要进行最少的内存换页即可,这就是List和Dictionary在遍历时效率差异的根本原因。
所以根据value 的查找 dic 的效率是高于 List 的 但是遍历的话 则Dic 要差点。这就好比你要摘抄书里边的所有文字 是根据目录 查一个找一篇文章 快,还是直接从正文开始 从头到尾快遍历快一样。单独的找某一篇知道题目(key)的文章 当然是从目录快了
再谈Dictionary
也许很多人说,既然Dictionary如此强大,那么我们为什么不用Dictionary来代替一切集合呢?
在这里我们除了刚才的遍历问题,还要提到Dictionary的存储空间问题,在Dictionary中,除了要存储我们实际需要的Value外,还需要一个辅助变量Key,这就造成了内存空间的双重浪费。
而且在尾部插入时,List只需要在其原有的地址基础上向后延续存储即可,而Dictionary却需要经过复杂的Hash计算,这也是性能损耗的地方。
List<T>和 DataTable:
List<T>和 DataTable
DataTable,IList性能比较
1)二进制序列化的情况
从测试结果可以看出,IList<T>序列化的文件大小比DataTable小得多,这意味着在数据传输中带宽占用小很多,所以在设计Remoting接口时尽量使用IList<T>作返回值。
2)XML序列化的情况
从测试结果可以看出,IList<T>序列化后的文件比同样比DataTable小,但差距已经没有二进制序列化那么明显了。而且IList<T>的二进制序列化和XML序列化相差很大,所以remoteing中建议使用二进制序列化。
3)操作性比较
DataTable有支持数据的提交、回滚、查询等强大的方法,但访问单元格内容的时候不方便,还要类型转换。
IList<T>则访问项的属性比较方便,有属性自动提示,不用类型转换,有LINQ的协助也能实现强大的查询
转载时请保留作者署名jilodream/王若伊_恩赐解脱(博客链接:http://www.cnblogs.com/jilodream/
3、迭代器Iterator
参考博客 https://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html
为什么要设计迭代器:
迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。简单来说,迭代器模式使得你能够获取到序列中的所有元素而不用关心是其类型是array,list,linked list或者是其他什么序列结构。这一点使得能够非常高效的构建数据处理通道(data pipeline)--即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是LINQ的核心模式。
在.NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接口,那么就能够被迭代;调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,他是数据序列中的一个位置记录。迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。
在C#1中已经内建了对迭代器的支持,那就是foreach语句。使得能够进行比for循环语句更直接和简单的对集合的迭代,编译器会将foreach编译来调用GetEnumerator和MoveNext方法以及Current属性,如果对象实现了IDisposable接口,在迭代完成之后会释放迭代器。但是在C#1中,实现一个迭代器是相对来说有点繁琐的操作。C#2使得这一工作变得大为简单,节省了实现迭代器的不少工作。
如何手动实现一个迭代器: 参考上面的博客
4 控制并发数量
参考博客https://blog.csdn.net/qq_14853875/article/details/81938574
5、多线程的创建方式
参考微博https://www.jianshu.com/p/00b33e5cd149
主要方式 Thread 、ThreadPool 、Task
6、防止表单重复提交
参考博客https://www.cnblogs.com/huanghuizhou/p/9153837.html
摘抄自上面的微博
1 通过JavaScript屏蔽提交按钮(不推荐)
通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无效,从而实现防止表单重复提交。
ps:js代码很容易被绕过。比如用户通过刷新页面方式,或使用postman等工具绕过前段页面仍能重复提交表单。因此不推荐此方法。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>表单</title>
<script type="text/javascript">
//默认提交状态为false
var commitStatus = false;
function dosubmit(){
if(commitStatus==false){
//提交表单后,讲提交状态改为true
commitStatus = true;
return true;
}else{
return false;
}
}
</script>
</head>
<body>
<form action="/path/post" onsubmit="return dosubmit()" method="post">
用户名:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
2 给数据库增加唯一键约束(简单粗暴)
在数据库建表的时候在ID字段添加主键约束,用户名、邮箱、电话等字段加唯一性约束。确保数据库只可以添加一条数据。
数据库加唯一性约束sql:
alter table tableName_xxx add unique key uniq_xxx(field1, field2)
服务器及时捕捉插入数据异常:
try {
xxxMapper.insert(user);
} catch (DuplicateKeyException e) {
logger.error("user already exist");
}
3 利用Session防止表单重复提交(推荐)
服务器返回表单页面时,会先生成一个subToken保存于session,并把该subToen传给表单页面。当表单提交时会带上subToken,服务器拦截器Interceptor会拦截该请求,拦截器判断session保存的subToken和表单提交subToken是否一致。若不一致或session的subToken为空或表单未携带subToken则不通过。
首次提交表单时session的subToken与表单携带的subToken一致走正常流程,然后拦截器内会删除session保存的subToken。当再次提交表单时由于session的subToken为空则不通过。从而实现了防止表单重复提交。
mvc配置文件加入拦截器配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="xxx.xxx.interceptor.AvoidDuplicateSubmissionInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
拦截器
package xxx.xxxx.interceptor;
import xxx.xxx.SubToken;
import org.apache.struts.util.TokenProcessor;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
public class AvoidDuplicateSubmissionInterceptor extends
HandlerInterceptorAdapter {
public AvoidDuplicateSubmissionInterceptor() {
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SubToken annotation = method
.getAnnotation(SubToken.class);
if (annotation != null) {
boolean needSaveSession = annotation.saveToken();
if (needSaveSession) {
request.getSession(false)
.setAttribute(
"subToken",
TokenProcessor.getInstance().generateToken(
request));
}
boolean needRemoveSession = annotation.removeToken();
if (needRemoveSession) {
if (isRepeatSubmit(request)) {
return false;
}
request.getSession(false).removeAttribute("subToken");
}
}
}
return true;
}
private boolean isRepeatSubmit(HttpServletRequest request) {
String serverToken = (String) request.getSession(false).getAttribute(
"subToken");
if (serverToken == null) {
return true;
}
String clinetToken = request.getParameter("subToken");
if (clinetToken == null) {
return true;
}
if (!serverToken.equals(clinetToken)) {
return true;
}
return false;
}
}
控制层 controller
@RequestMapping("/form")
//开启一个Token
@SubToken(saveToken = true)
public String form() {
return "/test/form";
}
@RequestMapping(value = "/postForm", method = RequestMethod.POST)
@ResponseBody
//开启Token验证,并且成功之后移除当前Token
@SubToken(removeToken = true)
public String postForm(String userName) {
System.out.println(System.currentTimeMillis());
try{
System.out.println(userName);
Thread.sleep(1500);//暂停1.5秒后程序继续执行
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis());
return "1";
}
表单页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form method="post" action="/postForm">
<input type="text" name="userName">
<input type="hidden" name="subToken" value="${subToken}">
<input type="submit" value="提交">
</form>
</body>
</html>
4使用AOP自定义切入实现
实现原理:
自定义防止重复提交标记(@AvoidRepeatableCommit)。
对需要防止重复提交的Congtroller里的mapping方法加上该注解。
新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。
每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。
重复提交时Aspect会判断当前redis是否有该key,若有则拦截。
自定义标签
import java.lang.annotation.*;
/**
* 避免重复提交
* @author hhz
* @version
* @since
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidRepeatableCommit {
/**
* 指定时间内不可重复提交,单位毫秒
* @return
*/
long timeout() default 30000 ;
}
自定义切入点Aspect
/**
* 重复提交aop
* @author hhz
* @version
* @since
*/
@Aspect
@Component
public class AvoidRepeatableCommitAspect {
@Autowired
private RedisTemplate redisTemplate;
/**
* @param point
*/
@Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)")
public Object around(ProceedingJoinPoint point) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = IPUtil.getIP(request);
//获取注解
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
//目标类、方法
String className = method.getDeclaringClass().getName();
String name = method.getName();
String ipKey = String.format("%s#%s",className,name);
int hashCode = Math.abs(ipKey.hashCode());
String key = String.format("%s_%d",ip,hashCode);
log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
AvoidRepeatableCommit avoidRepeatableCommit = method.getAnnotation(AvoidRepeatableCommit.class);
long timeout = avoidRepeatableCommit.timeout();
if (timeout < 0){
//过期时间5分钟
timeout = 60*5;
}
String value = (String) redisTemplate.opsForValue().get(key);
if (StringUtils.isNotBlank(value)){
return "请勿重复提交";
}
redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS);
//执行方法
Object object = point.proceed();
return object;
}
}
7 AOP原理
参考博客https://yq.aliyun.com/articles/569532
反射+代理
8 日志的配置
常用Nlog、 Log4net
以log4net为例
Level:
[Flags]
public enum LogLevel
{
Debug = 2,
Info = 4,
Warn = 8,
Error = 16
}
Model:
public bool EnableConsoleLog { get; set; } = true;
public bool EnableFileLog { get; set; }
public string FileLogPath { get; set; } = "log";
public LogLevel FileLogLevel { get; set; } = LogLevel.Error;
public LogLevel ConsoleLogLevel { get; set; } = LogLevel.Debug;
public bool UseInService { get; set; }
加载配置主要分两种一种配置文件 一种代码
配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This section contains the log4net configuration settings -->
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
</appender>
<!-- Define some output appenders -->
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<!--日志路径-->
<file value="log.log" />
<!--是否向文件中追加日志-->
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<!--日志路径-->
<file value="/var/log/c3709/" />
<!--是否向文件中追加日志-->
<appendToFile value="true" />
<rollingStyle value="Composite" />
<!--否只写到一个文件中-->
<staticLogFileName value="false" />
<datePattern value="yyyyMMdd'.log'" />
<!--日志保留天数-->
<maxSizeRollBackups value="10" />
<!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
<maximumFileSize value="1MB" />
<layout type="log4net.Layout.PatternLayout">
<!--记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger 操作者ID:%property{Operator} 操作类型:%property{Action}%n 当前机器名:%property%n当前机器名及登录用户:%username %n 记录位置:%location%n 消息描述:%property{Message}%n 异常:%exception%n 消息:%message%newline%n%n-->
<conversionPattern value="记录时间:%date | 线程ID:[%thread]| 日志级别:%-5level| %logger [%property{NDC}]| - %message%newline|" />
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="ALL" />
<appender-ref ref="ConsoleAppender" />
<appender-ref ref="FileAppender" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
</configuration>
加载
public Log4netHelper()
{
try
{
ILoggerRepository repository = LogManager.CreateRepository("NETCoreRepository");
//获取配置文件路径
//string test = Assembly.GetEntryAssembly().Location;
// string m_Config_File_Path = AppDomain.CurrentDomain.BaseDirectory + "log4net." +
//"config";
#region windows下的路径
//string m_Config_File_Path = new DirectoryInfo("../").FullName + "luck_app_Common" + "\\" + "logHelper" + "\\" + "log4net." + "config";
#endregion
#region linux下的路径
string m_Config_File_Path = new DirectoryInfo(".").FullName + "/" + "bin/Debug/netcoreapp2.1" + "/" + "logHelper" + "/" + "log4net." + "config";
#endregion
if ([图片上传失败...(image-6de2a0-1565664632623)]
System.IO.File.Exists(m_Config_File_Path))
{
//加载配置信息
log4net.Config.XmlConfigurator.Configure(repository, new [图片上传失败...(image-edcc33-1565664632623)]
System.IO.FileInfo(m_Config_File_Path));
//log4net.Config.XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
//生成日志对象
log = LogManager.GetLogger([图片上传失败...(image-ed7611-1565664632623)]
repository.Name, "NETCorelog4net");
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Print("加载日志对象出错" + ex.Message);
}
}
代码加载方式
void UseCodeConfig(Hierarchy repository, LogLevel logLevel)
{
var ip = JimuHelper.GetLocalIPAddress();
if (_options.EnableFileLog && (_options.FileLogLevel & logLevel) == logLevel)
{
PatternLayout layout = new PatternLayout
{
ConversionPattern = "%date{yyyy-MM-dd HH:mm:ss.fff} %-5p [" + ip + "] %m%n"
};
layout.ActivateOptions();
RollingFileAppender roller = new RollingFileAppender
{
AppendToFile = false
};
var path = _options.EnableFileLog ? _options.FileLogPath : "log";
roller.File = $@"{path}/{logLevel.ToString().ToLower()}/";
roller.PreserveLogFileNameExtension = true;
roller.StaticLogFileName = false;
roller.MaxSizeRollBackups = 0;
roller.DatePattern = $@"yyyyMMdd"".log""";
roller.RollingStyle = RollingFileAppender.RollingMode.Date;
roller.Layout = layout;
roller.MaxFileSize = 10000000;
switch (logLevel)
{
case LogLevel.Debug:
roller.Threshold = Level.Debug;
break;
case [图片上传失败...(image-73ac4e-1565664758430)]
LogLevel.Info:
roller.Threshold = [图片上传失败...(image-385b04-1565664758430)]
Level.Info;
break;
case LogLevel.Warn:
roller.Threshold = Level.Warn;
break;
case LogLevel.Error:
roller.Threshold = Level.Error;
break;
}
roller.ActivateOptions();
repository.Root.AddAppender(roller);
}
if (_options.EnableConsoleLog && (_options.ConsoleLogLevel & logLevel) == logLevel)
{
//ManagedColoredConsoleAppender managedColoredConsoleAppender = new
ManagedColoredConsoleAppender console = new ManagedColoredConsoleAppender();
PatternLayout layoutConsole = new PatternLayout
{
ConversionPattern = "%n%date{yyyy-MM-dd HH:mm:ss.fff} %-5level [" + ip + "] %m",
};
switch (logLevel)
{
case LogLevel.Debug:
//console.Threshold = Level.Debug;
console.AddFilter(new LevelRangeFilter() { LevelMax = Level.Debug, LevelMin = Level.Debug });
//console.AddFilter(new LevelMatchFilter() { LevelToMatch = Level.Debug, AcceptOnMatch = true });
break;
case [图片上传失败...(image-10e192-1565664758429)]
LogLevel.Info:
//console.Threshold = [图片上传失败...(image-9cbe4-1565664758429)]
Level.Info;
console.AddFilter(new LevelRangeFilter() { LevelMax = [图片上传失败...(image-4ff928-1565664758429)]
Level.Info, LevelMin = [图片上传失败...(image-14baf6-1565664758429)]
Level.Info });
//console.AddFilter(new LevelMatchFilter() { LevelToMatch = [图片上传失败...(image-7c7080-1565664758429)]
Level.Info, AcceptOnMatch = true });
break;
case LogLevel.Warn:
//console.Threshold = Level.Warn;
console.AddFilter(new LevelRangeFilter() { LevelMax = Level.Warn, LevelMin = Level.Warn });
//console.AddFilter(new LevelMatchFilter() { LevelToMatch = Level.Warn, AcceptOnMatch = true });
break;
case LogLevel.Error:
//console.Threshold = Level.Error;
console.AddFilter(new LevelRangeFilter() { LevelMax = Level.Error, LevelMin = Level.Error });
//console.AddFilter(new LevelMatchFilter() { LevelToMatch = Level.Error });
break;
}
console.AddMapping(
new ManagedColoredConsoleAppender.LevelColors { Level = Level.Error, ForeColor = ConsoleColor.DarkRed });
console.AddMapping(
new ManagedColoredConsoleAppender.LevelColors { Level = Level.Warn, ForeColor = ConsoleColor.DarkYellow });
layoutConsole.ActivateOptions();
console.Layout = layoutConsole;
console.ActivateOptions();
repository.Root.AddAppender(console);
}
//MemoryAppender memory = new MemoryAppender();
//memory.ActivateOptions();
//repository.Root.AddAppender(memory);
//repository.Root.Level = Level.Debug;
repository.Configured = true;
}
加载方式
_options = options ?? new JimuLog4netOptions { EnableConsoleLog = true };
string repositoryName = $"jimuLogger-{Guid.NewGuid()}";
var repLogger = LogManager.CreateRepository(repositoryName);
UseCodeConfig((Hierarchy)repLogger, LogLevel.Debug);
UseCodeConfig((Hierarchy)repLogger, [图片上传失败...(image-c98552-1565665014973)]
LogLevel.Info);
UseCodeConfig((Hierarchy)repLogger, LogLevel.Warn);
UseCodeConfig((Hierarchy)repLogger, LogLevel.Error);
_logger = LogManager.GetLogger(repositoryName, MethodBase.GetCurrentMethod().DeclaringType);
20190814
1、ASP .net core启动流程
参考博客:https://www.cnblogs.com/artech/p/asp-net-core-pipeline.html
简单描述:
class Program
2: {
3: static void Main(string[] args)
4: {
5: new WebHostBuilder()
6: .UseKestrel()
7: .UseStartup<Startup>
8: .Build()
9: .Start();
10: }
11: }
1创建宿主WebHost->2( .UseKestrel())创建Server用于接收相应请求->3中间件管道
一个建立在ASP.NET Core之上的应用一般都是根据某个框架开发的,开发框架基本上是建立在某个特殊的中间件上。以ASP.NET Core MVC这个最著名的框架为例,它实际上是利用一个叫做 “路由” 的中间件实现了请求地址与Controller/Action之间的映射,并在此基础实现了激活Controller、执行Action以及呈现View等一系列的功能。所以应用程序可以视为某个中间件的一部分,如果一定要将它独立出来,整个请求处理管道将呈现出如右图所示的结构。(出自上面的参考博客)
2、Asp.net core 中间件
参考博客https://www.cnblogs.com/stulzq/p/7760648.html
里面有 中间件调用的详解、内置中间件以及如何创建一个中间件
[https://www.cnblogs.com/feimaoicoding/p/10878721.html] 一个Dapper中间件例子(https://www.cnblogs.com/feimaoicoding/p/10878721.html)
3** ASP.net core的异常处理**
参考博客https://blog.csdn.net/ailanzhe/article/details/79726568
主要分为四种处理方式:(摘抄自上面博客)
1使用开发人员异常页面
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();//使用异常记录页面
}
2配置HTTP错误代码页 Configuring status code pages
app.UseStatusCodePages(async context =>
{
context.HttpContext.Response.ContentType = "text/plain";
await context.HttpContext.Response.WriteAsync(
"Status code page, status code: " +
context.HttpContext.Response.StatusCode);
});
3使用MVC过滤器
全局过滤
public void OnException(ExceptionContext context)
{
var controller = context.ActionDescriptor;
ILog log = LogManager.GetLogger(Startup.Repository.Name, controller.ToString());//初始化Log4net日志
#region 记录到内置日志
//var logger = _loggerFactory.CreateLogger(context.Exception.TargetSite.ReflectedType);
//logger.LogError(new EventId(context.Exception.HResult),
//context.Exception,
//context.Exception.Message);
#endregion
if (_env.IsDevelopment())
{
log.Error(context.Exception.ToString());
//var JsonMessage = new ErrorResponse("未知错误,请重试");
//JsonMessage.DeveloperMessage = context.Exception;
//context.Result = new ApplicationErrorResult(JsonMessage);
//context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
//context.ExceptionHandled = true;
}
else
{
log.Error(context.Exception.ToString());
context.ExceptionHandled = true;
context.Result=new RedirectResult("/home/Error");
}
}
4自定义异常捕获中间件 Middleware
/// <summary>
/// 自定义异常处理中间件
/// </summary>
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
var statusCode = context.Response.StatusCode;
await HandleExceptionAsync(context, ex.ToString());
}
}
private Task HandleExceptionAsync(HttpContext context, string msg)
{
HandleExceptionHelper hannd = new HandleExceptionHelper();
hannd.log.Error(msg);//记录到日志文件
return context.Response.WriteAsync("ERROR");
}
}
3、 GetHashCode的作用
一个判断对象是否相等的快速检查器
参考博客:https://blog.csdn.net/xiaof_90/article/details/52037462
里面有段解释很形象
Equals方法和GetHashCode方法的重写应该同时存在。如果Equals方法返回的结果是true,那么GetHashCode方法返回的结果应该相同。如果GetHashCode方法返回的结果相同,那么Equals方法返回的结果不一定是true。
下面用人话,举例解释一遍:
家里养了好多小动物,要喂食。喂过了的小动物,就记录一下。记录使用了HashSet。
先喂了一只猫Kathie——记录一下,哦,猫Kathie喂过了
然后来了一只狗Doggie——想了下,恩,刚刚喂过的是猫,所以Doggie肯定还没有喂呢,于是喂了Doggie,再记录一下
然后来了一只鹦鹉Talkie——想了下,刚刚只喂过猫和狗,没有喂过鹦鹉,所以Talkie肯定没有喂过,于是喂了Talkie,再记录下
然后来了只猫Meowie——想了下,刚刚喂过猫了,我得再好好想想,哦,刚刚喂的是Kathie,Meowie还没喂,所以喂了Meowie,记录一下
然后来了只猫Kathie——想了下,刚刚喂过猫了,我得再好好想想(这主人脑子好机械化…),这不是Kathie吗,第一个喂的就是你!又想来多吃!不喂!
……
上面的小例子里,先用动物的种类来判断待喂的动物有没有被喂过,就是调用GetHashCode;用动物的名字来判断有没有喂过,就是调用Equals。
如果GetHashCode返回的结果相同的话,就没必要调用Equals了;如果GetHashCode返回的结果不同,才会再调用Equals方法,做精确对比。
4、MySql索引的实现原理
参考博客:https://www.cnblogs.com/boothsun/p/8970952.html
二叉树、B树、B+树
20190815
1、C# 反射详解
参考博客:https://www.cnblogs.com/vaevvaev/p/6995639.html?tdsourcetag=s_pctim_aiomsg
2、C# 线程池ThreadPool 使用
参考博客:https://blog.csdn.net/subin_iecas/article/details/80290660
3、C# List线程安全方面的问题
Add 多线程下资源竞争重现 参考博客:https://www.cnblogs.com/GreenLeaves/p/10092128.html
安全删除List中的一个元素 参考博客https://blog.csdn.net/zhuqinfeng/article/details/73550121
public void RemoveField(System.String name)
{
System.Collections.IEnumerator it = fields.GetEnumerator();
while (it.MoveNext())
{
Field field = (Field) it.Current;
if (field.Name().Equals(name))
{
fields.Remove(field);
return ;//亮点
}
}
}
public void RemoveFields(System.String name)
{
for (int i = fields.Count - 1; i >= 0; i--)//亮点
{
Field field = (Field) fields[i];
if (field.Name().Equals(name))
{
fields.RemoveAt(i);
}
}
}
---------------------
版权声明:本文为CSDN博主「移山小斗车」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhuqinfeng/article/details/73550121
20190816
1、C#中闭包的概念理解
参考博客:https://www.cnblogs.com/jiejie_peng/p/3701070.html
2、C#下RabbitMQ使用
不同策略的使用 参考博客:https://www.cnblogs.com/MuNet/p/8546192.html
包含调度使用、消息确认、持久化、分发策略
使用EasyNetQ 参考博客:[https://www.cnblogs.com/edisonchou/p/aspnetcore_easynetq_basicdemo_foundation.html]