AOP基础-JDK动态代理
动态代理技术就是用来产生一个目标对象的代理对象的,代理对象应与目标对象(被代理的对象)有相同的方法,实现对目标对象访问的拦截,并增强目标对象的一些功能,而不需要目标对象去做任何的更改,使得目标对象有更加纯粹的业务逻辑。不多解释,上代码。
需求:在一个业务模块方法中添加日志。
一、常规的写法
1.1、接口Animals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package com.duchong.proxy_test; /** * @author DUCHONG * @since 2017-12-28 9:10 **/ public interface Animals {<br> /** * 吃 * @param name * @return */ String eat(String name); /** * 吠 * @param name * @return */ String bark(String name); /** * 跑 * @param name * @return */ String move(String name); } |
1.2、接口的实现类Dog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package com.duchong.proxy_test; /** * @author DUCHONG * @since 2017-12-28 9:21 **/ public class Dog implements Animals { @Override public String eat(String name) { System.out.println( "Dog eat method start....." ); return "dog eat " +name; } @Override public String move(String name) { System.out.println( "Dog move method start....." ); return "dog move " +name; } @Override public String bark(String name) { System.out.println( "Dog bark method start....." ); return "dog bark " +name; } public static void main(String[] args) { Dog dog= new Dog(); System.out.println(dog.eat( "Bone" )); System.out.println(dog.move( "Faster" )); System.out.println(dog.bark( "Wang Wang Wang" )); } } |
1.3、输出
以上写法,能实现要求,但是缺点是,像日志这种非业务逻辑相关的代码混在里面,看起来混乱,不易维护,而且这些代码块大多类似,对于这样的代码块,我们都知道,是可以抽取出来的,定义公共功能,这样就使得业务模块更简洁, 只包含核心业务代码。
二、基于动态代理的写法
2.1、接口的实现类Dog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.duchong.proxy_test; /** * @author DUCHONG * @since 2017-12-28 9:21 **/ public class Dog implements Animals { @Override public String eat(String name) { return "dog eat " +name; } @Override public String move(String name) { return "dog move " +name; } @Override public String bark(String name) { return "dog bark " +name; } } |
2.2、使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口(或者说JDK生成代理必须使用接口),然后对接口创建代理.
1 2 3 4 5 | @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException |
使用Proxy.newProxyInstance方法就可以返回一个代理对象,这个方法总共有3个参数
- ClassLoader loader 生成代理对象使用哪个类装载器加载
- Class<?>[] interfaces 生成哪个对象的代理对象,通过接口指定,或者说代理要实现的接口
- InvocationHandler h 产生的这个代理对象要做什么,这个接口里面只有一个方法,可以使用一个匿名内部类来实现
2.3、代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.duchong.proxy_test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * dog代理类 * * @author DUCHONG * @since 2017-12-28 10:17 **/ public class DogProxy{ //需要被代理的目标对象 private Animals target; public DogProxy(Animals target){ this .target=target; } /** * 返回基于接口创建的代理 * @return */ public Animals getDogProxy(){ return (Animals) Proxy.newProxyInstance(DogProxy. class .getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals( "eat" )){ System.out.println( "Dog eat method start....." ); return method.invoke(target,args); } else if (method.getName().equals( "move" )){ System.out.println( "Dog move method start....." ); return method.invoke(target,args); } else if (method.getName().equals( "bark" )){ System.out.println( "Dog bark method start....." ); return method.invoke(target,args); } else { return null ; } } }); } } |
2.4、Main运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package com.duchong.proxy_test; /** * @author DUCHONG * @since 2017-12-28 10:35 **/ public class Main { public static void main(String[] args) { //目标对象 Animals animals= new Dog(); //获得代理对象 Animals dog= new DogProxy(animals).getDogProxy(); //调用代理对象的方法 System.out.println(dog.eat( "Bone" )); System.out.println(dog.move( "Faster" )); System.out.println(dog.bark( "Wang Wang Wang" )); } } |
2.5、输出
2.6、InvocationHandler原理
如果Proxy生成代理对象的时候,指定了InvocationHandler,那么用户调用代理对象的任何方法,该方法都是调用InvocationHandler的invoke方法,而Method就是当前调用的那个方法,通过getName 即可获取当前被调用方法的名称,从而在invoke被调用之前,添加日志等处理,而invoke方法被调用是需要两个参数,一个是调用改方法的对象,一个是该方法的参数,即为代码中的target和args.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 电商平台中订单未支付过期如何实现自动关单?
· 用 .NET NativeAOT 构建完全 distroless 的静态链接应用
· 为什么构造函数需要尽可能的简单
· 探秘 MySQL 索引底层原理,解锁数据库优化的关键密码(下)
· 大模型 Token 究竟是啥:图解大模型Token
· 如何开发 MCP 服务?保姆级教程!
· 1.net core 工作流WorkFlow流程(介绍)
· 瞧瞧别人家的限流,那叫一个优雅!
· C# 工业视觉开发必刷20道 Halcon 面试题
· 从零散笔记到结构化知识库:我的文档网站建设之路
2016-12-28 maven可用镜像
2016-12-28 maven镜像