文章目录
前言
关于动态代理的博客有很多,本文是对于作者自己看过的博客的总结
为什么需要代理?
程序的设计原则:扩展开放,修改关闭;在一个项目中假设已经写好了一个功能(一个功能可以理解为就是一个方法)
,突然有一个需求,在这个功能上面加新的功能;如果直接在原来的代码上进行添加代码,会使得代码维护起来的成本变高;为什么成本会变高?突然那个功能不要了,是不是还得去删除多余的代码,维护起来十分的麻烦;此时可以引用出来代理的概念;
既然直接在原来的代码上面修改比较麻烦,那么我创建出来一个代理类,代理类上面有一个方法,这个方法里面既可以调用原来的方法(原有的功能不变)
,又可以在原来方法的上下文添加自己需要的代码(达到扩展了原来的功能的效果)
,符合开闭原则,是一种比较好的方式;
而代理分为动态代理以及静态代理;
怎么实现代理 核心是创建出来代理对象?
静态代理 - 代理对象的创建过程
所谓的静态代理就是我给每一个需要添加新功能的类都添加一个代理,这个时候,达到了扩展功能的需要,但是每个类都加一个代理,实在是没有必要,造成的代码的冗余量太大了,有点儿不好,此时引出来了动态代理(见下面描述);
静态代理需要的东西:
1、定义一个接口与以及其实现类
2、创建一个代理类同样实现这个接口
3、将目标对象注入到代理类,然后在代理类的对应方法调用目标类中的对应方法。在目标类的对应方法调用前后加入自己想要增加的逻辑,实现代码增强,达到代理的作用
动态代理 - 代理对象的创建过程 JDK
JDK 代理对象的创建本质上就是Proxy.newProxyInstance(loader, interfaces, handler);
方法中的三个参数进行传递,三个参数传递成功之后,代理对象就创建出来了;
// newProxyInstance 里面的三个参数的含义
loader – the class loader to define the proxy class
interfaces – the list of interfaces for the proxy class to implement
handler – the invocation handler to dispatch method invocations to
1.ClassLoader loader:加载代理对象的类加载器
2.Class<?>[] interfaces:代理类需要实现的接口,返回值类型即为该接口的类型
3.InvocationHandler h:调用处理器,通过重写其invoke方法扩展被代理对象的方法内容
所谓的动态代理,就是我创建一个大家通用的代理,所有需要修改的类,也就是需要添加新功能的类,使用这一个代理;在 Java 中这个动态代理就是:实现了 InvocationHandler 的类
模仿动态代理实现,本文需要四个类:
1 一个原始的接口;
2 一个实现了原始接口的类;
3 (一个实现了InvocationHandle的类)
可以在 invoke 方法中添加功能以及调用原来的功能,也就是实现了功能的扩展但是没有修改原来的代码;
上面的准备工作结束之后,满足了产生动态代理对象的所有东西,也就是 Proxy.newProxyInstance(loader, interfaces, handler);
里面的三个参数都有了,那么就可以使用代理对象原始的代码进行功能的增强;
在产生代理对象实例调用方法的的之前,会自动的创建一个代理对象类,不然实例是没有办法产生的;
上述的需要的四个类具体的代码实现
一个原始的接口,定义了一系列的方法;
public interface UserDao {
void saveUser();
}
一个实现了原始接口的类,因为需要对这个类进行增加功能,增强处理,也就是增强的目标;
public class UserDaoImpl implements UserDao{
@Override
public void saveUser() {
System.out.println(" ---- 保存用户 ---- ");
}
}
实现了 InvocationHandler 接口的的类,为了创建代理对象做准备;
public class UserHandler implements InvocationHandler
public class UserHandler implements InvocationHandler {
private UserDao userDao;
public UserHandler(UserDao userDao){
this.userDao = userDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强的代码,加入的新功能
saveUserStart();
// 原来的代码
Object obj = method.invoke(userDao, args);
// 增强的代码,加入的新功能
saveUserDone();
return obj;
}
public void saveUserStart(){
System.out.println("---- 开始插入 ----");
}
public void saveUserDone(){
System.out.println("---- 插入完成 ----");
}
}
一个使用代理对象产生的类调用方法的类;(也就是一个测试类);
public static void dynamicProxy(){
// 这个实例可以动态的创建,为了给代理类传递进去参数使用,不一定是创建 userDao 对象 ,创建任意对象都行
UserDao userDao = new UserDaoImpl();
// 创建出来 InvocatioinHandler 的一个实例,因为我们在上面重写了 UserHandler 的 invoke() 方法,目的是:通过重写其invoke方法扩展被代理对象的方法内容,在重写方法的前后添加自己需要的逻辑代码
InvocationHandler handler = new UserHandler(userDao);
// 自动创建出来的代理类的类加载器,和被代理的类使用同样的方式加载进去,也就是同样的类加载器加载进去
ClassLoader loader = userDao.getClass().getClassLoader();
// 代理对象需要实现的接口
Class<?>[] interfaces = userDao.getClass().getInterfaces();
// 创建一个代理类的实例,代理类底层自动创建,这里实例化,方法调用方法
// 反射创建代理实例
// 代理对象创建之前,代理类会在底层自动的生成,他里面有 UserHandler invoke 的代码执行逻辑
/**
上面的代码的核心目的就是创建出来一个代理类的实例,也就是代理类的对象;
创建一个完整的代理类对象,需要相关的三个参数,将三个参数创建,整合就是一个代理对象;
创建出来的代理对象中,不仅存在自己原来的方法,同时也是存在增强的代码的;
*/
UserDao proxy = (UserDao)Proxy.newProxyInstance(loader, interfaces, handler);
// 实际上执行的是自动创建的代理类的 invoke 方法,不是最原始的实现了接口的那个类里面的代码,因为 invoke 被重写了,添加了自己的逻辑代码进去了
// 执行的是代理对象的方法,原来最开始的方法被增强了。
proxy.saveUser();
}
invoke函数的第一个参数是调用该方法的实例,如果该方法是静态方法,那么可以用null或者用类来代替,第二个参数是变长的,是调用该方法的参数。
动态代理中产生的代理类长什么样子可以通过下面博客得到
总结动态代理类实现的逻辑就是:主要功能是对所有的方法进行初始化,到执行某个方法的时候调用我们自己实现的代理类去执行扩展功能和原始类的方法.