天天看點

Java基礎:動态代理

系列閱讀

  • Java基礎:類加載器
  • Java基礎:反射
  • Java基礎:注解
  • Java基礎:動态代理

概述

在運作時,動态建立一組指定的接口的實作類對象(代理對象)!

代理是實作AOP(面向切面程式設計)的核心和關鍵技術,動态代理的用途與裝飾模式很相似,就是為了對某個對象進行增強。所有使用裝飾者模式的案例都可以使用動态代理來替換。

代理:本來應該自己做的事情,卻請了别人來做,被請的人就是代理對象。

舉例:春季回家買票讓人代買

動态代理:在程式運作過程中産生的這個對象

而程式運作過程中産生對象其實就是我們剛才反射講解的内容,是以,動态代理其實就是通過反射來生成一個代理

工作原理

  • client調用代理
  • 代理的構造方法接收一個invocationhandler參數
  • client調用代理的各個方法,代理的各個方法會把調用請求轉發給invocationhandler
  • invocationhandler通過invoke()方法把調用請求分發給目标對象的各個方法
Java基礎:動态代理

1、在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過使用這個類和接口就可以生成動态代理對象。JDK提供的代理隻能針對接口做代理。我們有更強大的代理cglib。

2、Proxy類中的方法建立動态代理類對象

public static Object newProxyInstance(
    ClassLoader loader,
    Class<?>[]interfaces,
    InvocationHandler h)
           

傳回一個指定接口的代理類執行個體,該接口可以将方法調用指派到指定的調用處理程式。最終會調用InvocationHandler的方法

3、InvocationHandler

Object invoke(Object proxy,Method method,Object[] args)
           

在代理執行個體上處理方法調用并傳回結果。

4、Proxy類中建立動态代理對象的方法的三個參數

  • ClassLoader對象

    定義了由哪個ClassLoader對象來對生成的代理對象進行加載

  • Interface對象的數組

    表示的是我将要給我需要代理的對象提供一組什麼接口,如果我提供了一組接口給它,那麼這個代理對象就宣稱實作了該接口(多态),這樣我就能調用這組接口中的方法了

  • InvocationHandler對象

    表示的是當我這個動态代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上

每一個動态代理類都必須要實作InvocationHandler這個接口,并且每個代理類的執行個體都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個接口的invoke 方法來進行調用。

5、InvocationHandler接口中invoke()方法的三個參數:

  • Object proxy:代表動态代理對象
  • Method method:代表正在執行的方法
  • Object[] args:代表調用目标方法時傳入的實參
Java基礎:動态代理

6、Proxy.newProxyInstance()

建立的代理對象是在jvm運作時動态生成的一個對象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運作是動态生成的一個對象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示對象的标号

動态代理的實作

package cn.itcast_06;

/*
 * 使用者操作接口
 */
public interface UserDao {
    public abstract void add();
    public abstract void delete();
    public abstract void update();
    public abstract void find();
}
           
package cn.itcast_06;

public interface StudentDao {
    public abstract void login();

    public abstract void regist();
}
           
package cn.itcast_06;
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("添加功能");
    }

    @Override
    public void delete() {
        System.out.println("删除功能");
    }

    @Override
    public void update() {
        System.out.println("修改功能");
    }

    @Override
    public void find() {
        System.out.println("查找功能");
    }
}
           
package cn.itcast_06;

public class StudentDaoImpl implements StudentDao {

    @Override
    public void login() {
        System.out.println("登入功能");
    }

    @Override
    public void regist() {
        System.out.println("注冊功能");
    }
}
           
package cn.itcast_06;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目标對象

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("權限校驗");
        Object result = method.invoke(target, args);
        System.out.println("日志記錄");
        return result; // 傳回的是代理對象
    }
}
           
package cn.itcast_06;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        UserDao ud = new UserDaoImpl();
        ud.add();
        ud.delete();
        ud.update();
        ud.find();
        System.out.println("-----------");
        // 我們要建立一個動态代理對象
        // Proxy類中有一個方法可以建立動态代理對象
        // public static Object newProxyInstance(ClassLoader loader,Class<?>[]
        // interfaces,InvocationHandler h)
        // 我準備對ud對象做一個代理對象
        MyInvocationHandler handler = new MyInvocationHandler(ud);
        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
                .getClassLoader(), ud.getClass().getInterfaces(), handler);
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.find();
        System.out.println("-----------");

        StudentDao sd = new StudentDaoImpl();
        MyInvocationHandler handler2 = new MyInvocationHandler(sd);
        StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass()
                .getClassLoader(), sd.getClass().getInterfaces(), handler2);
        proxy2.login();
        proxy2.regist();
    }
}
           

Spring原理

public class ProxyFactoryBean {
    private Object mTarget;
    private Advice mAdvice;

    public Object getProxy(){
        Object proxy = Proxy.newProxyInstance(
                mTarget.getClass().getClassLoader(),
                mTarget.getClass().getInterfaces(),
                mHandler
        );
        return proxy;
    }

    private InvocationHandler mHandler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            mAdvice.beforeMethod(method);
            Object result = method.invoke(mTarget, args);
            mAdvice.afterMethod(method);
            return result;
        }
    };

    public void setTarget(Object target) {
        mTarget = target;
    }

    public void setAdvice(Advice advice) {
        mAdvice = advice;
    }

    public Object getTarget() {
        return mTarget;
    }

    public Advice getAdvice() {
        return mAdvice;
    }
}

public class BeanFactory {
    Properties mProperties = new Properties();
    public BeanFactory(InputStream in){
        try {
            mProperties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String name){
        String classname = mProperties.getProperty(name);
        Object bean = null;
        try {
            Class clazz = Class.forName(classname);
            bean = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (bean instanceof ProxyFactoryBean){
            Object proxy = null;
            ProxyFactoryBean factoryBean = (ProxyFactoryBean) bean;
            Advice advice = null;
            try {
                advice = (Advice) Class.forName(mProperties.getProperty(name+".advice")).newInstance();
                Object target = Class.forName(mProperties.getProperty(name+".target")).newInstance();
                factoryBean.setAdvice(advice);
                factoryBean.setTarget(target);
                proxy = factoryBean.getProxy();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return proxy;
        }
        return bean;
    }
}
           
public interface Advice {
    void beforeMethod(Method method);
    void afterMethod(Method method);
}
           
public class AopFrameworkTest {

    public static void main(String[] args) throws Exception {
        InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
        Object bean = new BeanFactory(ips).getBean("xxx");
        System.out.println(bean.getClass().getName());
        ((Collection)bean).clear();
    }
}