什么是代理模式
代理模式(Proxy Pattern)给某一个对象提供一个代理,并且由代理对象控制原对象得引用,代理对象在客户端和目标对象之间起到中间作用。
代理模式是常用得结构设计模式之一,当直接访问某些对象存在问题时候可以通过一个代理对象间接访问,为了保证客户端使用透明性,所访问得真实对象需要实现相同得接口。
代理模式可以分为静态代理和动态代理两种类型,而动态代理中又分为JDK动态代理和CGLIB代理两种。我们今天主要讲动态代理。
动态代理定义及应用
动态代理:是使用反射和字节码的技术,在运行期创建指定接口或类的子类,以及其实例对象的技术,通过这个技术可以无侵入的为代码进行增强。
动态代理应用非 常的广泛,在各种开源的框架中都能看到他们的身影,比如spring中的aop使用动态代理增强,mybatis中使用动态代理生成mapper。
JDK实现的动态代理
解析
利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
jdk实现的动态代理由两个重要的成员组成,分别是Proxy
、InvocationHandler
-
Proxy: 是所有动态代理的父类,它提供了一个静态方法来创建动态代理的class对象和实例
-
InvocationHandler: 每个动态代理实例都有一个关联的
InvocationHandler
,在代理实例上调用方法是,方法调用将被转发到InvocationHandler
的invoke方法
1、代码实现
IStudentService接口
/**
* @Author charles.yao
* @Description 抽象角色,一般都是用接口,或者抽象类解决。
* @Date 2022/10/21 15:32
*/
public interface IStudentService {
/**
* 增加
*/
public void addStudent();
/**
* 删除
*/
public void deleteStudent();
/**
* 更新
*/
public void updateStudent();
/**
* 查询
*/
public void selectStudent();
}
实现类StudentServiceImpl
/**
* @Author charles.yao
* @Description 学生信息service
* @Date 2022/10/21 15:32
*/
@Slf4j
@Service
public class StudentServiceImpl implements IStudentService {
@Override
public void addStudent() {
log.info("执行了addStudent");
}
@Override
public void deleteStudent() {
log.info("执行了deleteStudent");
}
@Override
public void updateStudent() {
log.info("执行了updateStudent");
}
@Override
public void selectStudent() {
log.info("执行了selectStudent");
}
}
ProxyInvocationHandler代理类,实现InvocationHandler接口重写invoke方法
/**
* @Author charles.yao
* @Description 实现InvocationHandler接口重写invoke方法, 自动生成一个代理类
* @Date 2022/10/21 15:35
*/
@Slf4j
public class ProxyInvocationHandler implements InvocationHandler {
//被代理得目标接口
private Object target;
public ProxyInvocationHandler() {
}
public ProxyInvocationHandler(Object target) {
this.target = target;
}
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成需要得动态代理类
*
* @return
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
/**
* 处理代理的实例,并返回结果
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("方法:{}被执行", method.getName());
//TODO 执行方法前需要增强得部分
log.info("执行方法前,需要对学生信息进行校验>>>>>>");
Object invoke = method.invoke(target, args);
log.info("执行方法结束>>>>>>");
return invoke;
}
}
测试方法
/**
* @Author charles.yao
* @Description jdk动态代理测试
* @Date 2022/10/21 15:44
*/
public class JdkProxyTest {
public static void main(String[] args) {
//真实类
StudentServiceImpl studentService = new StudentServiceImpl();
//代理角色,不存在的,
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
//设置要代理得对象
proxyInvocationHandler.setTarget(studentService);
//动态生成代理类
IStudentService iStudentService = (IStudentService) proxyInvocationHandler.getProxy();
iStudentService.addStudent();
}
测试结果
16:02:38.053 [main] INFO com.test.ProxyInvocationHandler - 方法:addStudent被执行
16:02:38.064 [main] INFO com.test.ProxyInvocationHandler - 执行方法前,需要对学生信息进行校验>>>>>>
16:02:38.064 [main] INFO com.test.StudentServiceImpl - 执行了addStudent
16:02:38.066 [main] INFO com.test.ProxyInvocationHandler - 执行方法结束>>>>>>
CGlib动态代理
1、解析
CGLIB(Code Generation Library)
是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承的方式实现代理(最后这部分我们深思一下,它可能有哪些局限,final方法是不能够被重写,所以它不能增强被final修饰的方法,这个等下我们来验证)
CGLIB的实现也有两个重要的成员组成,Enhancer
、MethodInterceptor
,其实这两个的使用和jdk实现的动态代理的Proxy
、InvocationHandler
非常相似
- Enhancer: 来指定要代理的目标对象,实际处理代理逻辑的对象,最终通过调用
create()
方法得到代理对象、对这个对象所有的非final方法的调用都会转发给MethodInterceptor
- MethodInterceptor: 动态代理对象的方法调用都会转发到
intercept
方法进行增强
2、代码示例
CGlib不像是JDK动态代理,CGlib需要导入Jar包,那么我用SpringBoot直接导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
StudentServiceImpl得类
/**
* @Author charles.yao
* @Description 学生信息service
* @Date 2022/10/21 15:32
*/
@Slf4j
@Service
public class StudentServiceImpl implements IStudentService {
@Override
public void addStudent() {
log.info("执行了addStudent");
}
@Override
public void deleteStudent() {
log.info("执行了deleteStudent");
}
@Override
public void updateStudent() {
log.info("执行了updateStudent");
}
@Override
public void selectStudent() {
log.info("执行了selectStudent");
}
}
代理类StudentServiceCGlib
/**
* @Author charles.yao
* @Description CGlib 实现servce
* @Date 2022/10/21 16:21
*/
@Slf4j
public class StudentServiceCGlib implements MethodInterceptor {
//需要代理得类
private Object target;
public StudentServiceCGlib() {
}
public StudentServiceCGlib(Object target) {
this.target = target;
}
/**
* 返回一个代理对象,是target对象得代理对象 *
* 利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
*
* @return
*/
public Object getProxyInstance() {
//创建一个cglib工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类对象,就是代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.info("开始对代理对象增强啦>>>>>>>>>>");
//TODO 添加业务增强代码
Object result = methodProxy.invokeSuper(o, objects);
log.info("对代理对象增强结束啦>>>>>>>>>>");
return result;
}
}
测试类CGlibProxyTest
/**
* @Author charles.yao
* @Description CGlib 动态搭理测试类
* @Date 2022/10/21 16:28
*/
public class CGlibProxyTest {
public static void main(String[] args) {
StudentServiceCGlib studentServiceCGlib = new StudentServiceCGlib(new StudentServiceImpl());
//利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
StudentServiceImpl proxyInstance = (StudentServiceImpl) studentServiceCGlib.getProxyInstance();
proxyInstance.addStudent();
proxyInstance.selectStudent();
}
}
执行结果:
16:29:50.953 [main] INFO com.server.test.StudentServiceCGlib - 开始对代理对象增强啦>>>>>>>>>>
16:29:50.985 [main] INFO com.server.test.StudentServiceImpl - 执行了addStudent
16:29:50.985 [main] INFO com.server.test.StudentServiceCGlib - 对代理对象增强结束啦>>>>>>>>>>
16:29:50.985 [main] INFO com.server.test.StudentServiceCGlib - 开始对代理对象增强啦>>>>>>>>>>
16:29:50.985 [main] INFO com.server.test.StudentServiceImpl - 执行了selectStudent
16:29:50.985 [main] INFO com.server.test.StudentServiceCGlib - 对代理对象增强结束啦>>>>>>>>>>
最后总结
在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,JDK代理效率高于CGLIB代理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理
2、如果目标对象实现了接口,也可以强制使用CGLIB
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
关注我的微信公众号