- 浏览: 254508 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
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(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。这个"spring aop jar 包"包含了实现这一功能所需的类和接口,...
Spring AOP,全称为Aspect Oriented Programming,是面向切面编程的一种编程范式,它是对传统的面向对象编程(OOP)的一种补充。在OOP中,核心是对象,而在AOP中,核心则是切面。切面是关注点的模块化,即程序中的...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点问题,如日志、事务管理、安全性等。本示例将简要介绍如何在Spring应用中实现AOP,通过实际的...
3、对spring aop认识模糊的,不清楚如何实现Java 自定义注解的 4、想看spring aop 注解实现记录系统日志并入库等 二、能学到什么 1、收获可用源码 2、能够清楚的知道如何用spring aop实现自定义注解以及注解的逻辑...
Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...
Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许开发者在不修改源代码的情况下,通过插入切面来增强或改变程序的行为。在本教程中,我们将深入探讨Spring AOP的不同使用方法,包括定义切点、通知类型...
Spring AOP,全称Aspect-Oriented Programming(面向切面编程),是Spring框架的一个重要模块,它通过提供声明式的方式来实现面向切面编程,从而简化了应用程序的开发和维护。在Spring AOP中,我们无需深入到每个...
现在,我们回到主题——"springaop依赖的jar包"。在Spring 2.5.6版本中,使用Spring AOP通常需要以下核心jar包: - `spring-aop.jar`:这是Spring AOP的核心库,包含了AOP相关的类和接口。 - `spring-beans.jar`:...
Spring AOP 和 Spring IOC 是 Spring 框架的两个核心组件,它们对于任何基于 Java 的企业级应用开发都至关重要。Spring AOP(面向切面编程)允许开发者在不修改源代码的情况下,通过“切面”来插入新的行为或增强已...
面向切面编程(AOP)是一种编程范式,旨在将横切关注点(如日志、安全等)与业务逻辑分离,从而提高模块化。...利用Java反射机制和Spring AOP框架,开发者可以方便地实现AOP,从而提升代码的模块化和可维护性。
**Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它允许程序员在不修改源代码的情况下,通过在运行时插入额外的行为(如日志记录、性能监控等)来增强对象的功能。动态代理则是Spring AOP实现的核心技术之一...
### Spring AOP面向方面编程原理:AOP概念详解 #### 一、引言 随着软件系统的日益复杂,传统的面向对象编程(OOP)逐渐暴露出难以应对某些横切关注点(cross-cutting concerns)的问题。为了解决这一挑战,面向方面编程...
在讨论Spring AOP(面向切面编程)时,首先需要理解几个核心概念。Spring AOP 是Spring框架提供的一个功能模块,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中解耦出来,通过在方法调用前后进行...
### Spring AOP 实现流程日志跟踪 #### 一、背景与目的 在现代软件开发过程中,为了确保系统的稳定性和可维护性,通常会引入非功能性的需求来增强应用程序的功能,比如日志记录、安全控制等。这些需求往往不是业务...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理等。在Java应用中,AOP通过代理模式实现了切面编程,使得我们可以将业务逻辑...
Spring源码最难问题:当Spring AOP遇上循环依赖 Spring源码中最难的问题之一是循环依赖问题,当Spring AOP遇上循环依赖时,该如何解决? Spring通过三级缓存机制解决循环依赖的问题。 在Spring中,bean的实例化...
Spring AOP,即Spring的面向切面编程模块,是Spring框架的重要组成部分,它允许开发者在不修改源代码的情况下,对程序进行横切关注点的处理,如日志、事务管理等。实现这一功能,主要依赖于三个核心的jar包:aop...
在`springAop1`这个压缩包中,可能包含了一个简单的应用示例,展示了如何定义一个切面类,以及如何在该类中定义通知方法。例如,我们可能会看到一个名为`LoggingAspect`的类,其中包含了`@Before`注解的方法,用于在...