系列文章中的大章節(jié)編號為代碼模塊號

一. aspect
1.1 LogAspect
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Before("execution(* com.nowcoder.controller.*Controller.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
StringBuilder sb = new StringBuilder();
for (Object arg : joinPoint.getArgs()) {
sb.append("arg:" + arg.toString() + "|");
}
logger.info("before method: " + sb.toString());
}
@After("execution(* com.nowcoder.controller.IndexController.*(..))")
public void afterMethod(JoinPoint joinPoint) {
logger.info("after method: ");
}
}
1) @Component
使用@Component標記為IOC容器中的組件
2) Logger LOG = LoggerFactory.getLogger()
用Logger工廠獲取Logger實例
3) JoinPoint 對象
JoinPoint對象封裝了SpringAop中切面方法的信息,在切面方法中添加JoinPoint參數(shù),就可以獲取到封裝了該方法信息的JoinPoint對象.
常用api:
| 方法名 | 功能 |
|---|---|
| Signature getSignature() | 獲取封裝了署名信息的對象,在該對象中可以獲取到目標方法名,所屬類的Class等信息 |
| Object[] getArgs() | 獲取傳入目標方法的參數(shù)對象 |
| Object getTarget() | 獲取被代理的對象 |
| Object getThis() | 獲取代理對象 |
2. async

異步點贊
為什么異步
點贊,回復評論的時候,表面上是贊數(shù)增加了,其實還有很多其他的工作要做。比如,對方要收到消息提醒,成就值增加。一些行為會引起一系列連鎖反應。如果在點贊時立馬處理,會影響程序運行效率。而且會造成代碼冗余,比如發(fā)布新聞,和回復評論都可以使得成就值增加,如果都跟著寫在后面的代碼里會把成就值增加這段代碼寫兩遍,所以大型服務需要服務化和異步化。
- 服務化
服務化:某一個單獨的業(yè)務獨立成一個工程,提供接口。不只是service層的一個類。
暴露一些接口,比如數(shù)據(jù)庫服務,如果一個部門要去數(shù)據(jù)庫查詢,小公司可能寫個SQL語句。對于大公司,需要一個組單獨做數(shù)據(jù)庫服務,暴露接口給別的部門用。好處是防止別的部門有數(shù)據(jù)庫權(quán)限,數(shù)據(jù)庫單獨有一個組維護,出問題找他們運維就好。 - 異步化
異步化:用戶點贊,用戶首先要知道的是這個贊已經(jīng)點上了。用戶提交Oj,用戶要立馬知道的是代碼有沒有通過。而后面應該的東西,比如積分增加了,用戶不會有立馬想知道的需求,如果間隔幾秒鐘在更新,用戶也不會有很大的意見。
概述
在一個網(wǎng)站中,一個業(yè)務發(fā)生的同時,還有一些后續(xù)業(yè)務需要發(fā)生。
比如點贊,除了完成點贊功能外,還有一系列,比如提醒被點贊的用戶等等,為了能夠?qū)崿F(xiàn)這些操作并且不拖慢單純點贊功能的實現(xiàn),我們將這些使用異步隊列實現(xiàn)。
處理流程如下圖:

- Biz(生產(chǎn))
Biz為業(yè)務部門,理解為點贊的實現(xiàn),也就是在實現(xiàn)點贊的同時通過EventProducer發(fā)送一個事件 - 進入隊列
這個事件進入隊列等待,隊列另一頭,有一個EventConsumer,不斷消費事件 - EventHandler(消費)
EventConsumer下面有很多EventHandler,只要EventHandler發(fā)現(xiàn)自己需要處理的事件類型,就會進行相應的操作。
優(yōu)點:①后續(xù)業(yè)務的實現(xiàn),不會拖慢主業(yè)務。②如果后續(xù)業(yè)務的服務器掛掉,只要重啟,繼續(xù)從優(yōu)先隊列消費事件即可。
2.1 LikeHandler
記得開啟Rrdis---redis-server.exe redis.windows.conf
具體的執(zhí)行動作,具體的實現(xiàn)類,這個是點贊后要執(zhí)行的行為,給別人發(fā)提醒。
@Component
public class LikeHandler implements EventHandler {
@Autowired
MessageService messageService;
@Autowired
UserService userService;
@Override
public void doHandle(EventModel model) {
Message message = new Message();
message.setFromId(3);
//message.setToId(model.getEntityOwnerId());
message.setToId(model.getActorId());
User user = userService.getUser(model.getActorId());
message.setContent("用戶" + user.getName()
+ "贊了你的資訊,http://127.0.0.1:8080/news/" + model.getEntityId());
message.setCreatedDate(new Date());
messageService.addMessage(message);
}
@Override
public List<EventType> getSupportEventTypes() {
return Arrays.asList(EventType.LIKE);
}
}
1)參考 Spring@Autowired注解與自動裝
Spring 2.5 引入了 @Autowired 注釋,它可以對類成員變量、方法及構(gòu)造函數(shù)進行標注,完成自動裝配的工作。 通過 @Autowired的使用來消除 set ,get方法。
- 事先在 Spring 容器中聲明
Spring 通過一個 BeanPostProcessor 對 @Autowired 進行解析,所以要讓 @Autowired 起作用必須事先在 Spring 容器中聲明AutowiredAnnotationBeanPostProcessorBean。
<!-- 該 BeanPostProcessor 將自動對標注 @Autowired 的 Bean 進行注入 -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
修改在原來注入spirng容器中的bean的方法
修改在原來注入spirng容器中的bean的方法:在域變量上加上標簽@Autowired,并且去掉 相應的get 和set方法<porpery >也去掉
在applicatonContext.xml中 把原來 引用的<porpery >標簽也去掉。
2) Date
java.util 包提供了 Date 類來封裝當前的日期和時間。 Date 類提供兩個構(gòu)造函數(shù)來實例化 Date 對象。
- 第一個構(gòu)造函數(shù)使用當前日期和時間來初始化對象。
Date( ) - 第二個構(gòu)造函數(shù)接收一個參數(shù),該參數(shù)是從1970年1月1日起的毫秒數(shù)。
Date(long millisec)
| 方法 | 作用 |
|---|---|
| boolean after(Date date) | 若當調(diào)用此方法的Date對象在指定日期之后返回true,否則返回false |
| boolean before(Date date) | 若當調(diào)用此方法的Date對象在指定日期之前返回true,否則返回false。 |
| long getTime( ) | 返回自 1970 年 1 月 1 日 00:00:00 GMT 以來此 Date 對象表示的毫秒數(shù)。 |
| String toString( ) | 把此 Date 對象轉(zhuǎn)換為以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。 |
3)Arrays.asList
List 是一種很有用的數(shù)據(jù)結(jié)構(gòu),如果需要將一個數(shù)組轉(zhuǎn)換為 List 以便進行更豐富的操作的話,可以這么實現(xiàn):
String[] myArray = { "Apple", "Banana", "Orange" };
List<String> myList = Arrays.asList(myArray);
或者
List<String> myList = Arrays.asList("Apple", "Orange");
上面這兩種形式都是十分常見的:將需要轉(zhuǎn)化的數(shù)組作為參數(shù),或者直接把數(shù)組元素作為參數(shù),都可以實現(xiàn)轉(zhuǎn)換。
2.2 LoginExceptionHandler
@Component
public class LoginExceptionHandler implements EventHandler {
@Autowired
MessageService messageService;
@Autowired
MailSender mailSender;
@Override
public void doHandle(EventModel model) {
// 判斷是否有異常登陸
Message message = new Message();
message.setToId(model.getActorId());
message.setContent("你上次的登陸ip異常");
message.setFromId(3);
message.setCreatedDate(new Date());
messageService.addMessage(message);
//郵件發(fā)送三
Map<String, Object> map = new HashMap<String, Object>();
map.put("username", model.getExt("username"));
mailSender.sendWithHTMLTemplate(model.getExt("email"), "登陸異常", "mails/welcome.html",
map);
}
@Override
public List<EventType> getSupportEventTypes() {
return Arrays.asList(EventType.LOGIN);
}
}
2.3 EventConsumer
解決的問題
如何將活動分發(fā)下去給相關(guān)的所有handle實現(xiàn)。
步驟
消費活動,在初始化前,先得到Handler接口所有的實現(xiàn)類,遍歷實現(xiàn)類。
通過getSupportEventType得到每個實現(xiàn)類對應處理的活動類型。反過來記錄在config哈希表中,config中的key是活動的類型,比如說是LIKE,COMMENT,是枚舉里的成員,value是一個ArrayList的數(shù)組,里面存放的是各種實現(xiàn)方法。見代碼中的。當從隊列中獲得一個活動時,這里用的是從右向外pop()一個活動實體。進行解析。這里的config.get(eventModel.getType())是一個數(shù)組,里面存放著所有關(guān)于這個活動要執(zhí)行的實現(xiàn)類。遍歷這個數(shù)組,開始執(zhí)行實現(xiàn)類里的方法。
一些注釋
-
implements InitializingBean,@Override afterPropertiesSet()
InitializingBean 通過實現(xiàn)此接口的afterPropertiesSet()方法記錄哪些Event需要哪些handler來處理 -
implements ApplicationContextAware
ApplicationContextAware 通過實現(xiàn)此接口的setApplicationContext方法獲取上下文 -
Map<EventType, List<EventHandler>> config = new HashMap<>();
用來存儲一個事件類型對應的所有的eventhandler,下次有該事件產(chǎn)生時,即可直接調(diào)用對應的list -
Map<String, EventHandler> beans = applicationContext.getBeansOfType(EventHandler.class);
找出上下文中所有實現(xiàn)了EventHandler接口的類,存入beans -
if (beans != null)……
遍歷所有的handler,將他們存入他們所監(jiān)聽的eventType對應的list中 -
List<String> messages = jedisAdapter.brpop(0, key);
從redis的隊列中取出事件,并存入list中 -
for (String message : messages)
遍歷取出的事件
7.1if (message.equals(key))
第一個元素是隊列名字,跳過
7.2if (!config.containsKey(eventModel.getType()))
跳過不能處理的事件
7.3for (EventHandler handler : config.get(eventModel.getType()))
處理他的所有的handler
@Service
//1------------------------
public class EventConsumer implements InitializingBean, ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
//2--------------
private Map<EventType, List<EventHandler>> config = new HashMap<>();
private ApplicationContext applicationContext;
@Autowired
private JedisAdapter jedisAdapter;
@Override
public void afterPropertiesSet() throws Exception {
//4----------------------
Map<String, EventHandler> beans = applicationContext.getBeansOfType(EventHandler.class);
//5-----------------------------
if (beans != null) {
for (Map.Entry<String, EventHandler> entry : beans.entrySet()) {
List<EventType> eventTypes = entry.getValue().getSupportEventTypes();//查看事件的監(jiān)視事件
for (EventType type : eventTypes) {
if (!config.containsKey(type)) {
config.put(type, new ArrayList<EventHandler>());
System.out.println("添加一個新的 :" + type);//20180802
}
config.get(type).add(entry.getValue());// 注冊每個事件的處理函數(shù)
System.out.println("注冊每個事件的處理函數(shù) :" + type + " " + entry.getValue()); //20180802
}
}
}
Thread thread = new Thread(new Runnable() {// 啟動線程去消費事件
@Override
public void run() {
while (true) {// 從隊列一直消費
String key = RedisKeyUtil.getEventQueueKey();
//6------------------------------
List<String> messages = jedisAdapter.brpop(0, key);
for (String message : messages) {
//7.1---------------
if (message.equals(key)) {
continue;
}
EventModel eventModel = JSON.parseObject(message, EventModel.class);
//7.2---------------
System.out.println("找到這個事件的處理handler列表 : " + eventModel.getType()) //20180802
if (!config.containsKey(eventModel.getType())) { //找到這個事件的處理handler列表
logger.error("不能識別的事件");
continue;
}
//7.3---------------
for (EventHandler handler : config.get(eventModel.getType())) {//處理他的所有的handler
System.out.println("處理事件 : " + eventModel.getType() + " " + handler.getClass() );//20180802
handler.doHandle(eventModel);
}
}
}
}
});
thread.start();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
1)Jedis
BLPOP key [key ...] timeout
BLPOP 是列表的阻塞式(blocking)彈出原語。
它是 LPOP 命令的阻塞版本,當給定列表內(nèi)沒有任何元素可供彈出的時候,連接將被 BLPOP 命令阻塞,直到等待超時或發(fā)現(xiàn)可彈出元素為止。
當給定多個 key 參數(shù)時,按參數(shù) key 的先后順序依次檢查各個列表,彈出第一個非空列表的頭元素。
非阻塞行為:
當 BLPOP 被調(diào)用時,如果給定 key 內(nèi)至少有一個非空列表,那么彈出遇到的第一個非空列表的頭元素,并和被彈出元素所屬的列表的名字一起,組成結(jié)果返回給調(diào)用者。
當存在多個給定 key 時, BLPOP 按給定 key 參數(shù)排列的先后順序,依次檢查各個列表。
假設現(xiàn)在有 job 、 command 和 request 三個列表,其中 job 不存在, command 和 request 都持有非空列表??紤]以下命令:BLPOP job command request 0
超時參數(shù) timeout 接受一個以秒為單位的數(shù)字作為值。超時參數(shù)設為 0 表示阻塞時間可以無限期延長(block indefinitely) 。
BRPOP key [key ...] timeout
它是 RPOP 命令的阻塞版本,當給定列表內(nèi)沒有任何元素可供彈出的時候,連接將被 BRPOP 命令阻塞,直到等待超時或發(fā)現(xiàn)可彈出元素為止。
BRPOP 除了彈出元素的位置和 BLPOP 不同之外,其他表現(xiàn)一致。
2)Map.Entry
Map.Entry是Map聲明的一個內(nèi)部接口,此接口為泛型,定義為Entry<K,V>。它表示Map中的一個實體(一個key-value對)。接口中有g(shù)etKey(),getValue方法。
3)Fastjson
2.4 EventHandler
設計為一個接口,handler都實現(xiàn)此接口。
public interface EventHandler {
void doHandle(EventModel model);//處理此事件
List<EventType> getSupportEventTypes();//添加監(jiān)視的事件類型
}
2.5 EventModel
即發(fā)送的隊列的事件模型,只有一些基本屬性和get、set方法。
其中一些set的return 設置為this,是因為方便連續(xù)set多個屬性。
public class EventModel {
private EventType type;
private int actorId;
private int entityType;
private int entityId;
private int entityOwnerId;
private Map<String, String> exts = new HashMap<String, String>();
public EventModel(EventType type) {
this.type = type;
}
public EventModel() {
}
public String getExt(String key) {
return exts.get(key);
}
public EventModel setExt(String key, String value) {
exts.put(key, value);
return this;//方便連續(xù)set多個屬性。
}
public EventType getType() {
return type;
}
public EventModel setType(EventType type) {
this.type = type;
return this;
}
public int getActorId() {
return actorId;
}
public EventModel setActorId(int actorId) {
this.actorId = actorId;
return this;
}
public int getEntityType() {
return entityType;
}
public EventModel setEntityType(int entityType) {
this.entityType = entityType;
return this;
}
public int getEntityId() {
return entityId;
}
public EventModel setEntityId(int entityId) {
this.entityId = entityId;
return this;
}
public int getEntityOwnerId() {
return entityOwnerId;
}
public EventModel setEntityOwnerId(int entityOwnerId) {
this.entityOwnerId = entityOwnerId;
return this;
}
public Map<String, String> getExts() {
return exts;
}
public void setExts(Map<String, String> exts) {
this.exts = exts;
}
}
2.6 EventProducer
活動生產(chǎn)者,相當于生產(chǎn)消費者中的生產(chǎn)者,在controller層執(zhí)行一個動作后,用這個類把需要異步的信息打包好,放進Redis的隊列中。放入是把EventModel序列化為JSON,存入Redis的列表中。
@Service
public class EventProducer {
@Autowired
JedisAdapter jedisAdapter;
public boolean fireEvent(EventModel model) {
try {
String json = JSONObject.toJSONString(model);
String key = RedisKeyUtil.getEventQueueKey();
jedisAdapter.lpush(key, json);
System.out.println("產(chǎn)生一個異步事件:" + eventModel.getType());//20180802
return true;
} catch (Exception e) {
return false;
}
}
}
1) JSON
由于 JSON 語法是 JavaScript 語法的子集,JavaScript 函數(shù) eval() 可用于將 JSON 文本轉(zhuǎn)換為 JavaScript 對象。
概覽
-
JSON(JavaScript Object Notation)是一種輕量級的數(shù)據(jù)交換格式。 - JSON 是存儲和交換文本信息的語法。類似 XML。
- JSON 比 XML 更小、更快,更易解析。
為什么使用 JSON?
對于 AJAX 應用程序來說,JSON 比 XML 更快更易使用:
使用 XML
- 讀取 XML 文檔
- 使用 XML DOM 來循環(huán)遍歷文檔
- 讀取值并存儲在變量中
使用 JSON
- 讀取 JSON 字符串
- 用
eval()處理 JSON 字符串
書寫格式
JSON 數(shù)據(jù)的書寫格式是:名稱/值對。
名稱/值對包括字段名稱(在雙引號中),后面寫一個冒號,然后是值:
“firstName” : “John”
這很容易理解,等價于這條 JavaScript 語句:
firstName = “John”
json嵌套
對于json嵌套,只要記住
- 符號
:”前是鍵,符號后是值 - 大括號成對找
一層層剝開,就清楚了。
2)前后臺的傳輸
- JSON.parseObject,是將Json字符串轉(zhuǎn)化為相應的對象
- JSON.toJSONString則是將對象轉(zhuǎn)化為Json字符串。
2.7 EventType
EventType 是獲得活動的類型,可以有點贊,評論,登陸等待
public enum EventType {
LIKE(0),
COMMENT(1),
LOGIN(2),
MAIL(3);
private int value;
EventType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
3. configuration
3.1. ToutiaoWebConfiguration
@Component
public class ToutiaoWebConfiguration extends WebMvcConfigurerAdapter {
@Autowired
PassportInterceptor passportInterceptor;
@Autowired
LoginRequiredInterceptor loginRequiredInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor);
registry.addInterceptor(loginRequiredInterceptor).
addPathPatterns("/msg/*").addPathPatterns("/like").addPathPatterns("/dislike");
super.addInterceptors(registry);
}
}
1)spring boot中使用攔截器
1、注冊攔截器
創(chuàng)建一個類MyWebConfig繼承WebMvcConfigurerAdapter,并重寫addInterceptors方法
多個攔截器組成一個攔截器鏈
- addPathPatterns
添加攔截規(guī)則 - excludePathPatterns
排除攔截
@Configuration
public class MyWebConfig extends WebMvcConfigurerAdapter {
@Autowired
MyiInterceptor myiInterceptor;
@Override //注冊 攔截器
public void addInterceptors(InterceptorRegistry registry) {
//攔截器myiInterceptor只攔截'/111'的請求,不攔截'/helloWorld'
registry.addInterceptor(myiInterceptor).addPathPatterns("/111")
.excludePathPatterns("/helloWorld");
super.addInterceptors(registry);
}
}
2、自定義攔截器
創(chuàng)建一個自定義攔截器MyiInterceptor實現(xiàn)HandlerInterceptor接口,重寫所有的方法實現(xiàn)自己的業(yè)務
6. interceptor

6.1 LoginRequiredInterceptor
@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
if (hostHolder.getUser() == null) {
httpServletResponse.sendRedirect("/?pop=1");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
1)攔截器(Interceptor)
作用
在Web開發(fā)中,攔截器(Interceptor)可以用來驗證是否登錄、預先設置數(shù)據(jù)以及統(tǒng)計方法的執(zhí)行效率等。
分類
Spring中的攔截器分兩種,一是HandlerInterceptor,另一個是MethodInterceptor。這里主要說以下HandlerInterceptor。
HandlerInterceptor是SpringMVC項目中的攔截器,攔截目標是請求的地址,比MethodInterceptor先執(zhí)行。實現(xiàn)一個HandlerInterceptor攔截器可以直接實現(xiàn)該接口,也可以繼承HandlerInterceptorAdapter類。
SpringMVC處理請求過程
SpringMVC處理請求的整個過程是
- 先根據(jù)請求找到對應的
HandlerExecutionChain,它包含了處理請求的handler和所有的HandlerInterceptor攔截器 - 然后在調(diào)用hander之前分別調(diào)用
每個HandlerInterceptor攔截器的preHandle方法
2.1 若有一個攔截器返回false,則會調(diào)用triggerAfterCompletion方法,并且立即返回不再往下執(zhí)行
2.2 若所有的攔截器全部返回true并且沒有出現(xiàn)異常,則調(diào)用handler返回ModelAndView對象;再然后分別調(diào)用每個攔截器的postHandle方法;最后,即使是之前的步驟拋出了異常,也會執(zhí)行triggerAfterCompletion方法。
多攔截器工作流程:
需要Override的三種方法
(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle)
controller 執(zhí)行之前調(diào)用
該方法將在請求處理之前進行調(diào)用。SpringMVC 中的Interceptor 是鏈式調(diào)用的,在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調(diào)用會依據(jù)它的聲明順序依次執(zhí)行,而且最先執(zhí)行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續(xù)進行下去。該方法的返回值是布爾值Boolean 類型的,當它返回為false 時,表示請求結(jié)束,后續(xù)的Interceptor 和Controller 都不會再執(zhí)行;當返回值為true 時就會繼續(xù)調(diào)用下一個Interceptor 的preHandle 方法,如果已經(jīng)是最后一個Interceptor 的時候就會是調(diào)用當前請求的Controller 方法。
(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)
controller 執(zhí)行之后,且頁面渲染之前調(diào)用
這個方法包括后面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調(diào)用。postHandle 方法,是在當前請求進行處理之后,也就是Controller 方法調(diào)用之后執(zhí)行,但是它會在DispatcherServlet 進行視圖渲染之前被調(diào)用,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進行操作。postHandle 方法被調(diào)用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會后執(zhí)行。
(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
頁面渲染之后調(diào)用,一般用于資源清理操作。
該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執(zhí)行。該方法將在整個請求結(jié)束之后,也就是在DispatcherServlet 渲染了對應的視圖之后執(zhí)行。這個方法的主要作用是用于進行資源清理工作的。
在Spring Boot中配置攔截器,需要寫一個配置類繼承WebMvcConfigurerAdapter類并添加該攔截器(見2)
@Component
public class XdtoutiaoWebConfiguration extends WebMvcConfigurerAdapter {
@Autowired
PassportInterceptor passportInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor);
super.addInterceptors(registry);
}
}
6.2 PassportInterceptor
@Component
public class PassportInterceptor implements HandlerInterceptor {
@Autowired
private LoginTicketDAO loginTicketDAO;
@Autowired
private UserDAO userDAO;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String ticket = null;
if (httpServletRequest.getCookies() != null) {
for (Cookie cookie : httpServletRequest.getCookies()) {
if (cookie.getName().equals("ticket")) {
ticket = cookie.getValue();
break;
}
}
}
if (ticket != null) {
LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket);
if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 0) {
return true;
}
User user = userDAO.selectById(loginTicket.getUserId());
hostHolder.setUser(user);
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
if (modelAndView != null && hostHolder.getUser() != null) {
modelAndView.addObject("user", hostHolder.getUser());
}
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
hostHolder.clear();
}
}