aop(Aspect-Oriented Programming)也就是面向切面编程,作为面向对象编程的一种补充。aop从程序运行角度考虑程序的流程,提取业务处理过程的切面。
Aspect(切面): 是通知(Advice)和切入点(Pointcut)的结合,通知和切入点共同定义了关于切面的全部内容---何时、何地、做什么。
Advice(通知): 所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能,通知定义了切面的”何时”和”做什么”)
Pointcut(切入点): 所谓切入点是指我们要对那些joinpoint进行拦截的定义. 切入点就定义了”何地”.JoinPoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.Target(目标对象): 代理的目标对象Weaving(织入): 是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象Introduction(引入): 在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.AOP代理:springAOP创建的代理对象。也可以说是对目标对象的加强。spring中的代理可以是jdk的动态代理,也可以是CGLIB代理。前者为实现接口的对象进行代理,后者是为了不实现接口的对象的代理。spring会根据具体的类是否有接口来以不同的方式处理代理过程。
1,JDK的动态代理
JDK的动态代理主要的核心方法是:java.lang.reflect.Proxy类的 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException该方法。
其中参数 :
loader
- 定义代理类的类加载器 、interfaces - 代理类要实现的接口列表、h
- 指派方法调用的调用处理程序 。其中的InvocationHandler为java.lang.reflect下面的接口
返回:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
InvocationHandler接口只有一个方法:Object invoke(Object proxy, Method method, Object[] args) throws Throwable
代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
如下要在一个CustmerService的保存更新操作上面加上事务处理。
ICustomeService接口:
package com.xiaohui.proxy; public interface ICustomeService { void save(Customer c); void update(Customer c); }CustomerServiceImplCustomerServiceImpl类
package com.xiaohui.proxy; public class CustomerServiceImpl implements ICustomeService{ public void save(Customer customer) { System.out.println("CustomerServiceImpl......save..."+customer.getName()); } public void update(Customer c) { System.out.println("CustomerServiceImpl......update..."+c.getName()); } }MyInvocationHandlerFactoryMyInvocationHandlerFactory类
package com.xiaohui.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyInvocationHandlerFactory implements InvocationHandler { //目标对象 private Object target; /** *被代理的对象 * @param target * @return 代理对象 */ public Object getProxyInstance(Object target){ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //proxy参数一般不用 //前置的处理逻辑程序 System.out.println("session.beginTranscation()"); //回调目标对象的方法 类似于filter中的chain.doFilter(request,response); Object obj = method.invoke(target, args); //后续的处理逻辑程序 System.out.println("session.getTranscation().commit()"); return obj; } }测试类:
@Test public void testProxy() throws Exception { ICustomeService service = new CustomerServiceImpl(); Customer customer = new Customer(); customer.setName("张珊"); MyInvocationHandlerFactory factory = new MyInvocationHandlerFactory(); ICustomeService serviceProxy =(ICustomeService) factory.getProxyInstance(service); serviceProxy.save(customer); System.out.println("-------------------------------"); serviceProxy.update(customer); }打印结果:
session.beginTranscation()
CustomerServiceImpl......save...张珊
session.getTranscation().commit()
-------------------------------
session.beginTranscation()
CustomerServiceImpl......update...张珊
session.getTranscation().commit()2,使用CGLIB代理
在使用Spring的CGLIB代理过程中主要使用到的类为:net.sf.cglib.proxy.Enhancer。其核心方法为:Object hancer.create();
代码如下:
MyCallbackFactory类,需要注意的是 该类实现的接口和JDK实现的接口名字完全一样,接口结构也一致,单spring对其做了另外的调整。所以开发者们不能实现错接口。
package com.xiaohui.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.InvocationHandler; public class MyCallbackFactory implements InvocationHandler { private Object target; public Object getInstance(Object target){ this.target = target; Enhancer hancer = new Enhancer(); hancer.setClassLoader(target.getClass().getClassLoader()); hancer.setSuperclass(target.getClass()); hancer.setCallback(this); return hancer.create(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("your before logic progream....."); Object obj = method.invoke(target, args); System.out.println("your after logic progream....."); return obj; } }测试类:
@Test public void testProxy2() throws Exception { CustomerServiceImpl service = new CustomerServiceImpl(); Customer customer = new Customer(); customer.setName("张珊"); MyCallbackFactory factory = new MyCallbackFactory(); CustomerServiceImpl proxy = (CustomerServiceImpl) factory.getInstance(service); proxy.update(customer); System.out.println(proxy.getClass().getSuperclass().getName()); }打印结果:
your before logic progream.....
CustomerServiceImpl......update...张珊
your after logic progream.....
com.xiaohui.proxy.CustomerServiceImpl
由此可以看出该代理对象为真实对象的子类。需要代理的对象实不实现接口都无所谓,都可以被CGLIB代理。3,通过实现org.aopalliance.intercept.MethodInterceptor接口,使用spring的org.springframework.aop.framework.ProxyFactoryBean类配置xml获取代理,内部机制就是上面的两种。
TranscationInterceptor类:
package com.xiaohui.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class TranscationInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("your Transcation...logic progream...Before...."); Object obj = invocation.proceed(); System.out.println("your Transcation...logic progream...After...."); return obj; } }ICustomeService接口:
package com.xiaohui.aop; public interface ICustomeService { void save(Customer c); void update(Customer c); }CustomerServiceImpl类:
package com.xiaohui.aop; public class CustomerServiceImpl implements ICustomeService{ public void save(Customer customer) { System.out.println("CustomerServiceImpl......save..."+customer.getName()); } public void update(Customer c) { System.out.println("CustomerServiceImpl......update..."+c.getName()); } }applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="transcationInteceptor" class="com.xiaohui.aop.TranscationInterceptor" /> <bean id="customerService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.xiaohui.aop.ICustomeService" /> <property name="target"> <bean class="com.xiaohui.aop.CustomerServiceImpl"/> </property> <property name="interceptorNames"> <list> <value>transcationInteceptor</value> </list> </property> </bean> </beans>使用spring的这种配置,被代理的类如果没有实现接口就不用在ProxyFactoryBean中配<property name="proxyInterfaces" value="pacgake.xxxx" />,这样没有接口的代理会通过CGLIB创建代理。有接口的会通过JDK动态代理来创建。
测试代码:@Test public void testProxy2() throws Exception { Customer customer = new Customer(); customer.setName("张珊"); ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml"); ICustomeService service = ctx.getBean("customerService",ICustomeService.class); service.save(customer); }打印结果:
your Transcation...logic progream...Before....
CustomerServiceImpl......save...张珊
your Transcation...logic progream...After....
AspectJ是一门专门用于Java AOP编程的Java扩展语言。在Spring中,可以通过使用@AspectJ注释,来添加AOP功能。要使用@AspectJ注释编程,首先要在Spring的配置文件中引入aop命名空间:并加入 标签<aop:aspectj-autoproxy/><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <aop:aspectj-autoproxy/> </beans>
使用@AspectJ注解来功能来获取代理首先需要定义一个切面:(定义一个类,使用@Aspect声明)如下切面类TransCationService:package com.xiaohui.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class TransCationService { @Pointcut("execution(* com.xiaohui.aop.*ServiceImpl.*(..))") public void pointcut() { } @Before("pointcut()") public void beginTranscation(JoinPoint jp) { System.out.println("目标类:" + jp.getTarget().getClass().getName()); System.out.println("参数: " + Arrays.toString(jp.getArgs())); System.out.println("方法: " + jp.getSignature().getName()); System.out.println("session.brgintranscation()........"); } @AfterReturning("pointcut()") public void commit() { System.out.println("session.getTranscation().commit()"); } @AfterThrowing(pointcut = ("execution(* com.xiaohui.aop.*ServiceImpl.*(..))"), throwing = "e") public void rollback(Throwable e) { System.out.println("出现异常......" + e.getMessage()); System.out.println("session.rollback...."); } @After("pointcut()") public void closeSession() { System.out.println("session.close()"); } }上面的@Pointcut("execution(* com.xiaohui.aop.*ServiceImpl.*(..))") public void pointcut(){}其定义了一个切入点表达式:其中第一个‘*’ 表示返回值为任何类型,第二个‘*’表示在包com.xiaohui.aop下面的以ServiceImpl结尾的类,第三个‘*’表示所有方法()里的.. 表示任意参数类表。,最终的意思为,在com.xiaohui.aop包下面返回值为任何类型,且类名以ServiceImpl结尾的参数类表不限所有方法。@Before("pointcut()"):前置通知,在目标代理对象执行方法之前调用。直接引用了上面已经定义好的切入点,使用方法名。@AfterReturning("pointcut()"):后置通知,在目标代理对象成功执行方法之后调用。@After("pointcut()"):最终通知,在目标代理对象执行方法之后,无论目标对象方法成功调用与否,都会执行。@AfterThrowing(pointcut=("execution(* com.xiaohui.aop.*ServiceImpl.*(..))"),throwing="e"):异常通知:在一个方法抛出异常后执行。throwing,表示抛出的异常。其切入点为自己重新定义的切入点表达式。applicationContext.xml配置如下:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="advice" class="com.xiaohui.aop.TransCationService"/> <bean id="customerService" class="com.xiaohui.aop.CustomerServiceImpl"/> <aop:aspectj-autoproxy/> </beans>
ICustomerService接口和测试类和2.3中的代码一致,为了测试有异常抛出的情况,我们对CustomerServiceImpl类的save方法进行一点点修改。save方法如下:public void save(Customer customer) { System.out.println("CustomerServiceImpl......save..."+customer.getName()); if(new Random().nextInt(10)>5){ throw new RuntimeException("保存失败....."); } }这样就可以出现抛出异常的情况。没有异常的打印结果如下:
目标类:com.xiaohui.aop.CustomerServiceImpl
参数: [com.xiaohui.aop.Customer@1700391]
方法: save
session.brgintranscation()........
CustomerServiceImpl......save...张珊
session.getTranscation().commit()
session.close()有异常抛出的打印结果如下:(在测试的过程中发现如果在切面类中将最终通知的方法 定义在 异常通知的方法 上面,则会先打印session.close(),后打印session.rollback....,有点不解)目标类:com.xiaohui.aop.CustomerServiceImpl
参数: [com.xiaohui.aop.Customer@118223d]
方法: save
session.brgintranscation()........
CustomerServiceImpl......save...张珊
出现异常......保存失败.....
session.rollback....
session.close()出过这几个通知外,还有一个环绕通知:@Around("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")TransCationService切面类:package com.xiaohui.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class TransCationService { public void pointcut() { } public void beginTranscation(JoinPoint jp) { System.out.println("目标类:" + jp.getTarget().getClass().getName()); System.out.println("参数: " + Arrays.toString(jp.getArgs())); System.out.println("方法: " + jp.getSignature().getName()); System.out.println("session.brgintranscation()........"); } public void commit() { System.out.println("session.getTranscation().commit()"); } public void closeSession() { System.out.println("session.close()"); } public void rollback(Throwable e) { System.out.println("出现异常......" + e.getMessage()); System.out.println("session.rollback...."); } @Around("execution(* com.xiaohui.aop.*ServiceImpl.*(..))") public Object around(ProceedingJoinPoint point) { this.beginTranscation(point); try { Object obj = point.proceed(); this.commit(); return obj; } catch (Throwable t) { this.rollback(t); } finally { this.closeSession(); } return null; } }测试的结果和上面的测试结果一致。
xml中同样需要引入aop的命名空间。但这次不需要打开<aop:aspectj-autoproxy/>
同样,也不需要使用注解来声明切面类和使用advice声明方法。
xml中配置如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <aop:config> <aop:pointcut expression="execution(* com.xiaohui.aop.*ServiceImpl.*(..))" id="ponit"/> <!-- 配置切面 --> <aop:aspect ref="advice"> <aop:before method="beginTranscation" pointcut-ref="ponit"/> <aop:after-throwing method="rollback" pointcut-ref="ponit" throwing="e"/> <aop:after-returning method="commit" pointcut-ref="ponit"/> <aop:after method="closeSession" pointcut-ref="ponit"/> </aop:aspect> </aop:config> <!-- 定义切面对象 --> <bean id="advice" class="com.xiaohui.aop.TransCationService"/> <!-- 定义目标代理对象 --> <bean id="customerService" class="com.xiaohui.aop.CustomerServiceImpl"/> </beans>
测试代码和ICustomerService接口以及CustomerServiceImpl类都和3中的一样,至于TransCationService,有无注解都没关系,因为在xml中 没有开启<aop:aspectj-autoproxy/>,所以不影响测试。测试结果也和上面3中的一致。遇到同样的问题是在xml中配置<aop:after-throwing> 在<aop:after>之后时打印结果顺序和配置他的顺序一致,有所不解。 before无论位置在哪都先执行没问题。