当前位置:   article > 正文

反射机制及动态代理_反射的底层原理动态代理

反射的底层原理动态代理

一、Java反射机制概述:

二、理解Class类并获取Class实例

 1:对于java.lang.Class类的理解

   1)编译过程:程序经过java.exe命令以后,会生成一个或多个字节码文件(.class文件)
   2)类的加载过程:接着我们使用java.exe命令对某个字节码文件进行解释运行,这就相当于将某个字节码文件加载到内存中。此过程就成为类的加载。加载到内存中的类,我们就成为运行时类,此时运行时类,就作为Class类的一个实例,换句话说,Class的实例就对应着一个运行时类。
   3)Class类的实例:加载到内存中的运行时类,会缓存一定的时间。在此时间内,我们可以通过不同的方式来获取此运行时类。1.类(内部类,外部类)2.接口 3.数组 4.枚举类 5.注解 6.基本数据类型 7.void

 2:获取Class实例的四种方式

   1)Person类

package com.bjpowernode.ba01;

public class Person {
    private String name;
    public int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public void show(){
        System.out.println("你好,我是一个人");
    }
    private String showNation(String nation){
        System.out.println("我的国籍是:"+nation);
        return nation;
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private Person(String name) {
        this.name = name;
    }
    public Person() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

   2)测试代码

public class RelectTest {
    @Test
    public void test1() throws ClassNotFoundException {
        //方式一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);
        //方式三:调用Class的静态方法:forName(String classPath)
        Class clazz3 = Class.forName("com.bjpowernode.ba01.Person");
        System.out.println(clazz3);
        //方式四:使用类的加载器:ClassLoader
        ClassLoader classLoader = RelectTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.bjpowernode.ba01.Person");
        System.out.println(clazz4);
        System.out.println(clazz1==clazz2);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

三、类的加载与ClassLoader的理解

  1:类的加载过程

在这里插入图片描述
在这里插入图片描述

  2:ClassLoader的理解

在这里插入图片描述
在这里插入图片描述
测试代码:

public class ClassLoaderTest {
    @Test
    public void test1(){
        //对于自定义的类,使用类加载器进行加载
        ClassLoader  classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);
        //调用系统类加载的getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);
        //调用扩展类加载器的getParent():无法获取引导类加载器
        //引导类加载器主要负责加载java的核心类库,无法加载自定义类的,例如String类。
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);

        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

控制台输出:
在这里插入图片描述
使用ClassLoader加载配置文件:

public void test2() throws IOException {
        Properties pros = new Properties();
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc.properties");
        pros.load(is);
        
        String user = pros.getProperty("user");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意:使用此方法加载配置文件时,配置文件必须在src下,而不是在此moudle下。

四、创建运行时类的对象

public class NewInstanceTest {
    @Test
    public void test() throws IllegalAccessException, InstantiationException {
        //获取运行时类
        Class<Person>clazz = Person.class;
        //调用newInstance()方法创建运行时类的对象,底层还是使用运行时类的无参构造创建的对象。
        Person person = clazz.newInstance();
        System.out.println(person);

        //InstantiationException:这个异常是没有该运行时类的无参构造,无法创建对象。
        //IllegalAccessException:这个异常是无参构造没有访问权限,比如是private后的构造器,newInstance()也无法访问到。
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

注意:
  InstantiationException:这个异常是没有该运行时类的无参构造,无法创建对象。
  IllegalAccessException:这个异常是无参构造没有访问权限,比如是private后的构造器,newInstance()也无法访问到。

五、获取运行时类的完整结构

 1:获取运行时类的属性

@Test
    public void test(){

        Class clazz = Person.class;
        //获取属性结构
        //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        //declaredFields():获取当前运行时类中声明的所有属性。(不含父类中的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            //1.获取属性的权限修饰符
            int modifier = f.getModifiers();
            //2.获取属性的数据类型
            Class type = f.getType();
            //3.获取属性的变量名
            String name = f.getName();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

 2:获取运行时类的方法结构

@Test
    public void text2(){
        Class clazz = Person.class;
        //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[]methods = clazz.getMethods();
        //getDeclaredMethods()获取当前运行时类中声明的所有方法(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

 3:获取运行时类的构造器结构

@Test
    public void text3(){
        Class clazz = Person.class;
        //getConstructors():获取当前运行时类及其所有父类中声明为public权限的构造器
        Constructor[] constructors = clazz.getConstructors();
        //getDeclaredConstructors():获取当前运行时类中声明的所有构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

 4:获取运行时类的父类

@Test
    public void text4(){
        Class clazz = Person.class;
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

 5:获取运行时类的接口,所在包,注解等

@Test
    public void text5(){
        Class clazz = Person.class;
        //获取运行时类实现的接口
        Class[] interfaces = clazz.getInterfaces();
        //获取运行时类父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        //获取运行时类所在的包
        Package aPackage = clazz.getPackage();
        //获取运行时类声明的注解
        Annotation[] annotations = clazz.getAnnotations();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

六、调用运行时类的指定结构

 1:调用运行时类中的指定属性

@Test
    public void text() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
        //获取运行时类的对象
        Person p = (Person)clazz.newInstance();
        //获取指定的属性:要求运行时类中属性声明为public
        Field id = clazz.getField("id");
        //保证当前属性是可访问的
        id.setAccessible(true);
        //设置当前属性的值
        id.set(p,1000);
        //获取当前属性的值
        Object o = id.get(p);
        System.out.println(o);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

 2:调用运行时类中的指定方法

@Test
    public void text1() throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        Class<Person> clazz = Person.class;
        //获取运行时类的对象
        Person p = clazz.newInstance();
        //1.获取某个指定的方法,getDeclaredMethod()参数1:指明获取的方法名称
        //                                         参数2:指明获取的方法的形参列表
        Method show = clazz.getDeclaredMethod("showNation", String.class);
        //2.保证当前方法是可以访问的
        show.setAccessible(true);
        //3.调用方法的invoke()参数1:方法的调用者  参数2:此方法的实参
        Object returnvalue = show.invoke(p, "中国");
        System.out.println(returnvalue);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

七、反射的应用:动态代理

 1:代理的特点及代理的模式

​    换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑
    客户类真正想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。客户类对目标对象的访问是通过访问代理对象来实现的。当然,代理类与目标类要实现同一个接口。
    例如:有A,B,C三个类,A原来可以调用C类的方法,现在因为某种原因C类不允许A类调用其方法,但B类可以调用C类的方法。A类通过B类调用C类的方法。这里B是C的代理。A通过代理B访问C。

 2:使用代理模式的作用

   1.功能增强:在你原有的功能上,增加了额外的功能,新增加的功能,叫做功能增强。
   2.控制访问:代理类不让你访问目标,例如商家不让用户访问厂家。

 3:静态代理

   1)代理类是自己手工实现的,自己创建一个java类,表示代理类。2)同时你所要代理的目标类是确定的。
   举例:在淘宝上买U盘,淘宝中间商赚差价。
1、接口

package com.bjpowernode.service;

public interface UsbSell {
    public float sell(int amount);
}
  • 1
  • 2
  • 3
  • 4
  • 5

2、生产U盘的厂商

package com.bjpowernode.factory;

import com.bjpowernode.service.UsbSell;

public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int amount){
        return 85.0f;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3、淘宝(中间代理商)

package com.bjpowernode.shangjia;

import com.bjpowernode.factory.UsbKingFactory;
import com.bjpowernode.service.UsbSell;

public class TaoBao implements UsbSell {
    UsbKingFactory usbKingFactory = new UsbKingFactory();
    @Override
    public float sell(int amount) {
        float price  = usbKingFactory.sell(amount);
        price = price+25;
        return price;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4、用户购买

package com.bjpowernode;

import com.bjpowernode.shangjia.TaoBao;

public class ShopMain {
    public static void main(String[] args) {
        TaoBao taoBao  = new TaoBao();
        float price = taoBao.sell(1);
        System.out.println(price);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

 4:动态代理

  1、动态代理优势:在静态代理中目标类很多的时候,可以使用动态代理,避免静态代理的缺点。静态代理中目标类即使很多。
​     1)代理类数量可以很少。2)当你修改了接口中的方法时,不会影响代理类。
  2、动态代理:在程序执行的过程中,**使用jdk的反射机制,创建代理对象,**并动态的指定要代理的目标类。换句话说:**动态代理是一种创 建java对象的能力,**让你不用创建代理类(TaoBao类),就能创建代理类对象。
  3、动态代理的实现:
    1)jdk动态代理(理解):使用java反射包中的类和接口实现动态代理的功能,反射包 java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy
​    2)cglib动态代理(了解):cglib是第三方的工具库,创建代理对象。cglib的原理是继承,cglib通过继承目标类,创 建它的子类,在子类中重写父类中同名的方法,实现功能的修改。
   由于cglib是继承、重写的方法,所以要求目标类不能是final的,方法也不能是final的。cglib的要求目标类比较松, 只要继承就可以了。cglib在很多的框架中使用,比如mybatis,spring框架中都有使用。
  4、JDK动态代理(必须要有接口才能使用JDK动态代理

 5:动态代理实战

   1)实现动态代理的步骤:

    1、创建接口,定义目标类要完成的功能

package com.bjpowernode.service;

public interface UsbSell {
    float sell(int amount);
}
  • 1
  • 2
  • 3
  • 4
  • 5

    2、创建目标类实现接口

package com.bjpowernode.factory;

import com.bjpowernode.service.UsbSell;

public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int amount) {
        //目标方法
        System.out.println("目标类中,执行sell目标方法");
        return 85.0f;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    3、创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能

package com.bjpowernode.handler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//必须实现InvocationHandler 接口,完成代理类要做的功能1.调用目标方法  2.功能增强
public class MySellHandler implements InvocationHandler {
    private Object target = null;
    public MySellHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行目标方法
        Object res = method.invoke(target, args);
        //功能增强
        if(res != null){
            Float price = (Float)res;
            price = price + 25;
            res = price;
        }
        return res;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

    4、使用proxy类的静态方法,创建代理对象。并把返回值转为接口类型。

package com.bjpowernode;

import com.bjpowernode.factory.UsbKingFactory;
import com.bjpowernode.handler.MySellHandler;
import com.bjpowernode.service.UsbSell;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MainShop {
    public static void main(String[] args) {
        //创建代理对象,使用Proxy
        //1、创建目标对象
        UsbSell factory = new UsbKingFactory();
        //2、创建InvocationHandler对象
        InvocationHandler handler = new MySellHandler(factory) ;
        //3、获取代理对象,代理对象即为proxy(注意:将该代理对象强转为原接口的对象)
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), handler);
        //4.通过代理对象执行方法
        float price = proxy.sell(1);
        System.out.println("通过代理执行对象"+price);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
   2)动态代理流程分析图:

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/767985
推荐阅读
相关标签
  

闽ICP备14008679号