- 浏览: 250828 次
- 性别:
- 来自: 广州
-
文章分类
最新评论
-
pshaoyi:
连接创建没问题,在执行的时候就报错了。Caused by: c ...
jdbc连接池简单封装 -
imbangbang:
中文乱码,没有解决掉
Java实现Zip压缩与解压(解决中文乱码问题) -
chjy1983:
麻烦也发份给我,chl_82@126.com,谢谢!
Java实现Zip压缩与解压(解决中文乱码问题) -
jidacwj:
楼主写的这个有问题呀首先,多线程分割文件第45行,我尝试打印每 ...
Java 大文件读取排序 -
www6wzk:
非常好的学习例子,十分感谢!
Jbpm工作流实例—报销申请
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语句信息,并分类处理数据库死锁信息。
评论
18 楼
hzxlb910
2013-07-17
要提供代码下载就好了,我就喜欢拷贝粘贴,呵呵,谢谢你的文章
17 楼
ciki
2010-08-03
不错,收藏,学习中。
16 楼
kililanxilu
2010-06-24
用Aspectj里面的测试方法中,应该是IUserBean ub = (IUserBean)ctx.getBean("UserBean"); 吧?
15 楼
kililanxilu
2010-06-18
不错的帖子,写的很详细~!
14 楼
zuozhengfeng
2010-03-31
总结得挺好的!支持啊
13 楼
gowish
2010-03-05
不错,正好用到,对AOP有了个大致了解。
12 楼
ronartest
2010-01-06
汗,,为什么不顺便传一下代码
11 楼
ariestiger
2009-11-24
好东西,讲得蛮清楚的。
个人感觉啊,讲AOP的家伙都在前面要讲一大堆的
Joinpoint:它定义在哪里加入你的逻辑功能,对于Spring AOP,Jointpoint指的就是Method。
Advice:特定的Jointpoint处运行的代码,对于Spring AOP 来讲,有Before advice、AfterreturningAdvice、ThrowAdvice、AroundAdvice(MethodInteceptor)等。
Pointcut:一组Joinpoint,就是说一个Advice可能在多个地方织入,
Aspect:实际是Advice和Pointcut的组合,但是Spring AOP 中的Advisor也是这样一个东西,但是Spring中为什么叫Advisor而不叫做Aspect。
Target:被通知的对象。
Proxy:将通知应用到目标对象后创建的对象
Weaving:将Aspect加入到程序代码的过程,对于Spring AOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。
这种玩意,我当初就是被这些鬼东西搞糊了,
其实我觉得如果是用AOP的话,用标注比配置文件要好,为什么?你哪来那么多需求要通过配置文件来修改什么切面,连接点?我觉得是完全没必要的,能用标注就标注。
其实说穿了,在Spring中:
一个切面就是一个类,这个类中有些方法会在其他对象的某些方法执行前,执行后,执行中触发执行。。。。。
一个切入点就是一个空方法,它就是一个点,我的理解是只是为了把业务方法(也就是那些将要被切面切到的方法)刺一个洞出来,好让切面中的方法能织入进去。。。。
一个Advice就是一个切面方法了,不过分了几种,什么之前(before),之后(after),等乱七八糟的,它就是切面要织入业务方法的方法(有点别扭,但我的理解就是这样的,把两个不相干的方法揉在一起去)。。。。。。。
还有一个问题,我怎么知道,你这些切面方法要切入哪些类,哪些方法?这个就是在切入点中的那个表达式来搞定的,由这个表达式来匹配,匹配的类当中的方法将被织入。。。。。
就这么多,感觉,用的时候我想不应该弄得那么复杂,尽量保持清晰,简单,那些太过复杂的玩意尽管能显示个人功力,但我觉得本来就是为了所谓的解耦,本来就是为了什么代码的清晰,搞得太复杂反而不好。。。。。。。
个人感觉啊,讲AOP的家伙都在前面要讲一大堆的
Joinpoint:它定义在哪里加入你的逻辑功能,对于Spring AOP,Jointpoint指的就是Method。
Advice:特定的Jointpoint处运行的代码,对于Spring AOP 来讲,有Before advice、AfterreturningAdvice、ThrowAdvice、AroundAdvice(MethodInteceptor)等。
Pointcut:一组Joinpoint,就是说一个Advice可能在多个地方织入,
Aspect:实际是Advice和Pointcut的组合,但是Spring AOP 中的Advisor也是这样一个东西,但是Spring中为什么叫Advisor而不叫做Aspect。
Target:被通知的对象。
Proxy:将通知应用到目标对象后创建的对象
Weaving:将Aspect加入到程序代码的过程,对于Spring AOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。
这种玩意,我当初就是被这些鬼东西搞糊了,
其实我觉得如果是用AOP的话,用标注比配置文件要好,为什么?你哪来那么多需求要通过配置文件来修改什么切面,连接点?我觉得是完全没必要的,能用标注就标注。
其实说穿了,在Spring中:
一个切面就是一个类,这个类中有些方法会在其他对象的某些方法执行前,执行后,执行中触发执行。。。。。
一个切入点就是一个空方法,它就是一个点,我的理解是只是为了把业务方法(也就是那些将要被切面切到的方法)刺一个洞出来,好让切面中的方法能织入进去。。。。
一个Advice就是一个切面方法了,不过分了几种,什么之前(before),之后(after),等乱七八糟的,它就是切面要织入业务方法的方法(有点别扭,但我的理解就是这样的,把两个不相干的方法揉在一起去)。。。。。。。
还有一个问题,我怎么知道,你这些切面方法要切入哪些类,哪些方法?这个就是在切入点中的那个表达式来搞定的,由这个表达式来匹配,匹配的类当中的方法将被织入。。。。。
就这么多,感觉,用的时候我想不应该弄得那么复杂,尽量保持清晰,简单,那些太过复杂的玩意尽管能显示个人功力,但我觉得本来就是为了所谓的解耦,本来就是为了什么代码的清晰,搞得太复杂反而不好。。。。。。。
10 楼
310628570
2009-11-12
挺好的~支持一下 呵呵~
9 楼
pan_java
2009-10-23
不错,不知那些人评了新手贴!
8 楼
royzhou1985
2009-10-08
pan_java 写道
不对吧没有变量类型
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
已修改 呵呵
7 楼
royzhou1985
2009-10-08
whaosoft 写道
lz 其实这已经是好多地方用了很久了 不过也支持你
只是总结一下 方便以后查阅
6 楼
acmilancn
2009-10-08
看完对AOP有个大概的了解了
5 楼
whaosoft
2009-10-08
lz 其实这已经是好多地方用了很久了 不过也支持你
4 楼
pan_java
2009-10-08
不对吧没有变量类型
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
private static final sysLogger = SystemLogger.getInstance();
参数与变量同名
public void transferFunds(String from, String to, int amount)
CustomerAccount from = findAccount(fromAcct);
CustomerAccount to = findAccount(toAcct);
3 楼
zcq100
2009-10-07
非常好,谢谢楼主
2 楼
Fire_Iter
2009-10-06
这么详尽的帖子没人顶。支持!谢谢了!LZ
1 楼
YiSingQ
2009-10-06
LZ。。。写得很详细啊。。。学习的好资料。。。
相关推荐
Spring框架的关键组件之一是面向方面编程(AOP)框架。 面向方面的编程需要将程序逻辑分解成不同的部分。 此教程将通过简单实用的方法来学习Spring框架提供的AOP/面向方面编程。
主要介绍了Spring aop失效的几种解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
描述一下Spring AOP? 在Spring AOP中关注点(concern)和横切关注点(cross-cutting concern)有什么不同? AOP有哪些可用的实现? Spring中有哪些不同的通知类型(advice types)? Spring AOP 代理是什么? 引介...
SpringAOP Spring AOP(面向方面的编程)用于模块化“横截面”服务。 用一种简单的方式,我们可以说它是一个旨在拦截某些进程的组件,例如,在执行某个方法时,Spring AOP可以审核该执行方法,并在该方法执行...
根据maven+spring搭建的一个web项目,里面是一些spring aop的实例,有配置实例也有注解实例
关于AOP注解前置通知、后置通知、返回通知、异常通知的注解注释及应用
Spring源码最难问题:当Spring AOP遇上循环依赖 Spring源码中最难的问题之一是循环依赖问题,当Spring AOP遇上循环依赖时,该如何解决? Spring通过三级缓存机制解决循环依赖的问题。 在Spring中,bean的实例化...
spring aop jar 包
1、spring切入点 2、spring前置织入,传入参数处理 3、spring后置织入,传入参数处理 4、环绕织入,参数及返回值处理 5、返回后织入,返回值处理 6、异常织入,异常处理 maven环境下,测试用例可直接运行
主要介绍了Spring AOP中使用args表达式的方法,结合实例形式分析了spring面向切面AOP中使用args表达式具体步骤、相关实现技巧与操作注意事项,需要的朋友可以参考下
1.简单的AOP日志实现-某方法之前的前后记录日志; 2.简单的AOP日志实现-需要记录方法的运行时间; 3.简单的AOP日志实现-增加检查订单功能;
使用 Spring AOP 进行方法耗时监测的好处有以下几点: 1. 代码实现简单,易于维护:使用 Spring AOP 可以将耗时监测的逻辑与业务逻辑进行解耦,避免业务逻辑代码的冗余和代码维护难度的提高。 2. 安全性高:使用 ...
学习Spring开发的AOP面向切面编程时所需要的jar包,包括com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
Spring aop 学习资料
我看这里面的都是要付费下载,我这里面有所有AOP和IOC引入的jar包,大家随便下载
压缩包中函数Spring AOP开发时使用注解和xml文件配置demo