在寫控制器過程中看到了如下注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLogger {
String module() default "";
String methods() default "";
String source() default "";
}
以前在SpringBoot的小項(xiàng)目中泊交,Controller與Service并沒有完全分離,界限不清晰眷射,因此這個項(xiàng)目幫助我又一次深入理解了AOP镇辉。
一、AOP的使用(記錄操作日志的例子)
1厅目、自定義注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OperLog {
//操作類型
String operType() default "";
//操作人
String user() default "";
//操作人下標(biāo)
int userIndex() default -1;
}
2番枚、攔截器 (切面類)
@Aspect
@Component
public class OperLogInterceptor {
觸發(fā)條件為:com.opr包下面所有類且注解為OperLog的
@Around("within(com.opr..*) @annotation(operLog)")
public Object doAroundMethod(ProceedingJoinPoint pjd,OperLog operLog) throws Throwable {
long startTime=System.currentTimeMillis();//開始時間
Object[] params = pjd.getArgs();//獲取請求參數(shù)
System.out.println("監(jiān)聽到傳入?yún)?shù)為:");
for(Object param:params) {
System.out.println(param);
}
//###################上面代碼為方法執(zhí)行前#####################
Object result = pjd.proceed();//執(zhí)行方法,獲取返回參數(shù)
//###################下面代碼為方法執(zhí)行后#####################
System.out.println("返回參數(shù)為:" + result);
String user = operLog.userIndex()==-1?operLog.user():(String)params[operLog.userIndex()];//操作人
String operType = operLog.operType();//操作類型
System.out.println("操作人: " + user +" 操作類型: " + operType);
long endTime=System.currentTimeMillis();//結(jié)束時間
float excTime=(float)(endTime-startTime)/1000;
System.out.println("執(zhí)行時間:"+excTime+"s");
System.out.println("#######################分隔符##########################");
return result;
}
}
3损敷、Service
@Service("userService")
public class UserServiceImpl implements UserService {
//設(shè)置默認(rèn)值葫笼,模擬登錄
private final String userName = "admin";
private final String password = "123456";
@Override
@OperLog(operType="用戶登錄",userIndex = 0 )//0為下標(biāo),代表傳入的第一個參數(shù)拗馒,這里取userName為示例路星。實(shí)際場景可以在session中取操作人!
public boolean userLogin(String userName,String password) {
boolean flag = false;
if(this.userName.equals(userName) && this.password.equals(password)) {
flag = true;
}
return flag;
}
}
4诱桂、控制器
@Controller
@RequestMapping("user")
public class UserController {
@Autowired UserService userService;
/***
* 首頁
* @param request
* @param response
* @return ModelAndView
*/
@RequestMapping("index")
public ModelAndView index(HttpServletRequest request,HttpServletResponse response) {
return new ModelAndView("index");
}
/***
* 登錄
* @param request
* @param response
* @return ModelAndView
*/
@RequestMapping("userLogin")
public ModelAndView userLogin(HttpServletRequest request,HttpServletResponse response,
String userName,String password) {
ModelAndView mv = new ModelAndView();
try {
boolean result = userService.userLogin(userName,password);
if(result) {
mv.setViewName("success");
}else {
mv.setViewName("error");
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return mv;
}
}
5洋丐、運(yùn)行效果
登陸界面:
前端頁面:
后臺顯示:
使用AOP實(shí)現(xiàn)用戶登陸驗(yàn)證
@Aspect
@Component
@Slf4j
public class SellerAuthorizeAspect {
@Autowired
private StringRedisTemplate redisTemplate;
//定義一個切入點(diǎn)
@Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" +
"&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
public void verify() {}
@Before("verify()")
public void doVerify() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//查詢cookie
Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
if (cookie == null) {
log.warn("【登錄校驗(yàn)】Cookie中查不到token");
throw new SellerAuthorizeException();
}
//去redis里查詢
String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));
if (StringUtils.isEmpty(tokenValue)) {
log.warn("【登錄校驗(yàn)】Redis中查不到token");
throw new SellerAuthorizeException();
}
}
}
CookieUtil是自己寫的獲取HttpServletRequest的工具類
ReidsTemplate 與 StringRedisTemplate 的區(qū)別:
RedisTemplate使用的序列類在在操作數(shù)據(jù)的時候呈昔,比如說存入數(shù)據(jù)會將數(shù)據(jù)先序列化成字節(jié)數(shù)組存入redis數(shù)據(jù)庫:
當(dāng)Redis當(dāng)中的數(shù)據(jù)值是以可讀的形式顯示出來的時候,只能使用StringRedisTemplate才能獲取到里面的數(shù)據(jù)友绝。
當(dāng)redis數(shù)據(jù)庫里面存的本來就是字符串?dāng)?shù)據(jù)或者要存取的數(shù)據(jù)就是字符串類型的時候堤尾,那么你就使用StringRedisTemplate即可。
但是如果你的數(shù)據(jù)是復(fù)雜的對象類型迁客,而取出的時候又不想做任何的數(shù)據(jù)轉(zhuǎn)換郭宝,直接從Redis里面取出一個對象,那么使用RedisTemplate是更好的選擇掷漱。
Q:實(shí)體類為什么要序列化:
序列化的實(shí)體是對象剩蟀,結(jié)果也是對象。
A:進(jìn)程間的對象傳送:無論是何種類型的數(shù)據(jù)切威,都會以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送育特。發(fā)送方需要把這個對象轉(zhuǎn)換為字節(jié)序列,才能在網(wǎng)絡(luò)上傳送先朦;接收方則需要把字節(jié)序列再恢復(fù)為對象缰冤。
redis的基本操作:
redisTemplate.opsForValue();//操作字符串String
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForValue().set(key,value));
redisTemplate.opsForValue().get(key));
RedisTemplate.opsForSet().isMember("red_123", "1")//根據(jù)key查看集合中是否存在指定數(shù)據(jù)