动态代理
概念
无侵入式的给方法增强功能

动态代理三要素
- 真正干活的对象
- 代理对象
- 利用代理调用方法
切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Test { public static void main(String[] args) {
BigStar bigStar = new BigStar("鸡哥"); Star proxy = ProxyUtil.createProxy(bigStar);
String result = proxy.sing("只因你太美"); System.out.println(result); } }
|
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 53 54 55 56 57 58 59
|
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sing".equals(method.getName())){ System.out.println("准备话筒,收钱"); }else if("dance".equals(method.getName())){ System.out.println("准备场地,收钱"); } return method.invoke(bigStar,args); } } ); return star; } }
|
1 2 3 4 5 6 7
| public interface Star { public abstract String sing(String name); public abstract void dance(); }
|
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
| public class BigStar implements Star { private String name;
public BigStar() { }
public BigStar(String name) { this.name = name; }
@Override public String sing(String name){ System.out.println(this.name + "正在唱" + name); return "谢谢"; }
@Override public void dance(){ System.out.println(this.name + "正在跳舞"); }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String toString() { return "BigStar{name = " + name + "}"; } }
|
Proxy.newProxyInstance理解
Proxy.newProxyInstance 是 Java 中用于创建动态代理对象的静态方法,它的作用是根据提供的类加载器、接口数组和 InvocationHandler 对象来动态生成一个代理对象。
具体来说,Proxy.newProxyInstance 方法的签名如下:
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException```
参数解释如下:
- `loader`:类加载器,用于加载代理类。通常可以使用被代理类的类加载器(`getClass().getClassLoader()`),或者系统类加载器(`ClassLoader.getSystemClassLoader()`)。 - `interfaces`:一个接口数组,代理类需要实现这些接口。 - `h`:实现了 `InvocationHandler` 接口的对象,处理代理对象上的方法调用。
`Proxy.newProxyInstance` 方法的返回值是一个实现了指定接口数组的代理类实例,这个代理类会在运行时动态生成,实现了指定的接口,并且在调用接口方法时会委派到 `InvocationHandler` 的 `invoke` 方法中进行处理。
**使用方法示例**:
假设有一个接口 `UserService` 和一个实现了该接口的类 `UserServiceImpl`,我们可以使用 `Proxy.newProxyInstance` 来创建一个代理对象,对方法调用进行拦截和增强:
```java public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); InvocationHandler handler = new MyInvocationHandler(userService); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[] { UserService.class }, handler ); proxy.getUser(1); proxy.saveUser(new User("Alice")); } }
interface UserService { User getUser(int id); void saveUser(User user); }
class UserServiceImpl implements UserService { public User getUser(int id) { System.out.println("Fetching user with id " + id); return new User("John"); } public void saveUser(User user) { System.out.println("Saving user: " + user.getName()); } }
class MyInvocationHandler implements InvocationHandler { private final UserService userService;
public MyInvocationHandler(UserService userService) { this.userService = userService; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before calling method: " + method.getName());
Object result = method.invoke(userService, args);
System.out.println("After calling method: " + method.getName());
return result; } }
class User { private String name;
public User(String name) { this.name = name; }
public String getName() { return name; } }
|
在上述示例中,通过 Proxy.newProxyInstance 方法创建了一个代理对象 proxy,并在 MyInvocationHandler 中实现了在方法调用前后输出日志的逻辑。当通过 proxy 调用 UserService 的方法时,实际上是调用 MyInvocationHandler 的 invoke 方法,从而实现了动态代理的功能。
总结来说,Proxy.newProxyInstance 方法是 Java 动态代理机制的核心之一,能够在运行时生成代理对象,为程序提供了非常灵活的扩展和拦截能力。
三个参数:
- 第一个参数:类加载器用于加载类,把哪个目标类增强
- 第二个参数:该动态代理方法是创建一个接口的Proxy的实现类,所以指定要给那个接口创建Proxy的实现
- 第三个参数:就是实现接口InvocationHandler,目的是使用接口中的invoke方法把目标对象中的方法增强
invoke方法理解
动态代理中的 invoke 方法是 java.lang.reflect.InvocationHandler 接口中定义的方法,用于处理代理对象方法的调用。让我们逐步解释这个方法的作用和理解方式:
- InvocationHandler 接口:动态代理需要实现
InvocationHandler 接口,该接口只定义了一个方法 invoke,用于在代理对象上调用方法时进行拦截和增强。
- invoke 方法签名:
1
| Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
|
- `proxy`:代理对象本身,通常情况下在 `invoke` 方法中很少直接使用。
- `method`:被调用的方法对象,通过它可以获取方法的信息(如方法名、参数类型等)。
- `args`:方法调用时传入的参数数组。
- invoke 方法的作用:
- 当通过代理对象调用方法时,实际上是调用了
InvocationHandler 的 invoke 方法。
- 在
invoke 方法中,可以编写代码来实现自定义的逻辑,例如在调用真实对象方法之前或之后执行额外的操作,或者根据条件选择是否调用真实对象的方法。
- 动态代理的实现:
- 动态代理不需要提前知道代理类的具体方法,而是在运行时根据
InvocationHandler 的 invoke 方法的实现动态生成代理类。
- 通过
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 方法可以创建动态代理对象,其中 h 就是实现了 InvocationHandler 接口的对象。
- 示例理解:
假设有一个接口 UserService,动态代理的 invoke 方法可以这样实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class MyInvocationHandler implements InvocationHandler { private final UserService userService;
public MyInvocationHandler(UserService userService) { this.userService = userService; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before calling method: " + method.getName());
Object result = method.invoke(userService, args);
System.out.println("After calling method: " + method.getName());
return result; } }
|
在上述示例中,invoke 方法会在调用 userService 的方法之前和之后输出日志,可以根据实际需求编写更复杂的逻辑,比如权限检查、事务管理等。
总结来说,invoke 方法是动态代理的核心,通过它可以实现对代理对象方法的拦截和增强,从而达到控制和扩展方法调用的目的。
三个参数:
- 参数一:通过目标对象的接口生成的Proxy对象
- 参数二:调用真实对象的方法
- 参数三:方法传入的参数
只增强一个类中的其中一个方法
在invoke方法的实现中,加入判断
在 Java 中使用动态代理时,InvocationHandler 接口的 invoke 方法是动态代理的核心。这个方法决定了在代理对象上调用方法时的行为。在 invoke 方法中,你可以实现对方法调用的拦截和增强逻辑。
具体到你的问题,如果一个类中有两个方法,但你只想增强其中一个方法,你可以在 invoke 方法中根据需要区分不同的方法调用,然后针对特定的方法进行增强操作。
下面是一个简单的示例,假设有一个接口 UserService 和一个实现类 UserServiceImpl,其中有两个方法 getUser 和 saveUser。我们只想增强 saveUser 方法的执行:
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 53 54 55 56 57 58 59 60 61 62 63 64 65
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
interface UserService { void getUser(int id); void saveUser(String name); }
class UserServiceImpl implements UserService { public void getUser(int id) { System.out.println("Fetching user with id " + id); } public void saveUser(String name) { System.out.println("Saving user: " + name); } }
class MyInvocationHandler implements InvocationHandler { private final UserService target;
public MyInvocationHandler(UserService target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("saveUser")) { System.out.println("Before saving user");
Object result = method.invoke(target, args);
System.out.println("After saving user");
return result; } else { return method.invoke(target, args); } } }
public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[] { UserService.class }, new MyInvocationHandler(userService) ); proxy.getUser(1); proxy.saveUser("Alice"); } }
|
在上述示例中,MyInvocationHandler 的 invoke 方法中通过 method.getName().equals("saveUser") 来判断当前调用的是 saveUser 方法,然后在调用前后添加了额外的输出。对于其他方法(比如 getUser 方法),直接调用了实际对象的方法。
这种方式可以根据实际需求灵活地对特定方法进行增强,而不影响其他方法的正常调用。
必须创建接口的实现类
在 Java 中使用动态代理时,确实需要有一个实际的类来实现你的接口。动态代理实际上是在运行时创建一个实现了指定接口的代理对象,但是它并不会创建接口的实现类。代理对象会通过 InvocationHandler 的 invoke 方法来处理所有接口方法的调用,但是实际的业务逻辑还是需要由接口的实现类来执行。
具体来说:
- 接口定义:你需要一个 Java 接口来定义你希望代理对象实现的方法。
- 实现类:你需要一个实际的类来实现这个接口,并提供具体的方法实现。
- 动态代理:通过
Proxy.newProxyInstance 方法创建一个代理对象,该对象实现了你指定的接口,并通过 InvocationHandler 的 invoke 方法来处理方法调用。
- InvocationHandler:在
invoke 方法中,你可以根据需要处理接口方法的调用,比如在方法调用前后添加额外的逻辑、进行权限检查等。但是最终,你仍然需要调用实际实现类的方法来执行业务逻辑。
在示例中,代理对象通过 method.invoke(target, args) 调用了实际的实现类 target 的方法。这个 target 是在 MyInvocationHandler 构造方法中传入的实现类对象。
所以,为了使用动态代理,你确实需要一个接口和一个实现了这个接口的类。动态代理机制不会自动创建接口的实现类,它仅提供了在运行时动态生成实现了接口的代理对象的能力。
如何获取到实现类被增强的方法
在动态代理中,通过 InvocationHandler 的 invoke 方法获取实现类中的方法是通过 Java 反射机制实现的。详细解释一下如何在 invoke 方法中获取实现类中的具体方法,并对其进行调用:
- 通过反射获取方法信息:
在 invoke 方法中,Method 对象提供了有关方法的信息,包括方法名称、参数类型等。当代理对象调用某个方法时,Method 参数传入了要调用的方法的信息。
- 调用实际对象的方法:
使用 Method 对象的 invoke 方法可以调用实际对象(在这里是实现类)中的方法。这是因为 Method 对象保存了方法的定义,可以通过反射机制动态地调用。
- 实现类对象的传递:
在 MyInvocationHandler 中,通常在构造方法中会传递一个实现了接口的实际对象(也称为目标对象或委托对象),例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class MyInvocationHandler implements InvocationHandler { private final UserService target;
public MyInvocationHandler(UserService target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result; } }
|
在 invoke 方法中,使用 method.invoke(target, args) 来调用实际对象 target 中对应的方法,并传递给它需要的参数 args。
- 处理方法调用前后的逻辑:
在 invoke 方法中,你可以在调用实际方法之前和之后添加额外的逻辑。例如,在调用前记录日志,或者在调用后进行一些资源清理等操作。
总结来说,动态代理通过 InvocationHandler 的 invoke 方法实现对方法调用的拦截和增强。通过传入实现了接口的实际对象,我们可以在 invoke 方法中利用反射调用这些实际对象中的方法,实现动态代理的功能。
额外拓展
动态代理,还可以拦截方法
比如:
在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。
但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public class ProxyUtil { public static Star createProxy(BigStar bigStar){ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("cleanWC".equals(method.getName())){ System.out.println("拦截,不调用大明星的方法"); return null; } return method.invoke(bigStar,args); } } ); return star;
|
练习
对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| ```java public class MyProxyDemo1 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>();
List proxyList = (List) Proxy.newProxyInstance( MyProxyDemo1.class.getClassLoader(), new Class[]{List.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("add")) { long start = System.currentTimeMillis(); method.invoke(list, args); long end = System.currentTimeMillis(); System.out.println("耗时时间:" + (end - start)); return true; }else if(method.getName().equals("remove") && args[0] instanceof Integer){ System.out.println("拦截了按照索引删除的方法"); return null; }else if(method.getName().equals("remove")){ System.out.println("拦截了按照对象删除的方法"); return false; }else{ method.invoke(list,args); return null; } } } );
proxyList.add("aaa"); proxyList.add("bbb"); proxyList.add("ccc"); proxyList.add("ddd");
proxyList.remove(0); proxyList.remove("aaa");
System.out.println(list); } }
|