OA專案(使用Spring AOP 給執行方法新增日誌功能)

踏 浪發表於2019-07-21

Spring AOP 實現有兩種:

①、基於代理類 ProxyFactoryBean 的 AOP 實現

②、AOP 配置方式實現 (本教程採用第二種)

本教程的目的:

原來搭建的 SSH2 + log4j 框架中,列印輸出的日誌資訊很單一,基本上只有執行的 SQL 語句,無法滿足我們的需求

所以就想到 使用 Spring AOP 技術完成日誌功能,在使用這個技術後我們可以

①、記錄每個方法執行的時間,從而為後面優化我們的程式

②、記錄什麼時間,誰做了什麼,從而完成審計功能

③、記錄自定義的異常資訊

一、編寫 java 類

package cn.oa.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class AllLogAdvice {

	private Logger logger = Logger.getLogger(AllLogAdvice.class);

	//前置通知,方法執行之前執行
	public void myBeforeAdvice(JoinPoint jionpoint) {
		// 獲取被呼叫的類名
		String targetClassName = jionpoint.getTarget().getClass().getName();
		// 獲取呼叫的方法名
		String targetMethodName = jionpoint.getSignature().getName();
		// 日誌格式 字串
		String logInfoText = "前置通知" + targetClassName + " 類的  " + targetMethodName + " 方法開始執行........";
		logger.info(logInfoText);
	}

	//後置通知,方法執行之後執行(不管是否發生異常)
	public void myAfterReturnAdvice(JoinPoint jionpoint) {
		// 獲取被呼叫的類名
		String targetClassName = jionpoint.getTarget().getClass().getName();
		// 獲取呼叫的方法名
		String targetMethodName = jionpoint.getSignature().getName();
		// 日誌格式 字串
		String logInfoText = "後置通知" + targetClassName + " 類的  " + targetMethodName + " 方法執行完畢........";
		logger.info(logInfoText);
	}

	// 異常通知
	public void myThrowingAdvice(JoinPoint jionpoint, Exception e) {
		// 獲取被呼叫的類名
		String targetClassName = jionpoint.getTarget().getClass().getName();
		// 獲取呼叫的方法名
		String targetMethodName = jionpoint.getSignature().getName();
		// 日誌格式 字串
		String logInfoText = "異常通知:執行 " + targetClassName + " 類的  " + targetMethodName + " 方法 傳送異常......." + "異常資訊:" + e.getMessage();
		logger.info(logInfoText);
	}
	// 環形通知
	public void myAroundAdvice(ProceedingJoinPoint jionpoint) throws Throwable {
		long beginTime = System.currentTimeMillis();
		jionpoint.proceed();
		long endTime = System.currentTimeMillis();
		// 獲取被呼叫的類名
		String targetClassName = jionpoint.getTarget().getClass().getName();
		// 獲取呼叫的方法名
		String targetMethodName = jionpoint.getSignature().getName();
		// 日誌格式 字串
		String logInfoText = "環形通知:執行 " + targetClassName + " 類的  " + targetMethodName + " 方法 用時......." + (endTime - beginTime);
		logger.info(logInfoText);
	}

}

二、編輯 spring 配置檔案

<?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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop.xsd">


	<!-- 自動掃描與裝配bean -->
	<context:component-scan base-package="cn.oa"></context:component-scan>


	<!-- 載入外部的properties配置檔案 -->
	<context:property-placeholder location="classpath:jdbc.properties" />


	<!-- 配置資料庫連線池(c3p0) -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<!-- 基本資訊 -->
		<property name="jdbcUrl" value="${jdbcUrl}"></property>
		<property name="driverClass" value="${driverClass}"></property>
		<property name="user" value="${username}"></property>
		<property name="password" value="${password}"></property>
		<!-- 其他配置 -->
		<!--初始化時獲取三個連線,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
		<property name="initialPoolSize" value="3"></property>
		<!--連線池中保留的最小連線數。Default: 3 -->
		<property name="minPoolSize" value="3"></property>
		<!--連線池中保留的最大連線數。Default: 15 -->
		<property name="maxPoolSize" value="5"></property>
		<!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default: 3 -->
		<property name="acquireIncrement" value="3"></property>
		<!-- 控制資料來源內載入的PreparedStatements數量。如果maxStatements與maxStatementsPerConnection均為0,則快取被關閉。Default: 
			0 -->
		<property name="maxStatements" value="8"></property>
		<!-- maxStatementsPerConnection定義了連線池內單個連線所擁有的最大快取statements數。Default: 
			0 -->
		<property name="maxStatementsPerConnection" value="5"></property>
		<!--最大空閒時間,1800秒內未使用則連線被丟棄。若為0則永不丟棄。Default: 0 -->
		<property name="maxIdleTime" value="1800"></property>
	</bean>


	<!-- 配置SessionFactory -->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
	</bean>


	<!-- 配置宣告式的事務管理(採用基於註解的方式) -->
	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager" />
	

    <!-- 新增日誌處理 -->
	<bean id="allLogAdvice" class="cn.oa.aop.AllLogAdvice"></bean>
	<aop:config>	
		<aop:aspect id="logaop" ref="allLogAdvice">
			<aop:pointcut expression="execution(* cn.oa.action.*.* (..)) and !execution(* cn.oa.action.*.set* (..))" id="logpointcut"/>
			<aop:before method="myBeforeAdvice" pointcut-ref="logpointcut" />
			<aop:after-returning method="myAfterReturnAdvice" pointcut-ref="logpointcut" />
			<aop:after-throwing method="myThrowingAdvice" pointcut-ref="logpointcut" throwing="e"/>
			<aop:around method="myAroundAdvice" pointcut-ref="logpointcut" />
		</aop:aspect>
	</aop:config>


</beans>

PS: ①、新增了日誌處理的 aop

       ②、修改bean 的頭部名稱空間

       ③、切入點表示式:選擇 cn.oa.action 包中所有類的所有方法(但排除 set 方法外),做日誌記錄

三、將所有的 action 類取消 繼承 ActionSupport 類

至於為什麼選擇取消 繼承 ActionSupport 類?

我發現, 繼承 ActionSupport 類,當客戶端發出請求時,沒有繼承 ActionSupport 類的方法正常執行,而繼承 ActionSupport 類的Action 會報異常:java.lang.NoSuchMethodException: com.sun.proxy 

 

相關文章