动态代理

本节目标:

  • 动态代理演示

  • 动态代理原理

什么是动态代理?

使用一个代理将对象包装起来,然后用该代理对象取代原始对象,任何对原始对象的调用都得通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

Q1:做一个能加减乘除的计算器,用日志记录下计算前后的数据

没有设计思想,只求实现功能:

public class mathCalculatorImpl implements mathCalculator { /** * 日志在int result之前,后。 * @param i * @param j * @return */ @Override public int add(int i, int j) { System.out.println("日志记录:the method add begin with["+i+","+j+"]"); int result=i+j; System.out.println("日志记录:the method add ends withd:"+result); return result; } @Override public int sub(int i, int j) { System.out.println("日志记录:the method sub begin with["+i+","+j+"]"); int result=i-j; System.out.println("日志记录:the method sub ends withd:"+result); return result; } @Override public int mul(int i, int j) { System.out.println("日志记录:the method mul begin with["+i+","+j+"]"); int result=i*j; System.out.println("日志记录:the method mul ends withd:"+result); return result; } @Override public int div(int i, int j) { System.out.println("日志记录:the method div begin with["+i+","+j+"]"); int result=i/j; System.out.println("日志记录:the method div ends withd:"+result); return result; } } 

我们发现这样的代码有几个问题:

将日志与业务逻辑代码耦合在一起,不好修改,会使得负责写日志模块和负责业务逻辑处理的人员工作量加大,将不相关的代码耦合在一起,是没有设计思想的。重复代码多,在日后的需求更改之时,得修改所有的模块。

Q2:如何解决这种问题呢?
用动态代理完成日志代理功能

JDK的动态代理

Proxy (reflex  jar包下的):jdk的动态代理:是所有动态代理类的父类,专门用户生成代理类或者是代理对象
public static class<?> getProxyClass(ClassLoader loader,Class<?>..interfaces) 用于生成代理类的Class对象。 public static object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 用于生成代理对象 2.InvocationHandler (接口):完成动态代理的整个过程。 public object invoke(Object proxy,Method method,Object[] args)throws Throwable;(动态代理想做什么事就写在动态代理就行了) 

改进的计算器

package com.hxh.aop;

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class mathCalculatorProxy { //动态代理: 目标对象 如何获取代理对象 代理要做什么 // mathCalculatorImpl是目标对象,通过父类型来声明它的一个变量,mathCalculatorProxy是要创建一个对象 //的,我们会给这个类提供一个方法用来获取代理对象。你想要调用方法就得有一个对象,有一个对象就 // 得new一个对象,就得执行一个构造器,再去创建对象。传入一个目标对象,告诉我要代理哪个对象,然后 // 赋值到target,相当于保存了目标对象。 private mathCalculator target; public mathCalculatorProxy(mathCalculator target){ this.target=target; } //一个获取代理对象的方法 public Object getProxy(){ //定义代理对象 Object proxy; /** * loader: ClassLoader对象。类加载器对象,帮我们加载动态生成的代理类。 * interfaces:接口们,提供目标对象所有的接口。目的是让代理对象保证与目标对象都有接口中相同的方法。 * h: InvocationHandler类型的对象 */ ClassLoader loader=target.getClass().getClassLoader(); Class[] interfaces = target.getClass().getInterfaces(); //new InvocationHandler()匿名内部类,执行newProxyInstance的时候不会执行invoke方法。 //proxy都还没生成,不能传入invoke方法里(参数) proxy= Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() { /**invoke:代理对象调用代理方法,会回来调用invoke方法. * proxy:代理对象,在invoke方法中一般不会使用 * method:正在被调用的方法对象 * args:正在被调用的方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //完成动态代理的整个过程 //将方法的调用转回到目标对象上。 //获取方法的名字 String methodName=method.getName(); //记录日志,将日志的功能移到这里 System.out.println("LoggingProxy--> The method"+ methodName+"begin with:"+ Arrays.asList(args)); Object result=method.invoke(target,args);//目标对象执行目标方法,相当于执行 mathCalculatorImpl中的加减乘除 System.out.println("LoggingProxy--> The method"+methodName+"ends with:"+result); //记录日志 //通过日志代理来记录日志,完全没必要写在业务逻辑处理(实现类mathCalculatorImpl)的代码里面了 return result; } }); return proxy; } } 

Test类:

package com.hxh.aop;

public class Main { public static void main(String[] args) { //目标对象 mathCalculator target=new mathCalculatorImpl(); //获取代理对象 Object obj=new mathCalculatorProxy(target).getProxy(); //转回具体的对象 mathCalculator proxy= (mathCalculator) obj; int result=proxy.add(1,1); System.out.println("Main Result:"+result); } } 

运行截图:
在这里插入图片描述
Q3: 1.代理对象能否转换成目标对象的类型? 2.代理对象调用代理方法,为什么会执行invocationHandler中的invoke方法

模拟底层动态代理类

获取动态代理类的名字

 mathCalculator proxy= (mathCalculator) obj;

        System.out.println(proxy.getClass().getName()); 

运行结果:

com.sun.proxy.$Proxy0
    Proxy.class中
    protected Proxy(InvocationHandler h){ Objects.requireNonNull(h); this.h=h; //将h赋值给了当前对象的h(protect InvocationHandler h;) } 

也就是说我把我创建好的InvocationHandler传到代理类里面,代理类又从它的构造器里面把h传到了父类里面,这样的话,我们父类里面的h其实就是自己创建的h
在这里插入图片描述

这里面的h就是我们自己创建的h,意味着会执行InvocationHandler里面的invoke方法,在invoke方法里面我们就能够完成整个动态代理的过程。其实并没有使用一开始add的功能。真正的加法是在如下代码中操作的

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName=method.getName(); System.out.println("LoggingProxy--> The method"+ methodName+"begin with:"+ Arrays.asList(args)); Object result=method.invoke(target,args);//目标对象执行目标方法,相当于执行 mathCalculatorImpl中的加减乘除 System.out.println("LoggingProxy--> The method"+methodName+"ends with:"+result); return result; } 

实现了目标对象的加减乘除移到了上述代码中,在代码的前后进行日志的记录

动态代理类是在运行的时候,动态生成的,动态生成的东西在内存里面,不会存在硬盘里,但是从内存中抓取动态代理太难了,不过有一个更简单的方法可以实现该功能

第二种动态代理的实现方法

public class mathCalculatorProxy2 {
    private mathCalculator target; public mathCalculatorProxy2(mathCalculator target) { this.target = target; } public Object getProxy() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //定义代理对象 Object proxy; //第二种方法:public static class<?> getProxyClass(ClassLoader loader,Class<?>..interfaces) //用于生成代理类的Class对象。 //先获取Proxy的class对象 //反射机制 ClassLoader loader=target.getClass().getClassLoader(); Class []interfaces=target.getClass().getInterfaces(); Class proxyClass=Proxy.getProxyClass(loader,interfaces); //Class 创建对象? newInstance():依赖无参构造器(代理类没有) 通过Class对象获取到构造器对象,再通过 //构造器对象创建出具体的对象 Constructor con=proxyClass.getDeclaredConstructor(InvocationHandler.class); proxy=con.newInstance(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName=method.getName(); System.out.println("LoggingProxy--> The method"+ methodName+"begin with:"+ Arrays.asList(args)); Object result=method.invoke(target,args); System.out.println("LoggingProxy--> The method"+methodName+"ends with:"+result); return result; } }); return proxy; } } 

保存生成的动态代理类,将代码加入到Test类中即可

        Properties properties=System.getProperties(); properties.put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 
 

转载于:https://www.cnblogs.com/hxhcr/p/10862710.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值