框架基础-反射、代理、注解

文章目录

  • 框架基本原理
        • 前言
      • 一、反射
        • 1.1 概念
        • 1.2 Java的反射机制
            • FAQ:
        • 1.3 java类类型
            • FAQ:
        • 1.4 反射的主要功能及操作
          • 1.4.1运行时构造一个类的实例对象
          • 1.4.2运行时获取一个类的成员变量
          • 1.4.3运行时获取一个类的成员方法并且调用它
          • 1.4.4运行时获取一个类的注解
      • 二、动态代理
        • 2.1 代理模式
        • 2.2 静态代理
          • 2.2.1简单静态代理实现
          • 2.2.2反射实现静态代理
          • 2.2.3静态代理优缺点
        • 2.3 动态代理
          • 2.3.1动态代理的优点
          • 2.3.2动态代理实现
        • 2.4 动态代理源码分析
      • 三、注解
        • 3.1 概念
        • 3.2 注解的定义
        • 3.3 元注解
          • 3.3.1@Target
          • 3.3.2@Inherited
          • 3.3.3@Repeatable
        • 3.4 注解属性
          • 3.4.1注解的本质
          • 3.4.2注解属性类型
          • 3.4.3注解属性赋值
          • 3.4.5注解的获取
        • 3.5 jdk提供的注解
        • 3.6 注解的应用
      • 四、自己动手实现简单的MVC框架
        • 4.1 自定义注解
        • 4.2 自定义DispatcherServlet
        • 4.3 配置web.xml文件
        • 4.4 测试

框架基本原理

前言

我们使用的框架底层实现原理都脱离不了java的高级特性,反射、注解,还有一个比较重要的设计模式:动态代理。今天就给大家分享下我自己对这些概念的理解。

一、反射

1.1 概念

反射是指程序可以访问、检测、修改它本身状态或行为的一种能力。

1.2 Java的反射机制

java反射机制是指在程序运行状态中,给定任意一个类,都可以获取到这个类的属性和方法;给定任意一个对象都可以调用这个对象的属性和方法,这种动态获取类的信息和调用对象的方法的功能称之为java的反射。

简言之:反射机制可以让你在程序运行时,拿到任意一个类的属性和方法并调用它。

FAQ:
  1. 为什么是在运行时?
  • 编译期:检查是否有语法错误,如果没有就将其翻译成字节码文件。即.class文件。

  • 运行时:java虚拟机分配内存,解释执行字节码文件。

    反射是通过.class文件来获取类的属性及方法的。

1.3 java类类型

想要理解反射需要知道Class这个类,它的全称java.lang.Class类。

java是面向对象的语言,讲究万物皆对象,即使强大到一个类,它依然是另一个类(Class类)的对象,简言之:普通类是Class类的对象,Class类是所有类的类。(代码清单-javase.reflect.chapter1_3_1)

  • 普通对象的创建
// 普通对象的创建
ReflectService reflectService = new ReflectService();
  • Class类的创建

查看Class类的源码如下图(1.1),可以看到它的构造函数私有化了,所有我们不能用new的方式创建:

图1.1:
框架基础-反射、代理、注解_第1张图片

测试代码:

// 通过对象的getClass()创建
Class aClass1 = reflectService.getClass();
// 通过类的.class创建
Class aClass2 = ReflectService.class;
// 通过Class的静态方法Class.forName创建
Class aClass3 = Class.forName("reflect.ReflectService");
if (aClass1 == aClass2) {
            System.out.println("aClass1和aClass2是同一个类");
        }
if (aClass2 == aClass3) {
    System.out.println("aClass2和aClass3是同一个类");
}

输出结果:

aClass1和aClass2是同一个类
aClass2和aClass3是同一个类

FAQ:
  1. Class类对象是否是单例的?(代码清单-javase.reflect.chapter1_3_2)

    是单列的,在生成Class对象的时候,首先判断内存中是否已经加载。

    当我们编写一个新的Java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象

1.4 反射的主要功能及操作

1.4.1运行时构造一个类的实例对象
  • 通过1.3的方法先构造类对象,然后再通过类对象的newInstance()方法创建实例对象。(代码清单-javase.chapter1_4_1)

测试代码:

Class<?> aClass1 = Class.forName("reflect.ReflectService");
ReflectService instance = (ReflectService) aClass1.newInstance();
instance.sayHello("zhangsan");

输出结果:

hello zhangsan

  • 通过获取类的构造函数来创建对象(代码清单-javase.reflect.chapter1_4_2)

类的构造函数是java.lang.reflect.Constructor类的对象,通过Class的下列方法可以获取构造函数对象:

// 获得该类所有的构造器,不包括其父类的构造器,但包括私有的构造器,传参可以获得指定参数的构造器
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
// 获得该类所有public构造器,包括父类传参可以获得指定参数的构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes) 

测试代码:

Class<?> aClass1 = Class.forName("reflect.Person");
// 获取该类所有的public构造函数包括父类
Constructor<?>[] allConstructor = aClass1.getConstructors();
// 获取该类指定参数是string的构造函数
Constructor<?> publicConstructor = aClass1.getConstructor(String.class);
// 获取该类指定参数是string,int的私有构造函数
Constructor<?> privateConstructor = aClass1.getDeclaredConstructor(String.class, int.class);
for (Constructor<?> constructor : allConstructor) {
    System.out.println(constructor);
}
// 设置是否可访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不用设置
privateConstructor.setAccessible(true);
// 使用反射创建Person类的对象,并传入参数
Object instance = privateConstructor.newInstance("zhangsan", 30);
System.out.println(instance);

输出结果:

public reflect.Person(java.lang.String)
public reflect.Person()
Person{name=‘zhangsan’, age=30}

1.4.2运行时获取一个类的成员变量

类的成员变量是java.lang.reflect.Field类的对象,通过Class类的以下方法可以获取某个类的成员变量,值得一提的是变量是包含两部分的,变量类型和变量名

// 获得该类自身声明的所有变量,不包括其父类的变量,但包括私有的
public Field getDeclaredField(String name) 
// 获得该类自所有的public成员变量,包括其父类变量
public Field getField(String name) 

测试代码:(代码清单-javase.reflect.chapter1_4_3)

Class<?> personClass = Class.forName("reflect.Person");
// 获取该类的所有属性
Field[] allFields = personClass.getDeclaredFields();
// 获取该类的所有public属性包括其父类
Field[] publicFields = personClass.getFields();
// 获取该类指定名称的私有属性
Field nameField = personClass.getDeclaredField("name");
// 获取该类指定名称的公共属性
Field descField = personClass.getField("desc");
for (Field field : allFields) {
    System.out.println(field);
}
// 获取属性的值
Constructor<?> constructor = personClass.getConstructor(String.class);
Person instance = (Person) constructor.newInstance("lisi");
// 设置读取私有变量权限
nameField.setAccessible(true);
Object value = nameField.get(instance);
System.out.println(value);

输出结果:

private java.lang.String reflect.Person.name
private int reflect.Person.age
public java.lang.String reflect.Person.desc
lisi

1.4.3运行时获取一个类的成员方法并且调用它

类的成员方法是java.lang.reflect.Method的对象,通过java.lang.Class类的以下方法可以获取到类的成员方法,通过方法类Method提供的一些方法,又可以调用获取到的成员方法。

// 得到该类所有的方法,不包括父类的,但包括私有的
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
// 得到该类所有的public方法,包括父类的
public Method getMethod(String name, Class<?>... parameterTypes) 

测试代码:(代码清单-javase.reflect.chapter1_4_4)

Class<Person> personClass = Person.class;
// 获取该类的所有方法
Method[] methods = personClass.getDeclaredMethods();
// 获取该类的所有public方法包括其父类
Method[] publicMethods = personClass.getMethods();
// 获取该类指定名称的私有方法
Method sayMethod = personClass.getDeclaredMethod("say");
// 获取该类指定名称的公共方法
Method setNameMethod = personClass.getMethod("setName", String.class);
for (Method method : methods) {
    System.out.println(method);
}
// 方法的调用
Person person = personClass.newInstance();
// 私有方法调用先设置权限
sayMethod.setAccessible(true);
sayMethod.invoke(person);
// 调用方法需要传入反射生成的实例对象,参数(参数可以是多个,按顺序)
setNameMethod.invoke(person, "wangwu");
System.out.println(person);

输出结果:

public java.lang.String reflect.Person.toString()
public java.lang.String reflect.Person.getName()
public void reflect.Person.setName(java.lang.String)
private void reflect.Person.say()
public java.lang.String reflect.Person.getDesc()
public void reflect.Person.setAge(int)
public void reflect.Person.setDesc(java.lang.String)
public int reflect.Person.getAge()
我是私有方法
Person{name=‘wangwu’, age=0, desc=‘null’}

1.4.4运行时获取一个类的注解

注解又称 Java 标注,是 JDK5.0 引入的一种注释机制。通过反射获取到上面元素,然后再通过其获取它们各自的注解。(类、属性、方法和构造函数上都可以获取各自的注解)

// 得到该元素所有注解,如果是类的话不包括父类的
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
// 得到该元素所有注解,如果是类的话包括父类的
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) 

测试代码:(代码清单-javase.reflect.chapter1_4_5)

Class<Person> personClass = Person.class;
// 获取该类的所有注解
 Annotation[] annotations = personClass.getDeclaredAnnotations();
Field nameField = personClass.getDeclaredField("name");
// 获取方法上的所有注解
Annotation[] fieldAnnotations = nameField.getAnnotations();
for (Annotation annotation : annotations) {
    // 获取注解上的属性
    if (annotation.annotationType().equals(MyInterface.class)) {
        MyInterface myInterface = (MyInterface) annotation;
        System.out.println(myInterface.name());
    }
    System.out.println(annotation);
}

Class其他的一些方法:

方法名称 返回值 备注
getGenericSuperclass Type 获取class对象的直接超类的
getGenericInterfaces Type[] 获取class对象的所有接口的type集合
isPrimitive boolean 判断是否是基础类型
isArray boolean 判断是否是集合类
isAnnotation boolean 判断是否是注解类
isInterface boolean 判断是否是接口类
isEnum boolean 判断是否是枚举类
isAnonymousClass boolean 判断是否是匿名内部类
isAnnotationPresent(Deprecated.class) boolean 判断是否被某个注解类修饰(参数为注解类型)
getName String 获取class名字 包含包名路径
getPackage Package 获取class的包信息
getSimpleName String 获取class类名
getModifiers int 获取class访问权限(如图1.2)
getDeclaredClasses Class[] 获取内部类
getDeclaringClass Class 获取外部类
getClassLoader ClassLoader 获取类加载器
getSuperclass Class[] 获取某类所有的父类
getInterfaces Class[] 获取某类所有实现的接口
  • Class类实现了Type接口

图1.2:

框架基础-反射、代理、注解_第2张图片

二、动态代理

2.1 代理模式

为其他对象提供一个代理对象以控制对该对象的操作。

代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。

实现方式:创建接口 > 实现接口 > 创建代理类

2.2 静态代理

如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理。具有下面几种特性:

  • 由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。
  • 静态代理类通常只代理一个类。
  • 静态代理事先知道要代理的是什么。
2.2.1简单静态代理实现

代码如下:

1、创建一个开车接口:

public interface Drive{
    /**
     * 开车
     */
    void drive();
}

2、实现接口:

public class WhiteCar implements Drive{
    @Override
    public void drive() {
        System.out.println("今天开了一辆白色的车");
    }
}

3、创建代理类:

public class DriveProxy implements Drive{
    private Drive drive;

    public DriveProxy(Drive drive) {
        this.drive = drive;
    }

    @Override
    public void drive() {
        // 执行前
        System.out.println("将喝醉的你扶上车");
        drive.drive();
        // 执行后
        System.out.println("把你送到楼上");
    }
}

测试代码:(代码清单-javase.proxy.chapter2_1)

/**
 * 简单静态代理实现
 */
@Test
public void chapter2_1() {
    Drive proxy1 = new DriveProxy(new WhiteCar());
    Drive proxy2 = new DriveProxy(new RedCar());
    proxy1.drive();
    proxy2.drive();
}

输出结果:

将喝醉的你扶上车
今天开了一辆白色的车
把你送到楼上

将喝醉的你扶上车
今天开了一辆红色的车
把你送到楼上

2.2.2反射实现静态代理

代码如下:

1、创建一个工厂接口:

public interface Factory{
    /**
     * 工厂销售货物
     * 
     * @param num 数量
     *            
     * @return 总金额
     */
    int sale(int num, int price) throws Exception;

    /**
     * 厂家通知
     * @throws Exception
     */
    void active() throws Exception;
}

2、实现接口:

public class AppleFactory implements Factory{

    @Override
    public int sale(int num, int price) {
        System.out.println("苹果厂家卖给了代理商 " + num + " 个苹果,总计" + num * price + "元");
        return num * price;
    }

    @Override
    public void active() {
        System.out.println("厂家通知:近期苹果要降价了");
    }
}

3、创建代理类:

public class FactoryProxy implements Factory{

    private Factory factory;
    // 接口中的方法
    private Method saleMethod;
    private Method activeMethod;

    public FactoryProxy(Factory factory) throws Exception {
        this.factory = factory;
        // 获取Factory的class对象
        Class<Factory> aclass = (Class<Factory>) factory.getClass();
        // 获取类所有的方法
        Method[] methods = aclass.getDeclaredMethods();
        for (Method method : methods) {
            if ("sale".equals(method.getName())) {
                saleMethod = method;
            }
            if ("active".equals(method.getName())) {
                activeMethod = method;
            }
        }
    }

    @Override
    public int sale(int num, int price) throws Exception {
        // 调用前
        System.out.println("顾客给了 " + num * price + "元,想要买" + num + "个苹果");
        // 修改入参调用
        int result = (int) saleMethod.invoke(factory, num, price - 5);
        // 调用后
        System.out.println("代理商调整苹果价格,并通知顾客:");
        // 修改返回值
        return result + 125;
    }

    @Override
    public void active() throws Exception {
        activeMethod.invoke(factory);
    }
}

测试代码:(代码清单-javase.proxy.chapter2_2)

/**
 * 反射静态代理实现
 */
@Test
public void chapter2_2() throws Exception {
    Factory proxy = new FactoryProxy(new AppleFactory());
    int money = proxy.sale(5, 20);
    System.out.println("苹果涨价了,总共需要"+money+"元");
    proxy.active();
}

输出结果:

顾客给了 100元,想要买5个苹果
苹果厂家卖给了代理商 5 个苹果,总计75元
代理商调整苹果价格,并通知顾客:
苹果涨价了,总共需要200元
厂家通知:近期苹果要降价了

2.2.3静态代理优缺点

使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。这个时使用动态代理就是最好的解决方案。

2.3 动态代理

顾名思义,动态代理是利用反射机制在运行时在内存中动态的创建代理类。

这里我们用jdk的动态代理来实现。通常具有一下特性:

  • 在程序运行时,通过反射机制动态生成
  • 动态代理类通常代理接口下的所有类
  • 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定
  • 动态代理的调用处理程序必须实现InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类
  • Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库
2.3.1动态代理的优点

Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法也可以)。此外,也可以减少代码量,如果采用静态代理,类的方法比较多的时候,得手写大量代码。

2.3.2动态代理实现

在 java 的java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口可以生成 JDK 动态代理类和动态代理对象。

创建jdk代理的处理程序类:

public class JDKProxy implements InvocationHandler{
    /**
     * 服务对象
     */
    private Object target;

    public JDKProxy(Object target) {
        this.target = target;
    }

     /**
     * 增强方法,用来处理需要增强的逻辑
     * 
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是jdk动态代理");
        Object result = null;
        // 反射方法调用之前
        if (null != args && args.length > 0) {
            System.out.println("顾客给了 " + (int) args[0] * (int) args[1] + "元,想要买" + args[0] + "个苹果");
            // 更改参数
            args[1] = (int) args[1] - 5;
        }
        // 执行方法
        result = method.invoke(target, args);
        // 反射方法调用之后
        System.out.println("代理商调整苹果价格,并通知顾客:");
        if (null != result) {
            return (int) result + 125;
        }
        return result;
    }

}

测试代码:(代码清单-javase.proxy.chapter3_1)

@Test
public void chapter3_1() throws Exception {
    Factory apple = new AppleFactory();
    JDKProxy jdkProxy = new JDKProxy(apple);
    Factory appleProxy = (Factory) Proxy.newProxyInstance(apple.getClass().getClassLoader(),
            apple.getClass().getInterfaces(), jdkProxy);
    int money = appleProxy.sale(5, 20);
    System.out.println("苹果涨价了,总共需要" + money + "元");
	appleProxy.active();
}

输出结果:

我是jdk动态代理
顾客给了 100元,想要买5个苹果
苹果厂家卖给了代理商 5 个苹果,总计75元
代理商调整苹果价格,并通知顾客:
苹果涨价了,总共需要200元
我是jdk动态代理
厂家通知:近期苹果要降价了
代理商调整苹果价格,并通知顾客:

2.4 动态代理源码分析

我们带着结果来看源码,我们知道动态代理生成的代理类是由jvm在内存中拼装并生成的,那我们可以拿到这个生成的代理类吗?当然是可以的,看下面的代码我们把jvm生成的代理类保存在D盘中:

/**
 * 将生成的动态代理类保存到磁盘
 * 
 * @throws Exception
 */
@Test
public void generateDynamicClassFileToDisk() throws Exception {
    byte[] bytes = ProxyGenerator.generateProxyClass("appleClass", AppleFactory.class.getInterfaces());
    FileOutputStream out = null;
    try {
        out = new FileOutputStream("D:/appleClass.class");
        out.write(bytes);
        out.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

然后用idea打开,会自动反编译出来,如下:

public final class appleClass extends Proxy implements Factory {
    private static Method m1;
    private static Method m2;
    //接口中的sale方法
    private static Method m3;
    //接口中的active方法
    private static Method m4;
    private static Method m0;

    // 构造方法,参数为InvocationHandler,就是我们写的jdk代理实现的接口
    public appleClass(InvocationHandler var1) throws  {
        super(var1);
    }

    // 接口中的sale方法
    public final int sale(int var1, int var2) throws Exception {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (Exception | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }
    
	// 接口中的active方法
    public final void active() throws Exception {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (Exception | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
	// 默认会重写equals、hashCode、toString方法,也就是说这三个方法也被代理了
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    // 静态代码块,初始化各个Method
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("proxy.staticproxy.Factory").getMethod("sale", Integer.TYPE, Integer.TYPE);
            m4 = Class.forName("proxy.staticproxy.Factory").getMethod("active");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这个类就是我们生成的动态代理类,看起来跟我们静态代理自己写的类是不是很相似。我们点击Proxy.newProxyInstance来查看源码:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    // 校验参数InvocationHandler对象是否为null
    Objects.requireNonNull(h);
    // 克隆被代理对象所有接口
    final Class<?>[] intfs = interfaces.clone();
    // 生成代理类之前检查访问权限,SecurityManager是java安全管理器
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * 先从缓存查找是否存在这个类,如果不存在再生成(最重要的方法)
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            // 检查代理类的访问权限
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        // 通过反射生成构造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        // 如果Class作用域为私有,通过 setAccessible 支持访问
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // 通过Proxy Class构造函数生成Proxy代理类实例对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

利用getProxyClass0(loader, intfs)生成代理类Proxy的Class对象。

/**
 * Generate a proxy class.  Must call the checkProxyAccess method
 * to perform permission checks before calling this.
 */
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    //如果接口数量大于65535,抛出非法参数错误
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    //如果指定接口的代理类已经存在与缓存中,则不用新创建,直接从缓存中取即可;
    //如果缓存中没有指定代理对象,则通过ProxyClassFactory来创建一个代理对象。
    return proxyClassCache.get(loader, interfaces);
}

ProxyClassFactory内部类创建、定义代理类,返回给定ClassLoader 和interfaces的代理类。

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // 代理类的名字的前缀统一为“$Proxy”
    private static final String proxyClassNamePrefix = "$Proxy";

    // 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            /*
             * 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * 验证这个Class对象是不是接口
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * 校验接口是否重复
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }
 		// 代理类所在的包
        String proxyPkg = null;    
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * 记录非公共代理接口的包,以便代理类将在同一个包中定义。
         * 验证所有非公共代理接口都在同一个包中。
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * 生成代理类的名称
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * 生成指定代理类的字节码文件
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // defineClass0是本地方法
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

到此动态代理源码解析完毕,运行流程如下图:

框架基础-反射、代理、注解_第3张图片

三、注解

3.1 概念

引用java官方文档的一句话:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。

通俗将注解就是为这个元素(类、字段、方法等)增加的说明或功能。例如:@Overvide这个注解就用来说明这个方式重写父类的。

接下我将从注解的定义、元注解、注解属性、自定义注解、注解解析及JDK 提供的注解这几个方面深入了解注解

3.2 注解的定义

日常开发中新建Java类,我们使用class、interface比较多,而注解和它们一样,也是一种类的类型,他是用的修饰符为 @interface

如图:新建注解

框架基础-反射、代理、注解_第4张图片

注解的格式:public @interface MyTestAnnotation {}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    public int id();
    public String description() default "no description";
}

这样一个注解就定义好了,此时我们还不能使用它,我们还需要给它定义一些元注解。

3.3 元注解

什么是元注解呢?我们可以理解为注解的注解,即修饰注解的注解。用来给予我们自定义的注解一些规则。

Java提供五种元注解,分别是@Target、@Retention、@Document、@Inherited和@Repeatable(jdk1.8加入)。主要作用如下:

名称 作用 属性值 取值
@Target 标识注解用于什么地方 ElementType TYPE:作用接口、类、枚举、注解
FIELD:作用属性字段、枚举的常量
METHOD:作用方法
PARAMETER:作用方法参数
CONSTRUCTOR:作用构造函数
ANNOTATION_TYPE:作用于注解
TYPE_PARAMETER:作用于类型泛型,即泛型方法、泛型类、泛型接口
TYPE_USE:类型使用.可以用于标注任意类型除了class
@Retention 表示在什么级别保留注解 RetentionPolicy SOURCE:注解仅存在于源码中,在class字节码文件中不包含
CLASS:默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
RUNTIME:注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented 将注解中的元素包含到 Javadoc 中去
@Inherited 允许子类继承父类注解
@Repeatable 注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。 另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解

自定义注解的@Retention取值必须是RetentionPolicy.RUNTIME

3.3.1@Target

@Target注解可以包含多个取值,常用的TYPE、FIELD、METHOD、PARAMETER,下面的例子,定义一个注解并指定使用范围:

@Target({ElementType.METHOD,ElementType.TYPE}) // 可以使用在类上、方法上
@Retention(RetentionPolicy.RUNTIME) // 存在于运行时
@Inherited // 允许子类继承
public @interface UseCase {
    public int id();

    public String description() default "no description";
}
3.3.2@Inherited

定义一个注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InheritanceTest {
}

创建一个接口并使用自定义注解,创建一个继承类

@InheritanceTest
public class Parent {
}
public class Son extends Parent {
}

测试代码:(代码清单-javase.annotation.chapter3_1)

/**
 * 获取父类的注解
 */
@Test
public void chapter3_1() {
    Class<Son> sonClass = Son.class;
    InheritanceTest annotation = sonClass.getAnnotation(InheritanceTest.class);
    System.out.println(annotation);
}

输出结果:成功获取到了父类的注解

@annotation.inheritance.InheritanceTest()

3.3.3@Repeatable

定义@Value注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
    String value() default "value";
}

定义@Values注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
    Value[] value();
}

定义一个测试类,在其方法上加上相同的注解@Value

public class AnnotationClass{

    @Value("hello")
    @Value("world")
    public void test() {
        System.out.println("注解测试");
    }
}

测试代码:(代码清单-javase.annotation.chapter3_2)

/**
 * @Repeatabl测试用例
 */
@Test
public void chapter3_2() {
    Class<AnnotationClass> aClass = AnnotationClass.class;
    Method[] methods = aClass.getMethods();
    for (Method method : methods) {
        if ("test".equals(method.getName())) {
            Annotation[] annotations = method.getDeclaredAnnotations();
            System.out.println(annotations.length);
            System.out.println(method.getName() + "的注解为:" + Arrays.toString(annotations));
        }
    }
}

因为test方法上使用了两个@Value注解,所以猜测打印注解长度为2,然后打印详情,可是结果并不同。

输出结果:

1
test的注解为:[@annotation.repeatable.Values(value=[@annotation.repeatable.Value(value=hello), @annotation.repeatable.Value(value=world)])]

结果显示,test方法上的注解长度为 1 , 且打印信息为@Values注解,它的值包含了使用的两个注解。
因此可知在jdk8中,相同注解只是以集合的方式进行了保存,原理并没有变化。

注意事项:

  • @Repeatable所声明的注解,其元注解@Target的使用范围要比@Repeatable的值声明的注解中的@Target的范围要大或相同,否则编译器错误,显示@Repeatable值所声明的注解的元注解@Target不是@Repeatable声明的注解的@Target的子集。
  • @Repeatable注解声明的注解的元注解@Retention的周期要比@Repeatable的值指向的注解的@Retention得周期要小或相同。

3.4 注解属性

注解的属性其实和类中定义的变量有异曲同工之处,只是注解中的变量都是成员变量(属性),并且注解中是没有方法的,只有成员变量。

3.4.1注解的本质

注解的本质就是一个Annotation接口

/**Annotation接口源码*/
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    Class<? extends Annotation> annotationType();
}

通过以上源码,我们知道注解本身就是Annotation接口的子接口,也就是说注解中其实是可以有属性和方法,但是接口中的属性都是static final的,对于注解来说没什么意义,而我们定义接口的方法就相当于注解的属性,其实他就是接口的方法,这就是为什么注解的成员变量会有括号,不同于接口我们可以在注解的括号中给注解的成员变量赋值。

3.4.2注解属性类型

注解属性类型可以是以下面列出的类型:

序号 类型
1 String
2 枚举类型
3 注解类型
4 Class类型
5 基本数据类型
6 以上类型的一维数组类型
3.4.3注解属性赋值

如果注解只要一个属性,且属性名是value(),我们直接在注解后面的括号中直接给出该属性的值即可,如果是多个值,使用key = value的格式用逗号隔开即可。

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
    String name() default "mao";
    int age() default 18;
}

@MyTestAnnotation(name = "father",age = 50)
public class Father {
}
3.4.5注解的获取

当然是使用反射来获取了,这里再强调一下,自定义注解的@Retention取值必须是RetentionPolicy.RUNTIME。获取注解的3个主要方法:

 /**是否存在对应 Annotation 对象*/
  public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return GenericDeclaration.super.isAnnotationPresent(annotationClass);
    }

 /**获取 Annotation 对象*/
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        Objects.requireNonNull(annotationClass);

        return (A) annotationData().annotations.get(annotationClass);
    }
 /**获取所有 Annotation 对象数组*/   
 public Annotation[] getAnnotations() {
        return AnnotationParser.toArray(annotationData().annotations);
    }    

3.5 jdk提供的注解

注解 作用 备注
@Override 它是用来描述当前方法是一个重写的方法,在编译阶段对方法进行检查 jdk1.5中它只能描述继承中的重写,jdk1.6中它可以描述接口实现的重写,也能描述类的继承的重写
@Deprecated 它是用于描述当前方法是一个过时的方法
@SuppressWarnings 对程序中的警告去除

3.6 注解的应用

经过我们前面的了解,注解其实是个很方便的东西,它存活的时间,作用的区域都可以由你方便设置,只是你用注解可以做什么?

简单的举个例子,我们使用注解做一个参数校验,定义一个校验对象字段不为null的注解,如果为null就排除异常。

定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull {
    String value() default "";
}

创建一个house对象:

public class House{

    private String address;
    private Double area;
    @NotNull("拥有者不能为空")
    private String owner;

定义校验器:

/**
 * 功能描述:校验参数类
 *
 * @author baixufeng
 * @create 2020/7/14
 */
public class Validator{

    public static void validate(Object o) {
        Class<?> aClass = o.getClass();
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = null;
            try {
                value = field.get(o);
            } catch (Exception e) {
                e.printStackTrace();
            }
            NotNull annotation = field.getAnnotation(NotNull.class);
            if (annotation != null && null == value) {
                throw new RuntimeException(annotation.value());
            }
        }
    }
}

测试代码:(代码清单-javase.annotation.chapter3_3)

/**
 * 参数校验
 */
@Test
public void chapter3_3() {
    House house = new House();
    house.setArea(110.2);
    // house.setAddress("南京市玄武区");
    house.setOwner("正大天晴");
    Validator.validate(house);
}

我们把设置地址注释了,输出结果:

java.lang.RuntimeException: 地址不能为空

四、自己动手实现简单的MVC框架

我们先创建一个普通web项目,而不是spring,也不导入任何spring依赖,并创建项目目录,如下:

框架基础-反射、代理、注解_第5张图片
框架基础-反射、代理、注解_第6张图片

pom文件:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
</dependencies>

工程目录:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PxtscnBp-1595291796148)(C:\Users\smilevers\AppData\Roaming\Typora\typora-user-images\image-20200715111934027.png)]

4.1 自定义注解

这里我们创建5个注解,功能跟名称与spring保持一致

// Controller层
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
    String value() ;
}
// Service层
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Service {
    String value() ;
}
// 持久层
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Repostory {
    String value();
}
// 对象根据名称自动注入
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Qualifier {
    String value() ;
}
// 映射路径
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface RequsestMapping {
    String value();
}

4.2 自定义DispatcherServlet

在servlet包下创建一个DispatcherServlet类继承HttpServlet,并重写它的三个方法,init()、doGet()、doPost()。这三个方法的作用分别是,init容器初始化的时候执行一次,doGet浏览器使用get请求的时候运行,doPost浏览器使用post请求的时候运行,doGet和doPost相当于Servlet的service方法,浏览器每次请求都会运行一次。

public class DispatcherServlet extends HttpServlet{
    // 扫描的基包
    private String basePackage;
    // 基包下所有带包路径的全限定类名
    private List<String> packageNames = new ArrayList<String>();
    // 注解实例化,注解上的value,实例化注解对象
    private Map<String, Object> instanceMap = new HashMap<String, Object>();
    // 带包路径的全限定类名,注解上的名称
    private Map<String, String> nameMap = new HashMap<String, String>();
    // url和方法的映射关系
    private Map<String, Method> urlMethodMap = new HashMap<String, Method>();
    // method和全限定名类名映射关系,主要为了通过method找到该方法的对象利用反射执行
    private Map<Method, String> methodPackageMap = new HashMap<Method, String>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        basePackage = config.getInitParameter("base-package");
        try {
            // 1、扫描基包得到全部的带包路径的全限定名
            scanBasePackage(basePackage);
            // 2、把带有注解的类实例化放入map中,key为注解上的value
            instance(packageNames);
            // 3、自动注入标注了@Autowired注解的对象
            springioc();
            // 4、url地址与方法的映射关系
            urlhandler();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = uri.replaceAll(contextPath, "");
        // 通过path找到method
        Method method = urlMethodMap.get(path);
        if (null != method) {
            // 通过method拿到controller对象,准备运行反射
            String packageName = methodPackageMap.get(method);
            String controllerName = nameMap.get(packageName);

            Object contoller = instanceMap.get(controllerName);
            method.setAccessible(true);
            PrintWriter writer = null;
            try {
                Object result = method.invoke(contoller);
                // 解析结果,并返回给浏览器
                resp.setContentType("text/html;charset=utf-8");
                writer = resp.getWriter();
                writer.write(result.toString());
                writer.flush();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 方法和url的映射 方法和类名的映射
     * 
     * @throws ClassNotFoundException
     */
    private void urlhandler() throws ClassNotFoundException {
        if (packageNames.size() < 1) {
            return;
        }
        for (String name : packageNames) {
            Class<?> aClass = Class.forName(name);
            if (aClass.isAnnotationPresent(Controller.class)) {
                Method[] methods = aClass.getDeclaredMethods();
                StringBuffer baseUrl = new StringBuffer();
                if (aClass.isAnnotationPresent(RequsestMapping.class)) {
                    RequsestMapping annotation = aClass.getAnnotation(RequsestMapping.class);
                    baseUrl.append(annotation.value());
                }
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequsestMapping.class)) {
                        RequsestMapping annotation = method.getAnnotation(RequsestMapping.class);
                        baseUrl.append(annotation.value());
                        // 保存url与方法的关系
                        urlMethodMap.put(baseUrl.toString(), method);
                        // 保存方法与类名的关系
                        methodPackageMap.put(method, name);
                    }
                }
            }
        }
    }

    /**
     * 自动注入对象
     * 
     * @throws IllegalAccessException
     */
    private void springioc() throws IllegalAccessException {
        // 遍历实例化对象map集合
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Qualifier.class)) {
                    String name = field.getAnnotation(Qualifier.class).value();
                    field.setAccessible(true);
                    field.set(entry.getValue(), instanceMap.get(name));
                }
            }
        }
    }

    /**
     * 实例化所有带注解的类,并放入instanceMap集合中
     * 
     * @param packageNames
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void instance(List<String> packageNames)
            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (packageNames.size() < 0) {
            return;
        }
        for (String packageName : packageNames) {
            Class<?> aClass = Class.forName(packageName);
            if (aClass.isAnnotationPresent(Controller.class)) {
                Controller controller = aClass.getAnnotation(Controller.class);
                String controllerName = controller.value();
                // 保存实例化对象与注解value关系
                instanceMap.put(controllerName, aClass.newInstance());
                // 保存类的全限定名与注解value关系
                nameMap.put(packageName, controllerName);
                System.out.println("controller" + packageName + ",value:" + controller.value());
            } else if (aClass.isAnnotationPresent(Service.class)) {
                Service service = aClass.getAnnotation(Service.class);
                String serviceName = service.value();
                instanceMap.put(serviceName, aClass.newInstance());
                nameMap.put(packageName, serviceName);
                System.out.println("service" + packageName + ",value:" + service.value());
            } else if (aClass.isAnnotationPresent(Repostory.class)) {
                Repostory repostory = aClass.getAnnotation(Repostory.class);
                String repostoryName = repostory.value();
                instanceMap.put(repostoryName, aClass.newInstance());
                nameMap.put(packageName, repostoryName);
                System.out.println("repostory" + packageName + ",value:" + repostory.value());
            }
        }
    }

    /**
     * 扫描基包得到全部的带包路径的全限定名,并加入packageNames列表
     * 
     * @param basePackage
     */
    private void scanBasePackage(String basePackage) {
        // 根据类所在的包获取绝对路径
        URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
        System.out.println(url.getPath());
        File basePackageFile = new File(url.getPath());
        System.out.println("sacn" + basePackageFile);
        File[] childFiles = basePackageFile.listFiles();
        // 递归获取类的全限定名
        for (File file : childFiles) {
            if (file.isDirectory()) {
                scanBasePackage(basePackage + "." + file.getName());
            } else if (file.isFile()) {
                packageNames.add(basePackage + "." + file.getName().split("\\.")[0]);
            }
        }
    }
}

4.3 配置web.xml文件

<web-app>
    <display-name>Archetype Created Web Applicationdisplay-name>
    <servlet>
        <servlet-name>dispatcherServletservlet-name>
        <servlet-class>com.smilevers.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>base-packageparam-name>
            <param-value>com.smileversparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServletservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>
web-app>

4.4 测试

1、创建dao层的接口和实现类,并添加注解@Repostory("studentDao")

public interface StudentDao {
    void getUser();
}

@Repostory("studentDao")
public class StudentDaoImpl implements StudentDao {
    public void getUser() {
        System.out.println("dao层运行成功");
    }
}

2、创建service层的接口和实现类,添加注解@Service("studentService"),并注入StudentDao

public interface StudentService {
    void getUser();
}

@Service("studentService")
public class StudentServiceImpl implements StudentService {

    @Qualifier("studentDao")
    private StudentDao studentDao;

    public void getUser() {
        System.out.println("service层调用成功");
        studentDao.getUser();
    }
}

3、创建controller层,添加@Controller("userController")@RequsestMapping("/user")注解,并注入StudentService

@Controller("userController")
@RequsestMapping("/user")
public class StudentController {
    @Qualifier("studentService")
    private StudentService studentService;

    @RequsestMapping("/getuser")
    public String getUser() throws IOException {
        System.out.println("controller访问成功");
        studentService.getUser();
        return "welcom myMVC";
    }
}

4、启动项目,查看控制台:

框架基础-反射、代理、注解_第7张图片

controller访问成功
service层调用成功
dao层运行成功

我们自定义的mvc框架运行成功!简单的依赖关系:

框架基础-反射、代理、注解_第8张图片

你可能感兴趣的:(Java基础)