Java浅析【反射机制】与【动态代理】

一、反射机制

1.反射机制原理

【反射的解释】
     反射机制为什么叫【反射】?一般情况下,我们使用某个类时知道这个类的具体作用,所以我们会直接实例化并进行调用方法或属性的操作,例如:迷路时如果看到志愿者穿着的人,我们会找他们问路。而对于一些不知道的类对象,例如:我们自身的衣装整不整齐,我们很难判断。这时就需要用到【镜子】通过【反射】可以得知自身的一些状态。
【反射机制的原理】
     

Apple apple = new Apple(); //直接初始化
apple.setPrice(4);

上面这样对类对象的初始化,是已经确定了要运行的类
而反射是一开始不知道要初始化的类是什么,所以无法使用new关键字来创建对象
使用JDK提供的反射API进行反射调用:

Class clz = Class.forName("com.zeta.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);

上面两段代码的执行结果一样,但是思路不一样,第一段代码在未运行时(编译时)就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.zeta.reflect.Apple)
总结:反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用相应的方法

2.反射机制的优缺点

优点

  • 增加程序的灵活性:
    通过修改配置文件,灵活更改获取类的全限定名称。避免将程序写死,降低耦合度。
  • 节省资源:
    Object o = new Object();运行时,java虚拟机会将java文件编译成.class文件,并记载到jvm的内存中,类Object会加载进入方法区,这时候会生成class类的实例对象加载到堆中。在大型工程项目中,很多类暂时用不到,没有必要把每一个类都生成对象。反射机制解决了这个问题,使java从本质的静态语言变成半动态语言。
  • 动态特性:
    通过一个类的Class对象,可以动态的创建实例、调用方法、访问属性等
    静态类型语言:变量定义时有类型声明,且在运行时不能修改
    动态类型语言:变量定义时无类型声明,在运行时确定,且在运行时可修改

缺点

  • 性能大幅下降
    反射机制是一种解释操作,用于字段和方法接入时要远慢于直接代码

3.反射机制的作用

其实反射机制最主要的用处就两个:

  • 根据类名在运行时创建实例(类名可以从配置文件中读取,不用new)

    • 实例:JDBC获得数据库驱动
    Class.forName("com.mysql.jdbc.Driver");
    

    java本身只提供接口,具体的实现需要mysql,orcal,sqlserver这些数据库厂商实现的,如果想要使用java连接到这些数据库,必须要使用这些厂商提供的驱动,厂商提供的驱动都是一些类,类是不会执行的,jvm要为他们生成对象。反射机制 Class.forName会加载类到JVM中,这个过程会执行该类的静态代码,从而加载驱动类。

    new com.mysql.jdbc.Driver()

    如果使用new的方式创建驱动类,会对这个类产生依赖,后续如果要更换数据库驱动,就得重新修改代码,使用反射的方式,只需要在配置文件中修改相应的驱动和url即可。

    jdbc.driver=com.mysql.jdbc.Driver
    
  • 通过Method.invoke()动态执行方法
    先通过类.getMethod反射机制来获取方法Method,然后Method.invoke执行方法

    Method set = class.getMethod("setPrice",int.class);
    set.invoke(appleObj,14);
    
    • 实例:动态代理
      当使用动态代理动态调用方法时,即:
    JdkProxyExample jdk = new JdkProxyExample();//创建代理类
    HelloWorld proxy = (HelloWorld)jdk.getProxy(new HellpWorldImpl());//生成代理对象,参数:目标对象
    proxy.sayHelloWorld();//通过代理对象增强目标对象的方法
    

    上述代码中的jdk为InvocationHandler代理类,proxy为生成的代理对象,proxy.sayHelloworld();相当于,代理类调用了invoke方法并传入了方法参数:

    jdk.invoke(proxy,sayHelloWorld);
    

二、代理模式

通过代理对象访问目标对象,可以在目标对象的基础上增强额外的功能,如:添加权限,访问控制等
代理三要素

  • 有共同的行为——接口
  • 目标对象——实现行为
  • 代理对象——实现行为,并增强目标对象行为

三、静态代理

简介:
正常编译,在运行前已经存在class。需要为每一个类都编写一个对应的代理类,并且让他们实现相同的接口
缺点:
代理的角色是固定的,需要手动为每一个类都编写相应的代理类,工作量太大
举例:
在这里插入图片描述
上述uml图中,其中RealSubject为目标对象,Proxy为代理对象,Subject为共同实现的接口【公共行为】。例如:结婚是【Subject】,是新人【目标对象】和婚庆公司【代理对象】共同要实现的接口,婚庆公司会在新人结婚的基础上,会增强一些行为,比如:主持婚礼、提供场地、准备婚宴等。
实现步骤:

Proxy proxy = new Proxy(RealSubject);
proxy.operation();

其中proxy.operation()是增强后的行为,其包括:

	Before();//前置增强
	Subject.operation();//此处为RealSubject
	After();//后置增强

四、动态代理

简介:
相比静态代理,动态代理在创建代理对象时更加灵活,动态代理类的字节码在程序运行时,由java反射机制动态产生,他会根据需要,在程序运行期间,动态的为目标对象创建代理对象。动态代理有两种实现方式:JDK实现和cglib实现,
动态代理的作用:
简化编程操作、提高可扩展性
1)在目标类源代码不改变的情况下,增加功能
2)减少代码的重复
3)专注业务逻辑diamante
3)解耦合,让你的业务功能和日志,事务非业务功能分离

JDK和Cglib动态代理实现原理

JDK动态代理实现原理

原理: JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口【公共接口】才能产生代理对象,在JDK动态代理中,要实现代理逻辑类必须去实现java.lang.reflevt.InvocationHandler接口,它里面定义了一个invoke方法,并提供接口数组用于下挂代理对象。
实现步骤:

  • 定义接口【公共接口】
public interface HelloWorld(){
	public void sayHelloWorld();
}
  • 实现接口【目标对象】
public class HelloWorldImpl implements HelloWorld{
	@Override
	public void sayHelloWorld(){
		System.out.println("HelloWorld!");
	}
}
  • 动态代理绑定和代理逻辑实现
public class JdkProxyExample implements InvocationHandler{
	//真实对象
	private Object target = null;
	/**
     * 建立代理对象和真实对象的代理关系,并返回代理对象
     * newProxyInstance方法有三个参数:
     * loader:目标对象的类加载器
     * interfaces:动态代理类需要实现的接口数组,
     * handler:代理对象
     */
	public Object getProxy(Object target){
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
	}
	/**
     * 代理方法逻辑【增强】
     */
     @Override
     public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
     	System.out.println("进入代理逻辑方法");
     	System.out.println("在调度真实对象之前的服务");
     	Object obj = method.invoke(target,args);//相当于调用sayHelloWold方法【方法反射】
     	System.out.println("在调度真实对象之后的服务");
     	return obj;//方法返回值
     }
}
  • 测试JDK动态代理
public void testJdkProxy(){
	JdkProxyExample jdk = new JdkProxyExample();
	//绑定关系,因为挂在接口HelloWorld下,所以声明代理对象HelloWorld proxy
	HelloWorld proxy = (HelloWorld)jdk.getProxy(new HelloWorldImpl());
	//注意,此时HelloWorld对象已经是一个代理对象,它会进入代理的逻辑方法invoke里
	proxy.sayHelloWorld();//实际运行的是jdk.invoke(proxy,sayHelloWorld,args);方法
}
//测试结果
进入代理逻辑方法
在调度真实对象之前的服务
Helo World
在调度真实对象之后的服务

总结:
JDK代理不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:
1)实现InvocationHandler
2)使用Proxy.newProxyInstance产生代理对象
3)被代理的对象必须实现公共接口

Cglib动态代理实现原理

原理: JDK动态代理必须提供接口才能使用,在一些不能提供接口【公共接口】的环境中,只能采用其他第三方技术,比如CGLIB动态代理。它的优势在于不需要提供接口,只需要一个非抽象类就能实现动态代理
实现步骤:

  • 不存在实现任何接口的类
public class ReflectServiceImpl{
	public void sayHello(String name){
	System.out.println("Hello" + name);
	}
}
  • CGLIB动态代理
public class CglibProxyExample implements MethodInterceptor{
	/**
     * 生成CGLIB代理对象
     * class类为目标对象类
     */
     public Object getProxy(Class cls){
     	//CGLIB增强类对象
     	Enhancer enhancer = new Enhancer();
     	//设置增强类型
     	enhancer.setSuperclass(cls);//cls为目标对象类
     	//定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
     	enhancer.setCallback(this);
     	//生成并返回代理对象
     	return enhancer.create();
     }
     /**
     * 代理方法逻辑【增强】
     */
     @Override
     public Object intercept(Object proxy, Method method, Objectp[] args,MethodProxy methodProxy) thorws Throwable{
    	 System.out.println("调用真实对象前");
    	 //CGLIB反射调用真实对象方法
    	 Object result = methodProxy.invokeSuper(proxy,args);
    	 System.out.println("调用真实对象后");
    	 return result;
     }
}
  • 测试CGLIB动态代理
public  void testCGLIBProxy(){
	CglibProxyExample cpe = new CglibProxyExample();
	ReflextServiceImpl obj = (ReflextServiceImpl)cpe.getProxy(ReflextServiceImpl.class);
	obj.sayHello("张三");
}
//测试结果
调用真实对象前
Hello 张三
调用真实对象后

总结
1.首先实现一个MethodInterceptor【方法拦截器】来生成返回代理对象
2.方法调用会被转发到该类的intercept()方法。

动态代理总结
1.JDK动态代理需要共同的接口
2.CGLIB不需要公共接口,只需要一个非抽象类就介意实现动态代理

参考文档
https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
参考文档
https://developer.aliyun.com/article/502557

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值