反射和代理的原理

什么是反射,反射原理
Java反射的原理:java类的执行需要经历以下过程,

编译:.java文件编译后生成.class字节码文件
加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例
链接
验证:格式(class文件规范) 语义(final类是否有子类) 操作

准备:静态变量赋初值和内存空间,final修饰的内存空间直接赋原值,此处不是用户指定的初值。

解析:符号引用转化为直接引用,分配地址

初始化
有父类先初始化父类,然后初始化自己;将static修饰代码执行一遍,如果是静态变量,则用用户指定值覆盖原有初值;如果是代码块,则执行一遍操作。

Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射获取class,然后进行各种操作。

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。总结说:反射就是把java类中的各种成分映射成一个个的Java对象,并且可以进行操作。

 

获取class的三种方式
先定义一个实体类Person:

Package reflex;

public class Person {

    //私有属性

    private String name = "Tom";

    //公有属性

    public int age = 18;

    //构造方法

    public Person() {   

    }

    //私有方法

    private void say(){

        System.out.println("private say()...");

    }

    //公有方法

    public void work(){

        System.out.println("public work()...");

    }

}

获取class方法

//1、对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object

//  类型的对象,而我不知道你具体是什么类,用这种方法

  Person p1 = new Person();

  Class c1 = p1.getClass();

       

//2、类名.class 的方式得到,该方法最为安全可靠,程序性能更高

//  这说明任何一个类都有一个隐含的静态成员变量 class

  Class c2 = Person.class;

        

//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,

//   但可能抛出 ClassNotFoundException 异常

  Class c3 = Class.forName("reflex.Person");

需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true。代码如下:

        Class class1 = Person.class;

        Person person = new Person();

        Class class2= person.getClass();

        if(class1.equals(class2)){

            System.out.println("class1.equals(class2)");

        }

 

Class具有的部分方法如下:

getName():获得类的完整名字。

  getFields():获得类的public类型的属性。

  getDeclaredFields():获得类的所有属性。包括private 声明的和继承类

  getMethods():获得类的public类型的方法。

  getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类

  getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。

  getConstructors():获得类的public类型的构造方法。

  getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。

  newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

Class能实现的功能
1判断对象属于哪个类

        Person person = new Person();

        Class class2= person.getClass();

        System.out.println("class2:"+class2);

输出:class2:class reflect.Person

 

2获取类信息

Class class1 = Person.class;

        Method[] methods = class1.getMethods();

        Method[] declaredMethods = class1.getDeclaredMethods();

        Field[] declaredFields = class1.getDeclaredFields();

3构建对象

        Person person = new Person();

        Class class2= person.getClass();

        Object o = class2.newInstance();

              //强转前先用instanceof判断

        if(o instanceof Person){

            ((Person) o).work();

        }

4动态执行方法

        Class class1 = Person.class;

        Method work = class1.getDeclaredMethod("work");

        Person person = new Person();

        work.invoke(person);

 

5动态操作属性

       Class class1 = Person.class;

        Person person = new Person();

        Field field = class1.getDeclaredField("age");

//age默认值是18

        field.set(person,22);

        System.out.println(person.age);

首先简单说明下为什么需要代理模式:为其他对象提供一种代理以控制对这个对象的访问,可以隔离客户端和委托类的中介。我们还可以借助代理来在增加一些功能,而不需要修改原有代码。

重点是代理模式的三种实现方式:

先给出简单的接口和实现类:

public interface IHello {
    void sayHello();
}
public final class Hello implements IHello{
    @Override
    public void sayHello() {
        System.out.println("hello");
 
    }
}
1静态代理模式

public class StaticProxy {
    IHello hello;
    public StaticProxy(IHello hello){
        this.hello=hello;
    }
    public void syaHello(){
        System.out.println("before");
        hello.sayHello();
        System.out.println("after");
    }
    public static void  main(String  args[]){
        new StaticProxy(new Hello()).syaHello();
    }
}
输出为:

before
hello
after

2java动态代理实现的动态代理

//java动态代理实现的动态代理
public class DynamicProxy implements InvocationHandler {
    Object target;
    public DynamicProxy(Object target){
        this.target=target;
    }
    // 此处生成接口的实现类,所以需要有接口,无法代理未实现接口的类
    public Object bind(){
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
                    this.target.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        method.invoke(this.target,args);
        System.out.println("after");
        return null;
    }
 
    public static void  main(String  args[]){
        //此处返回的必定是接口,不可以转为具体实体类,所以java反射要求被代理类必须实现了接口;但不要求final
       IHello hello = (IHello) new DynamicProxy(new Hello()).bind();
       hello.sayHello();
    }
 
}
java动态代理实现代理步骤:

a定义被代理类:接口及接口实现类

b定义代理类,代理类需要实现InvocationHandler接口的类并重写invoke方法

c生成被代理的类的实例:调用 Proxy.newProxyInstance(被代理的类.getClass().getClassLoader(),

                                                                                        被代理的类.getClass().getInterfaces(),

                                                                                         InvocationHandler的实现类);

              注意:newProxyInstance返回的是接口类型,所以java动态代理要求被代理类实现接口。

d被代理的类的实例调用需要执行的方法

3cglib实现的动态代理

maven先引入包:

<dependency>

    <groupId>cglib</groupId>

    <artifactId>cglib</artifactId>

    <version>2.2.2</version>

</dependency>

public class CGLibProxy {
    public static void  main(String  args[]){
        Enhancer enhancer = new Enhancer();
        // 此处将目标类设置为父类,生成该类的子类来实现动态代理,所以如果此时将Hello类声明为final,则会报IllegalArgumentException;但不要求实现接口
        enhancer.setSuperclass(Hello.class);
        enhancer.setCallback(new MethodInterceptor(){
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before");
                Object result = methodProxy.invokeSuper(o,objects);
                System.out.println("after");
                return result;
            }
        });
        Hello hello = (Hello)enhancer.create();
        hello.sayHello();
    }
}
Ehancer介绍:

Enhancer
Enhancer可能是CGLIB中最常用的一个类,和Java1.3动态代理中引入的Proxy类差不多(如果对Proxy不懂,可以参考这里)。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final class的原因。

public class SampleClass {
    public String test(String input){
        return "hello world";
    }
}

下面我们将以这个类作为主要的测试类,来测试调用各种方法

@Test
public void testFixedValue(){
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(SampleClass.class);
    enhancer.setCallback(new FixedValue() {
        @Override
        public Object loadObject() throws Exception {
            return "Hello cglib";
        }
    });
    SampleClass proxy = (SampleClass) enhancer.create();
    System.out.println(proxy.test(null)); //拦截test,输出Hello cglib
    System.out.println(proxy.toString()); 
    System.out.println(proxy.getClass());
    System.out.println(proxy.hashCode());
}

程序的输出为:

Hello cglib
Hello cglib
class com.zeus.cglib.SampleClass
EnhancerByCGLIB
EnhancerByCGLIB
e3ea9b7

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

    at com.zeus.cglib.SampleClass
EnhancerByCGLIB
EnhancerByCGLIB
e3ea9b7.hashCode(<generated>)
    ...

上述代码中,FixedValue用来对所有拦截的方法返回相同的值,从输出我们可以看出来,Enhancer对非final方法test()、toString()、hashCode()进行了拦截,没有对getClass进行拦截。由于hashCode()方法需要返回一个Number,但是我们返回的是一个String,这解释了上面的程序中为什么会抛出异常。

Enhancer.setSuperclass用来设置父类型,从toString方法可以看出,使用CGLIB生成的类为被代理类的一个子类,形如:SampleClass
EnhancerByCGLIB
EnhancerByCGLIB
e3ea9b7

Enhancer.create(Object…)方法是用来创建增强对象的,其提供了很多不同参数的方法用来匹配被增强类的不同构造方法。(虽然类的构造放法只是Java字节码层面的函数,但是Enhancer却不能对其进行操作。Enhancer同样不能操作static或者final类)。我们也可以先使用Enhancer.createClass()来创建字节码(.class),然后用字节码动态的生成增强后的对象。
 

原理上:

Java动态代理通过创建接口的实现类来完成对目标对象的代理,使用Java原生的反射API进行操作,在生成类上比较高效;但不能代理未实现接口的类;

CGLIB 在运行期间生成的是目标类扩展的子类,直接使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效;不能扩展final修饰的类或方法;

使用上:

Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类,但是不能代理final修改时的类;

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP ,spring中配置proxy-target-class='true'
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值