Spring学习之路AOP
---恢复内容开始---
1、AOP的主要作用及概念简介
AOP最大的用处在于事务处理上,业务层在项目中主要负责以下的操作:
·调用数据层进行处理;
·进行事务的处理;
·关闭数据库的连接操作;
但在实际操作中,往往还要进行日志处理,事务提交等等辅助性操作,此时aop就派上用场。一个优秀的代理模式是将不同的切入点代码单独定义,而后组织在一个程序网上。AOP就在spring中充当了这样一个角色。
AOP有以下几个概念:
·切入点:可以理解为所有要操作的方法定义。要求业务层的方法必须统一风格。
·分离点:将那些不可再分的组件单独提取出去定义为单独的操作功能;
·横切关注点:将所有与开发无关的程序组成类单独提取而后组织运行;
·植入:将所有的切入点、关注点的代码组成在一张完整的程序结构中。
在Spring中采用通知的形式完成,即当触发到了某些操作之后自然地进行一些固定的操作。在整个SpringAOP中包含有以下几类通知形式:
·前置通知:在某一操作执行之前处理。
·后置通知:在某一操作执行之后处理,但后置通知有以下几种子通知:
|-后置返回通知:负责处理返回结果的时候进行拦截;
|-后置异常通知:当出现异常后进行拦截;
|-后置最终通知:执行到最后无论如何都会进行拦截;
·环绕通知:具有以上通知到特点。(功能最强大)
2、AOP的简单练习
(1) 简单地构建Spring环境
1⃣️、定义IMemberService.java:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.ckw.IService; 2 3 import cn.ckw.vo.Member; 4 5 public interface IMemberService { 6 boolean insert(Member vo); 7 }
2⃣️、定义Membe.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.ckw.vo; 2 3 public class Member { 4 private String name; 5 private String id; 6 public String getName() { 7 return name; 8 } 9 public void setName(String name) { 10 this.name = name; 11 } 12 public String getId() { 13 return id; 14 } 15 public void setId(String id) { 16 this.id = id; 17 } 18 @Override 19 public String toString() { 20 return "Member [name=" + name + ", id=" + id + "]"; 21 } 22 23 }
3⃣️、定义MemberServiceImpl.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.ckw.serviceImpl; 2 3 import org.springframework.stereotype.Service; 4 5 import cn.ckw.IService.IMemberService; 6 import cn.ckw.vo.Member; 7 @Service 8 public class MemberServiceImpl implements IMemberService{ 9 @Override 10 public boolean insert(Member vo) { 11 //throw new NullPointerException("this is a exception"); 12 System.out.println("这是业务层的调用"); 13 return true; 14 } 15 16 }
以上模拟了业务实现,随后要加入的辅助性操作,都是通过Spring容器的配置完成。
首先要在application.xml.文件中加入annotation支持,加入时不要忘记检查命名空间是否已经被引入:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-2.5.xsd"> 12 <context:annotation-config /> 13 <context:component-scan base-package="cn.ckw" /> 25 </beans>
然后要在3⃣️中类名的上一行加入注解@service,接着建立测试类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.ckw.test; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 import cn.ckw.IService.IMemberService; 7 import cn.ckw.vo.Member; 8 9 public class Test { 10 public static void main(String[] args) { 11 ApplicationContext ctx = new ClassPathXmlApplicationContext( 12 "applicationContext.xml"); 13 IMemberService ser=ctx.getBean("memberServiceImpl",IMemberService.class); 14 Member vo=new Member(); 15 vo.setId("001"); 16 vo.setName("ckw"); 17 System.out.println(ser.insert(vo)); 18 } 19 }
(2) 在(1)的基础上加入切面
4⃣️、定义切面方法类ServiceAspect.java:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.ckw.aspect; 2 3 import org.springframework.stereotype.Component; 4 5 @Component 6 public class ServiceAspect { 7 public void serviceBefore(){ 8 System.out.println("在操作之前调用"); 9 } 10 public void serviceBefore2(Object arg){ 11 System.out.println("在操作之前调用,传入的参数是:"+arg); 12 } 13 public void serviceAfterReturning(Object arg){ 14 System.out.println("在操作之后调用,返回值是:"+arg); 15 } 16 public void serviceAfter(){ 17 System.out.println("在操作之后调用"); 18 } 19 }
之后要在application.xml中加入配置,黄色为引用的命名空间,红色部分为定义的切入点,表达式为AspectJ包所定义的表达式(可以深入时再去探索)。蓝色部分必须相同,表示切面函数关联到哪个切入点,可以是引用已经定义的pointcut(切入点),也可以重新定义新的切入点:
<?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: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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:annotation-config />
<context:component-scan base-package="cn.ckw" />
<aop:config>
<!--首先定义程序的切入点-->
<aop:pointcut expression="execution(* cn.ckw..*.*(..))"
id="pointcut" />
<!--指定切入点的切入函数-->
<aop:aspect ref="serviceAspect">
<aop:before method="serviceBefore" pointcut-ref="pointcut"/>
<aop:after method="serviceAfter" pointcut="execution(* cn.ckw..*.*(..))" />
</aop:aspect>
</aop:config>
</beans>
(3) 加入可传参数的切面函数
在4⃣️类中加入以下方法的定义:
1 public void serviceBefore2(Object arg){ 2 System.out.println("在操作之前调用,传入的参数是:"+arg); 3 }
在xml中定义此方法,红色标记点为与(2)比较的改变的地方:
<?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: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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:annotation-config />
<context:component-scan base-package="cn.ckw" />
<aop:config>
<aop:pointcut expression="execution(* cn.ckw..*.*(..)) and args(vo)"
id="pointcut" />
<aop:aspect ref="serviceAspect">
<aop:before method="serviceBefore" pointcut-ref="pointcut"
arg-names="vo" />
<aop:after method="serviceAfter" pointcut="execution(* cn.ckw..*.*(..))" />
</aop:aspect>
</aop:config>
</beans>
(4)加入可返回的切面函数
在4⃣️类中加入以下方法的定义,其中arg为返回值:
public void serviceAfterReturning(Object arg){ System.out.println("在操作之后调用,返回值是:"+arg); }
在xml配置文件中的<aop:config>下添加,其中returning属性和arg-names属性只是起到标示作用,但两个要相同:
<aop:after-returning method="serviceAfterReturning" pointcut="execution(* cn.ckw..*.*(..))" returning="ret" arg-names="ret" />
(5)异常拦截处理
在业务层中抛一个异常出来,比如在3⃣️类中的insert函数中抛出一个异常:
1 public boolean insert(Member vo) { 2 throw new NullPointerException("throw exception"); 3 }
在类4⃣️中加入以下方法:
public void serviceAfterThrowing(Exception exp){ System.out.println(exp); }
在xml中写入如下配置:
<aop:after-throwing method="serviceAfterThrowing" pointcut="execution(* cn.ckw..*.*(..))" arg-names="abc" throwing="abc"/>
(6)环绕拦截
在4⃣️类中加入以下方法,环绕不仅可以拦截,甚至可以对传入参数和返回结果进行控制:
1 public Object serviceAround(ProceedingJoinPoint point) throws Throwable { 2 System.out.println("方法调用之前,返回值是:"+Arrays.toString(point.getArgs())); 3 Member vo=new Member(); 4 vo.setId("002"); 5 vo.setName("ckw2"); 6 Object retVal=point.proceed(new Object[]{vo});//执行业务代码 7 System.out.println("在操作之后调用,返回值是--:"+retVal); 8 return true;//真正的返回值 9 }
同样的,要在xml中配置内容后,执行测试代码:
<aop:around method="serviceAround" pointcut="execution(* cn.ckw..*.*(..))"/>
学习AOP的读者,建议先将代理设计模式理解透彻。
(7)利用Annotation配置AOP
在xml之中加入AOP的annotation支持:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-2.5.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> 12 <context:annotation-config /> 13 <context:component-scan base-package="cn.ckw" /> 14 <!--<aop:config> 15 <aop:pointcut expression="execution(* cn.ckw..*.*(..)) and args(vo)" 16 id="pointcut" /> 17 <aop:aspect ref="serviceAspect"> 18 <aop:before method="serviceBefore2" pointcut-ref="pointcut" 19 arg-names="vo" /> 20 <aop:after method="serviceAfter" pointcut="execution(* cn.ckw..*.*(..))" /> 21 <aop:after-returning method="serviceAfterReturning" 22 pointcut="execution(* cn.ckw..*.*(..))" returning="ret" arg-names="ret" /> 23 <aop:around method="serviceAround" pointcut="execution(* cn.ckw..*.*(..))"/> 24 </aop:aspect> 25 </aop:config>--> 26 <aop:aspectj-autoproxy/> 27 </beans>
修改ServiceAspect类:
1 package cn.ckw.aspect; 2 3 import java.util.Arrays; 4 5 import org.aspectj.lang.ProceedingJoinPoint; 6 import org.aspectj.lang.annotation.AfterReturning; 7 import org.aspectj.lang.annotation.Around; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Before; 10 import org.springframework.stereotype.Component; 11 12 import cn.ckw.vo.Member; 13 14 @Component 15 @Aspect 16 public class ServiceAspect { 17 @Before(value="execution(* cn.ckw..*.*(..))") 18 public void serviceBefore(){ 19 System.out.println("在操作之前调用"); 20 } 21 @Before(value="execution(* cn.ckw..*.*(..)) and args(vo)") 22 public void serviceBefore2(Object arg){ 23 System.out.println("在操作之前调用,传入的参数是:"+arg); 24 } 25 @AfterReturning(value="execution(* cn.ckw..*.*(..))", argNames="ret" ,returning="ret") 26 public void serviceAfterReturning(Object arg){ 27 System.out.println("在操作之后调用,返回值是:"+arg); 28 } 29 @Around(value="execution(* cn.ckw..*.*(..))") 30 public Object serviceAround(ProceedingJoinPoint point) throws Throwable { 31 System.out.println("方法调用之前,返回值是:"+Arrays.toString(point.getArgs())); 32 Member vo=new Member(); 33 vo.setId("002"); 34 vo.setName("ckw2"); 35 Object retVal=point.proceed(new Object[]{vo});//执行业务代码 36 System.out.println("在操作之后调用,返回值是--:"+retVal); 37 return true;//真正的返回值 38 } 39 public void serviceAfter(){ 40 System.out.println("在操作之后调用"); 41 } 42 }
-------转载请说明出处----------
---恢复内容结束---