关于spring的aop学习总结

spring的aop的目的是什么呢?为什么要有aop这个概念呢?这是我在学习aop所思考的事情。

1.spring的aop的目的就是为了把相同的代码抽出来,降低代码之间的耦合度的作用

2.大家学java肯定知道oop(面向对象编程),oop里面有四大特性封装,继承,多态和抽象。大家写程序知道它是由上到下或者下由到上,但是有时不光需要由上到下或者下由到上,还需要由左到右或者右由到左这种思想。aop(面向切面编程)的概念由此而生

我看过好多博客有的人写aop就是按书上来的。我喜欢简单,通俗,易懂的。

按成程序分,肯定有核心业务和非核心

核心业务代码,我们肯定要日志呀,异常呀。非核心指的就是指的日志和异常这些代码。这就让我们想到核心代码的哪个方法加,例如日志代码我可以统一放一起不。核心代码那么多,方法那多对吧。可能这一段是针对日志的,那一边段是针对异常的。无论核心代码还是非核心代码都有很多店需要关注对吧。

以上是我们可以想到这些问题?我们该如何处理呢?

这样应运而生了几个概念,我们关注点也叫横切点,针对核心代码被拦截方法的就叫做连接点,那拦截方法的就要做切入点。统一放到一起的切入面是不是就可以叫做切面了。那谁告诉切面什么时候该执行什么,那就得有人告诉他吧。所以通知就有了,核心代码肯定需要写在不同的对象吧。那目标对象(代理对象)概念应运而生。我们的目的得让他起作用吧,切面应用到目标对象(使代理对象)被创建的过程叫做织入,在原来代码不被修改,而起到作用叫做引用。

以上我把特抽象的概念,一步步的变成大家分析出来,这样那些概念是不是就很容易理解了,原理也很清楚了。我们总结一下aop概念

1.横切点(那些方法需要被拦截),2.连接点(被拦截的方法),3.切入点(拦截的方法),4.切入面(切入点的集合),5.目标对象(代理对象),7.通知(告诉切面什么时候执行目标对象那些方法),8.织入(创建代理对象的过程),9.引入(在不修改代码,将原来共有的放到一起。而不影响功能)

这里我有几个网址专门学习aop的:1.https://www.cnblogs.com/best/p/5736422.html,2.https://blog.csdn.net/qq_27093465/article/details/53367361,3.https://www.cnblogs.com/hongwz/p/5764917.html

这里强调一下,对于springaop采用2种代理对象

1.如果目标类实现了接口,则spring会采用jdk动态代理

2.如果目标类没有显现接口,则采用cglib动态代理

这个aop测试所使用到的jar包有以下:
aspectjrt.jar
aspectjweaver.jar
cglib-nodep-2.1_3.jar

spring.jar

下面是测试代码。

共同接口:(其实这个在实际使用中是可以省略的,又不是对他进行添加切面编程的)

[java]  view plain  copy
  1. package com.lxk.spring.aop;  
  2.   
  3. import java.util.List;  
  4.   
  5. /** 
  6.  * 目标对象和代理对象都实现的接口 
  7.  */  
  8. public interface PersonDao {  
  9.     void deletePerson();  
  10.     List<Person> getPerson() throws Exception;  
  11.     void savePerson();  
  12.     void updatePerson();  
  13. }  


目标对象实现目标接口:(这才是真正要面对的,要扩展的。要未他们做切面的地方)

[java]  view plain  copy
  1. package com.lxk.spring.aop;  
  2.   
  3. import com.google.common.collect.Lists;  
  4.   
  5. import java.util.List;  
  6.   
  7. /** 
  8.  * 目标对象:实现目标接口 
  9.  */  
  10. public class PersonDaoImpl implements PersonDao {  
  11.   
  12.     @Override  
  13.     public void deletePerson() {  
  14.         System.out.println("delete perosn");  
  15.     }  
  16.   
  17.     @Override  
  18.     public List<Person> getPerson() throws Exception {  
  19.         List<Person> personList = Lists.newArrayList();  
  20.         Person person1 = new Person();  
  21.         person1.setPid(1L);  
  22.         person1.setPname("person1");  
  23.         System.out.println("get person");  
  24.         personList.add(person1);  
  25.         Person person2 = new Person();  
  26.         person2.setPid(2L);  
  27.         person2.setPname("person2");  
  28.         personList.add(person2);  
  29.         return personList;  
  30.     }  
  31.   
  32.     @Override  
  33.     public void savePerson() {  
  34.         System.out.println("delete perosn");  
  35.     }  
  36.   
  37.     @Override  
  38.     public void updatePerson() {  
  39.         System.out.println("delete perosn");  
  40.     }  
  41.   
  42. }  

切面和切面里面的通知

这个才是对上面的要扩展的扩展,这就是切面里面的操作,,,对很多相同的操作提取到切面上去操作。这个只是把切面内部的方法写好,下面的配置文件说明这个切面切哪里也就是切入点在哪,怎么切,是前置,环绕,后置,还是异常等等。

[java]  view plain  copy
  1. package com.lxk.spring.aop;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.ProceedingJoinPoint;  
  5.   
  6. import java.util.ArrayList;  
  7. import java.util.List;  
  8.   
  9. /** 
  10.  * 切面(spring aop 就不需要拦截器啦) 
  11.  * (模拟hibernate里面保存数据要打开事物,然后各种增删改之后,再提交事物。) 
  12.  */  
  13. public class Transaction {  
  14.   
  15.     public void beginTransaction() {//前置通知  
  16.         //打开事物  
  17.         System.out.println("begin Transaction");  
  18.     }  
  19.   
  20.     /** 
  21.      * @param joinPoint 通过joinPoint可以得到目标类和目标方法的一些信息 
  22.      * @param val       目标方法的返回值 
  23.      *                  和<aop:after-returning returning="val"/>中returning的值保质一致 
  24.      */  
  25.     public void commit(JoinPoint joinPoint, Object val) {//后置通知  
  26.         String methodName = joinPoint.getSignature().getName();  
  27.         System.out.println(methodName);  
  28.         System.out.println(joinPoint.getTarget().getClass().getName());  
  29.         //提交事物  
  30.         System.out.println("commit");  
  31.         List<Person> personList = (ArrayList<Person>) val;  
  32.         for (Person person : personList) {  
  33.             System.out.println(person.getPname());  
  34.         }  
  35.     }  
  36.   
  37.     public void finalMethod() {  
  38.         System.out.println("最终通知");  
  39.     }  
  40.   
  41.     public void aroundMethod(ProceedingJoinPoint joinPoint) {//环绕通知  
  42.         try {  
  43.             System.out.println("around method");  
  44.             joinPoint.proceed();//调用目标类的目标方法  
  45.         } catch (Throwable e) {  
  46.             // TODO Auto-generated catch block  
  47.             e.printStackTrace();  
  48.         }  
  49.     }  
  50.   
  51.     /** 
  52.      * 异常通知 
  53.      */  
  54.     public void throwingMethod(Throwable except) {  
  55.         System.out.println(except.getMessage());  
  56.     }  
  57. }  

aop配置基于配置文件方式

  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"  
  4.        xmlns:aop="http://www.springframework.org/schema/aop"  
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  7.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  
  8.     <!--   
  9.         1、目标类  
  10.         2、切面  
  11.         3、进行aop的配置  
  12.         (目标接口没有:因为引入到容器是为了实例化对象,接口是不能实现对象的。)  
  13.         (也没有拦截器的引入,有的只是aop的配置,如上的3、)  
  14.      -->  
  15.     <!-- 目标类 -->  
  16.     <bean id="personDao" class="com.lxk.spring.aop.PersonDaoImpl"/>  
  17.     <!-- 切面的声明 -->  
  18.     <bean id="transaction" class="com.lxk.spring.aop.Transaction"/>  
  19.     <!-- aop配置 -->  
  20.     <aop:config>  
  21.         <!--  
  22.             配置aop的切入点  
  23.               id 是切入点的标识  
  24.               expression 为切入点的表达式  
  25.                  execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)  
  26.                     throws-pattern?)  
  27.                  modifiers-pattern  修饰符  可选  public private protected  
  28.                  ret-type-pattern  返回类型  必选  *  代表任意类型  
  29.                  declaring-type-pattern  方法的声明类型  
  30.                  name-patterm  方法名称类型  
  31.                      set*  以set开头的所有的方法名称  
  32.                      update*  以update开头的所有的方法名称  
  33.                  param-pattern  参数匹配  
  34.                      (..)  任意多个参数,每个参数任意多个类型  
  35.                      (*,String) 两个参数  第一个是任意类型,第二个是String  
  36.                      (String,*,Integer) 三个参数,第一个是String类型,第二个是任意类型,第三个是Integer类型  
  37.                     throws-pattern  异常的匹配模式  
  38.              例子:  
  39.                execution(* cn.itcast.spring.aop.xml.AService.*(..));  
  40.                   cn.itcast.spring.aop.xml.AService下的所有的方法  
  41.                execution(public * cn.itcast.oa..*.*(..))  
  42.                    返回值为任意类型,修饰符为public,在cn.itcast.oa包及子包下的所有的类的所有的方法  
  43.                exectuion(* cn.itcast.oa..*.update*(*,String))  
  44.                    返回值是任意类型,在cn.itcast.oa包及子包下所有的以update开头的参数为两个,第一个为任意类型  
  45.                    第二个为String类型的所有类的所有的方法  
  46.          -->  
  47.         <aop:pointcut expression="execution(* com.lxk.spring.aop.PersonDaoImpl.*(..))" id="perform"/>  
  48.         <!-- 配置切面(切面里面配置通知)—— ref 指向声明切面的类 -->  
  49.         <aop:aspect ref="transaction">  
  50.             <!-- 前置通知pointcut-ref 引用一个切入点 -->  
  51.             <aop:before method="beginTransaction" pointcut-ref="perform"/>  
  52.   
  53.             <!--  
  54.                 后置通知  
  55.                    *  returning 目标方法的返回值  
  56.                    *  如果目标方法中有可能存在异常,异常确实发生了,这个时候,后置通知将不再执行  
  57.              -->  
  58.   
  59.             <!--<aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>-->  
  60.   
  61.             <!--  
  62.                 最终通知  
  63.                    *   不能得到目标方法的返回值  
  64.                    *   无论目标方法是否有异常,最终通知都将执行  
  65.                    *   资源的关闭、连接的释放写在最终通知里  
  66.              -->  
  67.             <!--<aop:after pointcut-ref="perform" method="finalMethod"/>-->  
  68.   
  69.             <!--  
  70.                     环绕通知  
  71.                        *  ProceedingJoinPoint的proceed方法就是目标对象的目标方法  
  72.                        *  环绕通知可以控制目标对象目标方法执行  
  73.              -->  
  74.             <!-- 
  75.             <aop:around method="aroundMethod" pointcut-ref="perform"/> 
  76.              -->  
  77.             <!--  
  78.                     异常通知  
  79.                       在异常通知中获取目标方法抛出的异常  
  80.              -->  
  81.             <!--<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="except"/>-->  
  82.         </aop:aspect>  
  83.     </aop:config>
  84.     <!--aop开启注解方式 -->
        <aop:aspectj-autoproxy/>  
  85. </beans>  

这里写的是非常不错的关于aop配置的xml,注释很详细



AspectJ中的切入点匹配的执行点称作连接的(Join Point),在通知方法中可以声明一个JoinPoint类型的参数。通过JoinPoint可以访问连接点的细节。下面简要介绍JponPoint的方法:

1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表; 
2.Signature getSignature() :获取连接点的方法签名对象; 
3.java.lang.Object getTarget() :获取连接点所在的目标对象; 
4.java.lang.Object getThis() :获取代理对象本身; 

ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法: 

5.java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法; 

6.java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的参数替换原来的参数。 

package com.zhangguo.Spring052.aop02;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 通知类,横切逻辑
 *
 */
//次注解不影响aop的功能(spring注解中@component就是说把这个类交给Spring管理,又一次起个名字叫userManager,因为不清楚这个类是属于哪个层面,所以就用@Component)
@Component
@Aspect
public class Advices {
    @Before("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
    public void before(JoinPoint jp){
        System.out.println("----------前置通知----------");
        System.out.println(jp.getSignature().getName());
    }
    
    @After("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
    public void after(JoinPoint jp){
        System.out.println("----------最终通知----------");
    }
}
复制代码

 上面的代码与下面的配置基本等同

复制代码
    <!-- 通知 -->
    <bean id="advices" class="com.zhangguo.Spring052.aop01.Advices"></bean>
    
    <!-- aop配置 -->
    <aop:config proxy-target-class="true">
        <!--切面 -->
        <aop:aspect ref="advices">
            <!-- 切点 -->
            <aop:pointcut expression="execution(* com.zhangguo.Spring052.aop01.Math.*(..))" id="pointcut1"/>
            <!--连接通知方法与切点 -->
            <aop:before method="before" pointcut-ref="pointcut1"/>
            <aop:after method="after" pointcut-ref="pointcut1"/>
        </aop:aspect>
    </aop:config>
复制代码




猜你喜欢

转载自blog.csdn.net/liliping28/article/details/80008066