動態代理 原

具體場景

爲了使代理類與被代理類對第三方有相同的函數,代理類與被代理類一般實現一個公共的interface,定義如下

public interface Subject {

    void rent();

    void hello(String s);
}

被代理類定義如下

public class RealSubject implements Subject {
    @Override
    public void rent() {
        System.out.println("I want to rent my house");
    }

    @Override
    public void hello(String s) {
        System.out.println("hello :"+s);
    }
}

代理需求:在接口subject的每個方法前後分別輸出before invoke和after invoke

靜態代理解決方案

public class StaticSubjectProxy implements Subject {
    private RealSubject realSubject;
    @Override
    public void rent() {
        System.out.println("before invoke");
        realSubject.rent();
        System.out.println("after invoke");
    }

    @Override
    public void hello(String s) {
        System.out.println("before invoke");
        realSubject.hello(s);
        System.out.println("after invoke");
    }
}

但是這種處理存在弊端,如果subject接口的方法不止兩個,還有很多其他的方法,那麼靜態代理的實現就不太適合

動態代理解決方案

public class SubjectInvocationHandler implements InvocationHandler {
    private Subject subject; //被代理類

    public SubjectInvocationHandler(Subject interfaceClass) {
        this.subject = interfaceClass;
    }

    /**
     *
     * @param proxy  動態代理類的引用,通常情況下不需要它。但可以使用getClass()方法,
     *               得到proxy的Class類從而取得實例的類信息,如方法列表,annotation等。
     * @param method 方法對象的引用,代表被動態代理類調用的方法。從中可得到方法名,參數類型,返回類型等等
     * @param args args對象數組,代表被調用方法的參數。注意基本類型(int,long)會被裝箱成對象類型(Interger, Long)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!isDefaultMethod(method)){
            System.out.println("before invoke");
            System.out.println("Method: " + method);
            //執行被代理類方法
            method.invoke(subject,args);
            System.out.println("after invoke");
        }
        return null;
    }

    private boolean isDefaultMethod(Method method) {
        return (method.getModifiers()
                & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
                && method.getDeclaringClass().isInterface();
    }

生成代理類方法

public class SubjectProxy {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    public Subject create(){
        final Class<?>[] interfaces = new Class[]{Subject.class};
        final SubjectInvocationHandler handler = new SubjectInvocationHandler(subject);
        return (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),interfaces,handler);
    }
}
@Test
    public void test(){
        Subject subject = new RealSubject();
        SubjectProxy proxy = new SubjectProxy(subject);

        Subject proxySubject = proxy.create();
        proxySubject.rent();
        proxySubject.hello("world");
    }

上述動態代理無論subject包含多少方法,動態代理只需實現一次。 mybatis獲取mapper,使用到動態代理

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
  protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

參考:

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