策略模式
定义
策略模式定义了算法家族并将它们封装起来,让他们之间可以互相替换,此模式让算法变化而不会影响到使用算法的客户
适用场景
商场经常会进行各式各样的促销活动,这就要求收银软件需要按照不同的促销方式来对顾客的最终费用进行计算,这里的促销方式其实就是各类打折算法,并且这些算法之间还可以互相替换而不影响到使用这些算法的客户端也就是前面说的收银软件。
策略模式定义了算法家族并将它们封装起来,让他们之间可以互相替换,此模式让算法变化而不会影响到使用算法的客户
商场经常会进行各式各样的促销活动,这就要求收银软件需要按照不同的促销方式来对顾客的最终费用进行计算,这里的促销方式其实就是各类打折算法,并且这些算法之间还可以互相替换而不影响到使用这些算法的客户端也就是前面说的收银软件。
将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,适配器模式分为三种,类适配器模式,对象适配器模式,接口适配器模式,他们的各自适用场景:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可

|
|
CAS协议是一个简单且十分强大的基于票据管理的CAS专用协议,协议详细文档请移步http://jasig.github.io/cas/4.1.x/protocol/CAS-Protocol-Specification.html
该协议中涉及到一个或多个CAS客户端和服务端的交互,客户端嵌入了CAS的类库,而CAS服务端则是一个独立的应用程序。CAS服务端负责用户的认证和授予访问权限给访问请求。CAS客户端负责保护应用程序和接收CAS服务端对用户的认证数据。交互过程主要涉及到概念有:
CAS的认证过程就是通过执行以下几个相互关联的组件组成
- PrincipalNameTransformer
将输入到登录表单的用户标识字符串转换成一个临时的主体名称,然后该主体名称将被指定的Authentication Handler进行验证- AuthenticationManager
AuthenticationManager作为认证子系统的一个入口,其可以接收一个或多个凭据,并调用配置文件中指定的AuthenticationHandler组件对接收到的凭证进行验证。其存储着每次进行验证的结果和确定有效的安全策略- AuthenticationHandler
验证单个凭据并返回认证后的三种可能的结果:成功,失败,未尝试- PrincipalResolver
将认证凭据中的信息转换成一个安全的主体,通常主题中包含一些额外的元数据属性(例如:用户的细节信息如:隶属关系,组关系,邮件,昵称)- AuthenticationMetaDataPopulator
针对认证成功事件设置任意任意元数据的策略组件,该组件通常用于设置特定协议的数据
针对以上组件若未作特殊声明,均在deployerConfigContext.xml中配置
cas(Central Authentication Service)即集中式认证服务,是一种针对互联网的单点登录协议。
它的目的是允许一个用户访问多个应用程序,而只需提供一次凭证(如用户名和密码)。它还允许web应用程序在没有获得用户的安全凭据(如密码)的情况下对用户进行身份验证,从总体架构上来看CAS包含服务端和客户端两个独立的组件,他们之间通过各种协议进行通信。
操作系统内核对于I/O只有两种方式:阻塞与阻塞
阻塞I/O的一个特点是调用之后一定要等到系统内核层面完成所有操作后,调用才会结束;以读取磁盘上的一段文件为例,系统内核在完成磁盘寻道,读取数据,复制数据到内存中之后这个调用才会结束。阻塞I/O造成CPU等待I/O,浪费等待时间,CPU的处理能力不能得到充分利用为了提供性能。为此内核提供了非阻塞。非阻塞I/O和阻塞I/O的区别为调用后立即返回。
操作系统对计算机进行了抽象,将所有输入输出的设备抽象为文件。内核在进行文件I/O操作时,通过文件描述符进行管理,而文件描述符类似于应用程序与系统内核之间的凭证,应用程序如果需要进行I/O调用,需要先打开文件描述符,然后再根据文件描述符去实现文件的数据读写。从这个角度来讲阻塞I/O非阻塞I/O的区别在于阻塞I/O需要完成整个获取数据的过程。
URL路径限定
普通URL路径映射
@RequestMapping(value={“/test1”, “/user/create”}):多个URL路径可以映射到同一个处理器的功能处理方法。
URI模板模式映射
@RequestMapping(value=”/users/{userId}”):{×××}占位符, 请求的URL可以是 “/users/123456”或”/users/abcd”,通过@PathVariable可以提取URI模板模式中的{×××}中的×××变量
Ant风格的URL路径映射
@RequestMapping(value=”/product?”):可匹配”/product1”或”/producta”,但不匹配”/product”或”/productaa”;
正则表达式风格的URL路径映射
从Spring3.0开始支持正则表达式风格的URL路径映射,格式为{变量名:正则表达式},这样我们就可以通过6.6.5讲的通过@PathVariable提取模式中的{×××:正则表达式匹配的值}中的×××变量了
组合使用URL路径限定,是”或”的关系
如 @RequestMapping(value={“/test1”, “/user/create”})
组合使用是或的关系,即”/test1”或”/user/create”请求URL路径都可以映射到@RequestMapping指定的功能处理方法
//处理器的通用映射前缀
组合使用请求方法映射限定是”或”的关系
@RequestMapping(value=”/methodOr”, method = {RequestMethod.POST, RequestMethod.GET}):即请求方法可以是 GET 或 POST。
提示
1、一般浏览器只支持GET、POST请求方法,
2、除了GET、POST,还有HEAD、OPTIONS、PUT、DELETE、TRACE。
3、DispatcherServlet默认开启对 GET、POST、PUT、DELETE、HEAD的支持;
4、如果需要支持OPTIONS、TRACE,请添加DispatcherServlet在web.xml的初始化参数:dispatchOptionsRequest 和 dispatchTraceRequest 为true。
请求参数数据映射限定
请求参数数据映射限定
请求参数中指定参数名 = 值
@RequestMapping(params=”create”, method=RequestMethod.GET)
表示请求中有”create”的参数名且请求方法为”GET”即可匹配,如可匹配的请求URL”http://×××/parameter1?create“
请求参数中指定参数名 != 值
@RequestMapping(params=”submitFlag!=create”, method=RequestMethod.GET)
表示请求中的参数”submitFlag!=create”且请求方法为”GET”即可匹配,如可匹配的请求URL”http://×××/parameter1?submitFlag=abc“
组合使用请求参数名,是”且”的关系
@RequestMapping(params={“test1”, “test2=create”})
表示请求中的有”test1”参数名 且 有”test2=create”参数即可匹配,如可匹配的请求URL”http://×××/parameter3?test1&test2=create"。
请求头数据映射限定
请求头数据中有指定参数名
@RequestMapping(value=”/header/test1”, headers = “Accept”)
表示请求的URL必须为”/header/test1”且请求头中必须有Accept参数才能匹配。
请求头数据中没有指定参数名
@RequestMapping(value=”/header/test2”, headers = “!abc”)
表示请求的URL必须为”/header/test2”且请求头中必须没有abc参数才能匹配。(将Modify Header的abc参数值删除即可)
请求头数据中指定参数名 = 值
@RequestMapping(value=”/header/test3”, headers = “Content-Type=application/json”)
表示请求的URL必须为”/header/test3” 且 请求头中必须有”Content-Type=application/json”参数即可匹配。(将Modify Header的Content-Type参数值改为”application/json”即可)
请求头数据中指定参数名 != 值
@RequestMapping(value=”/header/test7”, headers = “Accept!=text/vnd.wap.wml”)
表示请求的URL必须为”/header/test7” 且 请求头中必须有”Accept”参数但值不等于”text/vnd.wap.wml”即可匹配
组合使用请求头数据:是”且”的关系
@RequestMapping(value=”/header/test8”, headers = {“Accept!=text/vnd.wap.wml”,”abc=123”})
表示请求的URL必须为”/header/test8” 且 请求头中必须有”Accept”参数但值不等于”text/vnd.wap.wml”且 请求中必须有参数”abc=123”即可匹配
基本概念
Media Type
互联网媒体类型,一般就是我们所说的MIME类型,用来确定请求的内容类型或响应的内容类型
常见Media Type:
text/html : HTML格式 text/plain :纯文本格式 text/xml :XML格式
image/gif :gif图片格式 image/jpeg :jpg图片格式 image/png:png图片格式
application/x-www-form-urlencoded :
application/xhtml+xml :XHTML格式 application/xml : XML数据格式
application/atom+xml :Atom XML聚合格式 application/json : JSON数据格式
application/pdf :pdf格式 application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)。
Content-Type
内容类型,即请求/响应的内容区数据的媒体类型
请求头的内容类型,表示发送到服务器的内容数据的媒体类型
request中设置请求头”Content-Type: application/x-www-form-urlencoded”表示请求的数据为key/value数据
request功能处理方法:只对请求头为”Content-Type:application/x-www-form-urlencoded”的请求进行处理(即消费请求内容区数据)
request.getContentType():可以得到请求头的内容区数据类型(即Content-Type头的值)
request.getCharacterEncoding():如”Content-Type:application/json;charset=GBK”,则得到的编码为”GBK”,否则如果你设置过滤器(CharacterEncodingFilter)则得到它设置的编码,否则返回null。
request.getParameter():因为请求的内容区数据为application/x-www-form-urlencoded格式的数据,因此我们可以通过request.getParameter()得到相应参数数据。
响应头的内容类型,表示发送到客户端的内容数据类型,和请求头的内容类型类似,只是方向相反
@RequestMapping(value = “/consumes”, consumes = {“application/json”})
此处使用consumes来指定功能处理方法能消费的媒体类型,其通过请求头的”Content-Type”来判断
此种方式相对使用@RequestMapping的headers = “Content-Type=application/json”更能表明你的目的
@RequestMapping(value = “/produces”, produces = “application/json”)
表示将功能处理方法将生产json格式的数据,此时根据请求头中的Accept进行匹配,如请求头”Accept:application/json”时即可匹配
此种方式相对使用@RequestMapping的headers = “Accept=application/json”更能表明你的目的
如类级别的映射为 @RequestMapping(value=”/narrow”, produces=”text/html”),方法级别的为@RequestMapping(produces=”application/xml”),此时方法级别的映射将覆盖类级别的,因此请求头“Accept:application/xml”是成功的,而“text/html”将报406错误码,表示不支持的请求媒体类型
@RequestMapping(produces={“text/html”, “application/json”}) :将匹配“Accept:text/html”或“Accept:application/json”
消费的数据,如JSON数据、XML数据都是由我们读取请求的InputStream并根据需要自己转换为相应的模型数据,比较麻烦;
生产的数据,如JSON数据、XML数据都是由我们自己先把模型数据转换为json/xml等数据,然后输出响应流,也是比较麻烦的。
Spring提供了一组注解(@RequestBody、@ResponseBody)和一组转换类(HttpMessageConverter)来完成我们遇到的问题
常见应用场景
日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。
本质也是AOP(面向切面编程),也就是说符合横切关注点的所有功能都可以放入拦截器实现
|
|
preHandle预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如我们上一章的Controller实现);
返回值:true表示继续流程(如调用下一个拦截器或处理器);
false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
postHandle后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
afterCompletion整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor接口的话,三个方法必须实现,不管你需不需要,此时spring提供了一个HandlerInterceptorAdapter适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {
//省略代码 此处所以三个回调方法都是空实现,preHandle返回true。
}
spring MVC拦截器1.JPG
图1:正常流程
spring MVC拦截器2.JPG
图2:中断流程
注意:中断流程中,比如是HandlerInterceptor2中断的流程(preHandle返回false),此处仅调用它之前拦截器的preHandle返回true的afterCompletion方法。
应用
需求:
记录每个请求的处理时间,得到一些慢请求(如处理时间超过500毫秒),从而进行性能改进
实现方案:
在进入处理器之前记录开始时间,即在拦截器的preHandle记录开始时间;
在结束请求处理之后记录结束时间,即在拦截器的afterCompletion记录结束实现,并用结束时间-开始时间得到这次请求的处理时间。
问题:
拦截器是单例,因此不管用户请求多少次都只有一个拦截器实现,即线程不安全
解决方案是使用ThreadLocal,它是线程绑定的变量,提供线程局部变量(一个线程一个ThreadLocal,A线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal)
代码
public class StopWatchHandlerInterceptor extends HandlerInterceptorAdapter {
private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-StartTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
long beginTime = System.currentTimeMillis();//1、开始时间
startTimeThreadLocal.set(beginTime);//线程绑定变量(该数据只有当前请求的线程可见)
return true;//继续流程
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
long endTime = System.currentTimeMillis();//2、结束时间
long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
long consumeTime = endTime - beginTime;//3、消耗的时间
if(consumeTime > 500) {//此处认为处理时间超过500毫秒的请求为慢请求
//TODO 记录到日志文件
System.out.println(
String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
}
}
}