基础不牢之代理简介

写在前面的话
代理设计模式是常用的设计模式的一种,设计模式是别人的经验规律总结,某些方面,它可以让你少走一点弯路,但是学习设计模式,一定要带着问题来思考,结合实用场景高效率的学习,而不是生搬硬套。

为什么使用代理?
代理可以使得代码之间的耦合度降低,软件设计的一条原则就是高内聚低耦合,说白了就是分工明确。aop的思路就是借用动态代理实现的,像spring中的日志管理和事务管理等。每一个需要织入的点,同时这个点和业务是无关的,这个时候借助代理,可以使得在不改变既有业务逻辑的同时,增强代码的功能性。

局限
代理最大的局限就是需要一个接口,并不是每个类都会有接口。

静态代理
静态代理需要一个接口,一个代理类,一个业务类,代理类和业务类都实现了这个接口。假定有一个数据操作的接口AccountDao,里面有一个save()的方法, 这个接口有一个实现类AccountDaoImpl。

package proxy;

public interface AccountDao {
	
	public void save();

}

package proxy;

public class AccountDaoImpl implements AccountDao{
	
	@Override
	public void save() {
		
		System.out.println("新增账户");
		
	}

}

现在需要在调用save方法的时候增加一些调用的日志,最直观的方法是直接在实现类上增加日志的逻辑,但是当实现类是在一个jar文件中的时候就变得无从下手了,而且这种做法使得代码耦合度变高,不符合高内聚低耦合的特性。
考虑使用另一个实现类:

package proxy;

public class AccountDaoProxy implements AccountDao{
	
	private AccountDao dao;
	
    public AccountDaoProxy(AccountDao dao) {
    	this.dao = dao;
    }
    
    @Override
    public void save() {
    	//打印日志
    	System.out.println("新增入库数据。");
    	this.dao.save();
    	
    }
	public static void main(String args[]) {
    	AccountDaoImpl dao = new AccountDaoImpl();
    	AccountDaoProxy proxy = new AccountDaoProxy(dao);
    	
    	proxy.save();
    }
}

这个实现类就是代理类,这种代理方式 需要给每个业务类生成自己的代理类,当给另外几个不同的方法增加相同的代码的时候操作变得很繁琐,比如另一个UserInfoDao也有update()的方法也想有这样的日志增强,这样就不得不再写一个代理类,为了解决这个办法,动态代理出现了。
动态代理
动态代理需要借助jdk反射包的InvocationHandler接口和Proxy类,直接上代码:

public class DaoInvocation<T> implements InvocationHandler{
	
	//封装调用的实际对象
	private T t;
	
	public DaoInvocation(T t) {
		this.t = t;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("新增 入库数据。");
		
		Object result = method.invoke(t, args);
		
		return result;
		
	}
}

调用:

public static void main(String args[]) {
		InvocationHandler invocake = new DaoInvocation<AccountDaoImpl>(new AccountDaoImpl());
		//三个参数,类加载器,接口数组,InvocationHandler
		AccountDao dao = (AccountDao)Proxy.newProxyInstance(AccountDao.class.getClassLoader(), new Class<?>[]{AccountDao.class}, invocake);
		dao.save();
	}

newProxyInstance 接受的三个参数为:classLoader, 接口数组,以及InvocationHandler的实现类。这看起来似乎有点摸不着头脑,为什么这么写就实现了代理的功能呢?
别慌,先看看newProxyInstance做了什么操作:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
		//此处生成了代理类,重点看这个代理类到底是什么样子的。
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

类是字节码文件,借助字节数组把这个内存中的类打印到本地文件,然后我们再借助反编译的手段即可查阅这个动态代理的类:
借助代理类可供外部调用的的方法getProxyClass(),注意此处不是getProxyClass0(),后者是private方法,但是前者最后也是调用了后者,前者调用前做了权限的校验。

public static void main(String args[]) throws Exception{
		//在内存中生成了动态的代理类
		Class<?> c = Proxy.getProxyClass(AccountDao.class.getClassLoader(), AccountDao.class);
		
		//字节码文件转换为字节数组
		byte b[] = ProxyGenerator.generateProxyClass("MyProxy", new Class[] {c});
		
		//输出到文件
		FileOutputStream fos = new FileOutputStream(new File("D:\\code\\MyProxy.class"));
		
		fos.write(b);
		
		fos.close();
		
	}

使用反编译工具后把关键代码贴上来:

public final void save() throws  {
      try {
         super.h.invoke(this, m3, (Object[])null);
      } catch (RuntimeException | Error arg1) {
         throw arg1;
      } catch (Throwable arg2) {
         throw new UndeclaredThrowableException(arg2);
      }
   }

这个h的实例化是在父类Proxy中完成的:

public class Proxy implements java.io.Serializable {

	protected InvocationHandler h;
	
	protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
}

结合上面Proxy的newProxyInstance方法,知道调用动态代理类的save方法的时候,最后调用的就是InvocationHandler里面的invoke方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值