作者:付政委
蟾宫曲·雪 | 天仙碧玉琼瑶,点点扬花,片片鹅毛
微信公众号:bugstack虫洞栈 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例[Ing]等。 你用剑?、我用刀?,好的代码都很烧?,望你不吝出招?!
在Java中动态代理是非常重要也是非常有用的一个技术点,如果没有动态代理技术几乎也就不会有各种优秀框架的出现,包括Spring。 其实在动态代理的使用中,除了我们平时用的Spring还有很多中间件和服务都用了动态代理,例如;
动态代理可以使用Jdk方式也可以使用CGLB,他们的区别,如下;
类型 | 机制 | 回调方式 | 适用场景 | 效率 |
---|---|---|---|---|
JDK | 委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法 | 反射 | 目标类是接口类 | 效率瓶颈在反射调用稍慢 |
CGLIB | 继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑 | 通过FastClass方法索引调用 | 非接口类,非final类,非final方法 | 第一次调用因为要生成多个Class对象较JDK方式慢,多次调用因为有方法索引较反射方式快,如果方法过多switch case过多其效率还需测试 |
1itstack-demo-test
2└── src
3 ├── main
4 │ └── java
5 │ └── org.itstack.demo
6 │ ├── proxy
7 │ │ └── cglib
8 │ │ └── CglibProxy.java
9 │ ├── jdk
10 │ │ ├── reflect
11 │ │ │ ├── JDKInvocationHandler.java
12 │ │ │ └── JDKProxy.java
13 │ │ └── util
14 │ │ └── ClassLoaderUtils.java
15 │ └── service
16 │ ├── IUserService.java
17 │ └── UserService.java
18 └── test
19 └── java
20 └── org.itstack.demo.test
21 └── ApiTest.java
service/IUserService.java
1public interface IUserService {
2
3 String queryUserNameById(String userId);
4
5}
service/UserService.java
1public class UserService implements IUserService {
2
3 public String queryUserNameById(String userId) {
4 return "hi user " + userId;
5 }
6
7}
reflect/JDKInvocationHandler.java & 代理类反射调用
1public class JDKInvocationHandler implements InvocationHandler {
2
3 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
4 System.out.println(method.getName());
5 return "我被JDKProxy代理了";
6 }
7
8}
reflect/JDKProxy.java & 定义一个代理类获取的服务
1public class JDKProxy {
2
3 public static <T> T getProxy(Class<T> interfaceClass) throws Exception {
4 InvocationHandler handler = new JDKInvocationHandler();
5 ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
6 T result = (T) Proxy.newProxyInstance(classLoader, new Class[]{interfaceClass}, handler);
7 return result;
8 }
9
10}
ApiTest.test_proxy_jdk() & 执行调用并输出反射类的字节码
1@Test
2public void test_proxy_jdk() throws Exception {
3
4 IUserService proxy = (IUserService) JDKProxy.getProxy(ClassLoaderUtils.forName("org.itstack.demo.service.IUserService"));
5 String userName = proxy.queryUserNameById("10001");
6 System.out.println(userName);
7
8 String name = "ProxyUserService";
9 byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{IUserService.class});
10
11 // 输出类字节码
12 FileOutputStream out = null;
13 try {
14 out = new FileOutputStream(name + ".class");
15 System.out.println((new File("")).getAbsolutePath());
16 out.write(data);
17 } catch (FileNotFoundException e) {
18 e.printStackTrace();
19 } catch (IOException e) {
20 e.printStackTrace();
21 } finally {
22 if (null != out) try {
23 out.close();
24 } catch (IOException e) {
25 e.printStackTrace();
26 }
27 }
28
29}
输出结果
1queryUserNameById
2我被JDKProxy代理了
将生成的代理类进行反编译jd-gui
部分内容抽取,可以看到比较核心的方法,也就是我们在调用的时候走到了这里
1public final String queryUserNameById(String paramString)
2 throws
3{
4try
5{
6 return (String)this.h.invoke(this, m3, new Object[] { paramString });
7}
8catch (Error|RuntimeException localError)
9{
10 throw localError;
11}
12catch (Throwable localThrowable)
13{
14 throw new UndeclaredThrowableException(localThrowable);
15}
16}
17
18
19static
20{
21try
22{
23 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
24 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
25 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
26 m3 = Class.forName("org.itstack.demo.service.IUserService").getMethod("queryUserNameById", new Class[] { Class.forName("java.lang.String") });
27 return;
28}
29catch (NoSuchMethodException localNoSuchMethodException)
30{
31 throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
32}
33catch (ClassNotFoundException localClassNotFoundException)
34{
35 throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
36}
37}
cglib/CglibProxy.java
1public class CglibProxy implements MethodInterceptor {
2
3 public Object newInstall(Object object) {
4 return Enhancer.create(object.getClass(), this);
5 }
6
7 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
8 System.out.println("我被CglibProxy代理了");
9 return methodProxy.invokeSuper(o, objects);
10 }
11
12}
ApiTest.test_proxy_cglib() & 调用代理类
1@Test
2public void test_proxy_cglib() {
3 CglibProxy cglibProxy = new CglibProxy();
4 UserService userService = (UserService) cglibProxy.newInstall(new UserService());
5 String userName = userService.queryUserNameById("10001");
6 System.out.println(userName);
7}
输出结果
1我被CglibProxy代理了
2hi user 10001