Aop是干嘛的为什么要使用它
在业务系统中,总有一些散落,渗透到系统的各处且不得不处理的事情,这些穿插在既定业务中的操作就是所谓的“横切逻辑”,也称切面,
我们怎样才不受这些附加要求的干扰,专心于真正的业务逻辑呢?我们很容易想到可以将这些重复性的代码抽取出来,放在专门的类和方
法中,这样便于管理和维护了。但即便如此,依然无法实现既定业务和横切逻辑的彻底解耦合因为业务代码中还要波阿里这些方法的调用
代码,当需要增加或减少横切逻辑的时候,还是要修改业务逻辑的时候,还是要修改业务方法中的调用代码才能实现。我们希望无须编写
显示的调用,在需要的时候,系统能够“自动”调用所需的功能,这正是AOP要解决的问题。
什么是面向切面编程
简答的说就是在不改变原程序的基础上为代码段增加新的功能,对代码段进行增强处理。它的设计思想来源于代理设计模式。在这种模
式下给编程人员的感觉是在原有代码乃至原业务不修改的勤快下,直接在业务流程中切入新代码,增加新功能,这就是所谓的面向切面编程
对概念有了一些了解后,还需了解一些基本概念。
- 切面(Aspect):一个模块化的横切逻辑(或称横切关注点),可能会横切多个对象。一个饭店可以提供给多人一起吃饭类似于这种概念
- 连接点(Join Poin):接点是个虚的概念,可简单理解为切入点的集合;它只是对应用程序的所有需要进行插入切面的一个统称。
- 增强处理(Adive):切面在某个特定连接点上执行的代码逻辑。如到饭店吃完饭不用自洗碗,做饭。
- 切入点(pointcut):对连接点的特征进行描述,可以使用正则表达式。增强处理和一个切入点表达式相关联,并在与这个切入点匹配的某个连接点上运行。
- 目标对象(Target object):被一个或多个切面增强的对象。
- AOP代理(AOP proxy):由AOP框架所创建的对象,实现执行增强处理的方法等功能。
- 织入(Weaving):将增强处理连接到应用程序中的类型或对象的过程。
- 增强处理类型:前置增强,后置增强,环绕增强,异常抛出增强,最终增强。
增强处理类型的先后执行顺序:前置 环绕 环绕 最终 异常|后置。异常抛出增强则是方法中出了异常才会执行,执行了异常,后置则不会执行
好,概念的东西也就差不多这些了,来看下列子
定义一个people类
public class People { /** * 客户名 */ private String name; /** *吃饭的方法 */ public void havingDinner() { System.out.println(name+"吃饭中...吃完了"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
定义一个饭店
/** *定义切面 */ public class Hotel { public void order() { System.out.println("点菜完毕上菜"); } public void Dishwashing() { System.out.println("客人吃完,洗碗"); } }
上面的两个代码为增强处理
aop相关代码如下
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "> <bean id="people" class="cn.People" p:name="小明"/> <!-- 创建hotel对象--> <bean id="hotel" class="cn.Hotel"/> <!-- aop代理 --> <aop:config> <!-- 切入点,expression为条件 只有符合要求的才可以进入饭店吃饭 id为条件名--> <aop:pointcut expression="execution(public void havingDinner())" id="pointcut"/> <!-- ref属性通过htoel引用定义的切面 --> <aop:aspect ref="htole"> <!-- 吃饭之前进行此功能调用 并且满足匹配条件才调用饭店里点菜和上菜的方法 此标签为前置增强--> <aop:before method="order" pointcut-ref="pointcut"/> <!-- 吃饭之后才进行功能调用 并满足匹配条件饭店里洗碗的方法 此标签为后置增强 --> <aop:after-returning method="Dishwashing" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
aop:befoer为前置增强的标签
aop:after-returning为后置增强标签 其中methid属性为饭店里的方法名称。即增强处理的名称
测试类代码如下
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("peopleConfig.xml"); People people = (People)context.getBean("people"); people.havingDinner(); }
结果为
其中增强处理就是执行的点菜上菜及洗碗的方法,目标对象即使当中符合匹配条件的方法即为目标对象。
织入指的是行为把增强处理连接到目标对象即为织入,即把点菜和洗碗与吃饭连接起来这一过程为织入。
其它增强类型如下
<bean id="people" class="cn.People" p:name="小明"/> <bean id="hotel" class="cn.Hotel"/> <!-- aop代理 --> <aop:config> <aop:pointcut expression="execution(public String havingDinner())" id="pointcut"/> <aop:aspect ref="hotel"> <aop:before method="order" pointcut-ref="pointcut"/> <!-- 最终增强 --> <aop:after method="after" pointcut-ref="pointcut"/> <!-- 异常抛出增强 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/> <!-- 环绕增强 --> <aop:around method="around" pointcut-ref="pointcut"/> <!-- 后置增强 --> <aop:after-returning method="afterReturning" returning="result" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
后置增强处理代码
public void afterReturning(Object result) { log.info("后置增强:"+result); }
环绕增强处理代码
public Object around(ProceedingJoinPoint jp) throws Throwable{ //先执行try外面的 第二次执行try log.info("环绕增强!"); try { Object result=jp.proceed(); log.info("环绕增强!"); return result; } catch (Exception e) { e.printStackTrace(); throw e; }finally { log.info("环绕增强执行完毕!"); } }
如方法出现异常 结果如下
没有异常结果如下
这里顺序出现了异常,是环绕增强的问题去掉就好了
使用注解实现aop
增强处理如下
package cn; import org.apache.log4j.Logger; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; 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; /** *定义切面 */ @Aspect public class Hotel { Logger log = Logger.getLogger(Hotel.class); //定义公共的 匹配条件 @Pointcut("execution(public String havingDinner())") public void pointcut() {} @Before("pointcut()") public void order() { log.info("前置"); } //这样也是可以的@Around("execution(public String havingDinner())") @Around("pointcut()") public Object around(ProceedingJoinPoint jp) throws Throwable{ //先执行try外面的 第二次执行try log.info("环绕增强!"); try { Object result=jp.proceed(); log.info("环绕增强!"); return result; } catch (Exception e) { e.printStackTrace(); throw e; }finally { log.info("环绕增强执行完毕!"); } } @After("pointcut()") public void after() { log.info("最终增强"); } @AfterThrowing("pointcut()") public void afterThrowing() { log.info("最终增强"); System.err.println("异常抛出增强"); } @AfterReturning(pointcut="pointcut()",returning="result") public void afterReturning(Object result) { log.info("后置增强:"+result); } }
不同的注解对应不同的增强,其中@Aspect是必不可少的,没有它将会识别不到该类中的增强处理,并且还不会报错
aop配置如下
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "> <bean id="people" class="cn.People" p:name="小明"/> <context:component-scan base-package="cn"/> <bean class="cn.Hotel"/> <aop:aspectj-autoproxy/> </beans>
base-package属性为扫描的路径我喜欢直接写所有包的“父亲”,它会自动扫描下面子包,也可以这样写
<context:component-scan base-package="cn.dao,cn.pojo"/>
希望能大家有所帮助,如有写的不好的地方欢迎反馈