天天看點

java動态代理1、引言2、基本概念3、靜态代理4、JDK動态代理5、cglib動态代理6、目标是接口+注解的動态代理

1、引言

最近在看一些技術源碼的時候,發現很多地方都是動态代理, 真可謂是一切皆代理啊,是以我們需要弄明白代理模式這樣在看源碼的時候會好受很多。

2、基本概念

代理(Proxy)模式提供了間接通路目标對象的方式,即通過代理對象通路目标對象,這樣做的好處是:可以在目标對象的功能上,增加額外的功能補充,即擴充目标對象的功能。

這就符合了設計模式低開閉原則,即在對既有代碼不改動的情況下進行功能擴充。

舉個我們平常非常常見的例子, 明星和經紀人就是被代理和代理的關系,明細出演活動的時候,明細就是一個目标對象,他隻負責活動中的節目,而其他的瑣碎的事情就交給他的代理人(經紀人)來解決。這就是代理思想 中的一個例子。

3、靜态代理

靜态代理的特點:代理類和目标類必須實作 同一個接口或者繼承相同的父類。是以我們需要 定義一個接口。下面來看具體實作demo。

接口類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:28
 */
public interface Istart {

    void sing();
}
           

目标類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1416:07
 */
public class LDHStar implements Istart {

    @Override
    public void sing() {
        System.out.println("華仔唱歌");
    }
}
           

代理類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1416:08
 */
public class StaticPorxyManager implements Istart {

    private Istart target;
    public StaticPorxyManager(Istart target) {
        this.target = target;
    }

    @Override
    public void sing() {
        System.out.println("演唱會之前。。。。");
        this.target.sing();
        System.out.println("演唱會之後。。。。");

    }
}
           

測試類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:46
 */
public class StaticTeasMain {

    public static void main(String[] args) {
        Istart ldhStar = new LDHStar();

        StaticPorxyManager staticPorxyManager = new StaticPorxyManager(ldhStar);
        staticPorxyManager.sing();

    }
}
           

靜态代理類優缺點:

有點:可以在不修改目标代碼的情況下,擴折額外 功能

缺點:因為代理類和目标類必須實作相同的接口,是以會有很多代理類,類太多, 同時,一旦接口增加方法,目标對象很代理對象都需要維護。而動态代理可以解決上面的問題。

4、JDK動态代理

動态代理的主要特點就是能夠在程式運作時JVM才為目标對象生成代理對象。

常說的動态代理也叫做JDK 代理也是一種接口代理,之是以叫做接口代理,是因為被代理的對象也就是目标對象必須實作至少一個接口,沒有實作接口不能使用這種方式生成代理對象,JDK中生成代理對象的代理類就是Proxy,所在包是java.lang.reflect.

接口:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:28
 */
public interface Istart {

    void sing();
}
           

目标類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:29
 */
public class WangFengStar implements Istart {
    @Override
    public void sing() {
        System.out.println("汪峰唱歌。。。");
    }
}
           
InvocationHandler類:      
import java.lang.annotation.Target;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:32
 */
public class StarInvocationHandler implements InvocationHandler {

    private  Object target;
    public StarInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("演唱會前工作");
        //調用目标對象的目标方法
        method.invoke(target,args);
        System.out.println("演唱會後工作");
        return null;
    }
}
           

代理工廠:

import java.lang.reflect.Proxy;

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:30
 */
public class ProxyFactory {

    private Object target;
    private StarInvocationHandler starInvocationHandler;
    public ProxyFactory( Object target) {
        this.target = target;
        this.starInvocationHandler = new StarInvocationHandler(target);
    }

    public Object getProxyObject(){
       return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),starInvocationHandler);
    }
}
           

測試方法:

public class TeasMain {

    public static void main(String[] args) {
        Istart wangfengStar = new WangFengStar();
        Istart proxyObject = (Istart)new ProxyFactory(wangfengStar).getProxyObject();
        proxyObject.sing();
    }
}
           

JDK代理方式不需要代理對象實作接口,但是目标對象一定要實作接口,但是我們在項目中有很多需要代理的類并沒有實作接口,是以這也算是這種代理方式的一種缺陷。

5、cglib動态代理

如果我們 需要給沒有是實作任何接口的目标類生成代理對象,JDK方式是做不到的。這是就可以使用繼承目标類以目标對象子類的方式實作代理,這種方法就叫做Cglib代理,也叫做子類代理,他是在記憶體中建構一個子類對象進而 實作對目标對象功能的擴充。

Cglib是一個強大的高性能代碼生成包,他可以在運作期擴充java類和擴充java接口。它廣泛的被許多AOP架構使用,例如Sring AOP和synaop,為他們提供方法的intercepion(攔截)

Cglib包的底層是通過使用一個小而快的位元組碼處理架構ASM來轉換位元組碼并生成新的類。

Cglib子類代理實作方法:

1.需要引入cglib的jar檔案 cglib-2.2.2.jar asm-3.3.1.jar,但是Spring的核心包中已經包括了Cglib功能,是以直接引入s

pring-core-3.2.5.jar

即可.

2.引入功能包後,就可以在記憶體中動态建構子類

3.代理的類不能為final,否則報錯

4.目标對象的方法如果為final/static,那麼就不會被攔截,即不會執行目标對象額外的業務方法.

目标類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:57
 */
public class JieLunStar {

    public void sing(){
        System.out.println("傑倫唱歌");
    }
}
           

代理工廠:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:58
 */
public class CglibProxyFactory implements MethodInterceptor {

    //維護目标對象
    private Object target;

    public CglibProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * @Description:獲得目标類的代理對象
     * @author: zhenghao
     * @date: 2020/12/14 11:02
    */
    public Object getProxyObject(){
        //1、工具類
        Enhancer enhancer = new Enhancer();
        //2、設定父類
        enhancer.setSuperclass(target.getClass());
        //3、設定回調
        enhancer.setCallback(this);
        //4、建立代理類
        Object proxyObject = enhancer.create();
        return proxyObject;

    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("演唱會前工作");
        //執行目标對象的目标方法
        method.invoke(target,objects);
        System.out.println("演唱後前工作");
        return null;
    }
           

測試類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:46
 */
public class CglibTeasMain {

    public static void main(String[] args) {
        JieLunStar jieLunStar = new JieLunStar();
        JieLunStar proxyObject = (JieLunStar)new CglibProxyFactory(jieLunStar).getProxyObject();
        proxyObject.sing();
    }
}
           

到這三種代理方式我們都介紹完了,下面總結一下:

1、如果目标對象實作了接口,我們就采用JDK方式實作動态代理

2、如果目标對象沒有實作接口,我們就需要采用cglib方式實作動态代理;

6、目标是接口+注解的動态代理

注解類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1411:33
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {

    String value() default "";
}
           

目标類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1411:32
 */
public interface IUserDao {

    @Select("select * from user")
    String getUser();

  
}
           
InvocationHandler類:      
/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:32
 */
public class UserInvocationHandler implements InvocationHandler {


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //建立類的是一個具體的實作類
        if (Object.class.equals(method.getDeclaringClass())){
            return method.invoke(this,args);
        }else {
            //得到所有注解
            String value = method.getAnnotation(Select.class).value();
            System.out.println("接口上面的注解的内容:" + value);

            //實作具體的業務邏輯 如遠端http調用實作等
            return "user info";
        }

    }
}
           

代理工廠:

import java.lang.reflect.Proxy;

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1411:47
 */
public class InterfaceProxyFactory  {

    private  Class<?> target;
    private UserInvocationHandler userInvocationHandler;
    public InterfaceProxyFactory( Class<?> target) {
        this.target = target;
        this.userInvocationHandler = new UserInvocationHandler();
    }

    public Object getProxyObject(){
        return Proxy.newProxyInstance(target.getClassLoader(),new  Class[]{target},userInvocationHandler);
    }
}
           

測試類:

/**
 * @author zhenghao
 * @description:
 * @date 2020/12/1410:46
 */
public class InterfaceProxyTeasMain {

    public static void main(String[] args) {
        IUserDao proxyObject = (IUserDao) new InterfaceProxyFactory(IUserDao.class).getProxyObject();
        System.out.println(proxyObject.getUser());
    }
}