Spring 的 Aop
AOP简介:
Aspect Oriented Programming(AOP)是较为热门的一个话题。AOP,国内大致译作“面向切面编程”。
“面向切面编程”,这样的名字并不是非常容易理解,且容易产生一些误导。
笔者不止一次听到类似“OOP/OOD11即将落伍,AOP是新一代软件开发方式”这样的发言。而在AOP中,Aspect的含义,可能更多的理解为“切面”比较合适。所以笔者更倾向于“面向切面编程”的译法。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
AOP主要功能
日志记录,性能统计,安全控制,事务处理,异常处理等等wn及扩展
AOP中关键性概念 :
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
目标(Target):被通知(被代理)的对象
通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),请注意:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)
通知:
对应一个接口:
IBookBiz:
package com.cjh.aop.biz; public interface IBookBiz { // 购书 public boolean buy(String userName, String bookName, Double price); // 发表书评 public void comment(String userName, String comments); }
实现类 BookBizImpl:
package com.cjh.aop.biz.impl; import com.cjh.aop.biz.IBookBiz; import com.cjh.aop.exception.PriceException; public class BookBizImpl implements IBookBiz { public BookBizImpl() { super(); } public boolean buy(String userName, String bookName, Double price) { // 通过控制台的输出方式模拟购书 if (null == price || price <= 0) { throw new PriceException("book price exception"); } System.out.println(userName + " buy " + bookName + ", spend " + price); return true; } public void comment(String userName, String comments) { // 通过控制台的输出方式模拟发表书评 System.out.println(userName + " say:" + comments); } }
前置通知:
package com.cjh.aop.advice; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.MethodBeforeAdvice; /** * 前置通知 * 买书、评论前加系统日志 * * @author dell * */ public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // 哪个类被执行了 String clzName = arg2.getClass().getName(); // 哪 个方法被调用了 String methodName = arg0.getName(); // 传递的参数 String params = Arrays.toString(arg1); System.out.println("【前置通知】:" + clzName + "," + methodName + "(" + params + ")"); } }
后置通知:
package com.cjh.aop.advice; import java.lang.reflect.Method; import java.util.Arrays; import org.springframework.aop.AfterReturningAdvice; /** * 后置 通知(用bug) * 买书返利 * @author dell * */ public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { // 哪个类被执行了 String clzName = arg3.getClass().getName(); // 哪 个方法被调用了 String methodName = arg1.getName(); // 传递的参数 String params = Arrays.toString(arg2); System.out.println("【后置通知买书返利】:" + clzName + "," + methodName + "(" + params + ")"); } }
测试:
package com.cjh.aop.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.cjh.aop.biz.IBookBiz; public class Demo1 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml"); //目标 // IBookBiz bean = (IBookBiz) context.getBean("bookBiz"); // //bean的类型 // System.out.println(bean.getClass()); //代理对象 IBookBiz bean = (IBookBiz) context.getBean("bookBizProxy"); //买书 //报错之后,程序终止 bean.buy("蔡徐坤", "圣墟", 66d); } }
spring-context.xml
<!-- aop --> <!-- 目标 --> <bean id="bookBiz" class="com.cjh.aop.biz.impl.BookBizImpl"></bean> <!-- 通知 --> <!-- 前置通知 --> <bean id="myMethodBeforeAdvice" class="com.cjh.aop.advice.MyMethodBeforeAdvice"></bean> <!-- 后置通知 --> <bean id="myAfterReturningAdvice" class="com.cjh.aop.advice.MyAfterReturningAdvice"></bean> <!-- 环绕通知 --> <bean id="myMethodInterceptor" class="com.cjh.aop.advice.MyMethodInterceptor"></bean> <!-- 异常通知 --> <bean id="myThrowsAdvice" class="com.cjh.aop.advice.MyThrowsAdvice"></bean> <!-- 代理对象=目标+通知 --> <bean id="bookBizProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理目标 --> <property name="target" ref="bookBiz"></property> <!-- 代理的接口(目标所实现的接口) --> <property name="proxyInterfaces"> <list> <value>com.cjh.aop.biz.IBookBiz</value> </list> </property> <!-- 代理通知 --> <property name="interceptorNames"> <list> <!-- 前置通知 --> <value>myMethodBeforeAdvice</value> <!-- 后置通知 --> <value>myAfterReturningAdvice</value> <!-- 环绕通知 --> <value>myMethodInterceptor</value> <!-- 异常通知 --> <value>myThrowsAdvice</value> </list> </property> </bean>
结果:
环绕通知:
package com.cjh.aop.advice; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { // 哪个类被执行了 String clzName = arg0.getThis().getClass().getName(); // 哪 个方法被调用了 String methodName = arg0.getMethod().getName(); // 传递的参数 String params = Arrays.toString(arg0.getArguments()); System.out.println("【环绕通知】:" + clzName + "," + methodName + "(" + params + ")"); // proceed是代理对象调用目标对象方法的返回值 Object proceed = arg0.proceed(); System.out.println("【环绕通知】:" + clzName + "," + methodName + "(" + params + ")" + "方法调用的返回值:" + proceed); return proceed; } }
结果:
异常通知:
package com.cjh.aop.advice; import org.springframework.aop.ThrowsAdvice; import com.cjh.aop.exception.PriceException; public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex) { System.out.println("错误!!"); } }
程序异常就会出现:错误!!
过滤通知:只要配置xml
<!-- aop --> <!-- 目标 --> <bean id="bookBiz" class="com.cjh.aop.biz.impl.BookBizImpl"></bean> <!-- 通知 --> <!-- 前置通知 --> <bean id="myMethodBeforeAdvice" class="com.cjh.aop.advice.MyMethodBeforeAdvice"></bean> <!-- 后置通知 --> <bean id="myAfterReturningAdvice" class="com.cjh.aop.advice.MyAfterReturningAdvice"></bean> <!-- 环绕通知 --> <bean id="myMethodInterceptor" class="com.cjh.aop.advice.MyMethodInterceptor"></bean> <!-- 异常通知 --> <bean id="myThrowsAdvice" class="com.cjh.aop.advice.MyThrowsAdvice"></bean> <!-- 过滤通知 --> <bean id="myAfterReturningAdvicePlus" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 过滤规则 --> <property name="advice" ref="myAfterReturningAdvice"></property> <!-- 过滤条件(1个) --> <property name="pattern" value=".*buy"></property> <!-- 多个条件 --> <!-- <property name="pattern"> <list> <value>.*buy</value> </list> </property> --> </bean> <!-- 代理对象=目标+通知 --> <bean id="bookBizProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理目标 --> <property name="target" ref="bookBiz"></property> <!-- 代理的接口(目标所实现的接口) --> <property name="proxyInterfaces"> <list> <value>com.cjh.aop.biz.IBookBiz</value> </list> </property> <!-- 代理通知 --> <property name="interceptorNames"> <list> <!-- 前置通知 --> <value>myMethodBeforeAdvice</value> <!-- 后置通知 --> <value>myAfterReturningAdvice</value> <!-- 环绕通知 --> <value>myMethodInterceptor</value> <!-- 异常通知 --> <value>myThrowsAdvice</value> <!-- 过滤通知 --> <value>myAfterReturningAdvicePlus</value> </list> </property> </bean>
结果: