动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理实现:首先必须定义一个接口,还要有一个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类
public class Student { public void exam(){ System.out.println("奋笔疾书,完成考试啦"); } }
创建一个 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(); } }
结果: