OA项目第三天

Spring.Net中的IOC,DI相关配置以及实现,Spring.Net在MVC中的运用,队列的初次使用,Log4Net日志处理完善错误处理

OA项目第三天

Spring.Net中的IOC实现

首先我们先下载最新版本(貌似不会再更新了), https://blog.csdn.net/cctvcqupt/article/details/18600091

中文手册:http://vdisk.weibo.com/s/EzlFk3ZRCmti

然后我们在工程文件下创建一个lib文件夹,把需要的dll拷贝进去,添加引用

1
2
3
4
Common.Logging.dll
Spring.Core.dll
Spring.Core.pdb
Spring.Core

根据文档的IoC快速入门,我们开始以下操作,配置config(自动排序下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<description>An example that demonstrates simple IoC features.</description>
<object name="UserInfoServer" type="SpringNetDemo.UserInfoServer, SpringNetDemo"> </object>
</objects>
</spring>
</configuration>

在配置文件中,通过节点的子节点来配置程序中用到的对象。我们定义了UserInfoServer和IUserInfoServer,里面一个简单的ShowMsg方法

1
2
3
IApplicationContext ctx = ContextRegistry.GetContext();
IUserInfoServer lister = (IUserInfoServer)ctx.GetObject("UserInfoServer");
MessageBox.Show(lister.ShowMsg());//ShowMsg方法只是简单输出一个字符串

Spring.Net中的DI操作

首先我们前面的每定义一个Object,都得在Objects节点下添加,现在我们有一个更加便捷的方法,那就是新建一个xml文件,然后右键属性-复制到输出目录-这里选始终复制或者较新复制都可以,因为Spring.NETd的做法是在bin debug目录下去读取文件。然后我们可以把object节点放在这个xml文件下

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>

<objects xmlns="http://www.springframework.net">
<description>An example that demonstrates simple IoC features.</description>
<object name="UserInfoServer" type="SpringNetDemo.UserInfoServer, SpringNetDemo"> <!--后一个属性是程序集-->
<property name="Name" value="龙龟纹"/><!--属性赋值-->
<property name="Person" ref="Person"/><!--添加依赖关系-->
</object>
<object name="Person" type="SpringNetDemo.Person, SpringNetDemo"><!--上面的依赖关系-->
<property name="Age" value="12"/>
</object>
</objects>

在context节点下引用上面的xml

1
2
3
4
<context>
<resource uri="config://spring/objects"/>
<resource uri="file://server.xml"/>
</context>

然后我们修改下user infoserver类

1
2
3
4
5
6
public interface IUserInfoServer
{
string Name { get; set; }
Person Person { get; set; }
string ShowMsg();
}
1
2
3
4
5
6
7
public string Name { get ; set ; }
public Person Person { get; set ; }

public string ShowMsg()
{
return "我是Ioc,"+Name+"年龄是:"+Person.Age;
}

输出

1
2
3
我是Ioc,龙龟纹年龄是:12
---------------------------
确定

Spring.Net在MVC的应用

在Asp.net MVC中应该怎样使用Spring.Net?

  • 先导入dll文件。
  • 2kmAzK.png
  • 将案例中的Config文件夹拷贝到项目中。
  • 修改Config文件夹中的相关的配置信息。参考上面的IOC和DI操作
  • 修改Web.config文件中的配置。
  • 可以将Config/controllers.xml文件中的配置信息分离(可以将控制器的配置与业务类的配置分离。)注意web.config文件中也要修改一下。
  • 修改Global文件.继承SpringMvcApplication

记住,要是报错,安装negut包,webapi。

异常处理

mvc中有一个异常处理过滤器。我们可以自己写一个过滤器然后继承它,重写OnException方法,只要我们程序某一个控制器下某一个方法出错,都可以触发。当然现在的这个队列是在我们这台服务器上,后面我们还要做成分布式的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyExceptionAttribute:HandleErrorAttribute
{
public static Queue<Exception> ExecptionQueue = new Queue<Exception>();
/// <summary>
/// 可以捕获异常数据
/// </summary>
/// <param name="filterContext"></param>
public override void OnException(ExceptionContext filterContext)
{

base.OnException(filterContext);
Exception ex = filterContext.Exception;
//写到队列
ExecptionQueue.Enqueue(ex);
//跳转到错误页面.
filterContext.HttpContext.Response.Redirect("/Error.html");
}
}

必须还要注册一下(App_Start文件夹下)

1
2
3
4
5
6
7
8
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
filters.Add(new MyExceptionAttribute());
}
}

按照流程,我们是要开启一个线程去处理队列中的错误日志,那么线程在哪开启呢?我们可以在Application_Start方法中开启,当然这里的线程需要一个死循环去一直查看队列中的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//开启线程去处理
string filepath = Server.MapPath("/Log/");
ThreadPool.QueueUserWorkItem((a) =>
{
while (true)
{
if (MyExceptionAttribute.ExecptionQueue.Count > 0)
{
Exception ex = MyExceptionAttribute.ExecptionQueue.Dequeue();
if (ex != null)
{
string fileName = DateTime.Now.ToString("yyyy-MM-dd");
File.AppendAllText(filepath + fileName + ".txt", ex.ToString(), Encoding.UTF8);
}
else
{
Thread.Sleep(3000);
}
}
else
{
Thread.Sleep(3000);
}
}
}, filepath);//参数2是传递一个object。 包含方法所用数据的对象。
}

当然这种做法其实不是很好,如果要是需要我们把日志写到其他地方,数据库之类的就不太方便了。至少要修改代码。因此我们需要下面的解决办法

Log4Net日志处理

2k7zpW.png

具体配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!--Log4Net配置,位于configSections下-->
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>

<log4net>
<!-- OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL -->
<!-- Set root logger level to ERROR and its appenders -->
<root>
<level value="ERROR"/>
<appender-ref ref="SysAppender"/>
</root>

<!-- Print only messages of level DEBUG or above in the packages -->
<logger name="WebLogger">
<level value="ERROR"/>
</logger>
<appender name="SysAppender" type="log4net.Appender.RollingFileAppender,log4net" >
<param name="File" value="App_Data/" /><!--写到那个文件夹-->
<param name="AppendToFile" value="true" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="&quot;Logs_&quot;yyyyMMdd&quot;.txt&quot;" />
<param name="StaticLogFileName" value="false" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
<param name="Header" value="&#13;&#10;----------------------header--------------------------&#13;&#10;" />
<param name="Footer" value="&#13;&#10;----------------------footer--------------------------&#13;&#10;" />
</layout>
</appender>
<appender name="consoleApp" type="log4net.Appender.ConsoleAppender,log4net">
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
</log4net>
<!--Log4Net配置结束-->

怎么用(web应用)?在程序入口处也就是global下的Application_Start方法添加

1
2
3
4
log4net.Config.XmlConfigurator.Configure();//读取配置文件中关于log4net的配置

ILog logger = LogManager.GetLogger("errorMsg");
logger.Error(ex.ToString());//ex为错误信息

完成登陆

这里的素材用的是网上的,先把图片放在Content文件夹,前端直接复制到Index视图,做好jq,ajax,以及表单的js引用。

表单

1
2
3
4
5
6
7
8
@using (Ajax.BeginForm("UserLogin", "Login", new { }, new AjaxOptions() { HttpMethod = "post", OnSuccess = "afterLogin", LoadingElementId = "div1" }, new { id = "loginForm" }))
{
<table cellpadding="0" cellspacing="0">
......//前面的省略
<input type="button" id="btnLogin" value="登录" class="login-btn" /><span id="errorMsg" style="font-size: 14px; color: red; display: none"></span>
<div id="div1" style="display: none">正在登录,请稍后....</div>
</table>
}
1
2
3
4
5
6
$(function () {//给表单按钮添加点击事件
$("#btnLogin").click(function () {
validateInfo($("#loginForm"));//表单校验,可以看第二天的代码
$("#loginForm").submit();
});
});

ajax回调函数

1
2
3
4
5
6
7
8
9
10
function afterLogin(data) {
var serverDate = data.split(":");
if (serverDate[0] == "OK") {
location.href = "/Home/Index";
} else {
$("#errorMsg").css("display", "block");
$("#errorMsg").text(serverDate[1]);
changeCheckCode();//重新刷新验证码,这个很重要
}
}

验证码函数(就是给看不清楚重新点击的a标签更改src属性)

1
2
3
function changeCheckCode() {
$("#img").attr("src", $("#img").attr("src") + 1);
}

统一Sesson校验(我们后面用的是memcached进行校验)

APS.NET MVC中的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前前后后如果想再加一些额外的逻辑处理。这时候就用到了过滤器。具体的可以看这里:https://blog.csdn.net/heyangyi_19940703/article/details/51259684,其实我们上面的处理日志的OnException重写方法,也可以是继承Controller

1
2
3
4
5
6
7
8
9
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (Session["userinfo"]==null)
{
//filterContext.HttpContext.Response.Redirect("/Login/Index");
filterContext.Result = Redirect("/Login/Index");
}
}

然后让我们的UserInfoController : BaseController

**但是这样一来,我们的流程是这样的,浏览器请求UserInfoController,然后走到Sesson校验,然后它还是会继续往下走的,因为我们没有返回一个ActionResult,不像WebForm.因此还是会继续执行控制器下的方法。因此我们在过滤器那里返回一个。filterContext.Result = Redirect(“/Login/Index”); **

现在我们把登陆信息放在session,其实是有问题的。要是我们部署之后用户访问量大,那么效果久不太好。当我们的服务器有多台构建了一个集群的时候,我们先向A服务器请求,然后session赋值,然后跳转到B,但是B中没有session值,这时候久出现问题了。我们可以把缓存数据放到一台装有Memcache服务的机器上,存到内存里,这就是分布式缓存。这是共享的,而我们的session是每个人都专门在服务器内存上开辟储存的,因此我们在Memcache上必须解决这存的信息到底是谁是谁的。键值对,键不一样就行


OA项目第三天
http://www.kju147.com/2018/04/15/OA项目第三天/
作者
个人学习
发布于
2018年4月15日
许可协议