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 - 调用方法结束:之后执行!