Spring 静态代理和动态代理
动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。
静态代理:
优点:
1、 实现松散耦合。
2、做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点:
如果项目中有多个类,则需要编写多个代理类,工作量大,不好修改,不好维护,不能应对变化。
模拟某位学生去考试作为例子:
下面创建 考试接口
public interface Examable {
void exam();
}
创建Student学生实现这个考试接口(被代理)
public class Student implements Examable {
@Override
public void exam() {
System.out.println("奋笔疾书,完成考题。。。");
}
}
创键Cheater(代理)对象,同时也去实现Examable接口
public class Cheater implements Examable {
//被代理对象
private final Examable student;
public Cheater(Examable student){
this.student=student;
}
@Override
public void exam() {
System.out.println("在现场唱了一首歌,差点被劝退");
student.exam();//调用Student类的方法
}
}
测试:
public class Main {
//组合优于继承
public static void main(String[] args) {
//cheater 就是一个代理对象
//因为它是受student 委托,完成某个功能
//它要完成的主要功能,来自student
//除了委托做的事情,可能还会扩展一些行为
Examable xiaoming = new Student();//原来的行为
xiaoming.exam();
System.out.println("------下面是代理行为------");
Examable cheater = new Cheater(xiaoming);
cheater.exam();
}
}
结果:
奋笔疾书,完成考题。。。
------下面是代理行为------
在现场唱了一首歌,差点被劝退
奋笔疾书,完成考题。。。
动态代理:
使用JDK内置的Proxy实现
接着拿上面作为例子
下面创建 考试接口
public interface Examable {
void exam();
}
创建Student学生实现这个考试接口(被代理)
public class Student implements Examable {
@Override
public void exam() {
System.out.println("奋笔疾书,完成考题。。。");
}
}
创建一个 JdkProxy 类 实现 InvocationHandler 接口
public class JdkProxy implements InvocationHandler {
private Object object;//被代理
public JdkProxy() {
}
public JdkProxy(Object object) {
this.object = object; 初始化的时候就赋值
}
/**
* 当用户调用对象中的每个方法时都通过下面的方法执行,方法必须在接口
* proxy 被代理后的对象
* method 将要被执行的方法信息(反射)
* args 执行方法时需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Object invoke = null;
try {
invoke = method.invoke(object, args);
} catch (Exception e) {
System.out.println("异常的信息" + e.getMessage());
}
return invoke;//调用被代理对象原来的方法(行为)
}
}
测试:
public class Main {
public static void main(String[] args) {
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数person1.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
ClassLoader cl= Thread.currentThread().getContextClassLoader();
Examable o = (Examable) Proxy.newProxyInstance(
cl,//类加载器
new Class[]{Examable.class},//获取被代理对象的所有接口
new JdkProxy(new Student())//InvocationHandler对象
);
o.exam();//代理后的行为
}
}
结果:
使用内置的Proxy实现动态代理有一个问题:被代理的类必须实现接口,未实现接口则没办法完成动态代理。
2、动态代理,使用cglib实现
还是拿上面的例子来说吧。
这次我们不写接口了。不过 我们要实现MethodInterceptor接口,并实现方法
去maven 中心仓库 找到 CGlib 的依赖
Student类
![复制代码](https://i-blog.csdnimg.cn/blog_migrate/3261cce84d531bb17b20112813d07e17.gif)
public class Student {
public void exam(){
System.out.println("奋笔疾书,完成考试啦");
}
}
![复制代码](https://i-blog.csdnimg.cn/blog_migrate/3261cce84d531bb17b20112813d07e17.gif)
创建一个 CglibProxy 的类 并 实现 MethodInterceptor 接口 ,并实现方法
package com.nf147.sim.proxy.p5;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
/*
* 参数
* Object 为由CGLib动态生成的代理类实例
* method 为上文中实体类所调用的被代理的方法引用
* objects 为参数值列表
* methodProxy 为生成的代理类对方法的代理引用
* return 从代理实例的方法调用返回的值
* */
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o, objects);
}
}
测试:
package com.nf147.sim.proxy.p5;
import com.nf147.sim.proxy.p1.Student;
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
//第一种方式
//增强器,动态代码生成器
Enhancer enhancer = new Enhancer();
//设置 生成类 的 父类
enhancer.setSuperclass(Student.class);
//回调函数
enhancer.setCallback(new CglibProxy());
//动态生成字节码并返回代理对象
Student o = (Student) enhancer.create();
o.exam();
System.out.println("-----------");
//第二种方式
//这里是简化写法
//第一个参数 设置 生成类 的父类 ,第二参数 被代理类的所有接口 ,回调函数
Student student = (Student) Enhancer.create(Student.class, null, new CglibProxy());
student.exam();
}
}
结果: