- 浏览: 19867 次
- 性别:
- 来自: 上海
最近访客 更多访客>>
文章分类
最新评论
-
tonytony3:
好
spring AOP(3) -
Sq_I:
楼主能否把例子发给我一下530149995@qq.com非常感 ...
ExtJS专题-TreePanel -
Sq_I:
楼主能否把例子发给我一下
ExtJS专题-TreePanel
AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,一种新兴的编程技术。
AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,
AOP可以说也是这种目标的一种实现。它可以解决OOP和过程化方法不能够很好解决的横切
(crosscut)问题,
如:事务、安全、日志等横切关注。当未来系统变得越来越复杂,
横切关注点就成为一个大问题的时候,AOP就可以很轻松的解决横切关注点这个问题。
比如有这样一个情景:
这个例子虽然是很好的面向对象代码,但是在业务处理逻辑中夹杂这日志处理和权限判断,变得复杂混乱.
在 AOP 中,正交关注点(如安全和日志记录)被识别为系统中的常见横切关注点。说它们是横切,
是因为它们总是切入模块(如包、类和代码文件)的多个单位。也许横切关注点可能不是核心业务逻辑的一部分,但是它们是应用程序的基本部分。
AOP的实现主要是通过方法的拦截实现.在不使用AOP框架的情况下,我们可以通过JDK提供的动态代理来实现方法的拦截
注意:使用JDK提供的动态代理实现
要求我们的目标对象必须实现接口
IUserBean接口
IUserBean实现类 UserBean.java
我们希望在UserBean执行方法之前先检查userName是不是为空,以此做为权限判断.
当然我们可以在没个方法里面去加这些判断,但是这需要为每个方法都添加同样的判断,维护不便.
使用JDK提供的动态代理技术可以很方便的实现上面的需求:
通过java.lang.reflect.Proxy;提供的
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
方法可以生成一个动态代理对象
其中
loader是类装载器
interfaces是目标对象实现的一系列接口
h是一个实现InvocationHandler接口的类,我们对代理对象的所有操作都经过它处理
这样我们就可以拦截到UserBean的方法,在方法执行前先判断是否有权限,如果有则执行方法,
没有权限的话就不执行方法.
编写我们的代理类:
JDKProxy.java
通过调用createProxyObject可以生成代理对象,
编写测试类如下:
执行成功后输出:
this is addUser() method!
再次修改测试类:
即当用户没有权限时,控制台不输出东西,说明我们拦截方法对其做的权限判断生效了.
从上面这个例子可以成功拦截了调用的方法并对其做了相应的处理
如果不使用JDK提供的Proxy类
通过cglib创建代理类,好处是不要求我们的目标对象实现接口
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this); //回调,参数是一个实现MethodInterceptor接口的类,我们对代理对象的所有操作都经过它处理
return enhancer.create(); //创建代理对象
修改UserBean 去掉IUserBean接口
通过cglib创建代理类
CGLibProxy.java
编写测试类:
输出:
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通知可理解如下:
Spring提供两种方式实现AOP
一种是XML配置的方式
一种是annotation注解的方式
不管采用哪种方式,都必须在spring的配置文件中配置AOP支持:
其中<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
实现类UserBean.java
紧接着我们建立我们的切面类:使用@Aspect注解
MyInterceptor.java
切面定义好之后我们必须交给Spring管理,配置我们的bean.xml文件如下:
编写测试类如下:
运行测试类输出:
前置通知
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可以看出定义的各个通知的执行顺序,
例外通知只有在程序异常的情况下才会发生.
其他通知都会执行.
我们也可以在环绕通知里面将前面的几个通知实现了.
如果需要获取方法的参数我们必须在定义通知的时候做响应的设置:
比如我在前置通知希望获取到输入的参数需要修改MyInterceptor如下:
重新运行测试类输出:
前置通知royzhou
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可见我们成功的获取到了方法的参数
如果需要获取方法的返回值,则修改如下:
输出结果是:
前置通知1
进入方法
this is getUser() method!
后置通知haha
最终通知
退出方法
可见方法的返回值我们也成功拿到了.
如需在例外通知中获取例外的详细信息,我们只需要配置:
上面的例子介绍了使用注解方式来实现Spring的AOP
另外我们可以使用XML来配置:
使用XML配置Spring的AOP 我们的切面类MyInterceptor不需要做任何注解,就是一个普通的Java类
接下来我们需要在bean.xml文件中配置我们的切面/切入点/通知等信息
注意在前置通知中不支持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语句信息,并分类处理数据库死锁信息。
AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,
AOP可以说也是这种目标的一种实现。它可以解决OOP和过程化方法不能够很好解决的横切
(crosscut)问题,
如:事务、安全、日志等横切关注。当未来系统变得越来越复杂,
横切关注点就成为一个大问题的时候,AOP就可以很轻松的解决横切关注点这个问题。
比如有这样一个情景:
public class AccountManager { private static final sysLogger = SystemLogger.getInstance(); private AuthorizationManager authMgr = new AuthorizationManager(); public void transferFunds(String from, String to, int amount) { sysLogger.log("transfer funds from " + from + " to " + to); if(authMgr.accessAble(from) && authMgr.accessAble(to)) { sysLogger.log("access successfully"); CustomerAccount from = findAccount(from); CustomerAccount to = findAccount(to); from.debit(amount); to.credit(amount); } else { sysLogger.log("access deny"); } sysLogger.log("transfer funds from " + from + " to " + to + " $" + amount + " successfully!"); } }
这个例子虽然是很好的面向对象代码,但是在业务处理逻辑中夹杂这日志处理和权限判断,变得复杂混乱.
在 AOP 中,正交关注点(如安全和日志记录)被识别为系统中的常见横切关注点。说它们是横切,
是因为它们总是切入模块(如包、类和代码文件)的多个单位。也许横切关注点可能不是核心业务逻辑的一部分,但是它们是应用程序的基本部分。
AOP的实现主要是通过方法的拦截实现.在不使用AOP框架的情况下,我们可以通过JDK提供的动态代理来实现方法的拦截
注意:使用JDK提供的动态代理实现
要求我们的目标对象必须实现接口
IUserBean接口
package com.royzhou.aop; public interface IUserBean { public void getUser(); public void addUser(); public void updateUser(); public void deleteUser(); }
IUserBean实现类 UserBean.java
package com.royzhou.aop; public class UserBean implements IUserBean { private String user = null; public UserBean() { } public UserBean(String user) { this.user = user; } public void setUser(String user) { this.user = user; } public void addUser() { System.out.println("this is addUser() method!"); } public void deleteUser() { System.out.println("this is deleteUser() method!"); } public void getUser() { System.out.println("this is getUser() method!"); } public void updateUser() { System.out.println("this is updateUser() method!"); } }
我们希望在UserBean执行方法之前先检查userName是不是为空,以此做为权限判断.
当然我们可以在没个方法里面去加这些判断,但是这需要为每个方法都添加同样的判断,维护不便.
使用JDK提供的动态代理技术可以很方便的实现上面的需求:
通过java.lang.reflect.Proxy;提供的
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
方法可以生成一个动态代理对象
其中
loader是类装载器
interfaces是目标对象实现的一系列接口
h是一个实现InvocationHandler接口的类,我们对代理对象的所有操作都经过它处理
这样我们就可以拦截到UserBean的方法,在方法执行前先判断是否有权限,如果有则执行方法,
没有权限的话就不执行方法.
编写我们的代理类:
JDKProxy.java
package com.royzhou.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxy implements InvocationHandler { private Object targetObject; public Object createProxyObject(Object targetObject) { this.targetObject = targetObject; //生成代理对象 return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { UserBean userBean = (UserBean) targetObject; String userName = userBean.getUserName(); Object result = null; //权限判断 if(userName!=null && !"".equals(userName)) { //调用目标对象的方法 result = method.invoke(targetObject, args); } return result; } }
通过调用createProxyObject可以生成代理对象,
编写测试类如下:
package com.royzhou.aop; public class TestProxy { public static void main(String[] args) { JDKProxy jProxy = new JDKProxy(); IUserBean userBean = (IUserBean) jProxy.createProxyObject(new UserBean("royzhou")); userBean.addUser(); } }
执行成功后输出:
this is addUser() method!
再次修改测试类:
package com.royzhou.aop; public class TestProxy { public static void main(String[] args) { JDKProxy jProxy = new JDKProxy(); IUserBean userBean = (IUserBean) jProxy.createProxyObject(new UserBean()); userBean.addUser(); } }
即当用户没有权限时,控制台不输出东西,说明我们拦截方法对其做的权限判断生效了.
从上面这个例子可以成功拦截了调用的方法并对其做了相应的处理
如果不使用JDK提供的Proxy类
通过cglib创建代理类,好处是不要求我们的目标对象实现接口
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());
enhancer.setCallback(this); //回调,参数是一个实现MethodInterceptor接口的类,我们对代理对象的所有操作都经过它处理
return enhancer.create(); //创建代理对象
修改UserBean 去掉IUserBean接口
package com.royzhou.aop; public class UserBean { private String userName = null; public UserBean() { } public UserBean(String userName) { this.userName = userName; } public void addUser() { System.out.println("this is addUser() method!"); } public void deleteUser() { System.out.println("this is deleteUser() method!"); } public void getUser() { System.out.println("this is getUser() method!"); } public void updateUser() { System.out.println("this is updateUser() method!"); } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
通过cglib创建代理类
CGLibProxy.java
package com.royzhou.aop; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CGLibProxy implements MethodInterceptor { private Object targetObject; public Object createProxyObject(Object targetObject) { this.targetObject = targetObject; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass()); //非final 进行覆盖 enhancer.setCallback(this); //回调,通过 return enhancer.create(); //创建代理对象 } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { UserBean userBean = (UserBean) targetObject; String userName = userBean.getUserName(); Object result = null; if(userName!=null && !"".equals(userName)) { //调用目标对象的方法 result = methodProxy.invoke(targetObject, args); } return result; } . }
编写测试类:
package com.royzhou.aop; public class TestProxy { public static void main(String[] args) { CGLibProxy cProxy = new CGLibProxy(); UserBean userBean = (UserBean) cProxy.createProxyObject(new UserBean("royzhou")); userBean.addUser(); } }
输出:
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通知可理解如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { UserBean userBean = (UserBean) targetObject; String userName = userBean.getUserName(); Object result = null; if(userName!=null && !"".equals(userName)) { //调用目标对象的方法 try { //前置通知 result = method.invoke(targetObject, args); //后置通知 } catch(Exception e) { //例外通知 } finally { //最终通知 } } //环绕通知(前置通知之后,目标对象方法调用之前执行,全部执行完毕(最终通知)之后) return result; }
Spring提供两种方式实现AOP
一种是XML配置的方式
一种是annotation注解的方式
不管采用哪种方式,都必须在spring的配置文件中配置AOP支持:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/> </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
package com.royzhou.aop; public interface IUserBean { public void getUser(); public void addUser(); public void updateUser(); public void deleteUser(); }
实现类UserBean.java
package com.royzhou.aop; public class UserBean { public void addUser(String userName) { System.out.println("this is addUser() method!"); } public void deleteUser(int userId) { System.out.println("this is deleteUser() method!"); } public String getUser(String userId) { System.out.println("this is getUser() method!"); return "haha"; } public void updateUser(int userId, String userName) { System.out.println("this is updateUser() method!"); } }
紧接着我们建立我们的切面类:使用@Aspect注解
MyInterceptor.java
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截,(!void表示有返回值) * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类都被拦截 * 最后一个*表示类的所有方法都被拦截 * (..)表示方法的参数可以任意多个如[(java.lang.String,java.lang.Integer)表示第一个参数是String,第二个参数是int的方法才会被拦截] */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } @Before("pointCutMethod()") //定义前置通知 public void doBefore() { System.out.println("前置通知"); } @AfterReturning("pointCutMethod()") //定义后置通知 public void doAfterReturning() { System.out.println("后置通知"); } @AfterThrowing("pointCutMethod()") //定义例外通知 public void doAfterException() { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
切面定义好之后我们必须交给Spring管理,配置我们的bean.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/> <bean id="MyInterceptor" class="com.royzhou.aop.MyInterceptor" /> <bean id="UserBean" class="com.royzhou.aop.UserBean"></bean> </beans>
编写测试类如下:
package com.royzhou.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAOP { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); UserBean ub = (UserBean)ctx.getBean("UserBean"); ub.addUser("royzhou"); } }
运行测试类输出:
前置通知
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可以看出定义的各个通知的执行顺序,
例外通知只有在程序异常的情况下才会发生.
其他通知都会执行.
我们也可以在环绕通知里面将前面的几个通知实现了.
如果需要获取方法的参数我们必须在定义通知的时候做响应的设置:
比如我在前置通知希望获取到输入的参数需要修改MyInterceptor如下:
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类 * 最后一个*表示类的所有方法 * (..)表示方法的参数可以任意多个 */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样 @Before("pointCutMethod() && args(userName)") //定义前置通知 public void doBefore(String userName) { System.out.println("前置通知" + userName); } @AfterReturning("pointCutMethod()") //定义后置通知 public void doAfterReturning() { System.out.println("后置通知"); } @AfterThrowing("pointCutMethod()") //定义例外通知 public void doAfterException() { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
重新运行测试类输出:
前置通知royzhou
进入方法
this is addUser() method!
后置通知
最终通知
退出方法
可见我们成功的获取到了方法的参数
如果需要获取方法的返回值,则修改如下:
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类 * 最后一个*表示类的所有方法 * (..)表示方法的参数可以任意多个 */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样 @Before("pointCutMethod() && args(userName)") //定义前置通知 public void doBefore(String userName) { System.out.println("前置通知" + userName); } //配置returning="result", result必须和doAfterReturning的参数一致 @AfterReturning(pointcut="pointCutMethod()", returning="result") //定义后置通知 public void doAfterReturning(String result) { System.out.println("后置通知" + result); } @AfterThrowing("pointCutMethod()") //定义例外通知 public void doAfterReturning() { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
输出结果是:
前置通知1
进入方法
this is getUser() method!
后置通知haha
最终通知
退出方法
可见方法的返回值我们也成功拿到了.
如需在例外通知中获取例外的详细信息,我们只需要配置:
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MyInterceptor { /** * 定义切入点 * 第一个*表示方法的返回值,这里使用通配符,只有返回值符合条件的才拦截 * 第一个..表示com.royzhou.aop包及其子包 * 倒数第二个*表示包下的所有Java类 * 最后一个*表示类的所有方法 * (..)表示方法的参数可以任意多个 */ @Pointcut("execution(* com.royzhou.aop..*.*(..))") //定义一个切入点,名称为pointCutMethod(),拦截类的所有方法 private void pointCutMethod() { } //需要两个条件同时成立. args(userName)代表只有一个参数且为String类型 名称必须与doBefore方法的参数名称一样 @Before("pointCutMethod() && args(userName)") //定义前置通知 public void doBefore(String userName) { System.out.println("前置通知" + userName); } //配置returning="result", result必须和doAfterReturning的参数一致 @AfterReturning(pointcut="pointCutMethod()", returning="result") //定义后置通知 public void doAfterReturning(String result) { System.out.println("后置通知" + result); } //类似returning的配置 @AfterThrowing(pointcut="pointCutMethod()", throwing="e") //定义例外通知 public void doAfterException(Exception e) { System.out.println("异常通知"); } @After("pointCutMethod()") //定义最终通知 public void doAfter() { System.out.println("最终通知"); } @Around("pointCutMethod()") //定义环绕通知 public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
上面的例子介绍了使用注解方式来实现Spring的AOP
另外我们可以使用XML来配置:
使用XML配置Spring的AOP 我们的切面类MyInterceptor不需要做任何注解,就是一个普通的Java类
package com.royzhou.aop; import org.aspectj.lang.ProceedingJoinPoint; public class MyInterceptor { public void doBefore(String userName) { System.out.println("前置通知" + userName); } public void doAfterReturning(String result) { System.out.println("后置通知" + result); } public void doAfterException(Exception e) { System.out.println("异常通知"); } public void doAfter() { System.out.println("最终通知"); } public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object object = pjp.proceed(); //必须执行pjp.proceed()方法,如果不执行此方法,业务bean的方法以及后续通知都不执行 System.out.println("退出方法"); return object; } }
接下来我们需要在bean.xml文件中配置我们的切面/切入点/通知等信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/> <bean id="aspjectbean" class="com.royzhou.aop.MyInterceptor" /> <bean id="UserBean" class="com.royzhou.aop.UserBean"></bean> <aop:config> <aop:aspect id="asp" ref="aspjectbean"> <aop:pointcut id="mycut" expression="execution(* com.royzhou.aop..*.*(..))"/> <aop:before pointcut="execution(* com.royzhou.aop..*.*(..)) and args(userName)" method="doBefore" /> <aop:after-returning pointcut-ref="mycut" method="doAfterReturning" returning="result" /> <aop:after-throwing pointcut-ref="mycut" method="doAfterReturning" throwing="e" /> <aop:after pointcut-ref="mycut" method="doAfter"/> <aop:around pointcut-ref="mycut" method="doArround"/> </aop:aspect> </aop:config> </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语句信息,并分类处理数据库死锁信息。
发表评论
-
Spring学习笔记
2011-03-04 14:26 750传统的对象创建及维护是由应用对象本身维护及创建。 publ ... -
spring AOP(3)
2011-03-03 13:52 832在前面我们讨论了如何编写通知,但是我们却没有指明在应用系统的什 ... -
spring AOP(2)
2011-03-03 13:21 558前面写过一篇关于Spring AOP方面的文章,探讨了Spri ... -
spring AOP(1)
2011-03-03 13:18 540spring里面有个概念叫aop(面向切面编程),很好很强大又 ... -
[摘]Spring的jar包解析
2011-02-21 14:00 816下载的spring包中文件及各种包众多,在项目中往往只有部分是 ...
相关推荐
本学习笔记将深入探讨Spring AOP的核心概念、工作原理以及实际应用。 1. **核心概念** - **切面(Aspect)**:切面是关注点的模块化,包含业务逻辑之外的横切关注点,如日志、事务管理。 - **连接点(Join Point...
Spring Aop 学习笔记
**Spring AOP 学习笔记及实现Demo** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架中的一个重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。AOP的主要目的...
,文章属于基础级文章,适合入门级的小伙伴,它的概念,应用场景,实现原理及Spring的AOP的开发。全称:面向切面编程(AspectOrientedProgramming),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。...
Spring框架是Java开发中不可或缺的一部分,它为开发者提供了强大的依赖注入(IOC)和面向切面编程(AOP)功能,以及用于构建Web应用程序的MVC框架。Spring Boot则是基于Spring框架构建的应用程序启动器,旨在简化...
本笔记主要探讨了如何在Spring应用中使用AOP来实现横切关注点,如日志、事务管理、性能监控等。 首先,理解AOP的基本概念至关重要。AOP的核心是切面(Aspect),它封装了跨越多个对象的行为或责任。切面由两个主要...
这份"Spring框架学习笔记"涵盖了Spring框架的基础知识、核心组件以及高级特性,对于初学者来说是一份宝贵的资料。 一、Spring框架概述 Spring框架是为了解决企业应用开发的复杂性而设计的,它提供了一个全面的基础...
马士兵老师是知名的Java教育专家,他的Spring框架学习笔记深入浅出,对于初学者和进阶者来说都是一份宝贵的资源。这份笔记涵盖了Spring的核心概念、配置、AOP(面向切面编程)、DI(依赖注入)等关键知识点。 1. **...
本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...
在本次的Java Spring框架学习笔记中,将对Spring框架的核心概念进行详细解析,包括Spring的 IOC(控制反转)、AOP(面向切面编程)、jdbcTemplate、事务管理、Spring5新特性以及与Mybatis的整合。本学习笔记提供了...
标题和描述均提到了“spring指南学习笔记”,这意味着文档聚焦于Spring框架的学习心得与关键概念。Spring是一个开源的Java企业级应用框架,以其强大的依赖注入(Dependency Injection, DI)和面向切面编程(Aspect ...
### Spring学习笔记(精华全记录) #### Spring框架概述 Spring框架源自Rod Johnson的个人项目,最初于2002年末发布。Spring并非一开始就作为一个完整的框架出现,而是从一个项目逐步发展而来。随着项目的成熟,...
这份"Spring学习笔记+学习源码.zip"资源包含了深入学习Spring及其相关技术的知识点,以及实践代码,对提升Spring技能将大有裨益。 首先,我们来详细讨论Spring框架的主要组件和功能: 1. **依赖注入(Dependency ...
Spring框架是Java应用开发中广泛使用的轻量级框架,它以IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)为核心,提供了丰富的功能,包括但不限于组件管理、AOP(Aspect Oriented ...
Spring入门学习笔记,内容包括Spring介绍,Spring配置文件,Spring配置数据源,Spring的注解开发,Spring集成Junit,Spring的AOP,jdbcTemplate介绍,Spring控制事务流程,Spring集成web。
以下将详细介绍Spring学习笔记中的主要知识点。 **面向抽象编程** 面向抽象编程是一种设计原则,强调在代码中使用接口或抽象类,而不是具体实现类。这使得系统更具有灵活性,易于扩展和维护。在Spring框架中,我们...
根据提供的压缩包文件名,我们可以推测这是一个逐步学习Spring的系列笔记。从"Spring_day1"开始,可能涵盖了Spring的基础概念、环境搭建和基本配置。"Spring_day2"可能涉及了依赖注入和AOP的深入讲解。"Spring_day3...
总之,《Spring技术内幕 学习笔记》涵盖了Spring框架的众多核心知识点,从IoC容器、AOP到Web开发和数据访问,对于提升Spring开发技能具有很高的价值。通过深入学习和实践,开发者能够更好地理解和掌握Spring框架,...