Spring MVC AOP通過自定義註解方式攔截Controller等實現日誌管理
之前一直寫.net,沒玩過spring,一直沒用過aop(面向切面程式設計)這類功能,當然不是說.net裡面沒有這類框架,企業庫就可以微軟企業庫官網
開始上程式碼:
註解定義
package com.jiankunking.common;
import java.lang.annotation.*;
/**
* @author jiankunking
* @Date: 2016/8/15
* @Time: 11:09
* @annotation OperationLogger
*/
@Retention(RetentionPolicy.RUNTIME)//註解會在class中存在,執行時可通過反射獲取
@Target(ElementType.METHOD)//目標是方法
@Documented//文件生成時,該註解將被包含在javadoc中,可去掉
public @interface OperationLogger
{
/**
* 模組名字
*/
String modelName() default "";
/**
* 操作型別
*/
String option();
}
@interface是用來自定義註釋型別的。
註釋的宣告用@符號後面跟上這個註釋型別的名字,再後面跟上括號,括號中列出這個註釋中元 素/方法的key-value對。值必須是常量。
AOP攔截部分
package com.jiankunking.common;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author jiankunking
* @Date: 2016/8/15
* @Time: 11:11
* @annotation SysLogAspect
*/
@Aspect
@Component
public class SysLogAspect
{
private static final Logger logger = Logger.getLogger(SysLogAspect.class);
/**
* 定義Pointcut,Pointcut的名稱,此方法不能有返回值,該方法只是一個標示
*/
@Pointcut("@annotation(com.jiankunking.common.OperationLogger)")
public void controllerAspect()
{
System.out.println("我是一個切入點");
}
/**
* 前置通知(Before advice) :在某連線點(JoinPoint)之前執行的通知,但這個通知不能阻止連線點前的執行。
* @param joinPoint
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint)
{
System.out.println("=====SysLogAspect前置通知開始=====");
//handleLog(joinPoint, null);
}
/**
* 後通知(After advice) :當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。
* @param joinPoint
*/
@AfterReturning(pointcut = "controllerAspect()")
public void doAfter(JoinPoint joinPoint)
{
System.out.println("=====SysLogAspect後置通知開始=====");
//handleLog(joinPoint, null);
}
/**
* 丟擲異常後通知(After throwing advice) : 在方法丟擲異常退出時執行的通知。
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "controllerAspect()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e)
{
System.out.println("=====SysLogAspect異常通知開始=====");
//handleLog(joinPoint, e);
}
/**
* 環繞通知(Around advice) :包圍一個連線點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的呼叫前後完成自定義的行為,也可以選擇不執行。
* @param joinPoint
*/
@Around("controllerAspect()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("=====SysLogAspect 環繞通知開始=====");
//handleLog(joinPoint, null);
Object obj= joinPoint.proceed();
System.out.println("=====SysLogAspect 環繞通知結束=====");
return obj;
}
/**
* 日誌處理
*
* @param joinPoint
* @param e
*/
private void handleLog(JoinPoint joinPoint, Exception e)
{
try
{
//獲得註解
OperationLogger logger = giveController(joinPoint);
if (logger == null)
{
return;
}
String signature = joinPoint.getSignature().toString(); // 獲取目標方法簽名
String methodName = signature.substring(signature.lastIndexOf(".") + 1,
signature.indexOf("("));
String longTemp = joinPoint.getStaticPart().toLongString();
String classType = joinPoint.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
Method[] methods = clazz.getDeclaredMethods();
System.out.println("methodName: " + methodName);
for (Method method : methods)
{
if (method.isAnnotationPresent(OperationLogger.class)
&& method.getName().equals(methodName))
{
//OpLogger logger = method.getAnnotation(OpLogger.class);
String clazzName = clazz.getName();
System.out.println("clazzName: " + clazzName + ", methodName: "
+ methodName);
}
}
} catch (Exception exp)
{
logger.error("異常資訊:{}", exp);
exp.printStackTrace();
}
}
/**
* 獲得註解
* @param joinPoint
* @return
* @throws Exception
*/
private static OperationLogger giveController(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(OperationLogger.class);
}
return null;
}
}
Aspect通常用於將必要的但和業務無關的邏輯和業務邏輯分離。
Spring使用的AOP註解分為三個層次:
前提條件是在xml中放開了
<!-- 開啟切面程式設計功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
- @Aspect放在類頭上,把這個類作為一個切面。
- @Pointcut放在方法頭上,定義一個可被別的方法引用的切入點表示式。
- 5種通知。
- @Before,前置通知,放在方法頭上。
- @After,後置【finally】通知,放在方法頭上。
- @AfterReturning,後置【try】通知,放在方法頭上,使用returning來引用方法返回值。
- @AfterThrowing,後置【catch】通知,放在方法頭上,使用throwing來引用丟擲的異常。
- @Around,環繞通知,放在方法頭上,這個方法要決定真實的方法是否執行,而且必須有返回值。
在Maven中加入以下以依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.common</groupId>
<artifactId>spring-mvc-log4j</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringMVC + Log4j</name>
<properties>
<jdk.version>1.7</jdk.version>
<spring.version>4.3.2.RELEASE</spring.version>
<log4j.version>2.6.2</log4j.version>
<jstl.version>1.2</jstl.version>
<servletapi.version>3.1.0</servletapi.version>
<org.aspectj-version>1.7.4</org.aspectj-version>
<cglib.version>3.1</cglib.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Log4j start-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Log4j end-->
<!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servletapi.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
在spring-*.xml中加入spring支援,開啟aop功能
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- aop -->
<bean id="logService" class="com.jiankunking.common.SysLogAspect"></bean>
<!--Spring MVC使用ViewResolver來根據controller中返回的view名關聯到具體的View物件。使用View物件來渲染返回值以生成最終的檢視,如html,json或pdf等-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<!-- 啟動對@AspectJ註解的支援 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 啟用MVC註解 -->
<mvc:annotation-driven/>
<!-- 指定Sping元件掃描的基本包路徑 -->
<context:component-scan base-package="com.jiankunking.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--靜態檔案處理-->
<mvc:resources location="/resources/" mapping="/resources/**"/>
</beans>
註解也寫好了,spring也配置好了,在controller裡面怎麼用呢?
Controller應用
package com.jiankunking.controller;
import com.jiankunking.common.OperationLogger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/Welcome", produces = "text/html;charset=UTF-8")
public class WelcomeController
{
@OperationLogger(modelName = "WelcomeController", option = "getWelcome")
@RequestMapping(value = "/getWelcome", method = RequestMethod.POST)
public void getWelcome()
{
//異常攔截測試
//int i = 9 / 0;
System.out.println("controller方法執行!");
}
}
如何測試呢?
從前端發起ajax請求controller,即可:
$.ajax({
type: "POST",
url: "/Welcome/getWelcome",
contentType: "application/json",
data: null,
success: function ()
{
// alert("22");
},
error: function ()
{
// alert("失敗!");
}
});
效果如下:
由於這裡是通過Spring的@Aspect註解實現的AOP,所以同一個類中的某個方法A(該方法沒有註解標識)呼叫另一個有註解標識的方法B時,方法B上的註解是不會起作用的。
演示demo:
http://download.csdn.net/detail/xunzaosiyecao/9609918
xml方式實現aop攔截及aop基礎知識參考:
xml實現aop攔截
Spring Boot 自定義註解方式攔截Controller等實現日誌管理:
Spring Boot 自定義註解
個人微信公眾號:
作者:jiankunking 出處:http://blog.csdn.net/jiankunking
相關文章
- Spring AOP 日誌攔截器的事務管理Spring
- Dubbo自定義日誌攔截器
- Springboot AOP 自定義註解實現系統日誌Spring Boot
- Spring AOP 對Spring MVC的Controller切面攔截不起作用SpringMVCController
- SpringBoot自定義註解、AOP列印日誌Spring Boot
- Spring AOP 的實現方式(以日誌管理為例)Spring
- 通過 Spring AOP 註解實現自動代理Spring
- Spring 註解方式實現的AOPSpring
- 分享!! 如何自定義許可權校驗的註解並用AOP攔截實現許可權校驗
- 運用Spring Aop,一個註解實現日誌記錄Spring
- spring mvc攔截器,spring攔截器以及AOP切面的區別和原始碼SpringMVC原始碼
- Spring系列之aAOP AOP是什麼?+xml方式實現aop+註解方式實現aopSpringXML
- Springboot通過攔截器攔截請求資訊收集到日誌Spring Boot
- Spring 實現策略模式--自定義註解方式解耦if...elseSpring模式解耦
- Spring Boot中自定義註解+AOP實現主備庫切換Spring Boot
- Spring AOP實現後臺管理系統日誌管理Spring
- Feign通過自定義註解實現路徑的轉義
- SpringBoot中搭配AOP實現自定義註解Spring Boot
- Spring AOP整合redis(註解方式) 實現快取統一管理SpringRedis快取
- Spring AOP 在 XML檔案中實現 AspectJ 攔截SpringXML
- Aop+自定義註解實現資料字典翻譯
- redis分散式鎖-spring boot aop+自定義註解實現分散式鎖Redis分散式Spring Boot
- spring-AOP(二)實現原理之AspectJ註解方式Spring
- 聊聊如何通過自定義註解實現springmvc和sentinel整合SpringMVC
- HandlerInterceptor - 自定義攔截器
- spring boot 新增自定義監聽器、過濾器、攔截器Spring Boot過濾器
- EF Core3.0+ 通過攔截器實現讀寫分離與SQL日誌記錄SQL
- Spring AOP 實現業務日誌記錄Spring
- 手寫Spring MVC框架(二) 實現訪問攔截功能SpringMVC框架
- spring mvc 攔截器的使用SpringMVC
- Flume內建攔截器與自定義攔截器(程式碼實戰)
- 省掉bean自定義spring mvc註解注入json值BeanSpringMVCJSON
- Net 實現自定義Aop
- spring mvc即mvc攔截器例項(1)SpringMVC
- SpringBoot自定義攔截器實現IP白名單功能Spring Boot
- 使用自定義註解透過BeanPostProcessor實現策略模式Bean模式
- Spring中通過Annotation來實現AOPSpring
- Spring Aop基於註解的實現Spring