论坛首页 入门技术论坛

Spring AOP

浏览 20326 次
锁定老帖子 主题:Spring AOP
该帖已经被评为新手帖
作者 正文
   发表时间:2009-09-30   最后修改:2009-10-08
AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,一种新兴的编程技术。
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语句信息,并分类处理数据库死锁信息。
   发表时间:2009-10-06  
LZ。。。写得很详细啊。。。学习的好资料。。。
0 请登录后投票
   发表时间:2009-10-06  
这么详尽的帖子没人顶。支持!谢谢了!LZ
0 请登录后投票
   发表时间: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); 
0 请登录后投票
   发表时间:2009-10-08  
lz 其实这已经是好多地方用了很久了 不过也支持你

0 请登录后投票
   发表时间:2009-10-08  
看完对AOP有个大概的了解了
0 请登录后投票
   发表时间:2009-10-08  
whaosoft 写道
lz 其实这已经是好多地方用了很久了 不过也支持你


只是总结一下 方便以后查阅
0 请登录后投票
   发表时间: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); 

已修改 呵呵
0 请登录后投票
   发表时间:2009-10-23  
不错,不知那些人评了新手贴!
0 请登录后投票
   发表时间: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),等乱七八糟的,它就是切面要织入业务方法的方法(有点别扭,但我的理解就是这样的,把两个不相干的方法揉在一起去)。。。。。。。
还有一个问题,我怎么知道,你这些切面方法要切入哪些类,哪些方法?这个就是在切入点中的那个表达式来搞定的,由这个表达式来匹配,匹配的类当中的方法将被织入。。。。。
就这么多,感觉,用的时候我想不应该弄得那么复杂,尽量保持清晰,简单,那些太过复杂的玩意尽管能显示个人功力,但我觉得本来就是为了所谓的解耦,本来就是为了什么代码的清晰,搞得太复杂反而不好。。。。。。。
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics