AOP(Aspect OrientedProgramming, 面向切面/方面编程) 旨在从业务逻辑中分离出来横切逻辑【eg:性能监控、日志记录、权限控制等】,提高模块化,即通过AOP解决代码耦合问题,让职责更加单一。
运用技术:
SpringAOP使用了两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以需要两种代理机制,很大程度上是因为JDK本身只提供基于接口的代理,不支持类的代理。
切面植入的方法:
1、编译期织入
2、类装载期织入
3、动态代理织入---->在运行期为目标类添加增强生成子类的方式,Spring AOP采用动态代理织入切面
流行的框架:
AOP现有两个主要的流行框架,即Spring AOP和Spring+AspectJ
二者的区别:
1、织入的时期不同
Spring Aop采用的动态织入,而Aspectj是静态织入。静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。有不清楚的同学,可以自己补下基础的代理知识
2、从使用对象不同
Spring AOP的通知是基于该对象是SpringBean对象才可以,而AspectJ可以在任何Java对象上应用通知。
Spring AOP:如果你想要在通过this对象调用的方法上应用通知,那么你必须使用currentProxy对象,并调用其上的相应方法;于此相似,如果你想要在某对象的方法上应用通知,那么你必须使用与该对象相应的Spring bean
AspectJ:使用AspectJ的一个间接局限是,因为AspectJ通知可以应用于POJO之上,它有可能将通知应用于一个已配置的通知之上。对于一个你没有注意到这方面问题的大范围应用的通知,这有可能导致一个无限循环。
Spring AOP不同于大多数其他AOP框架。Spring AOP的目的并不是为了提供最完整的AOP实现(虽然Spring AOP具有相当的能力);而是为了要帮助解决企业应用中的常见问题,提供一个AOP实现与Spring IOC之间的紧密集成。由于Spring AOP是容易实现的,如果你计划在Spring Beans之上将横切关注点模块化,Spring的这一目标将是要点之一。但同样的目标也可能成为一个限制,如果你用的是普通的Java对象而不是Spring beans,并基于此将横切关注点模块化的话。另一方面,AspectJ可用于基于普通Java对象的模块化,但在实施之前需要良好的关于这个主题的知识。
在决定使用哪种框架实现你的项目之前,有几个要点可以帮助你做出合适的选择(同样适用于其他框架)。
明确你在应用横切关注点(cross-cutting concern)时(例如事物管理、日志或性能评估),需要处理的是Spring beans还是POJO。如果正在开发新的应用,则选择Spring AOP就没有什么阻力。但是如果你正在维护一个现有的应用(该应用并没有使用Spring框架),AspectJ就将是一个自然的选择了。为了详细说明这一点,假如你正在使用Spring AOP,当你想将日志功能作为一个通知(advice)加入到你的应用中,用于追踪程序流程,那么该通知(Advice)就只能应用在Spring beans的连接点(Joinpoint)之上
例子:在appbeans.xml中配置如下的切入点(pointcut),那么当调用myServices bean的service方法时就将应用日志通知(advice)。
<!—Configuration snippet in appbeans.xml -->
<bean id="myServices" class="com.ashutosh.MyServicesImpl " />
<aop:config>
<aop:aspect id="loggingAspect" ref="logging">
<aop:around method="log" pointcut="execution(public * *(..))"/>
</aop:aspect>
</aop:config -->
// Java file calling service method
ApplicationContext beans =newClassPathXmlApplicationContext("appbeans.xml");
MyServices myServices = (MyServices) beans.getBean("myServices");
myServices.service(); // Logging advice applied here
看一下日志通知将要被应用处的注释,在这里应用程序将记录被调用方法的详细信息。但是,当你在service()方法中调用同一个类中的其他方法时,如果你没有使用代理对象,那么日志通知就不会被应用到这个方法调用上。
例如:
// MyServices service method
public void service() {
performOperation();// Logging advice not going to apply here
}
如果你想要在通过this对象调用的方法上应用通知,那么你必须使用currentProxy对象,并调用其上的相应方法。
// MyServices service method
public void service() {
// Logging advice going to apply here
((MyServices) AopContext.currentProxy()).performOperation();
}
于此相似,如果你想要在某对象的方法上应用通知,那么你必须使用与该对象相应的Spring bean。
public void service() {
MyObject obj = new MyObject();
Obj.performOperation();// Logging advice not going to apply here
}
如果你想要应用该通知,那么上述代码必须修改为如下形式。
public void service() {
MyObject obj = new MyObject();
Obj.performOperation();// Logging advice not going to apply here
ApplicationContext beans =newClassPathXmlApplicationContext("appbeans.xml");
MyObject obj =(MyObject) beans.getBean("myObject");
obj.performOperation()// Logging advice applied here
}
于此不同,使用“AspectJ”你可以在任何Java对象上应用通知,而不需要在任何文件中创建或配置任何bean。
另一个需要考虑的因素是,你是希望在编译期间进行织入(weaving),还是编译后(post-compile)或是运行时(run-time)。Spring只支持运行时织入。如果你有多个团队分别开发多个使用Spring编写的模块(导致生成多个jar文件,例如每个模块一个jar文件),并且其中一个团队想要在整个项目中的所有Spring bean(例如,包括已经被其他团队打包了的jar文件)上应用日志通知(在这里日志只是用于加入横切关注点的举例),那么通过配置该团队自己的Spring配置文件就可以轻松做到这一点。之所以可以这样做,就是因为Spring使用的是运行时织入。
<!—Configuration -->
<bean id="myServices" class="com.ashutosh.MyServicesImpl " />
<aop:config>
<aop:aspect id="loggingAspect" ref="logging">
<aop:around method="log" pointcut="execution(public * *(..))"/>
</aop:aspect>
</aop:config -->
如果你使用AspectJ想要做到同样的事情,你也许就需要使用acj(AspectJ编译器)重新编译所有的代码并且进行重新打包。否则,你也可以选择使用AspectJ编译后(post-compile)或载入时(load-time)织入。
因为Spring基于代理模式(使用CGLIB),它有一个使用限制,即无法在使用final修饰的bean上应用横切关注点。因为代理需要对Java类进行继承,一旦使用了关键字final,这将是无法做到的。
例如,在Spring bean MyServicesImpl上使用关键字final,并配置一个“execution(public * *(..))”这样的切入点,将导致运行时异常(exception),因为Spring不能为MyServicesImpl生成代理。
// Configuration file
<bean id="myServices" class="com.ashutosh.MyServicesImpl" />
//Java file
public final classMyServicesImpl {
---
}
在这种情况下,你也许会考虑使用AspectJ,其支持编译期织入且不需要生成代理。
于此相似,在static和final方法上应用横切关注点也是无法做到的。因为Spring基于代理模式。如果你在这些方法上配置通知,将导致运行时异常,因为static和final方法是不能被覆盖的。在这种情况下,你也会考虑使用AspectJ,因为其支持编译期织入且不需要生成代理。
你一定希望使用一种易于实现的方式。因为Spring AOP支持注解,在使用@Aspect注解创建和配置方面时将更加方便。而使用AspectJ,你就需要通过.aj文件来创建方面,并且需要使用ajc(Aspect编译器)来编译代码。所以如果你确定之前提到的限制不会成为你的项目的障碍时,使用Spring AOP。
使用AspectJ的一个间接局限是,因为AspectJ通知可以应用于POJO之上,它有可能将通知应用于一个已配置的通知之上。对于一个你没有注意到这方面问题的大范围应用的通知,这有可能导致一个无限循环。
例如,创建一个包含如下切入点的方面
public aspectLogging {
Object around() : execution(public * * (..))
Sysytem.out.println(thisJoinPoint.getSignature());
return proceed();
}
在这种情况下,当proceed即将被调用时,日志通知会被再次应用,这样就导致了嵌套循环。
所以,如果你希望在Spring bean上采取比较简单的方式应用横切关注点时,并且这些bean没有被标以final修饰符,同时相似的方法也没有标以static或final修饰符时,就使用Spring AOP吧。相比之下,如果你需要在所提到的限制之上应用横切关注点,或者要在POJO上应用关注点,那么就使用AspectJ。你也可能选择同时使用两种方法,因为Spring支持这样。
转载:https://blog.csdn.net/a128953ad/article/details/50509437