Spring框架(三)AOP切面编程

“IOC控制反转完成了解耦合,那么功能扩展就交由我AOP切面编程来完成吧”

什么是切面

 

当前某一类下有三个方法名曰:func1()、func2()、func3(),在实现方法中我们依次调用,那么执行结果则是竖向调用。可现在面临的问题是——项目的整体架构已经完成,我们需要在不破坏原先设计的条件下去添加新的功能,这该怎么实现嘞?

这一问题就引入了切面:现在把某一方法当成切点(这里假设成func2),在切点处横斩一刀产生一个切面,这个切面就是用于引入新功能的载体。(新技能,get成功!)

几个常用名词:

切点:即原有功能(func2)、前置通知:走在切点之前执行的功能(功能1)、后置通知:切点执行后紧随其后的功能(功能2)、异常通知:切点处出现异常时执行的异常抛出。以上功能总和构成整个切面,将切面嵌入的过程称为织入

 

切面编程怎么实现

 

在Spring中提供了两种方式用于实现切面编程——Schema-base和AsceptJ,这里下面细写,先导Jar包(从现在开始本蒟蒻就开始使用开发神器IDEA啦

Java类编写部分

首先先手写一个Demo类和一个Test 测试类,Demo类下的三个方法用于设置切点方便测试,Test 用于测试功能。在func2() 方法里故意设置了一个异常,用于测试切面编程的异常抛出,测试时将注释松开

package com.yang.test;

public class Demo {
    /**
     * AOP 切面编程测试类
     */
    public void func1() {
        System.out.println("func1");
    }
    public void func2() throws Exception{
        //给切点故意设置一个异常
//        int i=5/0;
        System.out.println("func2");
    }
    public void func3() {
        System.out.println("func3");
    }
}

/这里是两个类之间吴迪的分界线/

package com.yang.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
//			Demo demo=new Demo(); //对象的创建和管理交由Spring处理
//			demo.func1();
//			demo.func2();
//			demo.func3();
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
        Demo demo=ac.getBean("demo", Demo.class);
        demo.func1();
        try {
            demo.func2(); //测试在func2方法上添加切面
        } catch (Exception e) {
//            e.printStackTrace();
        }
        demo.func3();
    }
}

 人都说Spring的配bean劝退了不少初学者,确实怪麻烦的,不过自从换了IDEA之后啊,腰也不疼了,腿也不酸了(走错片场了额 )倒是节省了不少功夫。

Spring映射文件添加aop命名空间,编写aop映射雏形。在官方帮助文档可以查看或CRTL+C(\滑稽)

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

前置通知、后置通知、异常抛出、还有环绕通知这四个类,在这里一并写好,后面的就交托给Spring映射文件里配bean处理

  • 前置通知,这里实现接口MethodBeforeAdvice,为了Schema-base方式下实现
  • import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class MyBeforeAdvice implements MethodBeforeAdvice {
    
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("执行前置通知");
        }
    }
  • 后置通知,也实现了一个接口,原因往上看
  • import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    public class MyAfterAdvice implements AfterReturningAdvice {
    
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println("执行后置通知");
            System.out.println("切面编程运行成功"); //测试切面结果
        }
    }
    
  • 异常通知:在这个类里有两个方法,第二个方法是为了测试AsceptJ方式而编写的,后续测试中松开注释即可
  • import org.springframework.aop.ThrowsAdvice;
    
    import java.rmi.RemoteException;
    
    public class MyThrowAdvice implements ThrowsAdvice {
        //多个参数只有最后一个异常类型参数是必须传入的
        //参数必须一个或四个
        public  void afterThrowing(Exception ex) throws Throwable{
            System.out.println("执行异常通知,schema-base"+ex.getMessage());
        }
    //    public void myexception(Exception e){
    //        System.out.println("执行异常通知,message:"+e.getMessage());
    //    }
    }
  • 环绕通知:这里引入了一个我也不太明白的东西——拦截器,先不管别跑题了,以后遇见了再研究
  • import org.aopalliance.intercept.MethodInterceptor; //拦截器
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyArround implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            System.out.println("环绕-前置通知");
            Object result=methodInvocation.proceed(); //放行,调用切点方法
            System.out.println("环绕-后置通知");
            return result;
        }
    }

 

Schema-base实现方式

  • 在此方式下,因为实现类都采用了接口,因此绑定上比较简便些,只需要将全类名写成bean引入即可

 

一、前置通知,后置通知,异常抛出 三类通知的映射编写,运行时先屏蔽 func2() 方法中设置的异常,因为当触发异常时,程序会中断执行,走不到后置通知。看每个advice-ref 就可以字面翻译出功能类型。

<bean id="mybefore" class="com.yang.advice.MyBeforeAdvice"> </bean>
<bean id="myafter" class="com.yang.advice.MyAfterAdvice"></bean>
<aop:config>
    <aop:pointcut id="mypoint" expression="execution(* com.yang.test.Demo.func2())"/>
    <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
    <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
    <aop:advisor advice-ref="mythrow" pointcut-ref="mypoint"/>
</aop:config>

func1
执行前置通知
func2
执行后置通知
切面编程运行成功
func3
 

func1
执行前置通知
执行异常通知,schema-base/ by zero
func3

二、环绕通知:其意思大概就是 前置通知+后置通知,实现方式也挺简便

<bean id="myarround" class="com.yang.advice.MyArround"> </bean>
<aop:config>
    <aop:pointcut id="mypoint" expression="execution(* com.yang.test.Demo.func2())"/>
    <aop:advisor advice-ref="myarround" pointcut-ref="mypoint"/>
</aop:config>

func1
环绕-前置通知
func2
环绕-后置通知
func3

 

AsceptJ实现方式

  • 这种实现方式下,通知类的编写比较随意,只要写一个方法就行,不需要实现接口,但映射文件里的绑定匹配比较严密,要具体到某一个类下的某一个方法。

 

在这里为了方便管理,把所有的通知方法写入一个MyAdvice 类下,直接用AsceptJ方式一个一个引入。

package com.yang.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAdvice {
    public void mybefore(){
        System.out.println("这是一个前置通知");
    }
    public void myafter(){
        System.out.println("这是一个后置通知");
    }
    public void myaftering(){
        System.out.println("这是afterReturning式的后置通知");
    }
    public void mythrow() {
        System.out.println("切点在这里抛出了一个异常");
    }
    public Object myarround(ProceedingJoinPoint p) throws Throwable {
        System.out.println("这里执行了环绕前置通知");
        Object result=p.proceed(); //放行
        System.out.println("这里执行了环绕后置通知");
        return result;
    }
}

映射部分则分标签逐步定位到每一个类下的每一个方法,需要注意的是第一、after 和 after-returning 同代表后置通知,但在映射里谁靠前先执行谁。第二、after-returning只能在切点正常运行下才会执行,而after则无论有没有异常都会执行

<bean id="demo" class="com.yang.test.Demo"> </bean>
<bean id="myadvice" class="com.yang.advice.MyAdvice"> </bean>
<aop:config>
    <aop:aspect ref="myadvice">
        <aop:pointcut id="mypoint" expression="execution(* com.yang.test.Demo.func2())"/>
        <aop:before method="mybefore" pointcut-ref="mypoint"/>
        <!--这里的先后顺序决定执行顺序-->
        <aop:after method="myafter" pointcut-ref="mypoint"/>
        <!--after-returning只能在切点正常运行下执行
        而after则是无论有没有异常抛出都会执行-->
        <aop:after-returning method="myaftering" pointcut-ref="mypoint"/>
        <aop:after-throwing method="mythrow" pointcut-ref="mypoint"/>
        <aop:around method="myarround" pointcut-ref="mypoint"/>
    </aop:aspect>
</aop:config>

func1
这是一个前置通知
这里执行了环绕前置通知
func2
这里执行了环绕后置通知
这是afterReturning式的后置通知
这是一个后置通知
func3

 

带参数的切点

如果切点带参数,则在映射里需要给绑定的方法添加参数,否则无法匹配

<aop:pointcut id="mypoint1" expression="execution(* com.yang.test.Demo.func2(String)) and args(name1)"/>

另外,如果通知方法中需要传参数,则应在对用通知的映射中添加 arg-names 属性

 

(2020.05.12添加) 

通过注解完成切面

这种方式要先在applicationContext.xml中引入context命名空间

<!--扫描可能存在注解的包-->
<context:component-scan base-package="com.duebass.advice,com.duebass.test"> </context:component-scan>
<aop:aspectj-autoproxy expose-proxy="true"> </aop:aspectj-autoproxy>

随后配置注解,切点处

@Component
public class Demo {
    @Pointcut("execution(* com.duebass.test.Demo.func1())")  //切点注解
    public void func1(){
//        int i=5/0;
        System.out.println("func1");
    }
}

通知处

@Component
@Aspect   //表示该类是个切面通知类
public class MyAdvice {
    @Before("com.duebass.test.Demo.func1()")
    public void mybefore() {
        System.out.println("前置通知执行成功");
    }
}

 

 

 

 

到这就暂时就无了无了无了...

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值