【spring系列】java動態代理

前言

​ 代理模式可以在訪問目標對象的基礎上,增強額外的功能。

​ 代理模式分爲靜態代理和動態代理,但是靜態代理的代理對象需要與目標對象實現一樣的接口,所以會有很多代理類,類太多.同時,一旦接口增加方法,目標對象與代理對象都要維護。動態代理便解決了此問題。

​ 動態代理在Spring,mybatis等框架中有廣泛的應用。所以在瞭解框架源碼前需要先了解動態代理。

1.java反射

​ 動態代理以java反射爲基礎。在瞭解動態代理前,需要先了解java反射機制。

​ Java 反射機制在程序運行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。這種 動態的獲取信息 以及 動態調用對象的方法 的功能稱爲 java 的反射機制

​ 如上看着着實懵逼,看下代碼:

​ 例如有一個UserInfo對象

public class UserInfo   {
    private Long id;
    private String name;

    public void init() throws Exception {
        System.out.println("這裏是 UserInfo init");
	}
    public void init(String a) throws Exception {
        System.out.println("這裏是 UserInfo init a:"+a);
    }
    public void init(String a,Integer b) throws Exception {
        System.out.println("這裏是 UserInfo init a:"+a+"   b:"+b);
    }
}

那麼創建此對象有以下幾種方式:


    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        //普通方式進行創建
        UserInfo userInfo = new UserInfo();
        //通過反射機制進行創建
        Class userInfoClass = UserInfo.class;
        UserInfo userInfo1 = (UserInfo) userInfoClass.newInstance();
        //反射進行創建
        Class userInfoClass2= Class.forName("com.example.model.UserInfo");
        UserInfo userInfo2 = (UserInfo) userInfoClass2.newInstance();
    }

通過反射執行對象中的方法

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class userInfoClass = UserInfo.class;
        Object userinfo = userInfoClass.newInstance();
        // 執行init(String a) 方法
        Method init = userInfoClass.getMethod("init", new Class[]{String.class});
        init.invoke(userinfo,new Object[]{"123"});
        // 執行init(String a,Integer b)方法
        Method init2 = userInfoClass.getMethod("init", new Class[]{String.class,Integer.class});
        init2.invoke(userinfo,new Object[]{"123",123});
    }

輸出:

這裏是 UserInfo init a:123
這裏是 UserInfo init a:123 b:123

詳細可參考 java反射機制詳解

java反射學習

2.java動態代理

java實現動態代理主要有兩種,jdk動態代理和CGLIB動態代理。

jdk動態代理是jdk原生,不需要引用其他jar包,但是隻能代理接口,如果需要對類進行動態代理則需要引用CLIGB動態代理。

2.1 jdk動態代理

Proxy提供了創建動態代理類和實例的靜態方法

InvocationHandler是由代理實例的調用處理程序實現的接口。

代理者需要實現InvocationHandler接口

示例:

IUserInfoService.java

public interface IUserInfoService {
    void addUserInfo(UserInfo userInfo);
}

接口實現類UserInfoService.java

// 這裏加上事務註解
@Transactional
public class UserInfoService implements IUserInfoService {
    @Override
    public void addUserInfo(UserInfo userInfo) {
        System.out.println("保存UserInfo");
    }
}

代理類ProxyHandler

public class ProxyHandler implements InvocationHandler {
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法調用之前");
        //是否有開啓事務註解
        boolean annotationPresent = object.getClass().isAnnotationPresent(Transactional.class);
        if(annotationPresent){
            System.out.println("開啓事務");
        }
        Object invoke = method.invoke(object, args);
        if(annotationPresent){
            System.out.println("提交事務");
        }
        return invoke;
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        IUserInfoService userInfoService = new UserInfoService();
        //獲取代理實現類
         // 由下面代理類可見,這裏其實等於  IUserInfoService o = new UserServiceProxy()
        //然後UserServiceProxy這個對UserInfoService進行了代理。
        IUserInfoService o = (IUserInfoService)Proxy.newProxyInstance(userInfoService.getClass().getClassLoader(),userInfoService.getClass().getInterfaces(),new ProxyHandler(userInfoService));
		// 通過代理類實現方法,
       
        o.addUserInfo(new UserInfo());
        //保存代理類.class文件
        createClassFile(o.getClass(),"UserServiceProxy");
    }
    public static void createClassFile(Class clazz, String proxyName) {
        // 根據類信息和提供的代理類名稱,生成字節碼
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String paths = clazz.getResource("/").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            //保留到硬盤中
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代理類

public final class UserServiceProxy extends Proxy implements IUserInfoService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public UserServiceProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final UserInfo getUserInfoById(Long var1) throws  {
        try {
            return (UserInfo)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void addUserInfo(UserInfo var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.example.proxy.IUserInfoService").getMethod("getUserInfoById", new Class[]{Class.forName("java.lang.Long")});
            m4 = Class.forName("com.example.proxy.IUserInfoService").getMethod("addUserInfo", new Class[]{Class.forName("com.example.model.UserInfo")});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

通過代理,java動態生成了UserServiceProxy類來對目標對象進行代理。

2.2 CLIGB動態代理

引入jar包

		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>3.3.0</version>
		</dependency>

被代理的類UserInfoService

public class UserInfoService implements IUserInfoService {
    @Override
    public void addUserInfo(UserInfo userInfo) {
        System.out.println("保存UserInfo");
    }
}

代理類,這裏要實現MethodInterceptor接口,在方法執行的時候會調用intercept方法,這一點和jdk代理類似

public class CglibHandler implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("執行方法");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("執行方法之後");
        return o1;
    }
}

接下來進行測試

public class Test {
    public static void main(String[] args) {
        //它是一個字節碼增強器,可以用來爲無接口的類創建代理 ,與JDK的Proxy類類似
        Enhancer enhancer = new Enhancer();
        // 設置被代理類
        enhancer.setSuperclass(UserInfoService.class);
        // 設置代理類
        enhancer.setCallback(new CglibHandler());
		//獲取代理類
        UserInfoService hello = (UserInfoService)enhancer.create();
        // 執行方法,此會調用 代理類的intercept方法
        hello.addUserInfo(new UserInfo());
    }
}

輸出:

執行方法
保存UserInfo
執行方法之後

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章