자바 의 springMvc 프레임 워 크 간단 실현
19895 단어 javamvc 프레임 워 크
본 논문 에서 블 로 거들 은 servlet 에서 contrller 층 까지 간단 한 구 조 를 실현 했다.이 프레임 워 크 를 통 해 우 리 는 spring 처럼 다음 과 같은 기본 주 해 를 사용 할 수 있 습 니 다.
본문 을 보기 전에 다음 과 같은 내용 을 먼저 알 아야 할 지도 모른다.
생각:
차단 기 는 어떻게 이 주 해 를 사용 하 는 방법 을 찾 았 습 니까?패키지 스 캔?어떻게 실현 합 니까?
분석:
패키지 스 캔 은 IO 흐름 과 관련 되 며, File 류 는 아래 의 모든 파일 을 재 귀적 으로 조회 할 수 있 습 니 다.
필터 링 가능:
게 으 름 피우다
MVC 디자인 모델 로 인해 저 희 는 api 인 터 페 이 스 를 같은 가방 에 두 기 때문에 저 희 는 스 캔 가방 을 직접 지정 할 수 있 습 니 다. 다른 가방 은 상관 하지 않 습 니 다.
스캐너 1.0 버 전의 실현
public class FileScanner {
private final String packetUrl = "com.dbc.review.controller";
private final ClassLoader classLoader = FileScanner.class.getClassLoader();
private List allClazz = new ArrayList<>(10); //
public List getAllClazz(){
return this.allClazz;
}
public String getPacketUrl(){
return this.packetUrl;
}
//
// , class, class class
public void loadAllClass(String packetUrl) throws Exception{
String url = packetUrl.replace(".","/");
URL resource = classLoader.getResource(url);
if (resource == null) {
return;
}
String path = resource.getPath();
File file = new File(URLDecoder.decode(path, "UTF-8"));
if (!file.exists()) {
return;
}
if (file.isDirectory()){
File[] files = file.listFiles();
if (files == null) {
return;
}
for (File f : files) {
String classname = f.getName().substring(0, f.getName().lastIndexOf("."));
if (f.isDirectory()) {
loadAllClass(packetUrl + "." + classname);
}
if (f.isFile() && f.getName().endsWith(".class")) {
Class clazz = Class.forName(packetUrl + "." + classname);
dealClass( clazz);
}
}
}
}
private void dealClass(Class clazz) {
if ((clazz.isAnnotationPresent(Controller.class))) {
allClazz.add(clazz);
}
}
// ,
public boolean invoke(String url,String requestMethod) {
for (Class clazz : allClazz){
Controller controller = (Controller) clazz.getAnnotation(Controller.class);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
if (requestMapping == null) {
continue;
}
for (String m : requestMapping.methods()) {
m = m.toUpperCase();
if (!m.toUpperCase().equals(requestMethod.toUpperCase())) {
continue;
}
StringBuilder sb = new StringBuilder();
String urlItem = sb.append(controller.url()).append(requestMapping.url()).toString();
if (urlItem.equals(url)) {
// api
try {
// method.getGenericParameterTypes() //
method.invoke(clazz.newInstance());
return true;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
return false;
}
}
}
}
}
return false;
}
@Test
public void test() throws Exception {
// 1. Filter
FileScanner fileScanner = new FileScanner();
// 2.
fileScanner.loadAllClass(fileScanner.getPacketUrl());
// 3. ,
// post /test/post , false
// true
fileScanner.invoke("/test/post","post");
// 4. , false, 405 。
// : controller ,
// : method , ,
}
}
TestController
@Controller(url = "/test")
public class TestController {
@RequestMapping(url = "/get",methods = "GET")
public void get(){
System.out.println(111);
}
@RequestMapping(url = "/post",methods = {"POST","get"})
public void post(){
System.out.println(22);
}
public void test(HttpServletRequest req, HttpServletResponse res){
System.out.println(req.getPathInfo());
}
}
스 캔 클래스 2.0 버 전
1.0 버 전 을 통 해 우 리 는 스 캔 패키지 에 있 는 모든 contrller 를 초보 적 으로 실현 하고 경로 맵 을 통 해 접근 할 수 있 습 니 다.그러나 적어도 다음 과 같은 문제 가 분명 하 다.
상기 두 가지 문제 에 대해 우 리 는 2.0 판 에서 수정 을 진행 합 니 다.
@XxgRequestBody
와 @XxgParam
를 통 해 매개 변 수 를 요청 체 에서 가 져 오 거나 url 에서 가 져 오 는 것 을 구분 합 니까?뒤에서 들 어.전단 에서 전 달 된 데이터 가 져 오기 ObjectMapper
을 통 해 서로 다른 유형의 매개 변 수 를 조립 하고 마지막 으로 호출 방법 invoke
은 인삼 / 인삼 이 없 는 방법 으로 처리한다.BeanDefinition
/**
* controller 、
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BeanDefinition {
private Class typeClazz; //
private String typeName; //
private Object annotation; //
private String controllerUrlPath; // controller path
private List methodDefinitions; // RequestMapping
}
MethodDefinition
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MethodDefinition {
private Class parentClazz; // class
private Method method; //
private String methodName; //
private Object annotation; //
private String requestMappingUrlPath; // url
private String[] allowedRequestMethods; // allowedRequestMethods
private List parameterDefinitions; //
private Object result; //
}
ParameterDefinition
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ParameterDefinition {
private Class paramClazz; //
private String paramName; //
private Object paramType; //
private boolean isRequestBody; // body
}
단일 모드 용기
스 캔 패키지 부여 및 uri 에 따라 대응 하 는 방법 가 져 오기
/**
* controller
*
*/
public class RequestPathContainer {
private static List requestList = new ArrayList<>();
private static final ClassLoader classLoader = RequestPathContainer.class.getClassLoader();
private static volatile RequestPathContainer instance = null;
public static RequestPathContainer getInstance() {
if (instance == null) {
synchronized(RequestPathContainer.class){
if (instance == null) {
instance = new RequestPathContainer();
}
}
}
return instance;
}
private RequestPathContainer() {
}
public List getRequestList() {
return requestList;
}
//
public void scanner(String packetUrl) throws UnsupportedEncodingException, ClassNotFoundException {
String url = packetUrl.replace(".", "/");
URL resource = classLoader.getResource(url);
if (resource == null) {
return;
}
String path = resource.getPath();
File file = new File(URLDecoder.decode(path, "UTF-8"));
if (!file.exists()) {
return;
}
if (file.isDirectory()){
File[] files = file.listFiles();
if (files == null) {
return;
}
for (File f : files) {
if (f.isDirectory()) {
scanner(packetUrl + "." + f.getName());
}
if (f.isFile() && f.getName().endsWith(".class")) {
String classname = f.getName().replace(".class", ""); // .class
Class clazz = Class.forName(packetUrl + "." + classname);
dealClass(clazz);
}
}
}
}
// , List
private void dealClass(Class clazz) {
if (!clazz.isAnnotationPresent(XxgController.class)) {
// controller
return;
}
List methodDefinitions = new ArrayList<>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//
MethodDefinition methodDefinition = convertMethodToMethodDefinition(method, clazz);
if (methodDefinition != null) {
methodDefinitions.add(methodDefinition);
}
}
if (methodDefinitions.size() == 0) {
return;
}
//
BeanDefinition beanDefinition = convertBeanToBeanDefinition(clazz, methodDefinitions);
requestList.add(beanDefinition);
}
// uri
public MethodDefinition getMethodDefinition(String uri, String method) {
for (BeanDefinition beanDefinition: requestList) {
if (!uri.contains(beanDefinition.getControllerUrlPath())) {
continue;
}
List methodDefinitions = beanDefinition.getMethodDefinitions();
for (MethodDefinition methodDefinition: methodDefinitions) {
StringBuilder sb = new StringBuilder().append(beanDefinition.getControllerUrlPath());
sb.append(methodDefinition.getRequestMappingUrlPath());
if (!sb.toString().equals(uri)) {
continue;
}
String[] allowedRequestMethods = methodDefinition.getAllowedRequestMethods();
for (String str : allowedRequestMethods) {
if (str.toUpperCase().equals(method.toUpperCase())) {
// ,
return methodDefinition;
}
}
}
}
return null;
}
/**
* controller
*/
private BeanDefinition convertBeanToBeanDefinition(Class clazz, List methodDefinitions) {
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setTypeName(clazz.getName());
beanDefinition.setTypeClazz(clazz);
XxgController controller = (XxgController) clazz.getAnnotation(XxgController.class);
beanDefinition.setAnnotation(controller);
beanDefinition.setControllerUrlPath(controller.value());
beanDefinition.setMethodDefinitions(methodDefinitions);//
return beanDefinition;
}
/**
*
*/
private MethodDefinition convertMethodToMethodDefinition(Method method, Class clazz) {
if (!method.isAnnotationPresent(XxgRequestMapping.class)) {
// RequestMapping
return null;
}
method.setAccessible(true);
Parameter[] parameters = method.getParameters();
//
List parameterDefinitions = new ArrayList<>();
for ( Parameter parameter : parameters) {
ParameterDefinition parameterDefinition = convertParamToParameterDefinition(parameter);
parameterDefinitions.add(parameterDefinition);
}
//
MethodDefinition methodDefinition = new MethodDefinition();
methodDefinition.setParameterDefinitions(parameterDefinitions); //
methodDefinition.setMethod(method);
methodDefinition.setMethodName(method.getName());
methodDefinition.setResult(method.getReturnType());
XxgRequestMapping requestMapping = method.getAnnotation(XxgRequestMapping.class);
methodDefinition.setRequestMappingUrlPath(requestMapping.value());
methodDefinition.setAnnotation(requestMapping);
methodDefinition.setAllowedRequestMethods(requestMapping.methods());
methodDefinition.setParentClazz(clazz);
return methodDefinition;
}
/**
*
*/
private ParameterDefinition convertParamToParameterDefinition(Parameter parameter) {
ParameterDefinition parameterDefinition = new ParameterDefinition();
if ( parameter.isAnnotationPresent(XxgParam.class)) {
parameterDefinition.setParamName(parameter.getAnnotation(XxgParam.class).value());
} else {
parameterDefinition.setParamName(parameter.getName());
}
parameterDefinition.setParamClazz(parameter.getType());
parameterDefinition.setParamType(parameter.getType());
parameterDefinition.setRequestBody(parameter.isAnnotationPresent(XxgRequestBody.class));
return parameterDefinition;
}
}
전역 servlet
차단 기 를 사용 하지 않 고 servlet 를 사용 하여 경로 배 포 를 진행 합 니 다.이 servlet 감청
/
public class DispatcherServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//
resp.setContentType("text/json;charset=utf-8");
RequestPathContainer requestPathContainer = RequestPathContainer.getInstance();
MethodDefinition methodDefinition = requestPathContainer.getMethodDefinition(req.getRequestURI(), req.getMethod());
if (methodDefinition == null) {
resp.setStatus(404);
sendResponse(R.failed(" "), req, resp);
return;
}
List parameterDefinitions = methodDefinition.getParameterDefinitions();
List
servletcontext 모니터 용기 초기 화
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
RequestPathContainer requestPathContainer = RequestPathContainer.getInstance();
String configClassName = servletContextEvent.getServletContext().getInitParameter("config");
Class appListenerClass = null;
try {
appListenerClass = Class.forName(configClassName);
XxgScanner xxgScanner = (XxgScanner)appListenerClass.getAnnotation(XxgScanner.class);
if (xxgScanner != null) {
try {
requestPathContainer.scanner(xxgScanner.value()); // controller , List
} catch (UnsupportedEncodingException | ClassNotFoundException e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
남 겨 진 문제
정적 자원 도 차단 되 었 습 니 다.
정적 자원 처리
default servlet
tomcat 의
conf/web.xml
파일 을 열 면 tomcat 기본 값 default servlet
이 있 습 니 다. 다음 설정 이 있 습 니 다.
default
org.apache.catalina.servlets.DefaultServlet
debug
0
listings
false
1
그러나 그 는 servlet - mapping, 즉 처리 경로 와 일치 하지 않 습 니 다. 그러면 우리 프로젝트 의 웹. xml 에서 정적 자원 을 처리 할 수 있 습 니 다.
DispatcherServlet
/
default
*.html
default
*.js
default
*.css
default
*.jpg
마지막.
1. 본 고 는 사실 주로 다음 과 같은 두 가지 조작 을 했다.
다음 절: AutoWired 속성 에 사용 되 는 간단 한 구현