前言:
在开发中一定被老大这样说过不要随意去修改别人已经写好的代码或者方法。但我们又希望原来的对象可以得到扩展,又能保护原对象,这时代理模式就出来了。
在百科中这样描述(定义):为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。aop的实现对方法前后进行拦截,改变原有对象或方法都是基于代理模式。代理模式分为静态代理和jdk动态代理。
静态代理
需求:张三看上了妹子,怕被拒绝而苦恼,于是李四说我替你把她约出来。
- 定义代理和被代理的公共的接口
//张三和李四的公共行为
public interface Person {
void meetGirl();
}
- 实现person接口,即具体动作的实现
//动作的具体实现
public class Man implements Person{
private String name;
public Man(String name){
this.name = name;
}
@Override
public void meetGirl() {
System.out.println(name+"正在和女孩吃饭,感谢你李四");
}
}
- 持有一个被代理对象的代理类,同样要实现person接口
//man代理类
public class ManProxy implements Person {
//被代理的对象
private Man man;
//代理
public ManProxy(Person man){
this.man = (Man) man;
}
//被代理的执行接口方法
@Override
public void meetGirl() {
man.meetGirl();
}
}
- client 测试,李四替张三约妹子
//代理的测试类
public class ProxyClient {
@Test
public void test(){
//被代理的对象
Person zhangsan = new Man("张三");
//生成代理对象李四 帮张三去约姑娘,把张三传给代理对象
Person lisi = new ManProxy(zhangsan);
lisi.meetGirl();
}
}
image.png
动态代理参考
代理类在程序运行时创建的代理方式被成为动态代理(生成相应的class文件在加入jvm)。 我们上面静态代理的例子中,代理类(ManProxy实现了接口和业务调用)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在代码中定义的,而是在运行时根据我们在代码中的“指示”动态生成的, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。动态代理分为两种,一种分为基于接口的jdk代理;另一种则是基于类的代理如cglib(spring aop 实现),javassist等。
jdk 动态代理
JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作;在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,实现InvocationHandler接口就可以生成被代理的对象。
- 假如如以上person 接口不变
- 业务逻辑的具体实现
public class RealPerson implements Person {
@Override
public void meetGirl() {
System.out.println("他们说你最美,来自jdk动态代理");
}
}
3 . 实现InvocationHandler接口定义代理类
//实现invocationHandler 接口的jdk动态代理类
public class SubjectHandler implements InvocationHandler {
//被代理的对象,包含相应的业务方法
private Object target;
//绑定被代理的对象
public SubjectHandler(Object target){
this.target = target;
}
// //或者如下调用是会更加简单 直接new SubjectHandler.bind(targect)
// public Object bind(Object target) {
// this.target = target; //接收业务实现类对象
//
// //通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
// //创建代理对象时,需要传递该业务类的类加载器、接口、handler实现类
// return Proxy.newProxyInstance(target.getClass().getClassLoader(),
// target.getClass().getInterfaces(), this);
// }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//具体要执行的逻辑
Object result = null;
System.out.println("代理前的操作-----");
//合并逻辑,并通过return决定是否执行
result = method.invoke(target,args);
System.out.println("方法处理后的操作-----");
//执行后续操作
return result;
}
}
- 测试
public class JdkProxyClient {
@Test
public void test(){
//需要代理的目标对象
RealPerson realPerson = new RealPerson();
//拦截器
SubjectHandler interceptor = new SubjectHandler(realPerson);
//生成代理类对象,执行代理类的业务方法
//newProxyInstance 参数:1.目标类的加载器 2.目标类接口 3.拦截器(处理的类)
Person person = (Person) Proxy.newProxyInstance(realPerson.getClass().getClassLoader(),realPerson.getClass().getInterfaces(),interceptor);
//执行代理的业务方法
person.meetGirl();
}
}
spring 中的cglib 动态代理
JDK动态代理的代理对象在创建时,需要根据接口内的方法名进行调用,如果业务实现类是没有实现接口或者业务实现类中新增了接口中没有的方法,就无法使用JDK动态代理了(因为无法被调用),spirng 的cglib是针对实现代理的需要实现MethodInterceptor 接口
- 假设以上实现类不变,接口可有可无,则定义代理类如下
//基于spring 的cglib的动态代理
public class CglibInterceptor implements MethodInterceptor {
//代理对象
private Object target;
//绑定被代理的对象
public Object createProxyObject(Object obj) {
this.target = obj;
//创建代理类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(this);
Object proxyObj = enhancer.create();
return proxyObj;// 返回代理对象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
//执行业务逻辑
System.out.println("我是cglib代理");
//合并逻辑
result = method.invoke(target,objects);
return result; //返回结果
}
}
- 测试
public class CglibProxyClient {
@Test
public void test(){
//目标对象
RealPerson realPerson = new RealPerson();
//拦截器
CglibInterceptor cglibInterceptor = new CglibInterceptor();
//获得代理对象,区别jdk动态,不需要接口
RealPerson cglib = (RealPerson) cglibInterceptor.createProxyObject(realPerson);
cglib.meetGirl();
}
}
image.png
总结
1.JDK动态代理只能对实现了接口的类生成代理,通过Proxy.newProxyInstance产生代理对象而不能针对类。
2.CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
网友评论