文档章节

Spring AOP

涩女郎
 涩女郎
发布于 2014/11/22 11:05
字数 4683
阅读 14
收藏 1

AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,一种新兴的编程技术。 
AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦, 
AOP可以说也是这种目标的一种实现。它可以解决OOP和过程化方法不能够很好解决的横切 
(crosscut)问题, 
如:事务、安全、日志等横切关注。当未来系统变得越来越复杂, 
横切关注点就成为一个大问题的时候,AOP就可以很轻松的解决横切关注点这个问题。 

比如有这样一个情景: 

Java代码  收藏代码

  1. public class AccountManager {  

  2.     private static final sysLogger = SystemLogger.getInstance();  

  3.     private AuthorizationManager authMgr = new AuthorizationManager();  

  4.       

  5.     public void transferFunds(String from, String to, int amount) {  

  6.         sysLogger.log("transfer funds from " + from + " to " + to);  

  7.         if(authMgr.accessAble(from) && authMgr.accessAble(to)) {  

  8.             sysLogger.log("access successfully");  

  9.             CustomerAccount from = findAccount(from);  

  10.             CustomerAccount to = findAccount(to);  

  11.             from.debit(amount);  

  12.             to.credit(amount);  

  13.         } else {  

  14.             sysLogger.log("access deny");  

  15.         }  

  16.         sysLogger.log("transfer funds from " + from + " to " + to + " $" + amount + " successfully!");  

  17.     }  

  18. }  



这个例子虽然是很好的面向对象代码,但是在业务处理逻辑中夹杂这日志处理和权限判断,变得复杂混乱. 
在 AOP 中,正交关注点(如安全和日志记录)被识别为系统中的常见横切关注点。说它们是横切, 
是因为它们总是切入模块(如包、类和代码文件)的多个单位。也许横切关注点可能不是核心业务逻辑的一部分,但是它们是应用程序的基本部分。 

AOP的实现主要是通过方法的拦截实现.在不使用AOP框架的情况下,我们可以通过JDK提供的动态代理来实现方法的拦截 

注意:使用JDK提供的动态代理实现 
要求我们的目标对象必须实现接口 

IUserBean接口 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public interface IUserBean {  

  4.       

  5.     public void getUser();  

  6.       

  7.     public void addUser();  

  8.       

  9.     public void updateUser();  

  10.       

  11.     public void deleteUser();  

  12. }  



IUserBean实现类 UserBean.java 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public class UserBean implements IUserBean {  

  4.       

  5.     private String user = null;  

  6.       

  7.     public UserBean() {  

  8.     }  

  9.   

  10.     public UserBean(String user) {  

  11.         this.user = user;  

  12.     }  

  13.       

  14.     public void setUser(String user) {  

  15.         this.user = user;  

  16.     }  

  17.   

  18.     public void addUser() {  

  19.         System.out.println("this is addUser() method!");  

  20.     }  

  21.   

  22.     public void deleteUser() {  

  23.         System.out.println("this is deleteUser() method!");  

  24.     }  

  25.   

  26.     public void getUser() {  

  27.         System.out.println("this is getUser() method!");  

  28.     }  

  29.   

  30.     public void updateUser() {  

  31.         System.out.println("this is updateUser() method!");  

  32.     }  

  33. }  



我们希望在UserBean执行方法之前先检查userName是不是为空,以此做为权限判断. 
当然我们可以在没个方法里面去加这些判断,但是这需要为每个方法都添加同样的判断,维护不便. 
使用JDK提供的动态代理技术可以很方便的实现上面的需求: 
通过java.lang.reflect.Proxy;提供的 
public static Object newProxyInstance(ClassLoader loader, 
  Class<?>[] interfaces, 
  InvocationHandler h) 
方法可以生成一个动态代理对象 
其中 
loader是类装载器 
interfaces是目标对象实现的一系列接口 
h是一个实现InvocationHandler接口的类,我们对代理对象的所有操作都经过它处理 
这样我们就可以拦截到UserBean的方法,在方法执行前先判断是否有权限,如果有则执行方法, 
没有权限的话就不执行方法. 

编写我们的代理类: 
JDKProxy.java 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import java.lang.reflect.InvocationHandler;  

  4. import java.lang.reflect.Method;  

  5. import java.lang.reflect.Proxy;  

  6.   

  7. public class JDKProxy implements InvocationHandler {  

  8.       

  9.     private Object targetObject;  

  10.       

  11.     public Object createProxyObject(Object targetObject) {  

  12.         this.targetObject = targetObject;  

  13.         //生成代理对象  

  14.         return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);  

  15.     }  

  16.   

  17.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  

  18.         UserBean userBean = (UserBean) targetObject;  

  19.         String userName = userBean.getUserName();  

  20.         Object result = null;  

  21.         //权限判断  

  22.         if(userName!=null && !"".equals(userName)) {  

  23.             //调用目标对象的方法  

  24.             result = method.invoke(targetObject, args);  

  25.         }   

  26.         return result;  

  27.     }  

  28. }  



通过调用createProxyObject可以生成代理对象, 
编写测试类如下: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public class TestProxy {  

  4.       

  5.     public static void main(String[] args) {  

  6.         JDKProxy jProxy = new JDKProxy();  

  7.         IUserBean userBean = (IUserBean) jProxy.createProxyObject(new UserBean("royzhou"));  

  8.         userBean.addUser();  

  9.     }  

  10. }  



执行成功后输出: 
this is addUser() method! 

再次修改测试类: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public class TestProxy {  

  4.       

  5.     public static void main(String[] args) {  

  6.         JDKProxy jProxy = new JDKProxy();  

  7.         IUserBean userBean = (IUserBean) jProxy.createProxyObject(new UserBean());  

  8.         userBean.addUser();  

  9.     }  

  10. }  



即当用户没有权限时,控制台不输出东西,说明我们拦截方法对其做的权限判断生效了. 

从上面这个例子可以成功拦截了调用的方法并对其做了相应的处理 

如果不使用JDK提供的Proxy类 
通过cglib创建代理类,好处是不要求我们的目标对象实现接口 
Enhancer enhancer = new Enhancer(); 
enhancer.setSuperclass(this.targetObject.getClass()); 
enhancer.setCallback(this); //回调,参数是一个实现MethodInterceptor接口的类,我们对代理对象的所有操作都经过它处理 
return enhancer.create(); //创建代理对象 


修改UserBean 去掉IUserBean接口 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public class UserBean {  

  4.       

  5.     private String userName = null;  

  6.       

  7.     public UserBean() {  

  8.     }  

  9.   

  10.     public UserBean(String userName) {  

  11.         this.userName = userName;  

  12.     }  

  13.   

  14.     public void addUser() {  

  15.         System.out.println("this is addUser() method!");  

  16.     }  

  17.   

  18.     public void deleteUser() {  

  19.         System.out.println("this is deleteUser() method!");  

  20.     }  

  21.   

  22.     public void getUser() {  

  23.         System.out.println("this is getUser() method!");  

  24.     }  

  25.   

  26.     public void updateUser() {  

  27.         System.out.println("this is updateUser() method!");  

  28.     }  

  29.   

  30.     public String getUserName() {  

  31.         return userName;  

  32.     }  

  33.   

  34.     public void setUserName(String userName) {  

  35.         this.userName = userName;  

  36.     }  

  37. }  



通过cglib创建代理类 

CGLibProxy.java 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import java.lang.reflect.Method;  

  4.   

  5. import net.sf.cglib.proxy.Enhancer;  

  6. import net.sf.cglib.proxy.MethodInterceptor;  

  7. import net.sf.cglib.proxy.MethodProxy;  

  8.   

  9. public class CGLibProxy implements MethodInterceptor {  

  10.        

  11.     private Object targetObject;  

  12.       

  13.     public Object createProxyObject(Object targetObject) {  

  14.         this.targetObject = targetObject;  

  15.         Enhancer enhancer = new Enhancer();  

  16.         enhancer.setSuperclass(this.targetObject.getClass()); //非final 进行覆盖  

  17.         enhancer.setCallback(this); //回调,通过  

  18.         return enhancer.create(); //创建代理对象  

  19.     }  

  20.   

  21.     public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  

  22.         UserBean userBean = (UserBean) targetObject;  

  23.         String userName = userBean.getUserName();  

  24.         Object result = null;  

  25.         if(userName!=null && !"".equals(userName)) {  

  26.             //调用目标对象的方法  

  27.             result = methodProxy.invoke(targetObject, args);  

  28.         }   

  29.         return result;  

  30.     }  

  31. }  



编写测试类: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public class TestProxy {  

  4.       

  5.     public static void main(String[] args) {  

  6.         CGLibProxy cProxy = new CGLibProxy();  

  7.         UserBean userBean = (UserBean) cProxy.createProxyObject(new UserBean("royzhou"));  

  8.         userBean.addUser();  

  9.     }  

  10. }  



输出: 
this is addUser() method! 

当取消用户权限时,控制台不输出任何东西. 

说明通过CGLib成功生成代理对象,拦截了目标对象的方法. 


Spring主要通过代理来实现AOP 

下面是AOP的一些基本概念: 

切面(Aspect):对横切关注点的抽象(类似类对对象的抽象) 

连接点(JoinPoint):被拦截到的点,泛指方法 

切入点(CutPoint):对哪些连接点进行拦截的定义 

通知(Advice):在特定的连接点,AOP框架执行的动作.前置/后置/例外/最终/环绕通知(调用方法之前执行,全部执行完毕之后) 

引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。 

目标对象(Target Object): 包含连接点的对象。也被称作 被通知或被代理对象。 

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 

织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。 

Adive通知可理解如下: 

Java代码  收藏代码

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  

  2.         UserBean userBean = (UserBean) targetObject;  

  3.         String userName = userBean.getUserName();  

  4.         Object result = null;  

  5.         if(userName!=null && !"".equals(userName)) {  

  6.             //调用目标对象的方法  

  7.             try {  

  8.                 //前置通知  

  9.                 result = method.invoke(targetObject, args);  

  10.                 //后置通知  

  11.             } catch(Exception e) {  

  12.                 //例外通知  

  13.             } finally {  

  14.                 //最终通知  

  15.             }  

  16.         }   

  17.         //环绕通知(前置通知之后,目标对象方法调用之前执行,全部执行完毕(最终通知)之后)  

  18.         return result;  

  19.     }  



Spring提供两种方式实现AOP 
一种是XML配置的方式 
一种是annotation注解的方式 

不管采用哪种方式,都必须在spring的配置文件中配置AOP支持: 

Xml代码  收藏代码

  1. <?xml version="1.0" encoding="UTF-8"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  4.        xmlns:context="http://www.springframework.org/schema/context"   

  5.        xmlns:aop="http://www.springframework.org/schema/aop"        

  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans  

  7.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  

  8.            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  

  9.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  

  10.         <aop:aspectj-autoproxy/>   

  11. </beans>  



其中<aop:aspectj-autoproxy/>表示打开aspect注解处理器 
(aspect的内容具体可查看http://www.ibm.com/developerworks/cn/java/j-aspectj/

使用注解方式实现AOP必须引入三个jar包: 
aspectjweaver.jar 
aspectjrt.jar 
cglib.jar 

首先建立好测试用的业务bean 
然后我们需要定义一个切面/定义切入点/通知等 

接口IUserBean.java 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public interface IUserBean {  

  4.       

  5.     public void getUser();  

  6.       

  7.     public void addUser();  

  8.       

  9.     public void updateUser();  

  10.       

  11.     public void deleteUser();  

  12. }  



实现类UserBean.java 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. public class UserBean {  

  4.       

  5.     public void addUser(String userName) {  

  6.         System.out.println("this is addUser() method!");  

  7.     }  

  8.   

  9.     public void deleteUser(int userId) {  

  10.         System.out.println("this is deleteUser() method!");  

  11.     }  

  12.   

  13.     public String getUser(String userId) {  

  14.         System.out.println("this is getUser() method!");  

  15.         return "haha";  

  16.     }  

  17.   

  18.     public void updateUser(int userId, String userName) {  

  19.         System.out.println("this is updateUser() method!");  

  20.     }  

  21. }  



紧接着我们建立我们的切面类:使用@Aspect注解 
MyInterceptor.java 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import org.aspectj.lang.ProceedingJoinPoint;  

  4. import org.aspectj.lang.annotation.After;  

  5. import org.aspectj.lang.annotation.AfterReturning;  

  6. import org.aspectj.lang.annotation.AfterThrowing;  

  7. import org.aspectj.lang.annotation.Around;  

  8. import org.aspectj.lang.annotation.Aspect;  

  9. import org.aspectj.lang.annotation.Before;  

  10. import org.aspectj.lang.annotation.Pointcut;  

  11.   

  12. @Aspect   

  13. public class MyInterceptor {  

  14.     /** 

  15.      * 定义切入点 

  16.      * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截,(!void表示有返回值) 

  17.      * 第一个..表示com.royzhou.aop包及其子包 

  18.      * 倒数第二个*表示包下的所有Java类都被拦截 

  19.      * 最后一个*表示类的所有方法都被拦截 

  20.      * (..)表示方法的参数可以任意多个如[(java.lang.String,java.lang.Integer)表示第一个参数是String,第二个参数是int的方法才会被拦截] 

  21.      */  

  22.     @Pointcut("execution(* com.royzhou.aop..*.*(..))"//定义一个切入点,名称为pointCutMethod(),拦截类的所有方法  

  23.     private void pointCutMethod() {   

  24.     }  

  25.       

  26.     @Before("pointCutMethod()"//定义前置通知  

  27.     public void doBefore() {  

  28.         System.out.println("前置通知");  

  29.     }  

  30.       

  31.     @AfterReturning("pointCutMethod()"//定义后置通知  

  32.     public void doAfterReturning() {  

  33.         System.out.println("后置通知");  

  34.     }  

  35.       

  36.     @AfterThrowing("pointCutMethod()"//定义例外通知  

  37.     public void doAfterException() {  

  38.         System.out.println("异常通知");  

  39.     }  

  40.       

  41.     @After("pointCutMethod()"//定义最终通知  

  42.     public void doAfter() {  

  43.         System.out.println("最终通知");  

  44.     }  

  45.       

  46.     @Around("pointCutMethod()"//定义环绕通知  

  47.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  

  48.         System.out.println("进入方法");  

  49.         Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行  

  50.         System.out.println("退出方法");  

  51.         return object;  

  52.     }  

  53. }  



切面定义好之后我们必须交给Spring管理,配置我们的bean.xml文件如下: 

Xml代码  收藏代码

  1. <?xml version="1.0" encoding="UTF-8"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  4.        xmlns:context="http://www.springframework.org/schema/context"   

  5.        xmlns:aop="http://www.springframework.org/schema/aop"        

  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans  

  7.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  

  8.            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  

  9.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  

  10.         <aop:aspectj-autoproxy/>   

  11.         <bean id="MyInterceptor" class="com.royzhou.aop.MyInterceptor" />  

  12.         <bean id="UserBean" class="com.royzhou.aop.UserBean"></bean>  

  13. </beans>  



编写测试类如下: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import org.springframework.context.ApplicationContext;  

  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  

  5.   

  6. public class TestAOP {  

  7.     public static void main(String[] args) {  

  8.         ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");  

  9.         UserBean ub = (UserBean)ctx.getBean("UserBean");  

  10.         ub.addUser("royzhou");  

  11.     }  

  12. }  



运行测试类输出: 
前置通知 
进入方法 
this is addUser() method! 
后置通知 
最终通知 
退出方法 

可以看出定义的各个通知的执行顺序, 
例外通知只有在程序异常的情况下才会发生. 
其他通知都会执行. 
我们也可以在环绕通知里面将前面的几个通知实现了. 

如果需要获取方法的参数我们必须在定义通知的时候做响应的设置: 
比如我在前置通知希望获取到输入的参数需要修改MyInterceptor如下: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import org.aspectj.lang.ProceedingJoinPoint;  

  4. import org.aspectj.lang.annotation.After;  

  5. import org.aspectj.lang.annotation.AfterReturning;  

  6. import org.aspectj.lang.annotation.AfterThrowing;  

  7. import org.aspectj.lang.annotation.Around;  

  8. import org.aspectj.lang.annotation.Aspect;  

  9. import org.aspectj.lang.annotation.Before;  

  10. import org.aspectj.lang.annotation.Pointcut;  

  11.   

  12. @Aspect  

  13. public class MyInterceptor {  

  14.     /** 

  15.      * 定义切入点 

  16.      * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 

  17.      * 第一个..表示com.royzhou.aop包及其子包 

  18.      * 倒数第二个*表示包下的所有Java类 

  19.      * 最后一个*表示类的所有方法 

  20.      * (..)表示方法的参数可以任意多个 

  21.      */  

  22.     @Pointcut("execution(* com.royzhou.aop..*.*(..))"//定义一个切入点,名称为pointCutMethod(),拦截类的所有方法  

  23.     private void pointCutMethod() {   

  24.     }  

  25.       

  26.     //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样  

  27.     @Before("pointCutMethod() && args(userName)"//定义前置通知  

  28.     public void doBefore(String userName) {  

  29.         System.out.println("前置通知" + userName);  

  30.     }  

  31.       

  32.     @AfterReturning("pointCutMethod()"//定义后置通知  

  33.     public void doAfterReturning() {  

  34.         System.out.println("后置通知");  

  35.     }  

  36.       

  37.     @AfterThrowing("pointCutMethod()"//定义例外通知  

  38.     public void doAfterException() {  

  39.         System.out.println("异常通知");  

  40.     }  

  41.       

  42.     @After("pointCutMethod()"//定义最终通知  

  43.     public void doAfter() {  

  44.         System.out.println("最终通知");  

  45.     }  

  46.       

  47.     @Around("pointCutMethod()"//定义环绕通知  

  48.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  

  49.         System.out.println("进入方法");  

  50.         Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行  

  51.         System.out.println("退出方法");  

  52.         return object;  

  53.     }  

  54. }  



重新运行测试类输出: 

前置通知royzhou 
进入方法 
this is addUser() method! 
后置通知 
最终通知 
退出方法 

可见我们成功的获取到了方法的参数 


如果需要获取方法的返回值,则修改如下: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import org.aspectj.lang.ProceedingJoinPoint;  

  4. import org.aspectj.lang.annotation.After;  

  5. import org.aspectj.lang.annotation.AfterReturning;  

  6. import org.aspectj.lang.annotation.AfterThrowing;  

  7. import org.aspectj.lang.annotation.Around;  

  8. import org.aspectj.lang.annotation.Aspect;  

  9. import org.aspectj.lang.annotation.Before;  

  10. import org.aspectj.lang.annotation.Pointcut;  

  11.   

  12. @Aspect  

  13. public class MyInterceptor {  

  14.     /** 

  15.      * 定义切入点 

  16.      * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 

  17.      * 第一个..表示com.royzhou.aop包及其子包 

  18.      * 倒数第二个*表示包下的所有Java类 

  19.      * 最后一个*表示类的所有方法 

  20.      * (..)表示方法的参数可以任意多个 

  21.      */  

  22.     @Pointcut("execution(* com.royzhou.aop..*.*(..))"//定义一个切入点,名称为pointCutMethod(),拦截类的所有方法  

  23.     private void pointCutMethod() {   

  24.     }  

  25.       

  26.     //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样  

  27.     @Before("pointCutMethod() && args(userName)"//定义前置通知  

  28.     public void doBefore(String userName) {  

  29.         System.out.println("前置通知" + userName);  

  30.     }  

  31.       

  32.     //配置returning="result", result必须和doAfterReturning的参数一致  

  33.     @AfterReturning(pointcut="pointCutMethod()", returning="result"//定义后置通知  

  34.     public void doAfterReturning(String result) {  

  35.         System.out.println("后置通知" + result);  

  36.     }  

  37.       

  38.     @AfterThrowing("pointCutMethod()"//定义例外通知  

  39.     public void doAfterReturning() {  

  40.         System.out.println("异常通知");  

  41.     }  

  42.       

  43.     @After("pointCutMethod()"//定义最终通知  

  44.     public void doAfter() {  

  45.         System.out.println("最终通知");  

  46.     }  

  47.       

  48.     @Around("pointCutMethod()"//定义环绕通知  

  49.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  

  50.         System.out.println("进入方法");  

  51.         Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行  

  52.         System.out.println("退出方法");  

  53.         return object;  

  54.     }  

  55. }  



输出结果是: 

前置通知1 
进入方法 
this is getUser() method! 
后置通知haha 
最终通知 
退出方法 

可见方法的返回值我们也成功拿到了. 

如需在例外通知中获取例外的详细信息,我们只需要配置: 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import org.aspectj.lang.ProceedingJoinPoint;  

  4. import org.aspectj.lang.annotation.After;  

  5. import org.aspectj.lang.annotation.AfterReturning;  

  6. import org.aspectj.lang.annotation.AfterThrowing;  

  7. import org.aspectj.lang.annotation.Around;  

  8. import org.aspectj.lang.annotation.Aspect;  

  9. import org.aspectj.lang.annotation.Before;  

  10. import org.aspectj.lang.annotation.Pointcut;  

  11.   

  12. @Aspect  

  13. public class MyInterceptor {  

  14.     /** 

  15.      * 定义切入点 

  16.      * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 

  17.      * 第一个..表示com.royzhou.aop包及其子包 

  18.      * 倒数第二个*表示包下的所有Java类 

  19.      * 最后一个*表示类的所有方法 

  20.      * (..)表示方法的参数可以任意多个 

  21.      */  

  22.     @Pointcut("execution(* com.royzhou.aop..*.*(..))"//定义一个切入点,名称为pointCutMethod(),拦截类的所有方法  

  23.     private void pointCutMethod() {   

  24.     }  

  25.       

  26.     //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样  

  27.     @Before("pointCutMethod() && args(userName)"//定义前置通知  

  28.     public void doBefore(String userName) {  

  29.         System.out.println("前置通知" + userName);  

  30.     }  

  31.       

  32.     //配置returning="result", result必须和doAfterReturning的参数一致  

  33.     @AfterReturning(pointcut="pointCutMethod()", returning="result"//定义后置通知  

  34.     public void doAfterReturning(String result) {  

  35.         System.out.println("后置通知" + result);  

  36.     }  

  37.       

  38.     //类似returning的配置  

  39.     @AfterThrowing(pointcut="pointCutMethod()", throwing="e"//定义例外通知  

  40.     public void doAfterException(Exception e) {  

  41.         System.out.println("异常通知");  

  42.     }  

  43.       

  44.     @After("pointCutMethod()"//定义最终通知  

  45.     public void doAfter() {  

  46.         System.out.println("最终通知");  

  47.     }  

  48.       

  49.     @Around("pointCutMethod()"//定义环绕通知  

  50.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  

  51.         System.out.println("进入方法");  

  52.         Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行  

  53.         System.out.println("退出方法");  

  54.         return object;  

  55.     }  

  56. }  



上面的例子介绍了使用注解方式来实现Spring的AOP 
另外我们可以使用XML来配置: 

使用XML配置Spring的AOP 我们的切面类MyInterceptor不需要做任何注解,就是一个普通的Java类 

Java代码  收藏代码

  1. package com.royzhou.aop;  

  2.   

  3. import org.aspectj.lang.ProceedingJoinPoint;  

  4.   

  5. public class MyInterceptor {  

  6.       

  7.     public void doBefore(String userName) {  

  8.         System.out.println("前置通知" + userName);  

  9.     }  

  10.       

  11.     public void doAfterReturning(String result) {  

  12.         System.out.println("后置通知" + result);  

  13.     }  

  14.       

  15.     public void doAfterException(Exception e) {  

  16.         System.out.println("异常通知");  

  17.     }  

  18.       

  19.     public void doAfter() {  

  20.         System.out.println("最终通知");  

  21.     }  

  22.       

  23.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  

  24.         System.out.println("进入方法");  

  25.         Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行  

  26.         System.out.println("退出方法");  

  27.         return object;  

  28.     }  

  29. }  



接下来我们需要在bean.xml文件中配置我们的切面/切入点/通知等信息 

Xml代码  收藏代码

  1. <?xml version="1.0" encoding="UTF-8"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  4.        xmlns:context="http://www.springframework.org/schema/context"   

  5.        xmlns:aop="http://www.springframework.org/schema/aop"        

  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans  

  7.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  

  8.            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  

  9.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  

  10.         <aop:aspectj-autoproxy/>   

  11.         <bean id="aspjectbean" class="com.royzhou.aop.MyInterceptor" />  

  12.         <bean id="UserBean" class="com.royzhou.aop.UserBean"></bean>  

  13.         <aop:config>  

  14.             <aop:aspect id="asp" ref="aspjectbean">  

  15.                 <aop:pointcut id="mycut" expression="execution(* com.royzhou.aop..*.*(..))"/>  

  16.                 <aop:before pointcut="execution(* com.royzhou.aop..*.*(..)) and args(userName)" method="doBefore" />  

  17.                 <aop:after-returning pointcut-ref="mycut" method="doAfterReturning" returning="result" />  

  18.                 <aop:after-throwing pointcut-ref="mycut" method="doAfterReturning" throwing="e" />  

  19.                 <aop:after pointcut-ref="mycut" method="doAfter"/>  

  20.                 <aop:around pointcut-ref="mycut" method="doArround"/>  

  21.             </aop:aspect>  

  22.         </aop:config>  

  23. </beans>  



注意在前置通知中不支持args-name指定参数,必须在pointcut中指定,否则服务器抛出异常:0 formal unbound in pointcut 

总结一下AOP的优点: 
面对方法编程并不是要取代面对对象编程,而是要提高它。AOP程序员一般来说都是90%使用OOP来解决问题,而10%是用AOP来解决OOP不能解决的问题。 

横切关注点(Cross-cutting Concerns) 
  很多时候你发现你的类并不能十分清晰和明白的表到你所想表达的功能意义,因为你真正的代码 
  大多被其它代码所包围了。如果你想很好的扩展或集成你所想表达的功能意义,你最好就用方面 
  的思想来考虑它了。 

开发中的分层(Layering Based on Deployment)   
    AOP另外一个很有用的地方就是可以用来为你的应用程序分层。很多时候你希望的一些特殊应用或 
  类是可以很好的配置的,但同时也希望这些东西是不臃肿和可以扩展的。AOP提供了很好的途径来 
  分层这些复杂的东西。JBOSS AOP提供了XML配置的机制来配置每个方面的开发。最好的例子就是 
  缓存服务,它提供了不同的锁机制。这些缓存锁机制可以很好的织入你的类,而不影响你的类的 
  代码,这样你的类就是很好的扩展性了。 

透明性(Transparency) 
  很多时候你都想把你的程序的焦点集中在商务应用和应用逻辑上,而不是关注于中间件的开发。 
  AOP允许你透明的应用中间件而不再使你的代码收到污染。一个很好的例子就是JBOSS AOP中的 
  用户认证上面。 

异常处理 
  处理异常是AOP提供给我们另外一个很有用的东西。例如,SQLException异常包含了SQL语句的 
  异常信息或者数据库的死锁等信息,但这些信息却使用不同错误代码和信息。AOP可以让你拦截 
  SQL语句信息,并分类处理数据库死锁信息。


本文转载自:http://royzhou1985.iteye.com/blog/480860

涩女郎
粉丝 37
博文 104
码字总数 160210
作品 0
浦东
高级程序员
私信 提问
Spring之 Aspect Oriented Programming with Spring

1. Concepts Aspect-Oriented Programming (AOP) complements OOP by providing another way of thinking about program structure. While OO decomposes applications into a hierarchy of ......

leodaxin
2018/07/29
0
0
主键不是id的实体使用insertUseGeneratedKeys方法问题

@Liuzh_533 你好,想跟你请教个问题: 看了你在 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error invoking SqlProvide......

rexxx
2017/05/27
612
1
Spring中的AOP(二)——AOP基本概念和Spring对AOP的支持

AOP的基本概念 AOP从运行的角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中的各个步骤,希望以更好的方式来组合业务逻辑的各个步骤。AOP框架并不与特定的代码耦合,AOP...

摆渡者
2014/03/17
2.1K
3
Spring 2.0 的AOP介绍及其通知类型

Spring 2.0的AOP 在Spring 2.0中最激动人心的增强之一是关于Spring AOP,它变得更加便于使用而且更加强大,主要是通过复杂而成熟的AspectJ语言的支持功能来实现,而同时保留纯的基于代理的J...

小星星程序员
2014/08/15
290
0
最最简单的spring及AOP实例

一、简单的spring实现(annotation方式) bean类 测试类: 运行结果: (xml方式) bean类 xml配置文件applicationContext.xml(放在包com.hello下) 测试类: 二、注解方式实现aop(需要导入...

wangxuwei
2017/10/24
166
0

没有更多内容

加载失败,请刷新页面

加载更多

前端技术之:Prisma Demo服务部署过程记录

安装前提条件: 1、已经安装了docker运行环境 2、以下命令执行记录发生在MackBook环境 3、已经安装了PostgreSQL(我使用的是11版本) 4、Node开发运行环境可以正常工作 首先需要通过Node包管...

popgis
今天
5
0
数组和链表

数组 链表 技巧一:掌握链表,想轻松写出正确的链表代码,需要理解指针获引用的含义: 对指针的理解,记住下面的这句话就可以了: 将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指...

code-ortaerc
今天
4
0
栈-链式(c/c++实现)

上次说“栈是在线性表演变而来的,线性表很自由,想往哪里插数据就往哪里插数据,想删哪数据就删哪数据...。但给线性表一些限制呢,就没那么自由了,把线性表的三边封起来就变成了栈,栈只能...

白客C
今天
43
0
Mybatis Plus service

/** * @author beth * @data 2019-10-20 23:34 */@RunWith(SpringRunner.class)@SpringBootTestpublic class ServiceTest { @Autowired private IUserInfoService iUserInfoS......

一个yuanbeth
今天
5
0
php7-internal 7 zval的操作

## 7.7 zval的操作 扩展中经常会用到各种类型的zval,PHP提供了很多宏用于不同类型zval的操作,尽管我们也可以自己操作zval,但这并不是一个好习惯,因为zval有很多其它用途的标识,如果自己...

冻结not
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部