难点:自动扫描类路径,并通过自定义类装载器加载所有处理请求的action,并关联到相应的处理uri上。
具体的实现步骤与代码:
1. 在web.xml文件配置一个servlet , 这个servlet 用来处理URL以.do 结尾的所有HTTP请求
<servlet> <description>This is the description of my J2EE component</description> <display-name>This is the display name of my J2EE component</display-name> <servlet-name>ControllerServlet</servlet-name> <servlet-class>com.alex.framework.ControllerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ControllerServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
package com.alex.framework; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ControllerServlet extends HttpServlet { //Map中的key用来存放URI,value用来存放URI相对应的Action对象(Action指处理各类请求的控制器) private Map<String,Object> map = new HashMap<String,Object>(); @Override public void init() throws ServletException { String classPath = this.getServletContext().getRealPath("/WEB-INF/classes"); scanClassPath(new File(classPath)); } /* * 扫描类路径所有类文件,如果类文件含有Control注解,则把注解的value(URI)放进Map中作为key, * 并将类的实例对象作为Map当中的value */ private void scanClassPath(File file) { try{ if(file.isFile()){ if(file.getName().endsWith(".class")){ String path = file.getPath(); MyClassLoader myClassLoader = new MyClassLoader(ControllerServlet.class.getClassLoader()); Class clazz = myClassLoader.load(path); Control control = (Control)clazz.getAnnotation(Control.class); if(control!=null){ String uri = control.value(); Object action = clazz.newInstance(); map.put(uri, action); } } }else{ File[] files = file.listFiles(); for(File child:files){ scanClassPath(child); } } }catch(Exception e){ throw new RuntimeException(e); } } //自定义一个类加载器,使得类加载器能够通过类文件路径获得该类的字节码文件 class MyClassLoader extends ClassLoader{ public MyClassLoader(ClassLoader parent){ super(parent); } public Class load(String path){ FileInputStream fis = null; try{ fis = new FileInputStream(path); byte[] buf = new byte[fis.available()]; int len = 0; int total = 0; int fileLength = buf.length; while(total<fileLength){ len=fis.read(buf, total,fileLength-total); total=total+len; } return super.defineClass(null, buf, 0, fileLength); }catch(Exception e){ throw new RuntimeException(e); }finally{ if(fis!=null){ try { fis.close(); } catch (IOException e) { throw new RuntimeException(e); } fis=null; } } } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); //获得http请求的URI并截掉后面的请求参数字符串 String uri = request.getRequestURI(); uri = uri.substring(request.getContextPath().length(), uri.length()-3); if(map.containsKey(uri)){ //通过http请求uri获得相对应的Action对象 Object obj = map.get(uri); //获得http请求的方法名 String methodName = request.getParameter("method"); //如果请求的方法null,则默认调用Action对象中的exec方法 if(methodName==null){ methodName="exec"; } Method method = null; try { //通过反射获得要执行的方法对象 method = obj.getClass().getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class); } catch (Exception e){ throw new RuntimeException("在"+obj.getClass().getName()+"上找不到与" + methodName +"相对应的方法!!!"); } try { //执行Action对象中的方法 method.invoke(obj, request,response); } catch (Exception e){ e.printStackTrace(); } } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } }
package com.alex.framework; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Control { String value(); }
框架便会调用CustomerAction中list方法对请求进行处理
@Control("/control/customer/customer") public class CustomerAction { private CustomerService customerService = new CustomerService(); public void list(HttpServletRequest request,HttpServletResponse response) throws Exception{ PageInfo pageInfo = WebUtils.request2Bean(request, PageInfo.class); PageBean pageBean = customerService.pageQuery(pageInfo); request.setAttribute("pageBean",pageBean); request.getRequestDispatcher("/WEB-INF/pages/customer/listCustomer.jsp").forward(request, response); }