Java知識總結----Spring攔截器(六)

        在看到攔截器的時候,大家一定會想到另外一個詞,就是過濾器。兩者到底有什麼區別呢?過濾器,從字面的意思理解就是過濾用的,當很多請求過來的時候,我們對其進行過濾,滿足一定條件的時候,才放行。在Java中,過濾器是使用Filter實現的,實現原理都是基於回調函數的。最常見的過濾器的應用就是字符編碼的過濾、用戶信息驗證的過濾等。攔截器呢,就是用來攔截的,可以在方法的執行時,添加一些其他的信息,攔截器是使用Interceptor實現的,實現原理是基於Java的反射機制的。最常見的攔截器的應用有:添加訪問日誌、性能監控等。今天我們就來看看怎麼用Spring的攔截器爲方法添加訪問日誌。

       首先,我們創建一個攔截器類,由於我們需要攔截的是方法,所以,就繼承MethodInterceptor類。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 監控日誌攔截器
 * @author lizhiyang
 *
 */
public class MonitorLogInterceptor implements MethodInterceptor {
	
	private Logger logger = LoggerFactory.getLogger(MonitorLogInterceptor.class);

	public Object invoke(MethodInvocation methodinvocation) throws Throwable {
		Method method = methodinvocation.getMethod();
		//方法執行前輸出
		logger.info("methodIn:methodName="+method.getName());
		try {
			//執行方法
			return methodinvocation.proceed();
		} finally {
			//方法執行後輸出
			logger.info("methodOut:methodName="+method.getName());
		}
	}

}

在方法的執行前和執行後都加上日誌輸出。

接着,有的時候,我們可能需要自定義多個攔截器,這個時候,我們需要有一個攔截器鏈,把這些攔截器都串起來。

import java.util.ArrayList;
import java.util.List;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 攔截器鏈
 * @author lizhiyang
 *
 */
public class InterceptorChain implements MethodInterceptor {

	private List<MethodInterceptor> chains;
	
	public Object invoke(MethodInvocation methodinvocation) throws Throwable {
		InterceptorChainSupport support = new InterceptorChainSupport(methodinvocation, new ArrayList<MethodInterceptor>(chains));
		return support.proceed();
	}

	public List<MethodInterceptor> getChains() {
		return chains;
	}
	
	public void setChains(List<MethodInterceptor> chains) {
		this.chains = chains;
	}
}

裏面我們用到了一個類:InterceptorChainSupport,他的實現如下:

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class InterceptorChainSupport implements MethodInvocation {
	
	private MethodInvocation proxy;
	private List<MethodInterceptor> chains;
	
	public InterceptorChainSupport(MethodInvocation proxy,List<MethodInterceptor> chains) {
		this.proxy = proxy;
		this.chains = chains;
	}

	public MethodInvocation getProxy() {
		return proxy;
	}

	public void setProxy(MethodInvocation proxy) {
		this.proxy = proxy;
	}

	public List<MethodInterceptor> getChains() {
		return chains;
	}

	public void setChains(List<MethodInterceptor> chains) {
		this.chains = chains;
	}

	public Object[] getArguments() {
		return proxy.getArguments();
	}

	public AccessibleObject getStaticPart() {
		return proxy.getStaticPart();
	}

	public Object getThis() {
		return proxy.getThis();
	}

	public Object proceed() throws Throwable {
		//如果攔截器鏈不空,則繼續執行攔截器
		if(chains != null && chains.size() > 0) {
			//遞歸調用,一直調用到攔截器鏈的最後一個
			return (chains.remove(0)).invoke(this);
		} else {
			return proxy.proceed();
		}
	}

	public Method getMethod() {
		return proxy.getMethod();
	}
}

InterceptorChainSupport是真正來處理攔截器鏈的,遍歷執行所有的攔截器。在InterceptorChain中構造InterceptorChainSupport的時候要特別注意,一定要new一個新的List來存放chains,否則,會造成調用鏈只能執行一次的情況。

此處的執行過程是這樣的:當調用相應的方法時,調用InterceptorChain.invoke()----->InterceptorChainSupport.proceed()---->***Interceptor.invoke()------>InterceptorChainSupport().proceed()------......---->真正的方法處理---->方法之後的攔截處理。

最後,我們在spring的配置文件中,來配置攔截器。

<!-- 配置日誌監控攔截器 -->
	<bean id="monitorLogInterceptor" class="com.demo.interceptor.MonitorLogInterceptor" />
	
	<!-- 配置攔截器鏈,保存所有的攔截器 -->
	<bean id="interceptorChain" class="com.demo.interceptor.InterceptorChain">
		<property name="chains">
			<list>
				<ref bean="monitorLogInterceptor"/>
			</list>
		</property>
	</bean>
	
	<!-- 配置攔截器和需要攔截的bean -->
	<bean id="serviceProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="interceptorNames">
			<list>
				<value>interceptorChain</value>
			</list>
		</property>
		<property name="beanNames">
			<value>*Service</value>
		</property>
	</bean>

這樣配置之後,在spring容器加載的時候,spring就知道了執行*Service類中的方法時候,需要應用interceptorChain中的攔截器。

這個時候呢,就有了一個問題,如果我不想給這個類的每個方法都進行攔截,只攔截一部分呢?這個時候我們可以藉助註解來實現。爲需要攔截的方法上加上註解。

首先我們創建一個註解類,MonitorLog。

/**
 * 	1.RetentionPolicy.SOURCE ——只在源代碼級別保留,編譯時就會被忽略
	2.RetentionPolicy.CLASS ——編譯時被保留,在class文件中存在,但JVM將會忽略
	3.RetentionPolicy.RUNTIME —— 被JVM保留,所以他們能在運行時被JVM或其他使用反射機制的代碼所讀取和使用.
 *
 */
@Retention(RetentionPolicy.RUNTIME)
/**
 * 該註解應用於方法
 */
@Target(ElementType.METHOD)
/**
 * 指明被註解的類會自動繼承,如果我們把註解放在接口的方法上,那麼實現該接口的類也會被繼承該註解
 */
@Inherited
/**
 * Documented 註解表明這個註解應該被 javadoc工具記錄.
 */
@Documented
public @interface MonitorLog {

}

然後我們在需要攔截的方法上天劍@MonitorLog註解。

public interface UserService {
	@MonitorLog
	public boolean insertUser(UserModel user);
	public UserModel getUser(int userId);
	public String test();
	public void user(String name);
}

我們現在還需要修改MonitorLogInterceptor類,只有添加@MonitorLog的方法才進行攔截,其他的不攔截。

public class MonitorLogInterceptor implements MethodInterceptor {
	
	private Logger logger = LoggerFactory.getLogger(MonitorLogInterceptor.class);

	public Object invoke(MethodInvocation methodinvocation) throws Throwable {
		Method method = methodinvocation.getMethod();
		//獲取方法的MonitorLog註解
		MonitorLog log = method.getAnnotation(MonitorLog.class);
		boolean bLog = false;
		//該方法存在MonitorLog註解,則輸出日誌
		if(log != null) {
			bLog = true;
			//方法執行前輸出
			logger.info("methodIn:methodName="+method.getName());
		}
		try {
			//執行方法
			return methodinvocation.proceed();
		} finally {
			if(bLog) {
				//方法執行後輸出
				logger.info("methodOut:methodName="+method.getName());
			}
		}
	}

}

以上,就是spring攔截器的一個簡單的應用。當然了,我們也可以使用spring的aop標籤,來進行具體的配置。

發佈了50 篇原創文章 · 獲贊 31 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章