目录
#0.什么是代理?
代理相当于是帮皇帝处理事务的大臣,在奏折给皇帝前先要给大臣过一遍,涂涂改改再呈上去。
但显然把程序比作“皇帝”不太合适,因为程序的效率可不是人能比的,那为什么程序也需要“大臣”呢?
原因有很多,列举几点:
1.当你往你的屎山的核心部分肆意的加东西,你不知道这会不会导致这坨东西塌了。
2.调用方法前可以通过代理进行安全检测/功能增强等,直接写在实现类显得臃肿且繁琐。
3.代理只有在实际调用时才会创建实现类,可以在一定程度上节省内存
代理分为静态代理与动态代理,如下:
#1.静态代理
静态代理非常简单,就是手动的去实现“大臣”,所有的奏折到皇帝之前都得进大臣的手里过一遍:
public class Minister implements Work{
Emperor emperor = new Emperor();
@Override
public void writeSomething() {
System.out.println("大臣处理后给皇帝");
emperor.writeSomething();
}
@Override
public void decide() {
System.out.println("大臣决策后给皇帝");
emperor.decide();
}
}
这里的大臣就形成了代理:所有方法得先经过大臣才能到皇帝手里。
而这种手动实现的代理,就称为静态代理。
#2.动态代理
如果想要更灵活的代理方式,就需要动态代理,它会根据目标类自动生成代理对象,而又分成两种:通过接口代理,通过类代理。
①通过接口代理
首先,代理类和目标类都得实现同一种接口(大臣和皇帝都得批奏折):
public interface Work {
public void writeSomething();
public void decide();
}
对于皇帝:
public class Emperor implements Work{
@Override
public void writeSomething() {
System.out.println("皇帝改奏折");
}
@Override
public void decide() {
System.out.println("皇帝决策");
}
}
这时,需要任命一个大臣代理:
public class CreateMinister {
public static Work creatMinister(Emperor emperor){ //传入代理目标类
Work Minister =(Work) Proxy.newProxyInstance(
//把返回的代理类转化成大臣类(都有Work接口)
ProxyUtil.class.getClassLoader(), //获取类加载器,了解即可
new Class[]{Work.class}, //获取方法表,也就是接口里面的方法
new InvocationHandler() { //开始定义具体的行为
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//第一个参数是代理对象,第二个是调用的方法,第三个是传入的参数
//当大臣类被调用时,都会调用这个方法
if(method.getName().equals("writeSomething"))
System.out.println("大臣处理后给皇帝");
else if(method.getName().equals("decide"))
System.out.println("大臣决策后给皇帝");
return method.invoke(emperor,args);
//调用指定对象的方法
}
}
);
return Minister; //返回经过设置的大臣
}
}
这个类创建了一个对emperor进行代理的代理类,拥有Work的所有方法,会对其中的方法进行自定义处理。
invoke意为调用的意思,意思是执行完自定义的操作后会调用代理目标的方法
当我们执行:
public class ToWork {
public static void main(String[] args) {
Emperor emperor = new Emperor();
Work Minister = CreateMinister.creatMinister(emperor);
Minister.writeSomething();
Minister.decide();
}
}
便可以看到结果:
大臣处理后给皇帝
皇帝改奏折
大臣决策后给皇帝
皇帝决策
②通过类代理
如果么有接口的话就得使用CGLIB来实现
CGLIB本质是会创建一个子类去继承目标类,对子类进行自定义来实现“代理”
CGLIB属于第三方库,得先引入:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>选择你的版本</version>
</dependency>
这里给出一个示例代码:
public class CglibProxyExample {
public static void main(String[] args) {
// 创建 CGLIB Enhancer 实例
Enhancer enhancer = new Enhancer();
// 设置需要代理的目标类
enhancer.setSuperclass(Emperor.class);
// 设置回调方法,用于拦截目标的方法调用
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 这里是设置代理的具体行为,参数和通过接口代理的意思大同小异
System.out.println("大臣处理:" + method.getName());
// 调用父类的方法
Object result = proxy.invokeSuper(obj, args);
return result;
}
});
//测试
// 创建代理对象
Emperor Minister = (Emperor) enhancer.create();
// 调用代理对象的方法
Minister.writeSomething();
}
}
注意!Java9以上 CGLIB 会被java因为安全性拦截!最好配合Spring使用!
#3.总结
其实无论是静态代理还是动态代理都是一个增强原有方法的方法,无非是通过代理人调用目标类。
动态代理中两者大同小异,相对比较简单。
但对于代理中是如何获得对应的方法,类以及权限,设计到了反射
总的来说,当你创建了一个代理对象,Java会自动把这个代理对象加上目标类的所有方法,然后让你来自定义在执行这个方法前/后还要额外干什么,代理就是如此。