SpringAop自定義切面注解、自定義過濾器及ThreadLocal詳解
一、切面表達(dá)式
execution()是最常用的切點(diǎn)函數(shù),其語法如下所示:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
execution(<修飾符模式>? <返回類型模式><聲明類型模式><方法名模式>(<參數(shù)模式>) <異常模式>?) 除了返回類型模式、方法名模式和參數(shù)模式外,其它項(xiàng)都是可選的
返回類型模式確定方法的返回類型必須是什么,以便匹配連接點(diǎn)。*最常用作返回類型模式。它匹配任何返回類型。只有當(dāng)方法返回給定類型時,完全限定的類型名稱才匹配。
參數(shù)模式稍微復(fù)雜一些:
()匹配一個不帶參數(shù)的方法,而(..)匹配任意數(shù)量(零個或更多)的參數(shù)。
(*)模式匹配采用任意類型的一個參數(shù)的方法。(*,String)匹配采用兩個參數(shù)的方法。第一個可以是任何類型,而第二個必須是字符串。
二、實(shí)戰(zhàn)代碼
Controller
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.CustomizableThreadCreator;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/controller")
public class TestController implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Resource
private ApplicationEventPublisher applicationEventPublisher;
@GetMapping("/test")
public String test(@RequestParam String name){
System.out.println("TestController請求進(jìn)來了:"+Thread.currentThread().getName());
TestEvent event = new TestEvent(this,name);
// String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
// for (int i = 0; i <beanDefinitionNames.length ; i++) {
// System.out.println(beanDefinitionNames[i]);
// }
applicationEventPublisher.publishEvent(event);
System.out.println("TestController請求出去了:"+Thread.currentThread().getName());
CustomizableThreadCreator bean = applicationContext.getBean(CustomizableThreadCreator.class);
System.out.println("CustomizableThreadCreator:"+bean.getThreadNamePrefix());
return "success";
}
@PostMapping("/test2")
@LogAespect
public String test2(@RequestBody TestRequest request){
System.out.println("TestController請求進(jìn)來了:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
System.out.println("hobby:"+request.getHobby());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("TestController請求出去了:"+Thread.currentThread().getName());
return "success";
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
TestController.applicationContext=applicationContext;
}
}定義切面
常用的兩種:一種是基于表達(dá)式,另一種是基于注解的。
注解十分靈活,所以編程中使用的較多。在你需要攔截的方法上面加上自定義注解@LogAespect即可。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class TestAspect {
@Pointcut("execution(public * com.test.realname.controller.TestController.test(..))")
public void pointCut(){
//這里一般無實(shí)際意義
}
@Before("pointCut()")
public void before()
{
System.out.println("===before====");
}
@AfterReturning("pointCut()")
public void afterReturning()
{
System.out.println("===afterReturning===");
}
@Around("@annotation(com.test.realname.controller.LogAespect)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long l = System.currentTimeMillis();
HttpServletRequest request = MyFilter.CURRENT_REQUEST.get();
String requestURI = request.getRequestURI();
Object proceed = joinPoint.proceed();
String s=null;
if(proceed instanceof String){
s = (String) proceed;
}
System.out.println("請求路徑"+requestURI);
System.out.println("響應(yīng)"+s);
System.out.println("cost time"+(System.currentTimeMillis()-l));
return proceed;
}
}定義切面注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAespect {
}
request
import java.io.Serializable;
public class TestRequest implements Serializable {
private String hobby;
private String streamNo;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getStreamNo() {
return streamNo;
}
public void setStreamNo(String streamNo) {
this.streamNo = streamNo;
}
@Override
public String toString() {
return "TestRequest{" +
"hobby='" + hobby + '\'' +
", streamNo='" + streamNo + '\'' +
'}';
}
}
request請求中的RequestBody只能讀取一次,因?yàn)镽equestBody是流ServletInputStream,只能讀取一次,所以需要對request請求進(jìn)行包裝,使其能多次重復(fù)讀取。
import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class HttpRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public HttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body= StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException{
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
}自定義過濾器,將request緩存到ThreadLocal中,方便在切面中使用。
ThreadLocal記得在finally中清空,防止內(nèi)存泄漏。
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Component
public class MyFilter extends OncePerRequestFilter {
public static final ThreadLocal<HttpServletRequest> CURRENT_REQUEST = new ThreadLocal<>();
public static boolean isJsonRequest(HttpServletRequest request){
if(request==null){
return false;
}
return StringUtils.contains(request.getContentType(),"json");
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
boolean jsonRequest = isJsonRequest(httpServletRequest);
try {
if(jsonRequest){
HttpRequestWrapper wrapper = new HttpRequestWrapper(httpServletRequest);
CURRENT_REQUEST.set(wrapper);
String s = StreamUtils.copyToString(wrapper.getInputStream(), StandardCharsets.UTF_8);
Map map = JSON.parseObject(s, Map.class);
System.out.println(map);
filterChain.doFilter(wrapper,httpServletResponse);
}else {
CURRENT_REQUEST.set(httpServletRequest);
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
}finally {
CURRENT_REQUEST.remove();
}
}
}正常返回
{hobby=ball}
TestController請求進(jìn)來了:http-nio-8080-exec-1
hobby:ball
TestController請求出去了:http-nio-8080-exec-1
請求路徑/controller/test2
響應(yīng)success
cost time1009
如果取消包裝的話,就會直接報錯
{hobby=ball} 2023-01-03 15:45:31.664 WARN 13596 — [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String com.test.realname.controller.TestController.test2(com.test.realname.controller.TestRequest)]
到此這篇關(guān)于SpringAop自定義切面注解、自定義過濾器及ThreadLocal詳解的文章就介紹到這了,更多相關(guān)SpringAop自定義切面注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatisPlus中@TableField注解的基本使用
這篇文章主要介紹了MyBatisPlus中@TableField注解的基本使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
JavaMail實(shí)現(xiàn)郵件發(fā)送的方法
這篇文章主要介紹了JavaMail實(shí)現(xiàn)郵件發(fā)送的方法,實(shí)例分析了java實(shí)現(xiàn)郵件發(fā)送的相關(guān)技巧,非常具有實(shí)用價值,需要的朋友可以參考下2015-04-04
使用Java visualVM監(jiān)控遠(yuǎn)程JVM的流程分析
我們經(jīng)常需要對我們的開發(fā)的軟件做各種測試, 軟件對系統(tǒng)資源的使用情況更是不可少,JDK1.6開始自帶的VisualVM就是不錯的監(jiān)控工具,本文給大家分享使用Java visualVM監(jiān)控遠(yuǎn)程JVM的問題,感興趣的朋友跟隨小編一起看看吧2021-05-05
Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:快速計算二進(jìn)制數(shù)中1的個數(shù)(Fast Bit Counting)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:快速計算二進(jìn)制數(shù)中1的個數(shù)(Fast Bit Counting),本文直接給出實(shí)現(xiàn)代碼,代碼中包含詳細(xì)注釋,需要的朋友可以參考下2015-06-06
Springboot整合多數(shù)據(jù)源代碼示例詳解
這篇文章主要介紹了Springboot整合多數(shù)據(jù)源代碼示例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08

