spring-aop
spring——aop(一)浅谈
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象对象编程)的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
有点丑,不好意思。首先xxx.class这个类里面有三个方法,分别是a ,b ,c(请先忽略eE这个方法,同时它也不属于这个类的)。他们实际没什么意义,这里假如这三个方法就只是简单打印一句话 "system.out.println("Hello World")"。接着这里假如有个需求:客户如果需要在 a,b,c这个三个方法中加一个简单功能,假设就是简单的打印一句话"system.out.println("Hello Spring-aop")",而且这个功能(这句话)要在打印Hello World执行完后在执行(或者在打印Hello World执行前执行)。我们怎么做呢?说到这里很多人都觉得挺简单的:只要在a,b,c方法中Hello World 的语句上加 "system.out.println("Hello Spring-aop")"就好了嘛。事实确实这样的,这样做当然一点问题都没有。可是这里只有a,b,c方法,而且只有一个类而已。如果上千个方法,上千个类都需要类似的功能呢。复制粘贴也太碍事和消耗时间了吧。这里不妨试想着:如果有一个方法,这个方法纯粹的封装某一个功能(或者一句话“Hello Spring-aop”),而且这个方法能按照我们的意愿控制,插入原有的方法中前后,是不是大大减少了工作量。如果这里eE方法恰巧符合我们的思想,它封装某一个功能,可能是一句话("Hello Spring-aop").这个方法能任意的插入 a,b,c方法中。并且是a,b,c执行前或者执行后,或者执行前和执行后。这样岂不是美滋滋?
以上只是一个简单的案例,这里总结一下。aop(面向切面编程)的思想:将各个层重复的代码横向的抽取出来,作为一个增强面,用上面例子解释这话。
各个层:不同的java类中的不同方法,a,b,c三个方法。
重复的代码:因为它们需要打印spring-aop这句话(功能),这是他们需要的,这就是他们特性
增强面:类似与eE方法,它的功能是封装一个功能(srping-aop),并且这个功能将贯穿各层。
理论:
连接点(Joinpoint)
程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强
切点(PointCut)
每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点--数据库的记录,切点--查询条件
增强(Advice)
增强是织入到目标类连接点上的一段程序代码。在Spring中,像BeforeAdvice等还带有方位信息
目标对象(Target)
需要被加强的业务对象
织入(Weaving)
织入就是将增强添加到对目标类具体连接点上的过程。
代理类(Proxy)
一个类被AOP织入增强后,就产生了一个代理类。
切面(Aspect)
切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。
代码实战:
注意除了spring的jar外,aop还需要依赖三个jar!!!
package com.test.interfaces; public class MathImpl implements IMath { @Override public void sum(int x, int y) { // TODO Auto-generated method stub System.out.println(x+y); } @Override public void max(int x, int y) { // TODO Auto-generated method stub if(x > y) { System.out.println("max :"+x); }else { System.out.println("max :"+y); } } }
package com.test.advice; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class MathAdvice { @Before("execution(* com.test.interfaces.MathImpl.*(..)) && args(x,y)") public void test(int x,int y) { System.out.println("x:"+x+",y:"+y); } }
package com.test.test; import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; import com.test.advice.MathAdvice; import com.test.interfaces.IMath; import com.test.interfaces.MathImpl; public class ManTest { public static void main(String[] args) { // TODO Auto-generated method stub IMath math = new MathImpl(); //获取工场 AspectJProxyFactory factory = new AspectJProxyFactory(); //设置目标对象 factory.setTarget(math); //设置切面 factory.addAspect(MathAdvice.class); //织入 生成代理对象 IMath proxy = factory.getProxy(); proxy.sum(4, 5); proxy.max(10, 9); } }
运行结果:
解释:
此处我们采用的注解的方式实现aop,开发者在实现aop的时候,只需要关注三个地方即可:切点,增强,目标对象。这里目标对象为MathImpl这个对象,里面封装了两个方法:求和,求最大值。在这个目标对象中,我们只是打印了计算结果,并不知道传进来的参数值。可是控制台却打印了目标对象两个方法的参数值,这里发生了什么?我们不妨看看增强(MathAdvice),在类的上面,我们注解了@Aspect,表示这个类是增强。在test()这个方法上面添加注解 :@Before("execution(* com.test.interfaces.MathImpl.*(..)) && args(x,y)"),这个注解里的表达式为:在“com.test.interfaces.MathImpl”这个类的所有方法执行之前(求和,求最大值),先执行增强的test(int x,int y)这个方法。(此处不多讲解,更多明细可以参考:http://blog.csdn.net/sunlihuo/article/details/52701548)。接着,我们继续看测试代码(主函数),我们创建一个目标对象和一个AspectJProxyFactory工厂。工厂的最终目的就是生成代理对象,最后通过代理对象管理目标对象的方法。
基于xml方式的aop
目标对象不变
package com.test.interfaces; public class MathImpl implements IMath { @Override public void sum(int x, int y) { // TODO Auto-generated method stub System.out.println(x+y); } @Override public void max(int x, int y) { // TODO Auto-generated method stub if(x > y) { System.out.println("max :"+x); }else { System.out.println("max :"+y); } } }
删除增强的所有注解
package com.test.advice; public class MathAdvice { public void test(int x,int y) { System.out.println("x:"+x+",y:"+y); } }
测试方法不需要AspectJProxyFactory工场,但是需要加载ioc容器。
package com.test.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.test.interfaces.IMath; public class ManTest { @SuppressWarnings("resource") public static void main(String[] args) { // TODO Auto-generated method stu ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); IMath math = (IMath) context.getBean("mathImpl"); math.sum(23, 12); math.max(90, 100); } }
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" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <bean id="mathImpl" class="com.test.interfaces.MathImpl"></bean> <!-- 切面 --> <bean id="mathAdvice" class="com.test.advice.MathAdvice"></bean> <!-- aop --> <aop:config> <!-- 增强 --> <aop:aspect ref="mathAdvice" > <!-- method 增强方法--> <aop:before method="test" pointcut="execution(* com.test.interfaces.MathImpl.*(..)) and args(x,y)" arg-names="x, y" /> </aop:aspect> </aop:config> </beans>
解释:
实现aop的方式彻底交给ico容器控制,首先需要实例化目标对象,以及增强,最后配置aop,方式与注解方式大同小异。
增强类型:
AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强
前置增强:org.springframework.aop.BeforeAdvice代表前置增强,因为Spring只支持方法级的增强,所以MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定
后置增强:org.springframework.aop.AfterReturningAdvice代表后增强,表示在目标方法执行后实施增强;
环绕增强:org.aopalliance.intercept.MethodInterceptor代表环绕增强,表示在目标方法执行前后实施增强;
异常抛出增强:org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强;
总结:
正如标题所言,我们只需要理解spring提供的两种实现aop的方式,以及aop的思想。待续......