AOP的简单练习

---恢复内容开始---

1、AOP的主要作用及概念简介

  AOP最大的用处在于事务处理上,业务层在项目中主要负责以下的操作:

    ·调用数据层进行处理;

    ·进行事务的处理;

    ·关闭数据库的连接操作;

  但在实际操作中,往往还要进行日志处理,事务提交等等辅助性操作,此时aop就派上用场。一个优秀的代理模式是将不同的切入点代码单独定义,而后组织在一个程序网上。AOP就在spring中充当了这样一个角色。

  AOP有以下几个概念:

    ·切入点:可以理解为所有要操作的方法定义。要求业务层的方法必须统一风格。

    ·分离点:将那些不可再分的组件单独提取出去定义为单独的操作功能;

    ·横切关注点:将所有与开发无关的程序组成类单独提取而后组织运行;

    ·植入:将所有的切入点、关注点的代码组成在一张完整的程序结构中。

  在Spring中采用通知的形式完成,即当触发到了某些操作之后自然地进行一些固定的操作。在整个SpringAOP中包含有以下几类通知形式:

    ·前置通知:在某一操作执行之前处理。

    ·后置通知:在某一操作执行之后处理,但后置通知有以下几种子通知:

      |-后置返回通知:负责处理返回结果的时候进行拦截;

      |-后置异常通知:当出现异常后进行拦截;

      |-后置最终通知:执行到最后无论如何都会进行拦截;

    ·环绕通知:具有以上通知到特点。(功能最强大)

2、AOP的简单练习

  (1) 简单地构建Spring环境

    1⃣️、定义IMemberService.java:

1 package cn.ckw.IService;
2 
3 import cn.ckw.vo.Member;
4 
5 public interface IMemberService {
6     boolean insert(Member vo);
7 }
IMemberService.java

    2⃣️、定义Membe.java 

 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 + ", ]";
21     }
22     
23 }
Member

    3⃣️、定义MemberServiceImpl.java

 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 }
MemberServiceImpl

    以上模拟了业务实现,随后要加入的辅助性操作,都是通过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,接着建立测试类:

 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 }
Test

 

   (2) 在(1)的基础上加入切面

    4⃣️、定义切面方法类ServiceAspect.java:

 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 }
ServiceAspect.java

    之后要在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..*.*(..))"
             color: #3366ff">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)"
             />
        <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              />
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 }

 

 

 

 

 

 -------转载请说明出处----------

---恢复内容结束---

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值