Java学习笔记:静态代理,JDK代理和CGLIB代理

这篇文章不对JDK代理和CGLIB代理的内部实现细节讲解,这只是简单提一下如何使用,以及代理技术在实际中的用处。

先说一下静态代理。

明确一个原则,写出的代码投入到生产中后,最好不要对代码再进行修改。

比如我们在一个账户类中有一个取钱的函数。

public interface Count{
    public void getMoney();
}
public class CountImpl implements Count{
    @Override
    public void getMoney() {
        System.out.println("取钱");
    }
}

写好这个类之后,前期确定没问题,于是代码就被投入到实际应用之中。但是过了一段时间后发现,应该在取钱这个操作的前后都加入一些操作更有利于用户的体验。如果可以对代码直接修改的话,写出来的代码就应该是这样。

public class CountImpl implements Count{
	public void getMoney() {
            System.out.println("取钱之前的操作");
            System.out.println("取钱");
            System.out.println("取钱之后的操作");
	}
}

但前面说了,代码投入到应用中后不要轻易修改。为了不修改源代码就完成我们新的需求,就可以用到代理技术。下面是静态代理的实现。

public interface Count{
    public void getMoney();
}

public class CountImpl implements Count{
    @Override
    public void getMoney() {
        System.out.println("取钱");
    }
}

public Class CountProxy implements Count{
    private Count count;
    public CountProxy(Count count){
        this.count = count;
    }
    public getMoney(){
        System.out.println("取钱之前的操作");
        count.getMoney();
        System.out.println("取钱之后的操作");
    }
}

第三个类,CountProxy就是代理类。在后端定义了代理类之后,在前端调用时,就可以实现不修改原来的函数而多增加一些操作,代码如下。

Count count = new CountImpl();
CountProxy proxy = new CountProxy(count);
proxy.getMoney();

输出结果

 


JDK动态代理

JDK动态代理是基于接口实现的动态代理。通俗地说就是被代理的类必须实现了一个接口。被实现的接口中的方法就是需要被代理的方法。下面将举例说明。

我们定义一个接口,接口中定义了两个方法。

public interface Interface1{
    public void test1();
    public void test2();
}

然后用一个类实现这个接口

public class Target implements Interface1{
    public void test1(){
        System.out.println("test1");
    }
    public void test2(){
        System.out.println("test2");
    }
}

写完后投入生产后运行一段时间后发现,两者都需要在执行之前后执行之后都需要执行一些相同的操作才符合业务需要,比如下面这样

操作1;
test1();
操作2;

操作1;
test2();
操作2;

如果使用静态代理的方法,那么就要写两个静态代理的函数,而且两者的相似度极高这时候我们就可以使用JDK动态代理简化代码量。

下面使用JDK动态代理

public class TargetProxy implements InvocationHandler{
    private Objcet target = null;
    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), this);
	}
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
        System.out.println("进入代理前的操作");
        //这相当于是在调用被代理方法
        Object obj = method.invoke(target,  args);
        System.out.println("进入代理后的操作");
        return obj;
    }
}

上面的代码算是套路模板了,使用JDK动态代理就基本可以照搬上面的代码,我们只需要在invoke()函数中定制自己的操作即可。

TargetProxy就是Target的代理类,invoke()函数是用定制代理操作的。bind()是用于确定需要被代理的对象。

其中具体的函数的实现可能会再以后的源码部分说,这篇文章只是介绍如何使用JDK动态代理。下面是使用的方式

TargetProxy targetProxy = new TargetProxy();
Interface1 proxy = (Interface1) targetProxy.bind(new Target());
proxy.test1();
proxy.test2();

bind()的参数是被代理的对象,也就是target。该函数返回代理类proxy。proxy代理Target,也就是说,Target实现的接口中的所有方法都被代理了。其中proxy的类型,也就是bind()方法返回的类型,只能是target实现的接口类型,这什么意思勒,比如下面这两个接口

public interface Interface1 {
    public void test1();
}

public interface Interface2{
    public void test2();
}

Target都实现这两个接口

public class JdkClass implements Interface2, Interface1{
    @Override
    public void test1() {
        System.out.println("test1");
    }

    @Override
    public void test2() {
        System.out.println("test2");
    }
}

那么bind()函数返回的代理类的类型可以被看成Interface1,也可以被看成Interface2,而且只能被看成这两个接口。

public class Main implements InvocationHandler {
    private Object target = null;
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("进入代理钱的方法");
        Object obj = method.invoke(target, args);
        System.out.println("进入代理后的方法");
        return obj;
    }
    public static void main(String[] args) {
        Main main = new Main();
        //bind返回值可以被看成Interface1类型
        Interface1 proxy1 = (Interface1) main.bind(new JdkClass());
        proxy1.test1();
        //bind返回值可以被看成Interface2类型
        Interface2 proxy2 = (Interface2) main.bind(new JdkClass());
        proxy2.test2();
    }
}

输出结果是

Java学习笔记:静态代理,JDK代理和CGLIB代理_第1张图片

 

 

 

 


CGLIB动态代理

CGLIB动态代理和JDK动态代理几乎一样,其使用方式也是固定的,功能和JDK动态代理完全一样。唯一不同的点是,JDK代理的类必须实现至少一个接口,而CGLIB没有这个限制,完全可以代理一个普通的类。

CGLIB使用方式如下。

先定义一个Target类作为需要被代理的真实对象

public class Target{
    public void test(){
        System.out.println("test");
    }
}

现在定义代理类,代理类的写法也是固定的,唯一需要改变的是intercept()函数。

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
    public Object getProxy(Class claz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(claz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("进入代理前的方法");
        Object result = methodProxy.invokeSuper(proxy, args);
        System.out.println("进入代理后的方法");
        return result;
    }
}

运行代理,实现效果和JDK代理一样。

CglibProxy cglibProxy = new CglibProxy();
Target target = (Target) cglibProxy.getProxy(Target.class);
target.test();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Java基础)