2021-03-27——自定义注解玩转SpringMVC

自定义注解玩转SpringMVC

目录

1、为什么使用注解

1.1 使用注解的目的

1.2 元注解

1.3 元注解释义

2 内置注解

3 在spring框架中常用注解介绍

4 自定义注解实现MVC

4.1 定义配置文件、自定义注解

4.2 controller、service

4.3 通过自定义注解实现MVC


什么是注解?

自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解。用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。比如,下面这段代码:

@Override
public String toString() {
    return "";
}

Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。

本文主要讲述,为什么要引入注解,注解是如何工作的,如何编写自定义的注解(通过例子实现MVC),让我们进入注解的世界吧。

1、为什么使用注解

假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。

1.1 使用注解的目的

  1. 简化配置
  2. 约定大于配置
  3. 易于维护
  4. 可读性强

1.2 元注解

Java5.0提供了四种特殊标注的注解,元注解:@RetentionPolicy、@Target、@Docment、@Inherited

1.3 元注解释义

@Retention  指定生命周期保留

该注解从单纯的单词来看,保留,那保留的是什么呢?保留其实是对其作用的注解类的一个生命周期或作用范围的定义,理解这个我们需要首先要理解我们编写的java原文是如何被JVM所执行的,一个java源文件要被JVM执行大概需要经过2部分 java源文件->字节码(class)-> 二进制机器码。

2021-03-27——自定义注解玩转SpringMVC_第1张图片

@Target     可被作用的目标类型

  •  TYPE: 作用为类
  •  FIELD 作用属性
  •  METHOD 作用方法
  •  PARAMETER 作用方法参数
  •  CONSTRUCTOR 作用构造方法
  •  LOCAL_VARIABEL 作用本地变量
  •  PACKAGE 作用于包
  •  TYPE_PARAMETER 1.8 提供 作用于泛型参数
  •  TYPE_USE 表示可以作用在包和方法除外的任何类型

@Inherited   是否可被子类继承

@Docment   生成Javadoc时被包含在Javadoc中

2 内置注解

2021-03-27——自定义注解玩转SpringMVC_第2张图片

@SafeVarargs-JDK7 引入

 用于告诉编译器在可变长参数中的泛型是类型安全的,可变长参数是本质上是一个数组,但是在java 中数组和泛型并不能很好的混合使用。

@FunctionalInterface-JDK 8 引入

 用于标记一个函数式接口 , 这里需要注意注解标注的接口必须满足函数式接口的定义,如果接口不符合函数式接口定义将会报错。

 什么是函数式接口?

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。

函数式接口特点:

  • o 接口有且只能有个一个抽象方法(抽象方法只有方法定义,没有方法体)。
  • o 允许有 default 实现方法。

3 在spring框架中常用注解介绍

在Spring中有很多提供了很多注解,这里给大家介绍一些常用的注解共大家来参考。

  • @Autowired 提供依赖注入的一个注解,注解可以作用在类、构造方法、方法、参数、属性。
  • @Bean可以将一个对象注册到Spring 容器中,转换为Spring 容器中的Bean可以理解最早通过XML文件中的Bean标签。
  • @Configuration 配置类注解,这个注解可以帮助我们做一些配置型的工作,比如我们想配置一个数据源,或者配置一些项目需要的变量,那么可以使用这个注解。
  • @Qualifier 依赖注入类注解,可以指定注入某一个具体的注解,比如我们现在有两个类都继承了同一个接口那么在Spring 注入的时候可以就不知道究竟要注入那个具体的类这个时候如果没有指定Spring 就会报错,所以这个时候我们可以通过@Qualifier告诉容器我们具体是需要使用那个注解。

4 自定义注解实现MVC

先说整体思路:

/**
 * 1、定义配置文件入口、包路径
 * 2、读取和加载配置文件,然后解析它
 * 3、自动扫描包路径下的类 java.lang.class
 * 4、通过反射实例化目标类型(IOC)
 * 5、设置对象之间的关联关系(DI)
 * 6、建立Url请求和API之间的映射关系(UrlhandlerMapping)
 */

4.1 定义配置文件、自定义注解

@WZWAutowried、@WZWController、@WZWRequestMapping、@WZWRequestParam、@WZWService

/**
 * @author wzw
 * 约定大于配置
 * int length();
 * String name() default "";
 * Autowired requestMapping();
 * Class eclazz();
 * Class sclazz();
 * String[] value();
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.FIELD})
// 允许子类继承父类的注解
//@Inherited
public @interface WZWAutowried {
    String value() default "";
}


@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
// 允许子类继承父类的注解
//@Inherited
public @interface WZWController {
}


@Documented
@Retention(RetentionPolicy.RUNTIME)
//@Inherited
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface WZWRequestMapping {
    String value() default "";  //约定大于配置
}


@Documented
@Retention(RetentionPolicy.RUNTIME)
//@Inherited
@Target({ElementType.PARAMETER})
public @interface WZWRequestParam {
    String value() default "";  //约定大于配置
}


@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
// 允许子类继承父类的注解
//@Inherited
public @interface WZWService {
    String value() default "";
}

配置文件:application.properties

scanPackage=com.wzw.core

4.2 controller、service

/**
 * @author wzw
 * @version 1.0
 * @description 控制器层
 * @date 2021/3/25 22:55
 */
@WZWController
@WZWRequestMapping("/wzwOrder")
public class WZWOrderController {

    @WZWAutowried
    private WZWOrderService wzwOrderService;

    @WZWRequestMapping("/getOrders")
    public void getOrders(@WZWRequestParam("wzwparam") String wzwparam){

        String str = wzwOrderService.getOrderName(wzwparam);
        System.out.println(str);

        System.out.println("成功进入-WZWOrderController");
    }
}
/**
 * @author wzw
 * @version 1.0
 * @description service
 * @date 2021/3/27 17:37
 */
public interface WZWOrderService {

    String getOrderName(String name);
}
/**
 * @author wzw
 * @version 1.0
 * @description
 * @date 2021/3/27 17:39
 */
@WZWService("wZWOrderService")
public class WZWOrderServiceImpl implements WZWOrderService {

    @Override
    public String getOrderName(String name) {
        return "My name is " + name;
    }
}

4.3 通过自定义注解实现MVC

注解测试类:WZWAnnotationTest.class

/**
 * @author wzw
 * @version 1.0
 * @description 注解测试类
 * @date 2021/3/25 23:01
 */
public class WZWAnnotationTest {

    private static Properties properties = new Properties();

    private List classNameList = new ArrayList<>();

    private Map iocContainerMap = new HashMap<>();

    //url ==  API mapping
    private Map urlHandlerContainerMap = new  HashMap<>();

    private Map controllerMap  =new HashMap<>();

    public static void main(String[] args) {
        /**
         * 1、定义配置文件入口、包路径
         * 2、读取和加载配置文件,然后解析它
         * 3、自动扫描包路径下的类 java.lang.class
         * 4、通过反射实例化目标类型(IOC)
         * 5、设置对象之间的关联关系(DI)
         * 6、建立Url请求和API之间的映射关系(UrlhandlerMapping)
         */
        WZWAnnotationTest wzwAnnotationTest = new WZWAnnotationTest();
        wzwAnnotationTest.init();

    }

    private void init() {
        // 读取和加载配置文件,然后解析它
        loadProperties();

        // 自动扫描包路径下的类
        scanPackage(properties.getProperty("scanPackage"));

        // 通过反射实例化目标类型(IOC)
        buildInstance(properties.getProperty("scanPackage"));

        // 设置对象之间的关联关系(DI)
        handlerMapping();

        // 建立Url请求和API之间的映射关系(UrlhandlerMapping)
        urlHandlerMapping();

        // 调用url测试
        methodRequest();

    }

    private void loadProperties() {
        FileInputStream fileInputStream = null;
        try {
            /**
             * 不同环境下有不同的路径:
             * 1.local C:\Users
             * 2.Server Tomcat Webapp
             */
            fileInputStream = new FileInputStream("D:\\target\\classes\\application.properties");

            // Tomcat server directory
//            this.getClass().getClassLoader().getResource("/").getPath();
//            this.getClass().getClassLoader().getResourceAsStream("application.properties");

            properties.load(fileInputStream);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fileInputStream) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private void scanPackage(String packagePath) {
        System.out.println(packagePath);

        URL url = this.getClass().getClassLoader().getResource(""+packagePath.replaceAll("\\.", "/"));
        String urlPath = url.getPath();
//        System.out.println(urlPath);
        try {
//            urlPath = url.getPath().replaceAll("%20", " ");
            urlPath = URLDecoder.decode(urlPath, "utf-8");
            System.out.println(urlPath);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        File file = new File(urlPath);
        for (File f : file.listFiles()) {
            if (f.isDirectory()) {
                scanPackage(packagePath + "." + f.getName());
            }
            else {
                String fName = f.getName();
                System.out.println(fName);

                String className = packagePath + "." + fName;
                classNameList.add(className);
            }
        }
    }

    private void buildInstance(String packagePath) {
        if (classNameList.isEmpty()) {
            return;
        }
        try {
            for (String clazzName : classNameList) {
                clazzName = clazzName.replaceAll(".class", "");
                System.out.println(clazzName);
                Class clazz = Class.forName(clazzName);
                // com.nx.qiuping.vip.core.wzwcontroller.WZWOrderController
                // com.nx.qiuping.vip.core.wzwcontroller.WZWOrderController@

                if (clazz.isAnnotationPresent(WZWController.class)
                        || clazz.isAnnotationPresent(WZWService.class)) {
                    iocContainerMap.put(clazzName, clazz.newInstance());
                    System.out.println("get the Annotation and is WZWController");
                } else {
                    System.out.println(clazz.getName());
                }

                /**
                 * 自定义注解 反射的扫描;
                 * spring mvc 自定义注解
                 * 继续:
                 * 1.class.getMethods  getDeclaredMethods()
                 * 2.for methods
                 * 3.get annotation and value
                 * 4.build url and method relationship
                 */

                Annotation[] annotations = clazz.getAnnotations();
                for (Annotation annotation : annotations) {

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handlerMapping() {
        System.out.println("进入:handlerMapping()");
        for (HashMap.Entry map : iocContainerMap.entrySet()) {
            Class aClass = map.getValue().getClass();
            Field[] fields = aClass.getDeclaredFields();
            try {
                for (Field field : fields) {
                    if (field.isAnnotationPresent(WZWAutowried.class)) {
                        String className = field.getType().getName();
                        Object autoBean = interfaceImpl(className, StringUtils.capitalize(field.getName()));
                        field.setAccessible(true);
                        field.set(map.getValue(), autoBean);
                        System.out.println("依赖注入:" + autoBean.getClass().getName());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    public void urlHandlerMapping() {
        // 完成url和方法的调用的映射
        for (HashMap.Entry map : iocContainerMap.entrySet()) {
            Class aClass = map.getValue().getClass();
            String mappingUrl = "";
            WZWRequestMapping annotation = aClass.getAnnotation(WZWRequestMapping.class);
            if (null != annotation) {
                mappingUrl = annotation.value();
            } else {
                continue;
            }
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(WZWRequestMapping.class)) {
                    WZWRequestMapping methodMapping = method.getAnnotation(WZWRequestMapping.class);
                    mappingUrl += methodMapping.value();
                    String realMappingUrl = mappingUrl.replaceAll("//", "/");
                    System.out.println("url:" + realMappingUrl);
                    urlHandlerContainerMap.put(realMappingUrl, method);
                }
            }
        }
    }

    public void methodRequest() {
        for (HashMap.Entry method : urlHandlerContainerMap.entrySet()) {
            Method m = method.getValue();
            //方法声明类
            Object object = iocContainerMap.get(m.getDeclaringClass().getName());
            //参数
            Object[] params = new Object[m.getParameterCount()];
            //参数注解
            Annotation[][] parameterAnnotations = m.getParameterAnnotations();
            try {
                m.invoke(object, params);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取指定接口的实现类
     */
    public Object interfaceImpl(String interfaceName, String implName) throws Exception {
        System.out.println("进入:interfaceImpl," + interfaceName + " , " +implName);
        Class interfaceClass = Class.forName(interfaceName);
        if (!interfaceClass.isInterface()) {
            return iocContainerMap.get(interfaceName);
        }
        for (HashMap.Entry map : iocContainerMap.entrySet()) {
            Object value = map.getValue();
            String simpleName = value.getClass().getSimpleName();
//            if (interfaceClass.isAssignableFrom(value.getClass()) && simpleName.equals(implName)) {
            if (interfaceClass.isAssignableFrom(value.getClass())) {
                return value;
            }
        }
        throw new Exception("class not fund");
    }

}

执行main方法,输出结果如下,到此自定义注解实现MVC完成。

url:/wzwOrder/getOrders
My name is null
成功进入-WZWOrderController

 

 

 

 

 

你可能感兴趣的:(进阶之路,java,spring)