• 大家一起写mvc(二)


    上一篇已经看了,我想大家都明白了mvc的原理,今天我们来说一下要写自己mvc框架必须要会的技术。

    mvc的目录是这样的

    src目录是我们核心的mvc代码。这个代码明天讲,今天主要讲的代码都在test目录下。

    在第一章我已经说了,写mvc技术需要用的就是java的反射原理,还有自定义注解。

    现在我们先讲一下注解的用处,可能这个自定义注解听着挺高深的,等你看完这个就会明白,其实很简单,就是xml的另一种方法。

    讲解我就都放在代码的注解里了,这样写比较方便。

    annotation注解:

    package com.test;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * jdk1.5以上
     * @Target 描述注解的使用范围
     * @Retention 注解生命周期
     * @Documented 文档相关
     * @Inherited 阐述了某个被标注的类型是被继承的
     * 关于注解的详细信息可以看下面这个博客。
     * http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
     * 这篇文章写的很好,我就不重复的叨逼叨了。。
     * 在mvc里 我们就用两个注解属性
     * @Target 标注使用范围,这个我们标注为method。即这个注解我们要用在方法级别上。
     * @Retention 生命周期,即运行时,Runtime
     * @author wanglong
     *
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnno {
        public String ActionName() default "";
        public String Result() default "";
    }

    现在我们定义注解了,我们要用在哪呢,就用在Action的方法上,比如我们写一个Action

    package com.test;
    
    public class TestAction {
        
        @Anno(ActionName="test.action",Result="index.jsp")
        public void TestAnno(User user)
        {
            System.out.println(user.getUsername());
        }
    }

    这样我们就在TestAction里的TestAnno方法上加上了我们自定义的注解。

    我们怎么获得注解里的值呢,就是通过反射。下面的代码是Junit4的一个testcase。

        public void testAnno() throws Exception{
            Class clazz = Class.forName("com.test.TestAction");
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods)
            {
                if(method.isAnnotationPresent(com.test.Anno.class))
                {
                    Anno anno = method.getAnnotation(com.test.Anno.class);
                    System.out.println(anno.ActionName()); //test.action
                    System.out.println(anno.Result());     //index.jsp
                }
            }
        }

    恩,这里我们已经获取到了,大家也知道该怎么用了吧,也知道在mvc里应该用在哪里了。注解我们就讲到这里。下面我们说反射

    java反射

    我也不知道大家会不会反射。。我这也不知道说到什么程度合适,所以我就把要在mvc中用到的反射方法说一下吧。

    首页,我们有一个登录页面-login.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <form action="test.action" method="post">
        <input type="text" value="admin" name="user.userName">
        <input type="text" value="adminPassword" name="user.password">
        <input type="submit" value="登录"/>
    </form>
    </body>
    </html>

    然后我们还要有一个user类

    package com.test;
    
    public class User {
        private String username;
        private String password;
        
        public String getUsername()
        {
            return username;
        }
        public void setUsername(String username)
        {
            this.username = username;
        }
        public String getPassword()
        {
            return password;
        }
        public void setPassword(String password)
        {
            this.password = password;
        }
    }

    我们看到login.jsp请求action是test.action.里面的参数分别是user.username跟user.password,我们通过servlet拦截后,可以通过java的自定义注解也就是我们上面的Anno这个注解来获取到test.action注解的这个(TestAnno)方法,但是我们怎么把页面上的这两个参数传给action呢,所以我们就用到反射,通过反射,我们把页面上user封装,然后在通过反射来调用TestAnno方法。

    首先,我们要通过反射获取到TestAnno这个方法的参数类型。

    /*
         * 反射获取方法参数类型
         */
        @org.junit.Test
        public void testActionParamType() throws Exception{
            Class clazz = Class.forName("com.test.TestAction");
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods)
            {
                Class[] params = method.getParameterTypes();
                for (int i = 0; i < params.length; i++)
                {
                    System.out.println(method.getName());//获取的是方法的名称 也就是 TestAnno
                    System.out.println(params[i]);//方法参数类型 这里打印       class com.test.User 说明这个方法只有一个参数
                }
            }
        }

    利用jdk的反射,我们获取不到方法的参数名称,也就是public void Test(String s){...}

    现在我们能获取到String类型,但是如果想获取这个s,利用jdk的反射是获取不到的,所以我们借助第三方jar包 javassist,这个jar包是对java反射的增强。下载地址百度找吧,如果找不到,可以去这个地址找 https://jarfiles.pandaidea.com/ 这是个很不错网站 大家可以收藏~就不用到处去求包了。下面是利用javassist来获取方法参数名称。

        /**
         * 利用javassist获取方法参数名称
         * @throws Exception
         */
        @org.junit.Test
        public void testGetParam() throws Exception{
            Class clazz = Class.forName("com.test.TestAction");
            String methodName = "TestAnno";
            ClassPool pool=ClassPool.getDefault();
            CtClass ctClass = pool.get("com.test.TestAction");
            CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
            MethodInfo methodInfo = ctMethod.getMethodInfo();
            CodeAttribute codeAttr = methodInfo.getCodeAttribute();
            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttr.getAttribute(LocalVariableAttribute.tag);
            if(attr==null){
                System.out.println("没有属性");
            }
            String[] paramsName = new String[ctMethod.getParameterTypes().length];
            int pos =Modifier.isStatic(ctMethod.getModifiers())?0:1;
            for (int i = 0; i < paramsName.length; i++)
            {
                paramsName[i] = attr.variableName(i+pos);
            }
            for (int i = 0; i < paramsName.length; i++)
            {
                System.out.println(paramsName[i]);
            }
        }

    这样我们就能获取到方法所需要的一切了。然后通过反射的invoke来调用方法就可以了。下面演示一下怎么使用invoke来调用方法及传参。

    首先我们新建一个测试类。我们来调用这个类的say方法

    package com.test;
    
    public class TestInvoke {
        public void say(String i ){
            System.out.println(i);
        }
    }

    然后我们在junit测试里添加测试方法。

    /**
         * 测试方法调用
         * @throws Exception
         */
        @org.junit.Test
        public void testInvoke() throws Exception{
            Class clazz = Class.forName("com.test.TestInvoke");
            Object obj = clazz.newInstance();
            /**
             * getMethod(方法名,方法参数类型).invoke(调用类的实例,方法参数);
             */
            clazz.getMethod("say", String.class).invoke(obj, "this is a test");
        }

    在控制台会打出this is a test。

    在补充一个反射的用法。获取类的属性,属性的类型。

        /**
         * 获取类的field 和类型
         * @throws Exception
         */
        @org.junit.Test
        public void testFieldType() throws Exception
        {
            Class clazz = Class.forName("com.test.User");
            Object o = clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();
            for (Field f : fields)
            {
                System.out.println(f.getName());
                System.out.println(f.getType());
                /**
                   控制台打印:
                    username
                    class java.lang.String
                    password
                    class java.lang.String
    
                 */
            }
        }


    总结一下,通过今天的学习我们都得到了什么。

    1.通过annotation,我们可以获得注解的名称,以及其他属性,以及注解的方法

    2.通过反射:我们可以获得一个类都有哪些方法,方法的参数类型,类的属性,类属性的类型。不知道这么说大家能不能明白。下面截个图给大家看一下。

    可以看到 画方框的,我们通过反射(javassist)都可以获取到,既然我们都能获取到,那我们是不是就能封装,转发调用了呢?

    一个简单的mvc就用到这些技术,所以上面这些看懂了的话,你也能写一个mvc了。

    给大家留一个问题:

    怎么通过反射把页面传过来的参数 user 封装起来。

    如果你能封装起来,我想就能通过invoke来调用action参数了吧?

    如果不明白没问题,我后面会教大家怎么做。如果会的话,就继续做用invoke来调用action方法吧。

    提示:

    1.反射
    2.request.getParameterMap

    写这么多实在太累了。。。

    明天可能会有一更,周五至周末要去参加婚礼,所以没时间更了。。。

    希望大家把代码都自己写一遍。不要copy。能记住才是自己的。。。

    谢谢观看。。。

    ps:有多少人看?

  • 相关阅读:
    Python之图片格式转换
    pip依赖安装与记录
    Spectral Graph Theory的一些定理
    Beamer加中文
    Python之json
    Windows之建立C++开发环境
    Mysql分表教程
    null和空 not null
    yii 隐藏index.php的步骤
    yii泛域名
  • 原文地址:https://www.cnblogs.com/-10086/p/4060683.html
Copyright © 2020-2023  润新知