`
chenzehe
  • 浏览: 532645 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

深入浅出 Spring AOP 动态代理 CGLib Aspectj

 
阅读更多

 0、AOP实现原理

面向方面编程(Aspect Oriented Programming,简称AOP)是一种声明式编程(Declarative Programming)。AOP的实现原理可以看作是Proxy/Decorator设计模式的泛化,如下为Proxy模式的简单例子

Proxy { 
    innerObject; // 真正的对象 
    f1() { 
        // 做一些额外的事情
        innerObject.f1(); // 调用真正的对象的对应方法
        // 做一些额外的事情 
    } 
} 
1、使用代理实现AOP

声明一个服务类接口IService.java

package com.chenzehe.aop;
public interface IService {
	int save();
}
 实现类:
package com.chenzehe.aop;
public class Service implements IService {
	final Logger	log	= LoggerFactory.getLogger(Service.class);
	
	@Override
	public int save() {
		log.info("*****save*****");
		return 0;
	}	
}

再声明一个代理类ServiceProxy也实现IService接口,内部调用Service的实现方法,并在前面和后台添加自己的切面方法:

package com.chenzehe.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServiceProxy implements IService {
	final Logger	log	= LoggerFactory.getLogger(ServiceProxy.class);
	private IService	service;	
	public ServiceProxy(IService service) {
		this.service = service;
	}
	
	@Override
	public int save() {
		int result;
		log.debug("*****Before*****");
		result = service.save();
		log.debug("*****After*****");
		return result;
	}	
}

添加单元测试类ProxyTest使用ServiceProxy

import org.junit.Test;
public class ProxyTest {
	@Test
	public void testSave() {
		IService service = new ServiceProxy(new Service());
		service.save();
	}
}
方法输出:
ServiceProxy - *****Before*****
       *****save*****
ServiceProxy - *****After*****

 2、使用动态代理实现AOP

上面代码实现了最简单的AOP功能,但是如果项目中有很多像Service这样的类,那就需要写很多ServiceProxy这样的代理类来实现,所以需要动态代码方法来实现,也就是实现InvocationHandler的接口, 这个类可以让我们在JVM调用某个类的方法时动态的为些方法做些什么事,如下代码,IService接口和实现类Service不变,添加动态代理实现类DynamicProxy:

 

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DynamicProxy implements InvocationHandler {
	final Logger	log = LoggerFactory.getLogger(ServiceProxy.class);
	/**
	 * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象)
	 */
	private Object	delegate;
	
	/**
	 * 动态生成方法被处理过后的对象 (写法固定)
	 */
	public Object bind(Object delegate) {
		this.delegate = delegate;
		return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(), this.delegate.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result;
		log.debug("*****Before*****");
		result = method.invoke(this.delegate, args);
		log.debug("*****After*****");
		return result;
	}
	
}

 单元测试代码如下:

 

package com.chenzehe.aop;

import org.junit.Test;

public class DynamicProxyTest {
	@Test
	public void testSave() {
		IService service = (IService) new DynamicProxy().bind(new Service());
		service.save();
	}
}

输出:
ServiceProxy - *****Before*****
       *****save*****
ServiceProxy - *****After*****

 

 3、通知解耦

上面代码虽然实现了切面通知,但是通知方法都是在代理方法中实现,这样耦合度太高,我们可以抽象出一个接口,这个接口里就只有两个方法,一个是在被代理对象要执行方法之前执行的方法,我们取名为before,第二个方法就是在被代理对象执行方法之后执行的方法,我们取名为after,接口定义如下 :

package com.chenzehe.aop;

import java.lang.reflect.Method;

public interface IOperation {
	/**
	 * 方法执行之前的操作
	 * 
	 * @param method
	 */
	void before(Method method);
	
	/**
	 * 方法执行之后的操作
	 * 
	 * @param method
	 */
	void after(Method method);
}

 我们去写一个实现上面接口的类.我们把作他真正的操作者,如下面是日志操作者的一个类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerOperation implements IOperation {
	final Logger	log = LoggerFactory.getLogger(LoggerOperation.class);
	
	@Override
	public void before(Method method) {
		log.info("Before:" + method.getName());
	}
	
	@Override
	public void after(Method method) {
		log.info("After:" + method.getName());
	}
	
}

 动态代理实现类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class OperationDynamicProxy implements InvocationHandler {
	/**
	 * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象)
	 */
	private Object	delegate;
	/**
	 * 切面操作方法
	 */
	private Object	operation;
	
	public OperationDynamicProxy() {
	}
	
	/**
	 * 动态生成方法被处理过后的对象 (写法固定)
	 */
	public Object bind(Object delegate, Object operation) {
		this.delegate = delegate;
		this.operation = operation;
		
		return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(), this.delegate.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result;
		// 反射得到操作者的实例
		Class clazz = this.operation.getClass();
		// 反射得到操作者的before方法
		Method start = clazz.getDeclaredMethod("before", new Class[] { Method.class });
		// 反射执行before方法
		start.invoke(this.operation, new Object[] { method });
		// 执行要处理对象的原本方法
		result = method.invoke(this.delegate, args);
		// 反射得到操作者的end方法
		Method end = clazz.getDeclaredMethod("after", new Class[] { Method.class });
		// 反射执行end方法
		end.invoke(this.operation, new Object[] { method });
		return result;
	}
	
}

 测试类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import org.junit.Test;

public class OperationDynamicProxyTest {
	@Test
	public void testSave() {
		IService service = (IService) new OperationDynamicProxy().bind(new Service(), new LoggerOperation());
		service.save();
	}
}

输出:
LoggerOperation - Before:save
       *****save*****
LoggerOperation - After:save

 

4、使用CGLib实现

上面实现AOP中被代理对象都是提供接口的,有时候我们的需求中被代理类并不提供接口,此时使用CGLib来实现,CGLib实现原理是继承被代理类,重写被代理类的的方法,然后在重写方法中添加自己的切面方法。

没有实现提供的类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

public class ServiceNotInterface {
	final Logger	log	= LoggerFactory.getLogger(ServiceNotInterface.class);
	
	public int save() {
		log.info("*****save*****");
		return 0;
	}	
}

 CGLib实现类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.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	delegate;
	/**
	 * 切面操作方法
	 */
	private Object	operation;
	
	/**
	 * 动态生成方法被处理过后的对象
	 */
	public Object bind(Object delegate, Object operation) {
		this.delegate = delegate;
		this.operation = operation;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.delegate.getClass());
		// 回调方法
		enhancer.setCallback(this);
		// 创建代理对象
		return enhancer.create();
		
	}
	
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		Object result;
		// 反射得到操作者的实例
		Class clazz = this.operation.getClass();
		// 反射得到操作者的before方法
		Method start = clazz.getDeclaredMethod("before", new Class[] { Method.class });
		// 反射执行before方法
		start.invoke(this.operation, new Object[] { method });
		// 执行要处理对象的原本方法
		result = methodProxy.invokeSuper(proxy, args);
		// 反射得到操作者的end方法
		Method end = clazz.getDeclaredMethod("after", new Class[] { Method.class });
		// 反射执行end方法
		end.invoke(this.operation, new Object[] { method });
		return result;
	}
	
}

 测试类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.junit.Test;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibProxyTest {
	@Test
	public void testSave() {
		ServiceNotInterface service = (ServiceNotInterface) new CGLibProxy().bind(new ServiceNotInterface(), new LoggerOperation());
		service.save();
	}
}

输出:
LoggerOperation - Before:save
       *****save*****
LoggerOperation - After:save

 

 5、Spring AOP概念

       切面(aspect):用来切插业务方法的类。
  连接点(joinpoint):是切面类和业务类的连接点,其实就是封装了业务方法的一些基本属性,作为通知的参数来解析。
  通知(advice):在切面类中,声明对业务方法做额外处理的方法。
  切入点(pointcut):业务类中指定的方法,作为切面切入的点。其实就是指定某个方法作为切面切的地方。
  目标对象(target object):被代理对象。
  AOP代理(aop proxy):代理对象。
  前置通知(before advice):在切入点之前执行。
  后置通知(after returning advice):在切入点执行完成后,执行通知。
  环绕通知(around advice):包围切入点,调用方法前后完成自定义行为。
  异常通知(after throwing advice):在切入点抛出异常后,执行通知。

       Spring提供了三种实现AOP的方式,Spring接口方式、schema配置方式和注解方式的三种实现方式。

6、接口方式

利用Spring AOP接口实现AOP,主要是为了指定自定义通知来供spring AOP机制识别。主要接口:前置通知 MethodBeforeAdvice ,后置通知:AfterReturningAdvice,环绕通知:MethodInterceptor,异常通知:ThrowsAdvice 。如下代码:

使用上面的接口IService和实现类Service

前置通知:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.MethodBeforeAdvice;

/**
 * 前置通知
 */

public class BaseBeforeAdvice implements MethodBeforeAdvice {
	
	final Logger	log	= LoggerFactory.getLogger(BaseBeforeAdvice.class);
	
	/**
	 * method : 切入的方法 <br>
	 * args :切入方法的参数 <br>
	 * target :目标对象
	 */
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		log.info("===========进入beforeAdvice()============ \n");
		log.info("准备在" + target + "对象上用");
		log.info(method + "方法");
		log.info("要进入切入点方法了 \n");
	}
	
}

 后置通知:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.AfterReturningAdvice;

/**
 * 后置通知
 */
public class BaseAfterReturnAdvice implements AfterReturningAdvice {
	
	final Logger	log	= LoggerFactory.getLogger(BaseAfterReturnAdvice.class);
	
	/**
	 * returnValue :切入点执行完方法的返回值,但不能修改 <br>
	 * method :切入点方法 <br>
	 * args :切入点方法的参数数组 <br>
	 * target :目标对象
	 */
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		log.info("==========进入afterReturning()=========== \n");
	}
	
}

 环绕通知:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 环绕通知
 */

public class BaseAroundAdvice implements MethodInterceptor {
	final Logger	log	= LoggerFactory.getLogger(BaseAroundAdvice.class);
	
	/**
	 * invocation :连接点
	 */
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		log.info("===========进入around环绕方法!=========== \n");
		// 调用目标方法之前执行的动作
		log.info("调用方法之前: 执行!\n");
		// 调用方法的参数
		Object[] args = invocation.getArguments();
		// 调用的方法
		Method method = invocation.getMethod();
		// 获取目标对象
		Object target = invocation.getThis();
		// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
		Object returnValue = invocation.proceed();
		log.info("===========结束进入around环绕方法!=========== \n");
		log.info("输出:" + args + ";" + method + ";" + target + ";" + returnValue + "\n");
		log.info("调用方法结束:之后执行!\n");
		return returnValue;
	}
	
}

 异常通知:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.ThrowsAdvice;

/**
 * 异常通知,接口没有包含任何方法。通知方法自定义
 */
public class BaseAfterThrowsAdvice implements ThrowsAdvice {
	final Logger	log	= LoggerFactory.getLogger(BaseAfterThrowsAdvice.class);
	
	/**
	 * 通知方法,需要按照这种格式书写
	 * 
	 * @param method
	 *            可选:切入的方法
	 * @param args
	 *            可选:切入的方法的参数
	 * @param target
	 *            可选:目标对象
	 * @param throwable
	 *            必填 : 异常子类,出现这个异常类的子类,则会进入这个通知。
	 */
	public void afterThrowing(Method method, Object[] args, Object target, Throwable throwable) {
		log.info("出错啦");
	}
}

 定义指定切点:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import java.lang.reflect.Method;

import org.springframework.aop.support.NameMatchMethodPointcut;

/**
 * 定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br>
 * 继承NameMatchMethodPointcut类,来用方法名匹配
 */
public class Pointcut extends NameMatchMethodPointcut {
	
	private static final long	serialVersionUID	= 5891054717975242200L;
	
	@SuppressWarnings("rawtypes")
	@Override
	public boolean matches(Method method, Class targetClass) {
		// 设置单个方法匹配
		this.setMappedName("delete");
		// 设置多个方法匹配
		String[] methods = { "delete", "save" };
		
		// 也可以用“ * ” 来做匹配符号
		// this.setMappedName("get*");
		
		this.setMappedNames(methods);
		
		return super.matches(method, targetClass);
	}
}

 配置:

<?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:p="http://www.springframework.org/schema/p"
	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-3.0.xsd     
          http://www.springframework.org/schema/context     
          http://www.springframework.org/schema/context/spring-context-3.0.xsd 
          http://www.springframework.org/schema/aop     
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
	default-autowire="byName">

	<!-- ==============================利用spring自己的aop配置================================ -->
	<!-- 声明一个业务类 -->
	<bean id="service" class="com.chenzehe.aop.Service" />

	<!-- 声明通知类 -->
	<bean id="baseBefore" class="com.chenzehe.aop.BaseBeforeAdvice" />
	<bean id="baseAfterReturn" class="com.chenzehe.aop.BaseAfterReturnAdvice" />
	<bean id="baseAfterThrows" class="com.chenzehe.aop.BaseAfterThrowsAdvice" />
	<bean id="baseAround" class="com.chenzehe.aop.BaseAroundAdvice" />

	<!-- 指定切点匹配类 -->
	<bean id="pointcut" class="com.chenzehe.aop.Pointcut" />

	<!-- 包装通知,指定切点 -->
	<bean id="matchBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="pointcut">
			<ref bean="pointcut" />
		</property>
		<property name="advice">
			<ref bean="baseBefore" />
		</property>
	</bean>

	<!-- 使用ProxyFactoryBean 产生代理对象 -->
	<bean id="businessProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 代理对象所实现的接口 ,如果有接口可以这样设置 -->
		<property name="proxyInterfaces">
			<value>com.chenzehe.aop.IService</value>
		</property>

		<!-- 设置目标对象 -->
		<property name="target">
			<ref local="service" />
		</property>
		<!-- 代理对象所使用的拦截器 -->
		<property name="interceptorNames">
			<list>
				<value>matchBeforeAdvisor</value>
				<value>baseAfterReturn</value>
				<value>baseAround</value>
			</list>
		</property>
	</bean>
</beans>

 测试类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAopInterfaceTest {
	
	@Test
	public void testSpringAopInterface() {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
		IService service = (IService) context.getBean("businessProxy");
		service.save();
	}
}

 输出:

       BaseBeforeAdvice - ===========进入beforeAdvice()============ 

       BaseBeforeAdvice - 准备在com.chenzehe.aop.Service@1e136a8对象上用
       BaseBeforeAdvice - public abstract int com.chenzehe.aop.IService.save()方法
       BaseBeforeAdvice - 要进入切入点方法了 

       BaseAroundAdvice - ===========进入around环绕方法!=========== 

       BaseAroundAdvice - 调用方法之前: 执行!

       Service - *****save*****
       BaseAroundAdvice - ===========结束进入around环绕方法!=========== 

       BaseAroundAdvice - 输出:[Ljava.lang.Object;@ee558f;public abstract int com.chenzehe.aop.IService.save();com.chenzehe.aop.Service@1e136a8;0

       BaseAroundAdvice - 调用方法结束:之后执行!

       BaseAfterReturnAdvice - ==========进入afterReturning()=========== 

       前置方法会在切入点方法之前执行,后置会在切入点方法执行之后执行,环绕则会在切入点方法执行前执行同事方法结束也会执行对应的部分。主要是调用proceed()方法来执行切入点方法。配置 businessProxy这个bean的时候,ProxyFactoryBean类中指定了,proxyInterfaces参数。这里把他配置了IService接口。因为在项目开发过程中,往往业务类都会有对应的接口,以方便利用IOC解耦。但Spring AOP却也能支持没有接口的代理。这就是为什么需要导入cglib.jar的包了。在目标切入对象如果有实现接口,spring会默认走jdk动态代理来实现代理类。如果没有接口,则会通过cglib来实现代理类。

 

 7、使用aspectj来配置AOP

使用上面没有实现接口的业务类ServiceNotInterface

定义切面类AspectAdvice,包含了所有的通知:

package com.chenzehe.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 定义一个切面
 * 
 */
public class AspectAdvice {
	final Logger	log	= LoggerFactory.getLogger(AspectAdvice.class);
	
	/**
	 * 前置通知
	 * 
	 * @param jp
	 *            连接点
	 */
	public void doBefore(JoinPoint jp) {
		log.info("===========进入before advice============ \n");
		log.info("要进入切入点方法了 \n");
	}
	
	/**
	 * 后置通知
	 * 
	 * @param jp
	 *            连接点
	 * @param result
	 *            返回值
	 */
	public void doAfter(JoinPoint jp, String result) {
		log.info("==========进入after advice=========== \n");
	}
	
	/**
	 * 环绕通知
	 * 
	 * @param pjp
	 *            连接点
	 */
	public void doAround(ProceedingJoinPoint pjp) throws Throwable {
		log.info("===========进入around环绕方法!=========== \n");
		
		// 调用目标方法之前执行的动作
		log.info("调用方法之前: 执行!\n");
		
		// 调用方法的参数
		Object[] args = pjp.getArgs();
		// 调用的方法名
		String method = pjp.getSignature().getName();
		// 获取目标对象
		Object target = pjp.getTarget();
		// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
		Object result = pjp.proceed();
		
		log.info("输出:" + args + ";" + method + ";" + target + ";" + result + "\n");
		log.info("调用方法结束:之后执行!\n");
	}
	
	/**
	 * 异常通知
	 * 
	 * @param jp
	 * @param e
	 */
	public void doThrow(JoinPoint jp, Throwable e) {
		log.info("删除出错啦");
	}
	
}

 配置文件:

<?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:p="http://www.springframework.org/schema/p"
	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-3.0.xsd     
          http://www.springframework.org/schema/context     
          http://www.springframework.org/schema/context/spring-context-3.0.xsd 
          http://www.springframework.org/schema/aop     
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
	default-autowire="byName">

	<!-- ==============================利用spring 利用aspectj来配置AOP================================ -->

	<!-- 声明一个业务类 -->
	<bean id="aspectBusiness" class="com.chenzehe.aop.ServiceNotInterface" />

	<!-- 声明通知类 -->
	<bean id="aspectAdvice" class="com.chenzehe.aop.AspectAdvice" />

	<aop:config>
		<aop:aspect id="businessAspect" ref="aspectAdvice">
			<!-- 配置指定切入的对象 -->
			<aop:pointcut id="point_cut" expression="execution(* com.chenzehe.aop.ServiceNotInterface.*(..))" />

			<!-- 前置通知 -->
			<aop:before method="doBefore" pointcut-ref="point_cut" />
			<!-- 后置通知 returning指定返回参数 -->
			<aop:after-returning method="doAfter"
				pointcut-ref="point_cut" returning="result" />
			<aop:around method="doAround" pointcut-ref="point_cut" />
			<aop:after-throwing method="doThrow" pointcut-ref="point_cut"
				throwing="e" />
		</aop:aspect>
	</aop:config>
</beans>

 测试类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAopAspectjTest {
	@Test
	public void testSpringAopAspectj() {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-aspectj-aop.xml");
		ServiceNotInterface service = (ServiceNotInterface) context.getBean("aspectBusiness");
		service.save();
	}
}

 输出:

       AspectAdvice - ===========进入before advice============ 

       AspectAdvice - 要进入切入点方法了 

       DefaultListableBeanFactory - Returning cached instance of singleton bean 'aspectAdvice'
       AspectAdvice - ===========进入around环绕方法!=========== 

       AspectAdvice - 调用方法之前: 执行!

       ServiceNotInterface - *****save*****
       AspectAdvice - 输出:[Ljava.lang.Object;@edf389;save;com.chenzehe.aop.ServiceNotInterface@59fb21;0

       AspectAdvice - 调用方法结束:之后执行!

 

 8、使用aspectj注解来配置AOP

基于注解的service类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @description
 * 
 * @author chenzehe
 * @email hljuczh@163.com
 */
@Component
public class ServiceAnonotation {
	final Logger	log	= LoggerFactory.getLogger(ServiceAnonotation.class);
	
	public int save() {
		log.info("*****save*****");
		return 0;
	}
	
}

 基于注解的切面:

package com.chenzehe.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 定义一个切面
 * 
 */
@Component
@Aspect
public class AspectAdviceAnonotation {
	final Logger	log	= LoggerFactory.getLogger(AspectAdviceAnonotation.class);
	
	/**
	 * 指定切入点匹配表达式,注意它是以方法的形式进行声明的。
	 */
	@Pointcut("execution(* com.chenzehe.aop.*.*(..))")
	public void anyMethod() {
	}
	
	/**
	 * 前置通知
	 * 
	 * @param jp
	 */
	@Before(value = "execution(* com.chenzehe.aop.*.*(..))")
	public void doBefore(JoinPoint jp) {
		log.info("===========进入before advice============ \n");
		log.info("要进入切入点方法了 \n");
	}
	
	/**
	 * 后置通知
	 * 
	 * @param jp
	 *            连接点
	 * @param result
	 *            返回值
	 */
	@AfterReturning(value = "anyMethod()", returning = "result")
	public void doAfter(JoinPoint jp, String result) {
		log.info("==========进入after advice=========== \n");
	}
	
	/**
	 * 环绕通知
	 * 
	 * @param pjp
	 *            连接点
	 */
	@Around(value = "execution(* com.chenzehe.aop.*.*(..))")
	public void doAround(ProceedingJoinPoint pjp) throws Throwable {
		log.info("===========进入around环绕方法!=========== \n");
		
		// 调用目标方法之前执行的动作
		log.info("调用方法之前: 执行!\n");
		
		// 调用方法的参数
		Object[] args = pjp.getArgs();
		// 调用的方法名
		String method = pjp.getSignature().getName();
		// 获取目标对象
		Object target = pjp.getTarget();
		// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
		Object result = pjp.proceed();
		
		log.info("输出:" + args + ";" + method + ";" + target + ";" + result + "\n");
		log.info("调用方法结束:之后执行!\n");
	}
	
	/**
	 * 异常通知
	 * 
	 * @param jp
	 * @param e
	 */
	@AfterThrowing(value = "execution(* com.chenzehe.aop.*.*(..))", throwing = "e")
	public void doThrow(JoinPoint jp, Throwable e) {
		log.info("删除出错啦");
	}
	
}

 配置文件:

<?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:p="http://www.springframework.org/schema/p"
	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-3.0.xsd     
          http://www.springframework.org/schema/context     
          http://www.springframework.org/schema/context/spring-context-3.0.xsd 
          http://www.springframework.org/schema/aop     
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
	default-autowire="byName">

	<context:component-scan base-package="com.chenzehe.aop" />
	<!-- 打开aop 注解 -->
	<aop:aspectj-autoproxy />

</beans>

 测试类:

/**
 * Huisou.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */

package com.chenzehe.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @description
 * 
 * @author chenzehe
 * @email hljuczh@163.com
 */

public class SpringAopAspectjAnonotationTest {
	@Test
	public void testSpringAopAspectjAnonotation() {
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-aspectj-anonotation-aop.xml");
		ServiceAnonotation service = (ServiceAnonotation) context.getBean("serviceAnonotation");
		service.save();
	}
}

 输出:

       AspectAdviceAnonotation - ===========进入before advice============ 

       AspectAdviceAnonotation - 要进入切入点方法了 

       AspectAdviceAnonotation - ===========进入around环绕方法!=========== 

       AspectAdviceAnonotation - 调用方法之前: 执行!

       ServiceAnonotation - *****save*****
       AspectAdviceAnonotation - 输出:[Ljava.lang.Object;@7ecd78;save;com.chenzehe.aop.ServiceAnonotation@16be68f;0

       AspectAdviceAnonotation - 调用方法结束:之后执行!

 

 

 

分享到:
评论

相关推荐

    Spring aop 之 静态代理 动态代理 Aspectj aop-config 等实现方式

    主要对Spring AOP的相关概念和简单的静态代理、动态代理以及常见的几种AOP配置方式做总结学习。主要包括:1. AOP的常见概念 2. 静态代理 3. jdk动态代理 4. Aspectj and Aspectjweaver 5. **aop-config** 6. CGLIB ...

    Spring AOP的AspectJ支持jar包

    Spring AOP的AspectJ支持jar包; 包括: com.springsource.net.sf.cglib-2.2.0.jar com.srpingsource.org.aopalliance-1.0.0.jar com.srpingsource.org.aspectj.weaver-1.68.RELEASE.jar

    Spring AOP代理详细介绍

    如果一个类实现了一个或多个接口,那么Spring就会使用默认的JDK动态代理,如果没有实现任何接口,就会使用cglib来代理。当然我们也可以手动改变这些设置。这也是比较容易掉坑的部分,如果设置错了代理方式,那么在...

    spring_aop4.rar_Home Home_jar 转换_spring AOP jar

    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换 如何强制使用CGLIB实现AOP? * 添加CGLIB库,SPRING_HOME/cglib/*.jar * 在spring配置文件中加入&lt;aop:aspectj-...

    Spring AOP源码深度解析:掌握Java高级编程核心技术

    动态代理是实现AOP的基础,它通过JDK动态代理或CGLIB代理生成被代理对象的子类。通知是织入到目标对象连接点上的一段程序,例如@Before、@After等。 切点定义了通知应该在哪些连接点上触发。而切面则是通知和切点的...

    SpringAOP切面编程依赖jar包.rar

    学习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

    AOP相关lib aspectj.jar cglib-nodep.jar等

    实测springAOP配置文件所需的包相关lib aspectj.jar cglib-nodep.jar等

    springAOP所依赖的jar包

    springAOP所依赖的jar包:aopalliance-1.0,aspectj-1.7.3,aspectjweaver-1.7.4,cglib-nodep-2.1_3

    Spring5,AOP相关jar包.zip

    com.springsource.net.sf.cglib-2.2.0.jar ...spring-aop-5.3.4.jar spring-aspects-5.3.4.jar spring-beans-5.3.4.jar spring-context-5.3.4.jar spring-core-5.3.4.jar spring-expression-5.3.4.jar

    spring AOP开发的补充依赖包

    spring官方下载包之外的必备补充依赖包,包括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, commons-logging-...

    Spring aop依赖包.rar

    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及JUNIT包

    spring AOP及JUNIT包 com.springsource.net.sf.cglib-2.2.0,aopalliance.jar junit4.4.jar aspectj.rar

    Spring3注解

    AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表;而动态代理则以 Spring AOP 为代表。其中静态代理是指使用 AOP 框架提供的命令进行编译...

    SpringMVC3.0-Jar全量包含cglib几个包-aop的aspectj几个包

    springMVC springframework cglib asm aspectjtl aspectjweaver aopalliance common-logging等一些必要jar包

    spring5基于aspectj实现aop操作所需jar包

    spring5基于aspectj实现aop操作所需jar包 com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.ar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aspects-5.2.6....

    《Java EE企业开发框架》 Spring AOP实验报告

    1. 定义一个MyClass类(或接口),包括math()、english()和physics()方法,可以简单模拟输出:上数学课/英语课/物理...请选择使用JDK动态代理、CGLIB代理、ProxyFactoryBean或者AspectJ中的某一种方式来实现增强处理。

    SpringFramework常见知识点.md

    Spring常见知识点 - 什么是Spring Framework? - Spring的优缺点 - Spring的优点 - Spring的缺点 - Spring 主要提供了哪些模块? - Spring主要使用了哪些... - cglib动态代理和jdk动态代理的区别? Spring常见知识

    CGLIB和Spring framework1

    CGLIB和Spring frameworkSpring框架支持两类主要的AOP方法:Spring代理和AspectJ[1],两类方法都可以将aspect注入到

    spring-aop-annotation-log-all

    spring-aop-4.0.4.RELEASE.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-aspects-4.1.2.RELEASE.jar ...

    学习Spring AOP所需要的jar包

    com.springsource.net.sf.cglib-sources-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar ... ...

Global site tag (gtag.js) - Google Analytics