`
bxf12315
  • 浏览: 27130 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于URL的契约式简易MVC框架

阅读更多

一直以来对0配置的mvc框架很感兴趣。

最近,突发奇想,想试验一下如何利用url来实现契约式的mvc零配置框架。

首先,我们以一个普通的url为例。

"http://ipaddress:prot/project_name/business_name/moudle_name/action_name/"

我们假定从business_name开始我们对应的是java中的类包,moudle_name我们对应成 相应业务的对象名称。

而action_name则是 这个对象中的方法名称。

上面是我们假定的规则。

然后我们可以通过reflect可以找到这个请求相关的方法。这样就可以不同过配置文件来配置相关信息。

同事,我们在定义相对应的annotation来为操作成功失败后做跳转。

以上就是一个大概的思路。现在给大家看看相应的代码。

 

 

首先是我们的web。xml文件

Xml代码 复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.4"    
  3.     xmlns="http://java.sun.com/xml/ns/j2ee"    
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee    
  6.     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
  7.     <servlet>  
  8.     <servlet-name>action</servlet-name>  
  9.     <servlet-class>com.pig.hub.dev.frameWork.action.ActionServlet</servlet-class>  
  10.     <init-param>  
  11.     <param-name>filename</param-name>  
  12.     <param-value>pig.web</param-value>  
  13.     </init-param>  
  14.      <load-on-startup>0</load-on-startup>  
  15.   </servlet>  
  16.   <servlet-mapping>  
  17.     <servlet-name>action</servlet-name>  
  18.     <url-pattern>*.pig</url-pattern>  
  19.   </servlet-mapping>  
  20.   <welcome-file-list>  
  21.     <welcome-file>index.jsp</welcome-file>  
  22.   </welcome-file-list>  
  23. </web-app>  
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>com.pig.hub.dev.frameWork.action.ActionServlet</servlet-class>
    <init-param>
    <param-name>filename</param-name>
    <param-value>pig.web</param-value>
    </init-param>
     <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.pig</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

 其中定义了com.pig.hub.dev.frameWork.action.ActionServlet来去拦截所有的请求(。pig)。filename这个属性是为了给出系统中相应的完成的包名。

 

下面是ActionServlet相应的java代码

Java代码 复制代码
  1. import com.pig.hub.dev.frameWork.action.exception.MakeUriException;   
  2.   
  3. /**  
  4.  * @author   
  5.  */  
  6. public abstract class AbActionServlet extends HttpServlet implements  
  7.         BasicActionServlet {   
  8.   
  9.     /**  
  10.      *   
  11.      */  
  12.     private static final long serialVersionUID = 7911661010594064823L;   
  13.   
  14.     public void doGet(HttpServletRequest request, HttpServletResponse response)   
  15.             throws ServletException, IOException {   
  16.         try {   
  17.             doWork(request, response);   
  18.         } catch (MakeUriException e) {   
  19.         }   
  20.     }   
  21.   
  22.     public void doPost(HttpServletRequest request, HttpServletResponse response)   
  23.             throws ServletException, IOException {   
  24.         try {   
  25.             doWork(request, response);   
  26.         } catch (MakeUriException e) {   
  27.         }   
  28.     }   
  29.   
  30.     /**  
  31.      * @param request  
  32.      * @param response  
  33.      * @throws ServletException  
  34.      * @throws IOException  
  35.      * @see com.pig.hub.dev.frameWork.action.BasicActionServlet#doWork(javax.servlet.http.HttpServletRequest,  
  36.      *      javax.servlet.http.HttpServletResponse)  
  37.      */  
  38.     public abstract void doWork(HttpServletRequest request,   
  39.             HttpServletResponse response) throws ServletException, IOException,   
  40.             MakeUriException;   
  41. }   
  42.   
  43.   
  44. ---------------------------------------------------------  
import com.pig.hub.dev.frameWork.action.exception.MakeUriException;

/**
 * @author 
 */
public abstract class AbActionServlet extends HttpServlet implements
		BasicActionServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7911661010594064823L;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			doWork(request, response);
		} catch (MakeUriException e) {
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			doWork(request, response);
		} catch (MakeUriException e) {
		}
	}

	/**
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 * @see com.pig.hub.dev.frameWork.action.BasicActionServlet#doWork(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	public abstract void doWork(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException,
			MakeUriException;
}


---------------------------------------------------------
Java代码 复制代码
  1. package com.pig.hub.dev.frameWork.action;   
  2.   
  3. import java.io.IOException;   
  4.   
  5. import javax.servlet.ServletException;   
  6. import javax.servlet.http.HttpServletRequest;   
  7. import javax.servlet.http.HttpServletResponse;   
  8.   
  9. import com.pig.hub.dev.frameWork.action.exception.MakeUriException;   
  10.   
  11. /**  
  12.  *   
  13.  */  
  14. public interface BasicActionServlet {   
  15.     /**  
  16.      * @param request  
  17.      * @param response  
  18.      * @throws ServletException  
  19.      * @throws IOException  
  20.      * @throws MakeUriException  
  21.      */  
  22.     public void  doWork(HttpServletRequest request, HttpServletResponse response)   
  23.             throws ServletException, IOException,MakeUriException;   
  24. }   
  25. -------------------------------------------------------  
package com.pig.hub.dev.frameWork.action;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.pig.hub.dev.frameWork.action.exception.MakeUriException;

/**
 * 
 */
public interface BasicActionServlet {
	/**
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 * @throws MakeUriException
	 */
	public void  doWork(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException,MakeUriException;
}
-------------------------------------------------------
Java代码 复制代码
  1. package com.pig.hub.dev.frameWork.action;   
  2.   
  3. import java.io.IOException;   
  4. import java.lang.reflect.InvocationTargetException;   
  5. import java.lang.reflect.Method;   
  6. import java.util.HashMap;   
  7.   
  8. import javax.servlet.ServletException;   
  9. import javax.servlet.http.HttpServletRequest;   
  10. import javax.servlet.http.HttpServletResponse;   
  11.   
  12. import com.pig.hub.dev.frameWork.action.config.MethodBean;   
  13. import com.pig.hub.dev.frameWork.action.exception.MakeUriException;   
  14. import com.pig.hub.dev.frameWork.action.exception.MappingException;   
  15. import com.pig.hub.dev.frameWork.action.exception.MethodException;   
  16.   
  17. public class ActionServlet extends AbActionServlet implements  
  18.         BasicActionServlet {   
  19.   
  20.     /**  
  21.      *   
  22.      */  
  23.     private static final long serialVersionUID = 2848825411739564234L;   
  24.   
  25.     private String fileName;   
  26.   
  27.     public void init() throws ServletException {   
  28.         fileName = this.getInitParameter("filename");   
  29.     }   
  30.     @SuppressWarnings("unchecked")   
  31.     public void doWork(HttpServletRequest request, HttpServletResponse response)   
  32.             throws ServletException, IOException, MakeUriException {   
  33.   
  34.         String s[] = BusinessConfig.makeUri(request);   
  35.            
  36.         String errorMessage="";   
  37.         HashMap map = new HashMap();   
  38.         if (s.length > 2)   
  39.             throw new MakeUriException();   
  40.   
  41.         try {   
  42.             MethodBean mBean = BusinessConfig.makeConfig(s, fileName);   
  43.             Method m = mBean.getM();   
  44.             Class c = mBean.getC();   
  45.             m.invoke(c.newInstance(), request, response);   
  46.             map = MappingUtil.makeHashMap(m);   
  47.             request.getRequestDispatcher((String) map.get("success")).forward(   
  48.                     request, response);   
  49.   
  50.         } catch (SecurityException e) {   
  51.             errorMessage = "someting";   
  52.   
  53.         } catch (ClassNotFoundException e1) {   
  54.             errorMessage = "someting";   
  55.         } catch (MethodException e2) {   
  56.             errorMessage = "someting";   
  57.         } catch (NoSuchMethodException e3) {   
  58.             errorMessage = "someting";   
  59.         } catch (IllegalArgumentException e4) {   
  60.             errorMessage = "someting";   
  61.         } catch (IllegalAccessException e5) {   
  62.             errorMessage = "someting";   
  63.         } catch (InvocationTargetException e6) {   
  64.             errorMessage = "someting";   
  65.         } catch (InstantiationException e) {   
  66.             errorMessage = "someting";   
  67.         } catch (MappingException e) {   
  68.             errorMessage = "someting";   
  69.         } finally {   
  70.             if (!errorMessage.equals(""))   
  71.                 request.getRequestDispatcher((String) map.get("fail")).forward(   
  72.                         request, response);   
  73.                
  74.         }   
  75.     }   
  76.   
  77. }  
package com.pig.hub.dev.frameWork.action;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.pig.hub.dev.frameWork.action.config.MethodBean;
import com.pig.hub.dev.frameWork.action.exception.MakeUriException;
import com.pig.hub.dev.frameWork.action.exception.MappingException;
import com.pig.hub.dev.frameWork.action.exception.MethodException;

public class ActionServlet extends AbActionServlet implements
		BasicActionServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 2848825411739564234L;

	private String fileName;

	public void init() throws ServletException {
		fileName = this.getInitParameter("filename");
	}
	@SuppressWarnings("unchecked")
	public void doWork(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException, MakeUriException {

		String s[] = BusinessConfig.makeUri(request);
		
		String errorMessage="";
		HashMap map = new HashMap();
		if (s.length > 2)
			throw new MakeUriException();

		try {
			MethodBean mBean = BusinessConfig.makeConfig(s, fileName);
			Method m = mBean.getM();
			Class c = mBean.getC();
			m.invoke(c.newInstance(), request, response);
			map = MappingUtil.makeHashMap(m);
			request.getRequestDispatcher((String) map.get("success")).forward(
					request, response);

		} catch (SecurityException e) {
			errorMessage = "someting";

		} catch (ClassNotFoundException e1) {
			errorMessage = "someting";
		} catch (MethodException e2) {
			errorMessage = "someting";
		} catch (NoSuchMethodException e3) {
			errorMessage = "someting";
		} catch (IllegalArgumentException e4) {
			errorMessage = "someting";
		} catch (IllegalAccessException e5) {
			errorMessage = "someting";
		} catch (InvocationTargetException e6) {
			errorMessage = "someting";
		} catch (InstantiationException e) {
			errorMessage = "someting";
		} catch (MappingException e) {
			errorMessage = "someting";
		} finally {
			if (!errorMessage.equals(""))
				request.getRequestDispatcher((String) map.get("fail")).forward(
						request, response);
			
		}
	}

}


以上基本有一个抽象类一个接口一个实现构成,其实只是用到了简单的模板方法。

其中,String s[] = BusinessConfig.makeUri(request);是为了得到url中相应的业务名对象名以及方法名称。

然后通过MethodBean mBean = BusinessConfig.makeConfig(s, fileName)来构建一个自定义的方法对象。

Java代码 复制代码
  1. package com.pig.hub.dev.frameWork.action.config;   
  2.   
  3. import java.lang.reflect.Method;   
  4.   
  5. import javax.servlet.http.HttpServletRequest;   
  6. import javax.servlet.http.HttpServletResponse;   
  7.   
  8. import com.pig.hub.dev.frameWork.action.exception.MethodException;   
  9.   
  10. public class MethodBean implements java.io.Serializable {   
  11.     /**  
  12.      *   
  13.      */  
  14.     private static final long serialVersionUID = -5753278542873561978L;   
  15.     private Class c;   
  16.     private Method m;   
  17.     private String name;   
  18.        
  19.   
  20.     public Class getC() {   
  21.         return c;   
  22.     }   
  23.   
  24.     public void setC(Class c) {   
  25.         this.c = c;   
  26.     }   
  27.   
  28.     public Method getM() {   
  29.         return m;   
  30.     }   
  31.   
  32.     public void setM() throws MethodException, ClassNotFoundException,   
  33.             SecurityException, NoSuchMethodException {   
  34.         if (name == null || name.equals(""))   
  35.             throw new MethodException();   
  36.         if (c == null)   
  37.             throw new ClassNotFoundException();   
  38.         m = c.getDeclaredMethod(name, HttpServletRequest.class,   
  39.                 HttpServletResponse.class);   
  40.     }   
  41.   
  42.     public String getName() {   
  43.         return name;   
  44.     }   
  45.   
  46.     public void setName(String name) {   
  47.         this.name = name;   
  48.     }   
  49. }  
package com.pig.hub.dev.frameWork.action.config;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.pig.hub.dev.frameWork.action.exception.MethodException;

public class MethodBean implements java.io.Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5753278542873561978L;
	private Class c;
	private Method m;
	private String name;
	

	public Class getC() {
		return c;
	}

	public void setC(Class c) {
		this.c = c;
	}

	public Method getM() {
		return m;
	}

	public void setM() throws MethodException, ClassNotFoundException,
			SecurityException, NoSuchMethodException {
		if (name == null || name.equals(""))
			throw new MethodException();
		if (c == null)
			throw new ClassNotFoundException();
		m = c.getDeclaredMethod(name, HttpServletRequest.class,
				HttpServletResponse.class);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

 通过 m = c.getDeclaredMethod(name, HttpServletRequest.class,  HttpServletResponse.class);得到url对应的方法对象。

下面就简单了

m.invoke(c.newInstance(), request, response);

执行而已。

 

现在通过契约式的url可以访问到相应的对象了。那么还差一个如何跳转。

首先我们自定义一个annotation

Java代码 复制代码
  1. package com.pig.hub.dev.frameWork.annotation;   
  2.   
  3. import java.lang.annotation.ElementType;   
  4. import java.lang.annotation.Retention;   
  5. import java.lang.annotation.RetentionPolicy;   
  6. import java.lang.annotation.Target;   
  7.   
  8. /**  
  9.  *   
  10.  */  
  11. @Retention(RetentionPolicy.RUNTIME)   
  12. @Target(ElementType.METHOD)   
  13. public @interface MappingConfig {   
  14.   
  15.     String success();   
  16.   
  17.     String fail();   
  18.   
  19.     String exception();   
  20.   
  21. }  
package com.pig.hub.dev.frameWork.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MappingConfig {

	String success();

	String fail();

	String exception();

}

 其中有三个属性,success fail exception分别指在成功后跳转的地方或者失败后跳转的。。。

下面是在实际对象中如何使用这三个对象。

Java代码 复制代码
  1. public class aa {   
  2.     @MappingConfig(success="../index.jsp",fail="",exception="")   
  3.     public void bb(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   
  4.         System.out.print("test------------");   
  5.         request.setAttribute("123""成功了");   
  6.     }   
  7.   
  8. }  
public class aa {
	@MappingConfig(success="../index.jsp",fail="",exception="")
	public void bb(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.print("test------------");
		request.setAttribute("123", "成功了");
	}

}

 然后通过

Java代码 复制代码
  1. package com.pig.hub.dev.frameWork.action;   
  2.   
  3. import java.lang.reflect.Method;   
  4. import java.util.HashMap;   
  5.   
  6. import com.pig.hub.dev.frameWork.action.exception.MappingException;   
  7. import com.pig.hub.dev.frameWork.annotation.MappingConfig;   
  8.   
  9. public class MappingUtil {   
  10.     @SuppressWarnings("unchecked")   
  11.     public static HashMap makeHashMap(Method m) throws MappingException {   
  12.         MappingConfig an = m.getAnnotation(MappingConfig.class);   
  13.   
  14.         if (an == null)   
  15.             throw new MappingException();   
  16.         HashMap map = new HashMap();   
  17.   
  18.         map.put("success", an.success());   
  19.         map.put("fail", an.fail());   
  20.         map.put("exception", an.exception());   
  21.         return map;   
  22.   
  23.     }   
  24.   
  25. }  
package com.pig.hub.dev.frameWork.action;

import java.lang.reflect.Method;
import java.util.HashMap;

import com.pig.hub.dev.frameWork.action.exception.MappingException;
import com.pig.hub.dev.frameWork.annotation.MappingConfig;

public class MappingUtil {
	@SuppressWarnings("unchecked")
	public static HashMap makeHashMap(Method m) throws MappingException {
		MappingConfig an = m.getAnnotation(MappingConfig.class);

		if (an == null)
			throw new MappingException();
		HashMap map = new HashMap();

		map.put("success", an.success());
		map.put("fail", an.fail());
		map.put("exception", an.exception());
		return map;

	}

}

 

得到要执行的方法上的annotation然后将他属性放入一个hashmap中。

下面就是跳转了(成功的)


   request.getRequestDispatcher((String) map.get("success")).forward(
     request, response);

 

这样就完成了一个基于url契约的零配置mvc的实现。

呵呵,实在有点简单,问题也很多。

写这个东西的目的其实在于学习一下java的基本知识。现在大家没事干就说这样那样的框架,我想不如自己来试着如何实现。通过自己来实现,可以明白好多东西,然后在去用那写框架我想会更加得心应手。

以上纯属功能上的实现,问题很多很多,例如在actionservlet中有一大堆的异常,这样写出来很别扭,其中还有自己定义的异常。

欢迎大家给我提出建议。

0
0
分享到:
评论
2 楼 bxf12315 2009-08-05  
哦 ror我还没搞过。这个只是想用来带着我负责项目组的成员学学用的。
呵呵
1 楼 linhong_1001 2009-08-05  
怎么都像是ROR,Grails就是这样做的

相关推荐

    采用非mvc框架的书店管理系统

    《采用非MVC框架的书店管理系统》 在IT行业中,软件架构的设计对于系统的稳定性和可扩展性至关重要。本文将深入探讨一个特殊的书店管理系统,它并未采用传统的MVC(Model-View-Controller)框架,而是利用了其他...

    基于语义契约模型的微服务接口自动化测试技术研究.rar

    基于语义契约模型的微服务接口自动化测试技术,便是为了解决这一问题而提出的。 语义契约模型是一种定义服务间交互规则的方法,它不仅关注接口的数据格式,还关注数据的业务含义。契约模型通常由生产者(提供服务的...

    springmvc demo

    Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发...

    PHP高级程序设计-模式-框架-测试

    抽象类、接口、契约式编程开始讲起,然后介绍了静态方法、单例模式、工厂模式和PHP 6 的新特性等内容,接着介绍了测试和文档方面的内容,还介绍了标准PHP 库SPL 方面的知识以及PHP 开发人员最有可能用到的MVC 模式,...

    一种使用AspectJ技术的Java契约式编程语言模型.pdf

    契约式编程是一种软件开发方法,它借鉴了形式化方法的理念,通过引入不变式、前置条件(precondition)和后置条件(postcondition)等概念,确保程序模块的语义清晰和正确性。这种方法有助于提高软件的可靠性和可...

    wcf+mvc程序

    **ASP.NET MVC(Model-View-Controller)**是一种用于构建Web应用程序的模式,它是ASP.NET框架的一部分。MVC模式将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)。模型处理业务逻辑...

    .NET4.0新特性之-契约式设计

    .NET4.0契约式设计,MSDN官方推荐学习视频,很好的资料。

    毕业设计,基于java语言,ssm框架和微信小程序开发的小契约(交友互动小程序).7z

    这篇文档将深入探讨一个毕业设计项目,该项目是一个基于Java语言,使用SSM框架,并通过微信小程序实现的小契约——一个交友互动平台。这个设计涵盖了多个关键的技术领域,包括后端开发、前端开发以及移动应用开发,...

    基于心理契约的员工忠诚度提升策略.pdf

    基于心理契约的员工忠诚度提升策略.pdf

    基于WSDL契约优先的web services服务器端和客户端开发方式

    在"基于WSDL契约优先的Web Services"开发中,我们首先定义服务的WSDL契约,然后根据这个契约生成服务器端和客户端的代码。 1. **WSDL契约优先**: WSDL契约优先的方法意味着首先编写服务的接口描述(WSDL文件),...

    基于语义契约模型的微服务接口自动化测试技术研究.pdf

    综上所述,基于语义契约模型的微服务接口自动化测试技术研究,不仅关注了微服务接口契约的构建与维护,还探索了数据语义描述和数据约束的构建方法,并提出了基于多约束组合的测试生成策略,从而在保证测试全面覆盖的...

    基于心理契约理论的高职院校师资队伍管理

    【基于心理契约理论的高职院校师资队伍管理】探讨了如何运用心理契约理论来优化高职院校师资队伍的管理,以适应互联网时代下员工与组织关系的变化。心理契约是员工与组织间无形的默契,体现在双方的期望上,包括交易...

    基于返利契约的电动汽车供应链协调策略研究.pdf

    基于返利契约的电动汽车供应链协调策略研究.pdf

    基于SOA的应用集成框架研究.pdf

    文章中提出的基于JBI规范的面向服务的应用集成框架,是一个利用企业服务总线(ESB)无缝集成应用与数据资源、支持流程服务编排并能进行组合服务应用开发的框架。该框架在异构数据集成中的应用示例表明,它可以有效地...

    web服务 asp mvc 静态类

    综上所述,这个话题涉及到使用ASP.NET MVC框架构建Web服务,通过静态类实现公共工具和服务,以及数据契约在数据交换中的作用。理解这些概念对于开发可扩展、可维护的Web应用程序至关重要。在实际项目中,应合理利用...

    QYBot:契约机器人框架

    QYBot是一款基于契约理念设计的机器人框架,它旨在为开发者提供一个高效、灵活的平台,用于构建自动化处理任务的智能机器人。这个框架的核心特性在于其契约设计理念,允许开发者定义机器人的行为规范,确保机器人在...

Global site tag (gtag.js) - Google Analytics