spring AOP(2)
前面写过一篇关于Spring AOP方面的文章,探讨了Spring AOP底层实现的一些细节知识,这里面涉及到了JAVA反射机制,代理模式以及CGLIB库的使用。也就是说,Spring AOP底层实现就是靠动态代理(针对有接口的类)和CGLIB(针对没有实现接口的一般类),那么,有了这些知识,再辅佐对核心配置XML文件解析的能力,其实就可以实现一个简易的基于IOC和AOP的小框架,大家可以自己尝试着写一下。下面呢我们就由浅入深地来看看在Spring中AOP是怎么实现的。
最简单的AOP实现只需要涉及3个概念:目标(Target),通知(Advice)和代理(Proxy)。目标呢,当然就是真实的需要被代理的对象,一般它会实现至少一个接口。通知呢,就是当目标的方法调用时需要调用的代码,也叫拦截器。而代理,毫无疑问就是加入了通知的目标了,它可以作为目标的替身出现。为了说明这三者的关系,我们来看一个网上有趣的小例子:一间书店开始打折促销,规则是每一名顾客只能买一本书,并且当顾客来到书店时,要说喜欢您来。顾客走的时候,还要说喜欢您再来!(麦当劳啊^_^) 顾客如果买到<hibernate in action>这本书,要抛出异常,告知他没有存货!呵呵,好啦,洪哥,我们动手吧!
package com.wepull.spring.book; public class NoThisBookException extends Exception { public NoThisBookException(String msg) { super(msg); } } package com.wepull.spring.book; public interface BuyBook { public void buyBook(String customer,String book) throws NoThisBookException;} package com.wepull.spring.book; public class MyBuyBook implements BuyBook{ public void buyBook(String customer, String book) throws NoThisBookException { if(book.equals("<hibernate in action>")) throw new NoThisBookException("对不起,没有"+book+"的存货了!"); System.out.println(customer+",你好,你已经购买了一本"+book+"!"); } }
package com.wepull.spring.book; import java.util.HashSet;import java.util.Set;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation; public class MyAroundAdvice implements MethodInterceptor { private Set customers=new HashSet(); //保存购过书的顾客信息 public Object invoke(MethodInvocation invocation) throws Throwable { Object[] args= invocation.getArguments(); if(customers.contains(args[0])){ System.out.println("对不起,一名顾客只能买一本打折书!"); return null; } customers.add(args[0]); return invocation.proceed(); } }package com.wepull.spring.book; import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice; public class MyBeforeAdvice implements MethodBeforeAdvice { public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { String customer=(String)arg1[0]; //第2个参数组就是被通知的方法传入的参数,本例中即customer,book System.out.println("喜欢您来!"+customer+"!"); //显示欢迎信息!,在buyBook方法前调用 } }package com.wepull.spring.book; import java.lang.reflect.Method;import org.springframework.aop.ThrowsAdvice; public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Method method, Object[] args, Object target, NoThisBookException e){ //可以定义多个方法,只要传入的参数是不同异常 System.out.println("对不起"+args[0]+",没货了。通知仓库,赶紧加书!"); }}package com.wepull.spring.book; import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice; public class MyAfterAdvice implements AfterReturningAdvice { public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { String customer=(String)arg2[0]; //同前置通知一样,参数组3为传入参数,具体见spring doc System.out.println("喜欢您再来!"+customer+"!"); //显示欢送信息! } }
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 各种通知 --> <!-- 前置通知 --> <bean id="myBeforeAdvice" /> </property> </bean></beans>
package com.wepull.spring.book; import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); BuyBook buyBook = (BuyBook) context.getBean("buyBook"); try { buyBook.buyBook("leno", "<struts in action>"); buyBook.buyBook("leno", "<spring in action>"); buyBook.buyBook("lenotang", "<hibernate in action>"); } catch (NoThisBookException e) { e.printStackTrace(); } }}