在软件 里,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

一 ,通知

(一)环境配置

  1. <!-- <注册目标对象>-->
  2. <bean id="someservice" class="com.abc.dao.ISomeServiceimpl"/>
  3. <!-- 注意切面,通知-->
  4. <bean id="myAdvice" class="com.abc.dao.MyMethodBeforeAdvice"/>
  5. <!-- <生成代理对象>-->
  6. <bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  7. <property name="targetName" value="someservice"/>或者asa
  8. <property name="interceptorNames" value="myAdvice"/>
  9. </bean>

(二)前置通知

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    //method:目标方法
    //objects 目标方法的参数列表
    //  o   :目标对象
    // before()里的方法会再目标方法之前执行
    public void before(Method method, Object[] objects, Object o) throws Throwable {

    }
}

后置通知


public class AfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice {
    //returnValue: 目标方法的返回值,
    //该方法虽然可以获取到目标方法的执行结果,但不能改变其值
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行后置通知方法");
             if(returnValue!=null){
                 returnValue=((String)returnValue).toUpperCase();    //<转化大小写>
             }
        System.out.println("returnValue=" +returnValue);
    }
}

环绕通知


public class Methodinterceptor implements MethodInterceptor {
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("执行环绕通知,目标方法执行");
         //目标方法执行, 该方法可以获取到目标方法的执行结果改变其值,
        Object result=methodInvocation.proceed();
        System.out.println("执行环绕通知,目标方法执行");
        if(result!=null){
            result=((String)result).toUpperCase();
        }
        System.out.println("returnValue=" +result);
        return result;
    }

异常通知


// 异常通知方法,有很多种,只有在程序有异常时才起作用
public class MyThowsAdvice implements ThrowsAdvice {
    public void afterThroing(Exception ex){
        System.out.println("执行异常通知方法 ex="ex.getMessage());
    }

可以前置后置或者环绕通知的同用

<bean id="someservice" class="com.abc.dao.ISomeServiceimpl"/>
  <!--  注册切面,通知-->
  <bean id="myAdvice" class="com.abc.dao.MyMethodBeforeAdvice"/>
  <bean id="AfterReturningAdvice" class="com.abc.dao.AfterReturningAdvice"/>
  <!--  <生成代理对象>-->
  <bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

    <property name="targetName" value="someservice"/>
    <property name="interceptorNames" value="myAdvice,AfterReturningAdvice"/>这这这

有接口用的jdbc的动态代理,没接口时只能用CJLB动态代理(但也可以用在有接口情况下)

<bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

    <property name="targetName" value="someservice"/>
    <property name="interceptorNames" value="myAdvice,AfterReturningAdvice"/>
    <property name="proxyClassLoader" value="true"/>  或者<property name="optimize" value="true"/>

  </bean>

image.png用的大致类包

二,顾问Advisor

advisor是另外一种切面,其中包含了advice(advice是aopalliance对通知(增强器)的顶层抽象,)

1,PointcutAdvisor

切入点顾问,用于指定切入织入的位置,既要将切面织入到哪个目标方法

配置文件

 <!--  <注册目标对象>-->
  <bean id="someservice" class="com.abc.dao.ISomeServiceimpl"/>
  <bean id="OtherService" class="com.abc.dao.OtherServiceimpl"/>
  <!--  注册切面,通知-->
  <bean id="myAdvice" class="com.abc.dao.MyMethodBeforeAdvice"/>

<!--  注册类过滤器和方法匹配器-->
  <bean id="myClassFilter" class="com.abc.dao.MyclassFilter"/>
  <bean id="myMethodMather" class="com.abc.dao.MyMethodMather"/>

<!--<注册切入点>-->
  <bean id="mypointcut" class="com.abc.dao.Mypointcut">
    <property name="classFilter" ref="myClassFilter"/>
    <property name="methodMatcher" ref="myMethodMather"/>
  </bean>
 <!--《注册切面,切入点顾问》-->
  <bean id="pointcutAdvisor" class="com.abc.dao.MyPointcutAdvisor">
    <property name="advice" ref="myAdvice"/>
    <property name="pointcut" ref="mypointcut"/>
  </bean>

  <!--  <生成代理对象>-->
  <bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetName" value="someservice"/>
    <property name="interceptorNames" value="pointcutAdvisor"/>
    <!--    <property name="proxyClassLoader" value="true"/>-->
    <!--    <property name="optimize" value="true"/>-->
  </bean>

  <!--  <生成代理对象>-->
  <bean id="otherServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetName" value="OtherService"/>
    <property name="interceptorNames" value="pointcutAdvisor"/>
  </bean>

</beans>

定义类过滤器

//定义类过滤器
public class MyclassFilter implements ClassFilter {
    public boolean matches(Class<?> aClass) {
        return aClass.isAssignableFrom(ISomeServiceimpl.class);
    }
}

定义方法匹配器

public class MyMethodMather implements MethodMatcher {
    //method:当前正在执行的目标方法

    public boolean matches(Method method, Class<?> aClass) {
        //判断当前正在执行的目标方法是否是指定的切入点方法
        String name = method.getName();
        if (name.equals("doFist")||name.equals("doSecond")){
            return true;
        }
        return false;
    }

    public boolean isRuntime() {
        //如果目标方法出现重载的方法,则让该方法返回true,
        return false;
    }
    //当上面两个方法都返回true,该方法才会启用
    //args:当前正在执行的目标方法的参数列表
    public boolean matches(Method method, Class<?> aClass, Object... objects) {
        return false;
    }
}

定义切入点和切入点顾问

public class Mypointcut  implements Pointcut {
    private ClassFilter classFilter;
    private MethodMatcher methodMatcher;

    public void setClassFilter(ClassFilter classFilter) {
        this.classFilter = classFilter;
    }

    public void setMethodMatcher(MethodMatcher methodMatcher) {
        this.methodMatcher = methodMatcher;
    }

    public ClassFilter getClassFilter() {
        return classFilter;
    }

    public MethodMatcher getMethodMatcher() {
        return methodMatcher;
    }
}

//定义切入点顾问
public class MyPointcutAdvisor implements PointcutAdvisor {
    private  Advice advice;
    private Pointcut pointcut;

    public void setAdvice(Advice advice) {
        this.advice = advice;
    }

    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
    }

    public Pointcut getPointcut() {
        return pointcut;
    }

    public Advice getAdvice() {
        return advice;
    }
//该方法没有启用
    public boolean isPerInstance() {
        return false;
    }
}

名称匹配方法切入点顾问

<!--  <注册目标对象>-->
  <bean id="someService" class="com.abc.dao.ISomeServiceimpl"/>
  <bean id="OtherService" class="com.abc.dao.OtherServiceImpl"/>

  <!--     注册切面,通知  ;-->
  <bean id="myAdvice" class="com.abc.Interceptor.MyIntroductionAdvice"/>
<!--  如需要类过滤器,自己定义然后注入(参照上面)-->

<!--//注册切面,引入顾问-->
  <bean id="pointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <property name="advice" value="myAdvice"/>        《主要用的bean 比较的是简单方法名》
    <property name="mappedName" value="doFist"/>
<!--    <property name="mappedNames" value="doFist,dosecend"/  两个方法一起调用 可以使用>-->
  </bean>

  <!--  <生成代理对象1>-->
    <bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="targetName" value="someService"/>
      <property name="interceptorNames" value="pointcutsAdvisor"/>

</bean>

  <!--  <生成代理对象2>-->
  <bean id="otherServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetName" value="OtherService"/>
    <property name="interceptorNames" value="pointcutAdvisor"/>
  </bean>

正则表达式切入点顾问

  <!--  <注册目标对象>-->
  <bean id="someService" class="com.abc.dao.ISomeServiceimpl"/>

  <!--     注册切面,通知  ;-->
  <bean id="myAdvice" class="com.abc.Interceptor.MyIntroductionAdvice"/>
<!--  如需要类过滤器,自己创建注入就可-->
<!--//注册切面,引入顾问-->
  <bean id="pointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice" value="myAdvice"/>
          <!--这里匹配的对象是 :全限定方法名
            正则表达式运算符:
            .号 表示一个字符,
            *号 表示其前面的内容可以出现0次或多次,
            +号 表示其前面的内容可以出现1次或多次,
<!--           &ndash;&gt;-->
<!--    <property name="pattern" value=".*doFist"/>-->
<!--    <property name="pattern" value=".*do."/>-->
<!--    <property name="pattern" value=".*F.*,.*T.*"/>-->
<!--    <property name="pattern" value=".*F.*/.*T.*"/>-->  《上面同用》
        <property name="pattern" value=".*S.*"/>        《这里需要注意全限定名称里有相同字母》

  </bean>



  <!--  <生成代理对象1>-->
    <bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="targetName" value="someService"/>
      <property name="interceptorNames" value="pointcutsAdvisor"/>

</bean>

2.IntroductionAdvisor

引入顾问,在不修改目标类的前提下,为目标对象增加新功能

配置文件

  <!--  <注册目标对象>-->
  <bean id="someService" class="com.abc.dao.ISomeServiceimpl"/>

  <!--     注册切面,通知 ;-->
  <bean id="myAdvice" class="com.abc.dao.MyIntroductionAdvice"/>
<!--//注册切面,引入顾问-->
  <bean id="myAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
    <constructor-arg ref="myAdvice"/>
    <constructor-arg value="com.abc.dao.ISomeService"/>
  </bean>



  <!--  <生成代理对象>-->
    <bean id="someServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="targetName" value="someService"/>
      <property name="interceptorNames" value="myAdvisor"/>
      <property name="proxyInterfaces" value="com.abc.dao.IOtherService"/>
</bean>
引入通知
public class MyIntroductionAdvice implements IOtherService, IntroductionInterceptor {
    public void doOther() {
        System.out.println("执行新增方法doOther()");
    }

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        String name=methodInvocation.getMethod().getName();
        if("doSome".equals(name)){
            //调用目标对象的目标方法
            //methodInvocation.proceed()相当于methodInvocation.getMethod().invoke(target,mi,getArguments());
            return methodInvocation.proceed();
        }
//        Object result=methodInvocation.proceed();
        return methodInvocation.getMethod().invoke(this,methodInvocation.getArguments());
    }

    public boolean implementsInterface(Class<?> intf) {
         return intf.isAssignableFrom(IOtherService.class);//包含equals()
    }
}

三,自动代理生成器


①默认Advisor自动代理生成器

!-- ProxyFactoryBean:①一次只能为一个目标对象生成代理对象,若要多个就会使配置文件变得拥堵
                       ②测试类想要访问的是目标对象,但正真访问的是代理对象,不符合逻辑
 -->
<!--注册自动代理生成类
      本质是一个Bean后处理器
            DefaultAdvisorAutoProxyCreator:  1,不能选择目标对象,所有的对象都会织入切面,
                                             2,不能选择切面类型,
                                             3,不能选择要织入的顾问,会将所有顾问作为切面织入到切入点中-->
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
     《切面只能是顾问,不能是通知》    
     《无论多少个目标对象,一句话解决》相当于代理对象

②Bean名称自动代理生成器

  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!--    目标对象--><property name="beanNames" value="someService"/>
<!--  目标顾问  --><property name="interceptorNames" value="pointcutAdvisor"/>
</bean>