学习笔记Spring之aop编程

什么是AOP切面编程?

官方点的回答就是:这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

官方的解释总是很难理解,但是带着问题去看就会很简单,为什么需要aop编程?

就像做饭一样,如果要做辣子鸡和葫芦鸡,那么他们都要将洗干净,接下来才会出现做法的区别。

aop切面编程就像是把鸡洗干净这个过程,是两个菜共有的步骤。并且和他们自身没有关系。

用图来解释:


洗干净就像一个切面,他们经过了这个切面,就被处理成另一种样子了

aop的几个重要概念:

  1. 切面(Aspect):简单的理解就是把那些与核心业务无关的代码提取出来,进行封装成一个或几个模块用来处理那些附加的功能代码。 
  2. 连接点(JoinPoint):能被添加通知代码的地点
  3. 切入点(CutPoint):再连接点被实现了具体的拦截方法就叫切入点   
  4. 通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作
  5. 织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。   

AOP编程的实现通过代理的技术实现

什么是动态代理代理模式是常用的Java设计模式。代理类主要负责为委托类预处理消息、过滤信息、把消息转发给委托类,以及事后处理信息等。

在AOP中又是怎么实现的呢?

不能直接通过代理具体的java类来实现,必须让代理对象通过代理这个java类实现的接口才能进行代理。

然后把要在委托类中实现的通知全部织入到连接点,此时连接点就变成了切入点。

步骤:

1.先定义实现类的接口,然后继承接口实现具体的类

2.编写通知代码,就是要被添加到切入点的代码(连接点的位置通过这个通知继承的接口来确定)

3.在bean.xml文件中先配置被代理对象,进行对象等实例化和值的传递

4.在bean.xml文件中通知对象,也就是处理器对象。

5.在bean.xml文件中配置被代理的接口集

6.在bean.xml文件中将通知织入代理对象

(到这里将通知和被代理接口都配置在了代理对象中,则完成关联。具体关联方法通过Spring源码实现

7.在bean.xml文件中配置被代理对象

先看类的接口

package it.aop;


public interface TestServiceInter {//通过代理这个接口来实现代理他的实现类
  public  void sayHello();
}

类的具体实现:

package it.aop;

public class TestService implements TestServiceInter{//代理的时候通过代理这个接口来代理这个类

   private String name;
public void sayHello() {
   System.out.println("say Hello");
   //出异常的话会调用异常处理器 int x=9/0;
    }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

后置处理器:在所有方法的后面运行处理器里的方法

package it.aop;



import java.lang.reflect.Method;


import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.BeforeAdvice;


public class MyMethodAfterAdvice implements AfterReturningAdvice {
/*
 * returnValue:获取的方法返回值
 * method:获取的方法
 * args:方法的参数
 * target:目标对象
 * */
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置处理器实现"+method.getName());

}

}

前置处理器:在所有方法的前面运行处理器里的方法

package it.aop;


import java.lang.reflect.Method;


import org.springframework.aop.MethodBeforeAdvice;


public class MyMethodBeforeAdvice implements MethodBeforeAdvice {


/*
* method:被调用方法名字
* args:给method传递的参数
* target:目标对象,这个target和bean里的target并没有关系
* */
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO 自动生成的方法存根
System.out.println("记录日志前置处理器实现"+method.getName()+target.getClass());
}


}

环绕处理器有一个MethodInvocation arg0参数 ,这个参数需要调用proceed()方法得到一个对象然后函数返回,这样才能成功调用,相当于综合了前置处理器和后置处理器。

package it.aop;


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


public class MyMethodInterceptor implements MethodInterceptor {


@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
// TODO Auto-generated method stub
System.out.println("环绕处理器处理前");
Object obj=arg0.proceed();
System.out.println("环绕处理器处理前");
return obj;
}


}

异常处理器:在所有方法出问题调用这个处理器里的方法

package it.aop;


import java.lang.reflect.Method;


import org.springframework.aop.AfterAdvice;
import org.springframework.aop.ThrowsAdvice;


public class MyThrowsAdvice implements ThrowsAdvice {
/*Method m:出异常的方法
* Object[] os:异常方法的方法参数
* Object target:目标对象
* Exception e:异常
* */
public void afterThrowing(Method m,Object[] os,Object target,Exception e) {
System.out.println("出问题了"+e.getMessage());
 
}

}


<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
    
   <!--配置被代理对象-->
   <bean id="testService" class="it.aop.TestService">
      <property name="name" value="余飞" />
   </bean>
   <!--配置处理器对象-->
   <bean id="myMethodBeforeAdvice" class="it.aop.MyMethodBeforeAdvice" ></bean>
    <!--配置处理器对象-->
   <bean id="myMethodAfterAdvice" class="it.aop.MyMethodAfterAdvice" />
    <!--配置环绕处理器对象-->
   <bean id="myMethodInterceptor" class="it.aop.MyMethodInterceptor" ></bean>
    <!--配置错误处理器对象-->
   <bean id="MyThrowsAdvice" class="it.aop.MyThrowsAdvice" />
 
   <!-- 配置代理对象 -->
   <bean id="proxyFactoryBean"  class="org..springframework.aop.framework.ProxyFactoryBean" >
      <!--配置代理的接口集,因为要通过代理类实现的接口来实现代理-->
      <property name="proxyInterfaces"  >
         <list>
  <value>it.aop.TestServiceInter</value>        
         </list>
        </property>
      <!--把通知织入到代理对象-->
         <property name="interceptorNames">
           <!--相当于把MyMethodBeforeAdvice前置通知和代理对象相关联-->
           <!--因为有多个通知,所以要用list存入-->
           <list>
           <!--因为前置通知定义了自定义切入点,所以名字要用自定义切入点的名字-->
          <value>myMethodBeforeAdvice</value>
          <value>myMethodAfterAdvice</value>
          <value>myMethodInterceptor</value>
          <value>MyThrowsAdvice</value>
          </list>
         </property>
      <!--配置被代理对象,可以指定-->
      <property name="target" ref="testService"/>
  
   </bean>

</beans>


如果想要定义某个处理器只对一个方法实行处理,那就需要用到引入通知可以自定义切入点,只需要在bean中配置。

<!--定义切入点,就是定义再那个函数的实现位置-->
                                          <bean id="myMethodBeforeAdviceFliter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <property name="advice" ref="myMethodBeforeAdvice" /> //需要控制在哪里实现的处理器
    <property name="mappedNames">     //控制处理器要进行通知的方法  
          <list>
             <value>sayHello</value>
          </list>  
     </property>

 </bean>

只需要加这段代码,选择需要进行前置配置的方法但是还要将通知织入到代理对象中里的<value></value>值改为定义切入点的id名称。


AspectJ的aop编程:

一直发现上面的方法太麻烦,好在有发现了另一种方法,就是使用aspectJ框架完成aop编程的设置

AspectJ 意思就是Java的Aspect,Java的AOP。

写起来特别简单

首先在xml文件中配置两个对象:

user为被增强对象,myUser为增强对象


接下来设置aop代理


在execution语句中就是设置切点的位置

里面的语句格式如下:

第一个参数:*设置返回值范围的方法。

第二个参数:com表示包范围()

第三个参数:User表示类文件范围(第一个参数有空格)

第四个参数:add表示方法范围

第五个参数:(..)匹配了一个接受任意数量参数的方法(零或者更多)。 

()匹配了一个不接受任何参数的方法,
(..)匹配了一个接受任意数量参数的方法(零或者更多)。 
(*)匹配了一个接受一个任何类型的参数的方法。 
(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。
  • 任意公共方法的执行:
    execution(public * *(..))
  • 任何一个名字以“set”开始的方法的执行:
    execution(* set*(..))
  • AccountService接口定义的任意方法的执行:
    execution(* com.xyz.service.AccountService.*(..))
  • 在service包中定义的任意方法的执行:
    execution(* com.xyz.service.*.*(..))
  • 在service包或其子包中定义的任意方法的执行:
    execution(* com.xyz.service..*.*(..))
  • 在service包中的任意连接点(在Spring AOP中只是方法执行):
    within(com.xyz.service.*)
  • 在service包或其子包中的任意连接点(在Spring AOP中只是方法执行):
    within(com.xyz.service..*)
  • 实现了AccountService接口的代理对象的任意连接点 (在Spring AOP中只是方法执行):
    this(com.xyz.service.AccountService)

接下来看下注解方式实现aop编程,操作更为简单、

第一步相同,先配置bean文件


第二步:


第三部:

在被增强的类上设置@Aspect

被织入的方法上设置

@Before(value ="execution(* anno.User.*(..))")

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值