Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

动态代理是啥东东

动态代理的目的就是为了在不改变源代码的情况下对某些业务逻辑进行增强,AOP即面向切面编程

简单来说,就是把原来应该执行的方法给拦截下来,执行我们代理类中提供的代码

具象化一些,就是在原来方法的前后新增加一些代码,同时不用改动原来的方法

AOP的关键术语解析

连接点 类中可以被增强的方法
切入点 真正被增强的方法
通知、增强 实际增强的代码
切面 把增强塞进切入点的过程

动态代理有两种类型

  • 有实现某些接口的类的对象动态代理
  • 未实现任何接口的类的对象的动态代理

有实现某些接口的类的对象的动态代理采用JDK动态代理(使用java.lang.reflect.Proxy类)

未实现任何接口的类的对象的动态代理采用CGLib动态代理

有接口的实现

既然有接口,那我们先写出一个简单的接口

1
2
3
4
5
package test.hugh;

public interface IUser {
int add(int a,int b);
}

接口有了,我们需要有一个类来实现它

1
2
3
4
5
6
7
8
package test.hugh;

public class User implements IUser{
public int add(int a,int b){
System.out.println("User 对象 add 方法执行");
return a+b;
}
}

前戏做足了!接下来就是动态代理的魔法时刻。

要用JDK动态代理,我们得使用到Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法得到我们增强后的对象

我们来看看它的三个参数

1.ClassLoader

类加载器,jvm启动时三个类加载器,分别是,

bootstrap 加载系统类用的
extclassloader 加载扩展类用的
appclassloader 加载应用类用的

我们写的这些类都是由appclassloader加载,因此大可不必拘泥这个ClassLoader是哪个类的,直接对应用类使用getClassLoader就好啦!比方说可以填这个

1
User.class.getClassLoader()

2.Class<?>interfaces

可以看出这个就是我们User类实现的一系列接口,我们直接老老实实填入User类所实现的接口就好啦

1
new Class[]{IUser.class}

3.InvocationHandler

这个东东是一个接口,我们的增强代码都是填写在这个接口里面的,看到这个接口里的代码可以知道它也是一个函数式接口,除了匿名内部类、外部类之外的写法,还可以用lambda表达式来做实现

这里我就用外部类实现了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class UserAgent implements InvocationHandler {
IUser user;
public UserAgent(IUser user){
this.user=user;//把被代理的对象拿过来
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* proxy是当前这个代理对象的实例
* method是切入点(被增强的方法)
* args是切入点的参数
* */
System.out.println("执行前增强");
int result = (Integer)method.invoke(user,args);//反射执行被代理对象的方法
System.out.println("执行后");
return result;
}
}

然后再原来参数的位置填上这个即可

1
new UserAgent(new User())

『补完参数后的代码』

1
Proxy.newProxyInstance(User.class.getClassLoader(), new Class[]{IUser.class},new UserAgent(new User()));

这样一个通过Proxy创建的代理对象,就变成了IUser接口的另一个具有增强功能的实现类,我们把他作为IUser接收下来

1
IUser myUser = (IUser) Proxy.newProxyInstance(User.class.getClassLoader(), new Class[]{IUser.class},new UserAgent(new User()));

接下来就可以用我们增强接口实现类的add方法啦!

1
2
int result = myUser.add(1, 2);
System.out.println(result);

执行的结果如下

1
2
3
4
执行前增强
User 对象 add 方法执行
执行后
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
package test.hugh;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class PureAOP {
public static void main(String[] args) {
System.out.println(User.class.getClassLoader());
IUser myUser = (IUser) Proxy.newProxyInstance(User.class.getClassLoader(), new Class[]{IUser.class},new UserAgent(new User()));
int result = myUser.add(1, 2);
System.out.println(result);
}
}
class UserAgent implements InvocationHandler {
IUser user;
public UserAgent(IUser user){
this.user=user;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(""+method.getName());
int result = (Integer)method.invoke(user,args);
System.out.println("执行后");
return result;
}
}

无接口的实现

在这里我们需要用到CGLib

添加maven依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

和JDK动态代理十分类似,这里不再赘述了,与JDK动态代理的区别在于,CGLib的动态代理更像是给被代理的类创建了一个子类

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 test.hugh;

public class Person {
public int add(int a,int b){
System.out.println("Person 的 add 方法执行");
return a+b;
}
}
package test.hugh;

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

import java.lang.reflect.Method;

public class PersonAgent implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前");
Integer result = (Integer) methodProxy.invokeSuper(o,objects);//调用切入点
System.out.println("执行后");
return result;
}
}

下面是用Enhancer创建Person代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
package test.hugh;

import net.sf.cglib.proxy.Enhancer;

public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback(new PersonAgent());
Person son = (Person) enhancer.create();
System.out.println(son.add(1, 1));
}
}

这是输出

1
2
3
4
5
6
执行前
Person 的 add 方法执行
执行后
2

Process finished with exit code 0

评论