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主要通过两种动态代理技术实现:JDK动态代理和CGLIB动态代理。 首先,让我们详细了解一下JDK动态代理。JDK动态代理基于Java的接口实现,它适用于目标对象实现了至少一个接口的情况。在运行时,JDK...
在Spring AOP(面向切面编程)中,CGLIB是一个重要的动态代理库,它用于在运行时创建子类以实现对目标对象的代理。CGLIB是针对那些不支持接口代理(例如Java中的final类)的情况而设计的。下面我们将深入探讨Spring ...
主要对Spring AOP的相关概念和简单的静态代理、动态代理以及常见的几种AOP配置方式做总结学习。主要包括:1. AOP的常见概念 2. 静态代理 3. jdk动态代理 4. Aspectj and Aspectjweaver 5. **aop-config** 6. CGLIB ...
Spring AOP的实现基于动态代理,对于接口实现类,它使用Java的`java.lang.reflect.Proxy`类来创建代理对象;对于没有接口的类,Spring使用CGLIB库生成子类。在运行时,Spring AOP会根据切面定义生成代理对象,然后...
5. **配置代理**:Spring会根据目标对象是否实现了接口来决定使用JDK动态代理还是CGLIB代理。如果目标对象实现了接口,Spring会选择JDK动态代理。动态代理类会继承自`java.lang.reflect.Proxy`,并实现目标对象的...
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会使用CGLIB动态代理。然而,这些代理方式有一定的限制,如无法处理静态方法或构造器。而AspectJ可以通过编译时织入或加载时织入,解决这些问题。 **四、实例解析** 在订单管理场景中,我们...
Spring提供了两种代理方式:JDK动态代理和CGLIB代理。JDK代理适用于那些实现了接口的类,而CGLIB代理则适用于没有实现接口的类。 引介(Introduction)是Spring AOP的一个特性,允许在通知对象中声明并实现它们原本...
总的来说,Spring AOP通过这四个关键的Jar包,结合AspectJ的强大功能和CGLIB的代理机制,为开发者提供了强大的面向切面编程支持,使得我们可以在不侵入原有业务代码的情况下,实现跨切面的关注点管理。
1. **代理**:Spring AOP支持两种类型的代理:JDK动态代理和CGLIB代理。JDK代理用于实现了接口的类,而CGLIB代理则用于没有接口或不希望使用接口的类。代理对象在调用目标方法时会执行切面逻辑。 2. **织入**:织入...
- **代理模式**:Spring AOP支持JDK动态代理和CGLIB代理,根据目标对象是否实现了接口来选择合适的代理方式。 - **自动代理生成**:Spring容器可以在启动时自动识别并生成代理对象,无需手动创建。 - **事务管理**...
在Spring AOP的源码中,`org.springframework.aop.framework.ProxyFactoryBean`是创建代理的主要类,它会根据目标类是否有接口选择JDK动态代理或CGLIB。`org.aopalliance.intercept.MethodInterceptor`接口定义了...
Spring AOP基于动态代理实现,可以与Spring IoC容器无缝集成,使得我们可以在不修改原有代码的情况下添加切面。此版本是3.1.1,适用于描述中提到的Spring 3.1版本。 4. **cglib-nodep-2.1_3.jar**:CGLIB(Code ...
Spring AOP基于两种代理机制实现:JDK动态代理和CGLIB代理。JDK动态代理适用于实现了接口的目标对象,通过反射机制创建一个代理类来拦截方法调用。而CGLIB是在运行时动态生成一个目标类的子类,从而实现对方法的...
- **动态代理**:Spring AOP使用JDK动态代理或CGLIB实现,如果目标对象实现了接口,优先使用JDK动态代理,否则使用CGLIB生成代理类。 总的来说,Spring AOP和AspectJ各有优缺点,选择哪种取决于具体的应用场景和...
- **动态代理**:Spring AOP主要使用JDK动态代理和CGLIB实现。如果目标类实现了接口,使用JDK动态代理;否则,使用CGLIB生成子类作为代理。 - **切点(Pointcut)**:定义连接点的筛选规则,通过表达式或注解来...
【Spring 框架系列(4) - 深入浅出 Spring 核心之面向切面编程(AOP)】 面向切面编程(AOP),全称为 Aspect Oriented Programming,是Spring框架的重要特性之一,用于实现代码的解耦,提高可维护性和可复用性。AOP的...
Spring AOP的实现基于动态代理,有两种代理方式:JDK动态代理和CGLIB动态代理。JDK动态代理主要针对实现了接口的类,而CGLIB代理则针对未实现接口的类,Spring会根据目标对象是否实现了接口来选择合适的代理方式。 ...
Spring 提供了两种主要的 AOP 实现方式:基于代理的和基于 AspectJ 的。基于代理的方式是 Spring 默认的实现,它通过 JdkDynamicProxy 或 CGLIB 创建代理对象来实现切面。而基于 AspectJ 的方式则更为强大,它允许...
基于代理的AOP是Spring默认的方式,它主要通过JDK动态代理或者CGLIB字节码生成技术来创建代理对象,实现代理逻辑。这种方式简单易用,但仅限于处理接口。而AspectJ则是更为强大的AOP解决方案,它可以处理非接口类,...