1 AOP概述 1.1 面向切面编程
Aspact Oriented Programming,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,Spring框架中的一个重要内容,是函数式编程的一种衍生规范。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
AOP采取横向抽取机制,取代了传统纵向继承 体系重复性代码。
AOP的应用:事务管理,性能监视,安全检查,缓存,日志。
Spring AOP使用纯Java的AOP框架,不需要专门的编译过程的类加载器,在运行期通过代理方式向目标类织入增强代码。
AspectJ是一个基于Java语言的AOP框架,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供代码的织入。
1.2 AOP实现原理 AOP底层采用代理机制进行实现。接口与实现类采用动态代理Proxy,有或没有接口的类都采用cglib字节码增强。
1.3 AOP术语
target:目标类,需要被代理的类。
Joinpoint 连接点:指可能被拦截到的方法。
PointCut 切入点:已经被增强的连接点。
advice 通知/增强:增强代码。
Weaving 织入:指把增强advice应用到目标对象target来创建代理对象proxy的过程。
proxy:代理类。
Aspect:切入点pointcut和通知advice的结合。
1.4 手动代理 1.4.1 JDK动态代理 目标类的接口:
1 2 3 4 5 public interface IUserService { public void addUser () ; public void updateUser () ; public void deleteUser () ; }
目标类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class UserServiceImpl implements IUserService { @Override public void addUser () { System.out.println("添加用户" ); } @Override public void updateUser () { System.out.println("更新用户" ); } @Override public void deleteUser () { System.out.println("删除用户" ); } }
切面类:增强代码与切入点的结合
1 2 3 4 5 6 7 8 9 public calss MyAspect{ public void before () { System.out.println("开启事务" ); } public void after () { System.out.println("提交事务" ); } }
工厂类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class UserServiceFactory { public static IUserService createUserService () { IUserService userService = new UserviceImpl(); MyAspect aspect = new MyAspect(); IUserService serviceProxy = (IUserservice) Proxy.newProxyInstance( UserServiceFactory.class .getclass (), userService .getClass .getInterfaces (), new InvocationHandler () { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throeable { aspect.before(); Object retObj = method.invoke(UserService.args); System.out.println("拦截返回值" + retObj); aspect.after(); return retObj; } } ); return serviceProxy; } }
现在,在测试类中调用由工厂类创建的userService对象,实际上是由JDK创建的目标类的代理。在调用userService的方法的前后,都会执行在切面类中定义的增强方法。
测试类:
1 2 3 4 5 6 7 8 public class test { @Test public void test1 () throws Exception { IUserService userService = UserServiceFactory.createUserService(); userService.deleteUser(); } }
1.4.2 cglib字节码增强
没有接口,只有实现类。
采用字节码增强框架cglib。在运行时,创建目标类的子类,从未对目标类进行增强。
所用jar包:spring-core
工厂类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class UserServiceFactory { public static UserServiceImpleateUserService () { IUserService userService = new UserviceImpl(); MyAspect aspect = new MyAspect(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.getClass()); enhancer.Callback(new MethodInterceptor(){ @Override public Object intercept (Object proxy, Method method, Object[] args, MethodProxy methodproxy) { aspect.before(); Object ret Obj = methodProxy.invokeSuper(proxy,args); aspect.after(); } }); UserServiceImpl serviceProxy = (UserServiceImpl) enhancer.create(); return serviceProxy; } }
测试类:
1 2 3 4 5 6 7 8 public class test { @Test public void test1 () throws Exception { UserServiceImpl userService = UserServiceFactory.createUserService(); userService.deleteUser(); } }
1.5 AOP联盟通知类型 AOP联盟为通知Advice定义了org.aopalliance.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
前置通知 org.springframework.aop.MethodBeforeAdvice,在目标方法执行前实施增强。
后置通知 org.springframework.aop.AfterRetunringAdvice,在目标方法执行后实施增强。
环绕通知 org.aopalliance.intercept.MethodInterceptor,在目标方法前后实施增强。
异常抛出通知 org.springframework.aop.ThrowAdvice,在方法抛出异常后实施增强。
引介通知 org.springframework.aop.Introductionterceptor,在目标类中添加一些新的方法和属性。
1.6 Spring编写代理 导入com.springsource.org.aopalliance-1.0.0.jar(AOP联盟制定的接口)和 spring-aop-5.2.3.RELEASE.jar(Spring对接口的实现)两个jar包。
目标类和手动代理时一样。
切面类:
1 2 3 4 5 6 7 8 9 10 11 12 import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class MyAspect implements MethodInterceptor { @Override public Object invole (MethodInvocation mi) throws Throwable { System.out.println("前" ); Object retobj = mi.proceed(); System.out.println("后" ); return retobj; } }
beans.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <bean id ="UserService" class ="com.Retur0.service.UserServiceImpl" /> <bean id ="myAspect" class ="com.Retur0.service.MyAspect" /> <bean id ="proxyService" class ="org.springframework.aop.framework.ProxyFactoryBean" > <property name ="interfaces" value ="com.Retur0.service.IUserService" /> <property name ="target" ref ="UserService" /> <property name ="interceptorNames" value ="myAspect" /> <property name ="optimize" value ="true" /> </bean >
test.java:
1 2 3 4 5 6 7 8 9 public class test { @Test public void test () { ApplicationContext context = new ClassPathXmlApplicationContext("beans3.xml" ); IUserService userService = (IUserService) context.getBean("serviceProxy" ); userService.deleteUser(); } }
1.7 Spring全自动代理 beans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.Retur0.service.UserServiceImpl" /> <bean id ="myAspect" class ="com.Retur0.aspect.MyAspect" /> <aop:config proxy-target-class ="true" > <aop:pointcut id ="myPointcut" expression ="execution(* com.Retur0.service.*.*(..))" /> <aop:advisor advice-ref ="myAspect" pointcut-ref ="myPointcut" /> </aop:config > </beans >
test.java
1 2 3 4 5 6 7 8 9 public class test { @Test public void test () { ApplicationContext context = new ClassPathXmlApplicationContext("beans4.xml" ); IUserService userService = (IUserService) context.getBean("userService" ); userService.deleteUser(); } }
2 AspectJ
AspectJ是一个基于java 语言的AOP框架。
Spring2.0 之后增加了对AspectJ切点表达式的支持。
@Aspect 是AspectJ1.5的新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面。
2.1 基于XML的AspectJ例子 需要的jar包:spring-aspects-5.2.3.RELEASE.jar
目标类使用之前的UserServiceImpl,切面类重写如下:、
AJAspect.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class AJAspect { public void myBefore () { System.out.println("advice before the method.." ); } public void myAfter () { System.out.println("advice after the method.." ); } public Object myAround (ProceedingJoinPoint jp) throws Throwable { System.out.println("advice around the method...before" ); Object obj = jp.proceed(); System.out.println("advice around the method...after" ); return obj; } }
beans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="userService" class ="com.Retur0.service.UserServiceImpl" /> <bean id ="AJAspect" class ="com.Retur0.aspect.AJAspect" /> <aop:config > <aop:aspect ref ="AJAspect" > <aop:pointcut id ="myPointcut" expression ="execution(* com.Retur0.service.UserServiceImpl.*(..))" /> <aop:before method ="myBefore" pointcut-ref ="myPointcut" > </aop:before > <aop:after method ="myAfter" pointcut-ref ="myPointcut" > </aop:after > <aop:around method ="myAround" pointcut-ref ="myPointcut" > </aop:around > </aop:aspect > </aop:config > </beans >
2.2 基于注解的例子 给目标类添加注解@Service。
给切面类添加注解@Component和@Aspect。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Component @Aspect public class AJAspect { @Pointcut ("execution(* com.Retur0.service.UserServiceImpl.*(..))" ) public void myPointcut () {} @Before ("myPointcut()" ) public void myBefore () { System.out.println("advice before the method.." ); } @After ("myPointcut()" ) public void myAfter () { System.out.println("advice after the method.." ); } @Around ("myPointcut()" ) public Object myAround (ProceedingJoinPoint jp) throws Throwable { System.out.println("advice around the method...before" ); Object obj = jp.proceed(); System.out.println("advice around the method...after" ); return obj; } }
beans.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:aop ="http://www.springframework.org/schema/aop" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <context:component-scan base-package ="com.Retur0" /> <aop:aspectj-autoproxy /> <aop:config > <aop:aspect ref ="AJAspect" > </aop:aspect > </aop:config > </beans >
2.3 注解总结 @Aspect 声明切片,修饰切面类,从而获得通知。
通知:
@Before 前置。
@AfterReturing 后置。
@Around 环绕。
@AfterThrowing 抛出异常。
@After 最终。
切入点:
@PointCut,修饰方法private void xxx(){},之后通过”方法名”过的切入点引用。