【面試題集】Java如何實現多重繼承及Spring內部類繼承實例

概念

品茗IT-面試題集-首發

如果大家正在尋找一個java的學習環境,或者在開發中遇到困難,可以加入我們的java學習圈,點擊即可加入,共同學習,節約學習時間,減少很多在學習中遇到的難題。

多重繼承指的是一個類可以同時從多於一個的父類那裏繼承行爲和特徵,C++是允許多繼承的,可以加上作用域來訪問相應的父類變量和函數;然而我們知道Java爲了保證數據安全,它只允許單繼承。

一般情況下,我們是不需要使用多重繼承的,如果必須使用,就要先考慮下你的代碼設計適合合理;但是也不排除它的使用場景,Spring的代碼中就有很多多重繼承的使用場景。

這裏講述下Java提供了兩種實現多重繼承的方式:接口和內部類。同時,最後講述下Spring中內部類繼承的實現及如何從Spring當前的HttpServletRequest獲取tomcat真實的Request。

接口實現

子類只能繼承一個父類,但是卻可以實現多個接口,事實上並不算多繼承:

下面是使用接口實現多繼承實例,假設有兩個client:httpclient和tcpclient,想實現一個通用得客戶端,根據類型選擇http還是tcp:

public interface HttpClient{
	void doHttp();
}
public interface TcpClient{
	void doTcp();
}
public class BaseHttpClient implements HttpClient {
	@Override
	public void doHttp() {
		System.out.println("我是 http");
	}
}
public interface NetType {
	public static final String HTTP = "HTTP";
	public static final String TCP = "TCP";
	
	void choseType(String type);
}
public class NetworkClientTask extends BaseHttpClient implements TcpClient, NetType {
	private String curNetType;

	public void doTask() {
		if (HTTP.equalsIgnoreCase(curNetType)) {
			super.doHttp();
		} else {
			this.doTcp();
		}
	}

	@Override
	public void doTcp() {
		System.out.println("我得自己實現tcp");
	}

	@Override
	public void choseType(String type) {
		this.curNetType = type;
	}
}

上面的代碼裏,NetworkClientTask 繼承了BaseHttpClient ,可以直接使用BaseHttpClient 的方法,但是要自己實現TcpClient的方法。

內部類實現

因爲上面的接口方式需要自己實現TcpClient的方法,很不方便,我們可以在上面的基礎上加點東西:

public class BaseTcpClient implements TcpClient {
	@Override
	public void doTcp() {
		System.out.println("我是 tcp");
	}
}
public class NetworkClientTask extends BaseHttpClient implements NetType {
	private String curNetType;

	public void doTask() {
		if (HTTP.equalsIgnoreCase(curNetType)) {
			super.doHttp();
		} else {
			new MyTcpClient().doTcp();
		}
	}

	class MyTcpClient extends BaseTcpClient {

	}

	@Override
	public void choseType(String type) {
		this.curNetType = type;
	}
}

上面的代碼裏,增加了一個實現TcpClient的類BaseTcpClient,同時,在NetworkClientTask類中,我們可以不再實現TcpClient接口,而是讓內部類MyTcpClient 繼承BaseTcpClient,這樣就可以通過調用MyTcpClient來實現調用BaseTcpClient的方法(有人會問,爲什麼不直接調BaseTcpClient?這個地方只是舉例而已,事實上,MyTcpClient可以對BaseTcpClient做裝飾,並供外部使用,這樣就隱藏了細節,Spring就是這樣做的)。

下面簡單講解下Spring中對內部類繼承的使用。

Spring內部類繼承實例

在Spring的Controller中,我們可以通過將HttpServletRequest作爲參數,來獲取當然的Request對象,但是這個Request對象距離它的實現(比如tomcat對Request的封裝)到底有多遠?

下面圖是Spring的HttpServletRequest

在這裏插入圖片描述

Spring當前的HttpServletRequestSecurityContextHolderAwareRequestWrapper類,但是SecurityContextHolderAwareRequestWrapper類拿的是HttpSessionSecurityContextRepository的內部類Servlet3SaveToSessionRequestWrapper的對象,HttpSessionSecurityContextRepository和Request是沒有關係的,但是它有個Request相關的內部類,內部類是這樣的:

private static class Servlet3SaveToSessionRequestWrapper extends
			HttpServletRequestWrapper {
	private final SaveContextOnUpdateOrErrorResponseWrapper response;

	public Servlet3SaveToSessionRequestWrapper(HttpServletRequest request,
			SaveContextOnUpdateOrErrorResponseWrapper response) {
		super(request);
		this.response = response;
	}

	@Override
	public AsyncContext startAsync() {
		response.disableSaveOnResponseCommitted();
		return super.startAsync();
	}

	@Override
	public AsyncContext startAsync(ServletRequest servletRequest,
			ServletResponse servletResponse) throws IllegalStateException {
		response.disableSaveOnResponseCommitted();
		return super.startAsync(servletRequest, servletResponse);
	}
}

內部類實現了HttpServletRequestWrapper,繼而實現了HttpServletRequest接口。但是這個內部類是private static類型的,是這樣被開放出去的:

public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
	···code····

	if (isServlet3) {
		requestResponseHolder.setRequest(new Servlet3SaveToSessionRequestWrapper(
				request, wrappedResponse));
	}

	return context;
}

Spring的HttpServletRequest中獲取tomcat真實的Request

Spring通過這種方式,將tomcat的Request包裹的死死的,但是並不是不能拿到,通過多次反射還是可以拿到的。

下面介紹下如何通過Spring當前的HttpServletRequest獲取tomcat真實的Request,繼而獲取session個數:

/**
 * 當前在線人數
 * @param request
 * @return
 * @throws Exception
 */
@RequestMapping(value = "/test", method = { RequestMethod.GET })
public ResultModel num(HttpServletRequest request) throws Exception {
	try {
		int activeSessions = getActiveSessions(request);
		if (activeSessions == -1) {
			while(true){
				Field requestFieldTop = getRequsest(request);
				if(requestFieldTop == null)break;
				requestFieldTop.setAccessible(true);
				request = (HttpServletRequest) requestFieldTop.get(request);
				activeSessions = getActiveSessions(request);
				if(activeSessions != -1){
					break;
				}
			}

		}
		return ResultModel.ok(activeSessions);
	} catch (Exception e) {
		return ResultModel.ok(10);
	}
}

private int getActiveSessions(HttpServletRequest request) throws Exception {
	if (request instanceof RequestFacade) {
		Field requestField = request.getClass().getDeclaredField("request");
		requestField.setAccessible(true);
		Request req = (Request) requestField.get(request);
		Context context = req.getContext();
		Manager manager = context.getManager();
		int activeSessions = manager.getActiveSessions();
		return activeSessions;
	}
	return -1;
}

private Field getRequsest(HttpServletRequest request) {
	Class<?> className = request.getClass();
	for (; className != Object.class; className = className.getSuperclass()) {// 獲取本身和父級對象
		try {
			Field requestFieldtmp = className.getDeclaredField("request");
			if (requestFieldtmp != null) {
				return requestFieldtmp;
			}
		} catch (Exception e) {
			continue;
		}
	}
	return null;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章