反射和动态代理

学习时看的博客:https://www.jianshu.com/p/0129001454d7
正文:
反射:
是在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制。
作用:相关方法可查看api
(一)在运行时判断任意一个对象所属的类;
(二)在运行时构造任意一个类的对象;
(三)在运行时判断任意一个类所具有的成员变量和方法;
(四)在运行时调用任意一个对象的方法;
(五)在运行时创建新类对象
(六)生成动态代理。(后面讨论)
反射机制都是利用Class对象来完成的。
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
获取这个Class对象,有三种方式:
1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
3:使用的Class类中的方法,静态的forName方法。
指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。
反射其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。大大增强了程序的拓展性。
缺点就是性能,反射相当于一系列解释操作,多一个步骤,性能比直接的java代码慢。
应用场景:

  1. 逆向代码,反编译器

  2. Spring等框架中。利用反射是实现DI(注入)。可以自定义注解实现一下。

  3. 观察或操作应用程序的运行时行为。调试或测试程序,因为可以直接访问方法、构造函数和成员字段。通过名字调用不知道的方法并使用该信息来创建对象和调用方法。

  4. 在Java程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型
    编译时的类型由声明该对象时使用的类型决定,运行时的类型由实际赋给对象的类型决定:
    如:Person p =new Student();
    编译时类型为Person,而运行时为Student
    如果编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,此时就必须使用反射。
    动态代理:
    代理模式:他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联。分为静态代理和动态代理。
    静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
    动态代理:在程序运行时,运用反射机制动态创建而成。
    静态代理的代理类:
    package proxy;
    //静态代理类
    public class StaticProxy implements ManagerInterface {
    private ManagerInterface manager;//持有实用类对象

    //通过构造方法传入对象
    public StaticProxy(ManagerInterface manager) {
    this.manager = manager;
    }

    @Override
    public void say() {
    System.out.println(“before”);
    manager.say();//调用实用类的方法
    System.out.println(“after”);

    }

}
缺点:
1) 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2) 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。可扩展性差。
动态代理的代理类:
动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象

在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持。
public class Handler implements InvocationHandler {
private Object obj;// 持有代理的对象
// 依赖
public Handler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“before”);
Object res = method.invoke(obj, args);
System.out.println(“after”);
return res;
}
}
客户端的两种代理的调用方式:
//客户端调用
public class appTest {
public static void main(String[] args) {
ManagerInterface p = new ManagerImpl();
p.say();// 直接调用
System.out.println("");
// 静态代理调用
ManagerInterface p1 = new StaticProxy§;
p1.say();
System.out.println("
");
//动态代理
Handler h = new Handler§;
ManagerInterface p2=(ManagerInterface) Proxy.newProxyInstance(p.getClass().getClassLoader(), p.getClass().getInterfaces(), h);
p2.say();
}
}

System.out.println(p2.getClass());会得到
class com.sun.proxy.$Proxy0 这就是jdk的代理类。这就是jdk动态代理的实现方式。
图解流程:
在这里插入图片描述
cglib动态方式:
JDK是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final 。
Cglib的实现:
Cglib动态代理(需要导入两个jar包,asm-5.2.jar,cglib-3.2.5.jar。版本自行选择)。
package proxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

//cglib的动态代理, 实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor{
private Object targetObj;//目标对象

//定义获取代理对象的方法
public Object getCglibProxy(Object objectTarget){
    //为目标对象target赋值
    this.targetObj = objectTarget;
    Enhancer enhancer = new Enhancer();
    //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
    enhancer.setSuperclass(objectTarget.getClass());
    enhancer.setCallback(this);// 设置回调 
    Object result = enhancer.create();//创建并返回代理对象
    return result;
}

//重写拦截方法
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
	System.out.println("before");
	Object res=arg1.invoke(targetObj, arg2);//方法执行,
	System.out.println("after");
	return res;
}
public static void main(String[] args) {
	CglibProxy cglibProxy=new CglibProxy();//实例化CglibProxy对象
	ManagerImpl managerImpl=(ManagerImpl) cglibProxy.getCglibProxy(new ManagerImpl());
	managerImpl.say();
}

}
区别:

  1. JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
  2. JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;
  3. 2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
  4. 在spring的Aop中,两种混合使用。如果被代理对象实现了接口,就优先使用JDK代理,如果没有实现接口,就用用cglib代理。可以进行配置强制使用哪一种。
    <aop:aspectj-autoproxy proxy-target-class=“true”/>)。
    注意使用cglib动态代理需要导入两个jar包,asm-5.2.jar,cglib-3.2.5.jar。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值