一、前言
AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
二、案例
以用户去飞猪上买票为例,很明显飞猪平台便是一个代理,飞猪平台在替用户购票的基础上,还增加了额外的功能,比如购票前要校验身份证,购票后需要提供凭证给用户。
1、定义购票行为接口,包括获取身份证号、姓名、余额
public interface TicketBuy { /** * 买票 */ void buy(); /** * 获取身份证号 * @return */ String getId(); /** * 获取姓名 * @return */ String getName(); /** * 获取余额 * @return */ Double getMoney(); }
2、定义用户类,即被代理类
public class Person implements TicketBuy { private String id; private String name; private Double money; @Override public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public void buy() { this.money --; System.out.println("用户:"+this.getName()+"买一张票,余额还剩:"+money); } }
3、定义飞猪平台类,即代理类
public class FlyPig implements InvocationHandler { private Person target; /** * 获取被代理人资料 * @param target * @return */ public Object getInstance(Person target){ this.target = target; Class clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("飞猪平台校验身份证+姓名:"+this.target.getId()+"+"+this.target.getName()); this.target.buy(); System.out.println("打印凭证!"); return null; } }
4、测试类:
public class Test { public static void main(String[] args) { Person person1 = new Person(); person1.setName("张三"); person1.setId("35052511111111111"); person1.setMoney(100.00); TicketBuy proxy = (TicketBuy)(new FlyPig().getInstance(person1)); proxy.buy(); } }
结果
5、解析