Java中的反射与代理

反射

1.什么是反射

Java反射是程序运行过程中,对于任何一个类或对象,都可以动态获取其信息和调用其方法。

2.使用

2.1. Class

Class类是反射的根源,你想要进行相关反射操作都要从获取Class开始。

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的.class文件
  • 通过Class可以完整地得到一个类中的所有被加载的结构
获取反射Class的几种方法
    public void testCreateReflect(){
        
        // 1.已知具体类:通过具体类获取
        Class<String> stringClass = String.class;
        // 2.已知类实例:通过类的实例获取
        Object object = new Object();
        Class<?> aClass = object.getClass();
        Class<? extends String> aClass1 = "Hello".getClass();
        // 3.已知类路径:通过静态方法
        try {
            Class<?> aClass2 = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        // 4.通过类加载器 todo
    }
Java中哪些数据类型可以获取Class
  • Class:所有的类
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解
  • primitive:基本数据类型
  • void

举例:

    public void createClassType(){
        Class<Object> c1 = Object.class;
        Class<Comparable> c2 = Comparable.class;  // 接口
        Class<String[]> c3_1 = String[].class; // 数组
        Class<int[][]> c3_2 = int[][].class;
        Class<ElementType> c4 = ElementType.class; // 枚举
        Class<Override> c5 = Override.class; // 注解
        Class<Integer> c6 = int.class; // 基本类型
        Class<Void> c7 = void.class;

        int[] a = new int[10];
        int[] b = new int[11];
        System.out.println(a.getClass()==b.getClass()); //只要元素的维度与类型一致,就是同一个class
    }
类的加载与ClassLoader(了解)

类的加载过程如下图:

image

在类的加载过程中需要使用ClassLoader,类加载器的作用把类(.class文件)加载进内存,并将这些静态数据转成成方法区运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class,作为方法区中类数据的访问入口。

有了类的对象我们就可以创建对应类的实例、字段、方法…

2.2.类的字段Field

有以下获取字段方法:

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

举例:

    public void testGetField() throws NoSuchFieldException {
        Class stdClass = Student.class;
        // 获取全部public字段
        Field[] fields = stdClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println();

        // 获取public字段"score":
        System.out.println(stdClass.getField("score"));
        // 获取继承的public字段"name":
        System.out.println(stdClass.getField("name"));
        // 获取private字段"grade":
        System.out.println(stdClass.getDeclaredField("grade"));
    }

class Student extends Person {
    public int score;
    private int grade;

    public Student(int grade) {
        this.grade = grade;
    }
}

class Person {
    public String name;
}

输出:

public int com.dpf.jdk8.reflect.test1.Student.score
public java.lang.String com.dpf.jdk8.reflect.test1.Person.name

public int com.dpf.jdk8.reflect.test1.Student.score
public java.lang.String com.dpf.jdk8.reflect.test1.Person.name
private int com.dpf.jdk8.reflect.test1.Student.grade

一个Field对象包含了一个字段的所有信息:

  • getName():返回字段名称,例如,"name"
  • getType():返回字段类型,也是一个Class实例,例如,String.class
  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。

修饰符判断举例:

  //     private final char value[];

	Field f = String.class.getDeclaredField("value");
    int m = f.getModifiers();
    Modifier.isFinal(m); // true
    Modifier.isPublic(m); // false
    Modifier.isProtected(m); // false
    Modifier.isPrivate(m); // true
    Modifier.isStatic(m); // false

修改字段的值,获取字段的值举例:

        Student student = new Student(10);
        Field field = student.getClass().getDeclaredField("grade");
        // 允许获取所有字段
        field.setAccessible(true);
        // 修改字段值
        field.set(student,20);
        System.out.println(field.get(student));

注意在获取private字段的值时要field.setAccessible(true)

2.3.类的方法Method

有以下获取Method方法:

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

举例:

static class Student extends Person {
    public int getScore(String type) {
        return 99;
    }
    private int getGrade(int year) {
        return 1;
    }
}

static class Person {
    public Person() {
    }

    public String getName() {
        return "Person";
    }
}

public void testMethod() throws NoSuchMethodException {
        Class stdClass = Student.class;

        // 获取所有public 方法
        Method[] methods = stdClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println();

        // 获取public方法getScore,参数为String:
        System.out.println(stdClass.getMethod("getScore", String.class));
        // 获取继承的public方法getName,无参数:
        System.out.println(stdClass.getMethod("getName"));
        // 获取private方法getGrade,参数为int:
        System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
    }

一个Method对象包含一个方法的所有信息:

  • getName():返回方法名称,例如:"getScore"
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

获取方法后调用方法举例:

   public void testMethodRun() throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();
        Method getName = personClass.getMethod("getName");
        System.out.println(getName.invoke(person));

        String hello = "Hello,Wrold";
        Class<? extends String> helloClass = hello.getClass();
        Method substring = helloClass.getMethod("substring", int.class);
        System.out.println(substring.invoke(hello, 6));
    }

Method.invoke(obj,args)obj为调用对象,静态方法obj可为null,args为参数

调用非public方法时,必须首先通过setAccessible(true)设置允许访问。

2.4.类的构造器

我们通常使用new创建实例:

Student stu = new Student();

在使用反射时,我们通常使用ClassnewInstance方法创建实例

Student stu = Student.class.newInstance();

调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。对于构造方法带有参数,我们可以通过获取Constructor对象创建实例对象。

Constructor常用方法:

  • getConstructor(Class...):获取某个publicConstructor
  • getDeclaredConstructor(Class...):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor

举例:

    static class User{
        private String username;
        private String password;

        public User() {
        }

        public User(String username, String password) {
            this.username = username;
            this.password = password;
        }
    }

    public void test() throws Exception {
        Class<User> userClass = User.class;
        Constructor<User> constructor = userClass.getConstructor(String.class, String.class);
        User user = constructor.newInstance("username", "password");

        // new String("");
        Constructor<String> stringConstructor = String.class.getConstructor(String.class);
        String str = stringConstructor.newInstance("str");
    }

2.5.其他

  • Class getSuperclass():获取父类类型
  • Class[] getInterfaces():获取当前类实现的所有接口

代理

代理简单的理解就是代为实现。

1.静态代理

这里指的代理是指设计模式结构型模式的代理模式。

举个例子,我们要买水,我们不想买我们可以让美团跑腿代买。

public interface Buy {
    void buyWater();
}

// 美团跑腿
public class Rider implements Buy {

    @Override
    public void buyWater() {
        System.out.println("买水");
    }
}

// 代买
public class StaticProxy {

    private Rider rider;

    public StaticProxy(Rider rider) {
        this.rider = rider;
    }

    public void proxyShow(){
        showPre();
        rider.buyWater();
        showPost();
    }

    public void showPre(){
        System.out.println("buy operation...");
    }

    public void showPost(){
        System.out.println("buy post operation...");
    }
}

// 客户下单
public class Client {
    public static void main(String[] args) {
        Rider rider = new Rider();
        StaticProxy staticProxy = new StaticProxy(rider);
        staticProxy.proxyShow();
    }
}


如果我们又要买药,那么我们可以在Buy接口添加买药的方法,Rider实现买药方法,在代理类中调用即可。但是这样违反了设计模式的开闭原则(对修改关闭,对扩展开发)。所以在开发中不建议使用静态代理。

2.动态代理

2.1.jdk动态代理

举个例子,mvc三层架构中的serviceserviceImpl

service:

public interface UserService {

    void insert(User user);

    User findUserById(int id);

}

serviceImpl:

public class UserServiceImpl implements UserService{
    @Override
    public void insert(User user) {
        System.out.println("insert....");
    }

    @Override
    public User findUserById(int id) {
        System.out.println("findUserById....");
        return new User();
    }
}

代理类:

public class DynamicProxyFactory implements InvocationHandler {

    private Object realObject;

    /**
     * 创建DynamicProxyFactory实例
     * @param realObject
     * @return
     */
    public Object createProxy(Object realObject){
        this.realObject = realObject;
        // 类加载器
        ClassLoader classLoader = DynamicProxyFactory.class.getClassLoader();
        // 被代理对象实现的所有接口
        Class<?>[] clazz = realObject.getClass().getInterfaces();
        // this代表当前类,一个InvocationHandler用于方法的调用
        return Proxy.newProxyInstance(classLoader,clazz,this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前置....");
        Object invoke = method.invoke(realObject, args);
        System.out.println("代理后置....");
        return invoke;
    }
}

代理测试

public static void main(String[] args) {

    DynamicProxyFactory dynamicProxyFactory = new DynamicProxyFactory();
    UserService userService = new UserServiceImpl();
    UserService targetService = (UserService)dynamicProxyFactory.createProxy(userService);

    targetService.findUserById(1);
    targetService.insert(new User());
}

输出结果:

代理前置....
findUserById....
代理后置....
代理前置....
insert....
代理后置....

2.2.cglib动态代理

引入依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

代理类:

public class CglibDynamicProxy implements MethodInterceptor {

    public Object createProxy(Object target){
        // 创建动态代理类对象
        Enhancer enhancer = new Enhancer();
        // 确定需要代理(增强)的类,设置其父类
        enhancer.setSuperclass(target.getClass());
        // 添加回调函数
        enhancer.setCallback(this);
        // 返回创建的代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置代理....");
        Object result = methodProxy.invokeSuper(obj, args);
        System.out.println("后置代理");
        return result;
    }
}

测试:

    public static void main(String[] args) {
        System.out.println();
        CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy();
        UserServiceImpl userService2 = (UserServiceImpl)cglibDynamicProxy.createProxy(new UserServiceImpl());
        userService2.findUserById(1);
    }

3.总结

Java中,实现动态代理有两种方式:

  • JDK动态代理:java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。

  • Cglib动态代理:Cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。

JDK动态代理和Cglib动态代理的区别:

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截)。

  • Cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。

Cglib与动态代理最大的区别就是:

  • 使用动态代理的对象必须实现一个或多个接口

  • 使用cglib代理的对象则无需实现接口,达到代理类无侵入。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值