一、代理概述
1. 概念
- 代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2. 分类
- 代理分为 静态代理 和 动态代理(包括 JDK动态代理、CGLIB动态代理)。
3. 区别
- 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。
- 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中。
二、JDK动态代理实现
1. JDK动态代理组成
- 1)实现接口,被代理的对象必须实现某个或某些接口;
- 2)拦截处理器,创建拦截处理器必须实现InvocationHandler接口并实现它的invoke方法;
- 3)反射机制,调用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法,通过反射机制创建代理对象;
其中,Proxy.newProxyInstance方法的参数是loader–加载接口的类加载器、interfaces–代理类实现的接口列表、h–拦截处理器。拦截处理器可以持有被代理对象,当然特殊情况也可以不持有被代理对象,比如不需要调用被代理对象的方法。
2. JDK动态代理原理图
3. JDK动态代理实例
- 创建接口 UserService
package com.java24k.example.service;
/**
* @Description: 用户接口
* @Author Java24K
* @Date 2020/7/9 7:37 下午
* @version: V1.0
*/
public interface UserService {
/**
* 增加用户
* @author Java24K
* @date 2020/7/9 7:38 下午
*/
void addUser();
}
- 创建接口实现 UserServiceImpl
package com.java24k.example.service.impl;
import com.java24k.example.service.UserService;
/**
* @Description: 用户接口实现
* @Author Java24K
* @Date 2020/7/9 7:38 下午
* @version: V1.0
*/
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("------增加一个用户------");
}
}
- 创建拦截处理器 UserInvocationHandler
package com.java24k.example.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Description: JDK动态代理拦截处理器
* @Author Java24K
* @Date 2020/7/9 7:41 下午
* @version: V1.0
*/
public class UserInvocationHandler implements InvocationHandler {
/**
* 真实对象 即被代理对象
*/
private Object realSubject;
public UserInvocationHandler(Object realSubject){
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码------");
// 执行被代理对象的目标方法
Object result = method.invoke(realSubject, args);
System.out.println("------插入后置处理代码------");
return result;
}
}
- 组装创建动态代理
package com.java24k.example.test;
import com.java24k.example.handler.UserInvocationHandler;
import com.java24k.example.service.UserService;
import com.java24k.example.service.impl.UserServiceImpl;
import java.lang.reflect.Proxy;
/**
* @Description: JDK动态代理测试类
* @Author Java24K
* @Date 2020/7/9 7:51 下午
* @version: V1.0
*/
public class JDKProxyTest {
public static void main(String[] args) {
// 创建真实对象 即被代理对象
UserService userService = new UserServiceImpl();
// 创建拦截处理器
UserInvocationHandler handler = new UserInvocationHandler(userService);
// 创建jdk动态代理
UserService jdkProxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
new Class[]{UserService.class}, handler);
// 使用调用动态代理 调用方法
jdkProxy.addUser();
}
}
- 运行结果
------插入前置通知代码-------------
---增加一个用户---
------插入后置处理代码-------------
Process finished with exit code 0
三、JDK动态代理优缺点
- 缺点:不能实现接口的类就不能用JDK动态代理。因为生成的代理类继承了Proxy类后无法多继承,所以只支持接口。
- 优点:java原生支持,不需要外部依赖。
四、JDK动态代理在框架中的运用
- Mybatis运用动态代理实现xml的Mapper文件和Mapper接口的映射。 xml的namespace中有接口的全路径名,mybatis根据全路径名生成代理,当调用接口的方法,实际是调用代理的方法,然后进入拦截器invoke方法,根据sql的类型执行不同的语句。注意,这个Mybatis动态代理没有真实的代理对象。
- Spring AOP 面向切面编程,就是使用代理在原方法前后做一些事情,即运行时增强,就用到JDK动态代理。