zoukankan      html  css  js  c++  java
  • 实现一个依赖注入的小程序

    先用servlet+jsp 实现一个小功能,传一个参数,输出当前时间

    @WebServlet("/hello/showDate")
    public class HelloServlet extends HttpServlet{
    
        @Override
        public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String date = sdf.format(new Date());
            String name = request.getParameter("name");
            request.setAttribute("date", "hi "+name+",Now is:"+date);
            request.getRequestDispatcher("/jsp/hello.jsp").forward(request, response);
        }
    }
    <body>
        <h1>${date}</h1>
    </body>

    访问地址 /hello/showDate?name=lee  效果

    功能很简单,现在模仿Spring 注解方式来实现这段代码

    ModelAndView类 存储页面返回信息

    public class ModelView {
        private String view;
        private Map<String, Object> modelMap;
    
        public String getView() {
            return view;
        }
    
        public void setView(String view) {
            this.view = view;
        }
    
        public Map<String, Object> getModelMap() {
            return modelMap;
        }
    
        public void setModelMap(Map<String, Object> modelMap) {
            this.modelMap = modelMap;
        }
    
    }

    controller类 负责做分发,@Controller注解这个类是controller类,@RequestMapping配置类路径和方法路径

    @Controller
    @RequestMapping("/hello")
    public class HelloController {
        
        private HelloService helloService;
        
        @RequestMapping("/showDate")
        public ModelView showDate(String name){
            ModelView modelView = helloService.showDate(name);
            return modelView;
        }
        
    }

    service 实现类 @Service 标识这个是service类

    @Service
    public class HelloService {
        
        public ModelView showDate(String name){
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String date = sdf.format(new Date());
            ModelView modelView = new ModelView();
            modelView.setView("/jsp/hello.jsp");
            Map<String,Object> map = new HashMap<String, Object>();
            map.put("date", "hi "+name+",Now is:"+date);
            modelView.setModelMap(map);
            return modelView;
        }
    }

    写完之后一堆错,这些注解我们都没有定义。没关系 一步一步来 ,先定义注解类

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Controller {
        
    }
    @Target({ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequestMapping {
        String value() default "/";
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Service {
    
    }

    到目前位置编译错误没有了,但是还没有实现我们想要的功能。

    理想中我们想要的应该是,启动JVM,自动实例化我们的controller,service类,将service类的实例注入到controller中,根据访问路径反射相应的类方法,

    首先,我们先在web.xml里定义一个DispatcherServlet类 用来处理所有项目路径请求,做总的分发器

        <servlet>
            <servlet-name>servlet</servlet-name>
            <servlet-class>com.servlet.DispatcherServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>servlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    public class DispatcherServlet extends HttpServlet {    
        @Override
        public void init(ServletConfig config){
            
        }
    }

    在初始化DispatcherServlet类时,我们希望程序扫描我们自己定义的base-package下的所有controller类和service类,并将service类实例注入到controller中,而且我们希望在程序启动时就做这些事情,并加载一些我们配置,如数据库连接等等,当然这个项目中只需要配一个base-package。

    所以我们补充一下web.xml中的配置

    <servlet>
            <servlet-name>servlet</servlet-name>
            <servlet-class>com.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>config</param-name>
                <param-value>config.properties</param-value>
            </init-param>
            
            <load-on-startup>0</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>servlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>

    还有配置资源文件

    base-package=com

    下面开始具体实现,先写一个工具类ClassUtil,定义一个方法getClass()来加载base-package包下的所有类

    public class ClassUtil {
        
        private static ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
        
        public static ClassLoader getClassLoader(){
            return classLoader;
        }
        public static Set<Class<?>> getClass(String path){
            Set<Class<?>> classSet = new HashSet<Class<?>>();
            String filePath = classLoader.getResource(path).getFile();
            load(filePath, path,classSet);
            return classSet;
        }
        private static void load(String path,String packageName,Set<Class<?>> classSet){
            File[] files = new File(path).listFiles();
            for(File file:files){
                String fileName = file.getName();
                if(file.isFile()){
                    if(fileName.endsWith(".class")){
                        try {
                            //并不是所有类都是需要实例化的,是否实例化置成false,等需要时再实例化
                            Class cls = Class.forName(packageName+"."+fileName.substring(0,fileName.lastIndexOf('.')), false, classLoader);
                            classSet.add(cls);
                        } catch (ClassNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }else{
                    load(file.getPath(),packageName+"."+fileName, classSet);
                }
            }
            
        }
    }

    现在我们有一个Set集合存储了扫描的类,接下来还需要一个工具类来做以下事情;

    1.实例化Set集合中的service类

    2.实例化Set集合中的controller类,并将controller类中service成员变量注入service实例

    3.将访问路径和controller类一一对应。

    所以 我们需要几个属性 Map<String,Object>  controllerMap 存储请求和controller的对应关系, Set<Class<?>> classSet 接收ClassUtil返回的加载过的类集合

    getService()方法实例化service, initController()方法实现目标2,3 并返回controllerMap。

    public class InitClassUtil {
        private static Map<String,Object> controllerMap;
        private static Set<Class<?>> classSet;
        public InitClassUtil(String config){
            classSet=scanClass(config);
            controllerMap=initController();
        } 
        public static Map<String, Object> getControllerMap() {
            return controllerMap;
        }
        private static Set<Class<?>> scanClass(String config){
            Properties props = PropsUtil.loadProps(config);
            return ClassUtil.getClass(props.getProperty("base-package"));
        }
        private static Map<Class<?>,Object> getService(){
            Map<Class<?>,Object> serviceMap = new HashMap<Class<?>,Object>();
            return serviceMap;
        }
        private static Map<String,Object> initController(){
            return controllerMap;    
        }    
    }

    具体实现

        private static Map<Class<?>,Object> getService(){
            Map<Class<?>,Object> serviceMap = new HashMap<Class<?>,Object>();
            for(Class<?> service:classSet){
                if(service.isAnnotationPresent(Service.class)){
                    try {
                        serviceMap.put(service, service.newInstance());
                    } catch (InstantiationException | IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            return serviceMap;
        }
        private static Map<String,Object> initController(){
            Map<Class<?>,Object> serviceMap = getService();
            Map<String,Object> controllerMap = new HashMap<String,Object>();
            for(Class<?> controller:classSet){
                if(controller.isAnnotationPresent(Controller.class)){
                    try {
                        Object object = controller.newInstance();
                        if(controller.isAnnotationPresent(RequestMapping.class)){
                            controllerMap.put(controller.getAnnotation(RequestMapping.class).value(), object);
                        }
                        //注入service实例
                        for(Field field :object.getClass().getDeclaredFields()){
                            if(serviceMap.containsKey(field.getType())){
                                field.setAccessible(true);
                                field.set(object, serviceMap.get(field.getType()));
                            }
                        }
                        
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            return controllerMap;
            
        }
    public class InitClassUtil {
        private static Map<String,Object> controllerMap;
        private static Set<Class<?>> classSet;
        public InitClassUtil(String config){
            classSet=scanClass(config);
            controllerMap=initController();
        } 
        private static Set<Class<?>> scanClass(String config){
            Properties props = PropsUtil.loadProps(config);
            return ClassUtil.getClass(props.getProperty("base-package"));
        }
        private static Map<Class<?>,Object> getService(){
            Map<Class<?>,Object> serviceMap = new HashMap<Class<?>,Object>();
            for(Class<?> service:classSet){
                if(service.isAnnotationPresent(Service.class)){
                    try {
                        serviceMap.put(service, service.newInstance());
                    } catch (InstantiationException | IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            return serviceMap;
        }
        private static Map<String,Object> initController(){
            Map<Class<?>,Object> serviceMap = getService();
            Map<String,Object> controllerMap = new HashMap<String,Object>();
            for(Class<?> controller:classSet){
                if(controller.isAnnotationPresent(Controller.class)){
                    try {
                        Object object = controller.newInstance();
                        if(controller.isAnnotationPresent(RequestMapping.class)){
                            controllerMap.put(controller.getAnnotation(RequestMapping.class).value(), object);
                        }
                        //注入service实例
                        for(Field field :object.getClass().getDeclaredFields()){
                            if(serviceMap.containsKey(field.getType())){
                                field.setAccessible(true);
                                field.set(object, serviceMap.get(field.getType()));
                            }
                        }
                        
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            return controllerMap;
            
        }
        public static Map<String, Object> getControllerMap() {
            return controllerMap;
        }
        
    }
    完成的initClassUtil类

    最后 回到DispatcherServlet,在初始化时调用这个工具类就可以了

        @Override
        public void init(ServletConfig config){
            System.out.println("初始化程序");
            new InitClassUtil(config.getInitParameter("config"));
        }

    最后在service()中,解析请求路径,并反射对应的controller类中的方法就OK了

    public class DispatcherServlet extends HttpServlet {
        @Override
        public void init(ServletConfig config){
            System.out.println("初始化程序");
            new InitClassUtil(config.getInitParameter("config"));
        }
        
        
        @Override
        public void doGet(HttpServletRequest request,HttpServletResponse response){
            doPost(request, response);
        }
        @Override
        public void doPost(HttpServletRequest req,HttpServletResponse resp){
            Object result;
            String url =req.getServletPath();
            String controllerUrl=url;
            Map<String,Object> controllerMap = InitClassUtil.getControllerMap();
            while(!controllerMap.containsKey(controllerUrl)&&!controllerUrl.equals("")){
                controllerUrl = controllerUrl.substring(0, controllerUrl.lastIndexOf('/'));
            }
            if(controllerUrl.equals("")){
                System.out.println("找不到路径对应的controller");
            }
            String methodUrl = url.substring(url.indexOf(controllerUrl)+controllerUrl.length());//获取方法路径
            Object controller = controllerMap.get(controllerUrl);
            for(Method method:controller.getClass().getDeclaredMethods()){
                if(method.isAnnotationPresent(RequestMapping.class)){
                    if(method.getAnnotation(RequestMapping.class).value().equals(methodUrl)){
                        List<Object> params = new ArrayList<Object>();
                        Enumeration<String> enumer = req.getParameterNames();
                        while(enumer.hasMoreElements()){
                            String paramName = enumer.nextElement();
                            params.add(req.getParameter(paramName));
                        }
                        try {
                            result=method.invoke(controller, params.toArray());
                            if(result instanceof ModelView){
                                ModelView modelView = (ModelView) result;
                                for(Entry<String, Object> entry:modelView.getModelMap().entrySet()){
                                    req.setAttribute(entry.getKey(),entry.getValue());
                                }
                                req.getRequestDispatcher(modelView.getView()).forward(req, resp);
                                return;
                            }
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } 
                        
                        }
                }
            }
        }
        @Override
        public void destroy(){
            System.out.println("销毁程序");
        }
    }

    完成。

    这段代码很粗糙,没有处理异常、有些判断并不完善,像扫描类时只扫描了.class 并没有Jar文件,而且解析路径反射调用的那段也应该抽象出来,而且方法中的参数赋值也必须按照顺序来,没有按照name或是@RequestParam 来注入。主要是因为太懒了,在JDK1.8中反射是可以获取方法的参数名,但是1.7获取参数名并不是很容易,Spring中获取参数名并注入其实调用了native方法(有待考证)。

    看个乐得了。

  • 相关阅读:
    mysql数据库存储的引擎和数据类型
    mysql数据库基本操作
    【转】linux yum命令详解
    [转]Linux rpm 命令参数使用详解
    【转】Linux GCC常用命令
    [转]linux下logrotate 配置和理解
    [转]Linux下chkconfig命令详解
    [转]linux之top命令
    [转]linux之ps命令
    互联网产品如何做到快与轻
  • 原文地址:https://www.cnblogs.com/tangyuanyuan/p/7778451.html
Copyright © 2011-2022 走看看