Spring2-AOP

一.AOP

1、名称:面向切面编程(Aspect Oriented Programming)
2、正常程序执行流程都是纵向执行流程
  面向切面编程,在原有纵向执行流程中添加横切面
  不需要修改原有程序代码
    高扩展性
    原有功能相当于释放了部分逻辑.让职责更加明确

3、面向切面编程概念:在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫做面向切面编程.
4、常用概念
  1)原有功能: 切点,pointcut
  2)前置通知: 在切点之前执行的功能.beforeadvice
  3)后置通知: 在切点之后执行的功能,afteradvice
  4)如果切点执行过程中出现异常,会触发异常通知.throwsadvice
  5)切面:所有功能总称。(切入点pointcut和通知advice的结合
  6)织入:把切面嵌入到原有功能的过程叫做织入
5、spring 提供了 2 种 AOP 实现方式
  1)Schema-based
    每个通知都需要实现接口或类
    配置 spring 配置文件时在<aop:config>配置
  2)AspectJ
    每个通知不需要实现接口或类
    配置 spring 配置文件是在<aop:config>的子标签<aop:aspect>中配置

二. Schema-based 实现

(一)前置、后置通知(Schema-based 方式)

1、导入 jar

 2、新建通知类
  1)新建前置通知类
    arg0: 切点方法对象 Method 对象
    arg1: 切点方法参数
    arg2:切点在哪个对象中

public class MyBeforeAdvice implements MethodBeforeAdvice { 
    @Override 
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { 
        System.out.println("执行前置通知"); 
    } 
}

  2)新建后置通知类
    arg0: 切点方法返回值
    arg1:切点方法对象
    arg2:切点方法参数
    arg3:切点方法所在类的对象

public class MyAfterAdvice implements AfterReturningAdvice { 
    @Override 
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { 
        System.out.println("执行后置通知"); 
    } 
}

  3)配置 spring 配置文件
    引入 aop 命名空间
    配置通知类的<bean>
    配置切面
    通配符,匹配任意方法名,任意类名,任意一级包名
    如果希望匹配任意方法参数 (..)

<?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">
    <!-- 配置通知类对象,在切面中引入 --> 
    <bean id="mybefore" class="com.su.advice.MyBeforeAdvice"></bean>
    <bean id="myafter" class="com.su.advice.MyAfterAdvice"></bean>
    <!-- 配置切面 --> 
    <aop:config>
        <!-- 配置切点 --> 
        <aop:pointcut expression="execution(* com.su.test.Demo.demo2())" id="mypoint"/>
        <!-- 通知 --> 
        <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/> 
        <aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/> 
    </aop:config>
    <!-- 配置 Demo 类,测试使用 --> 
    <bean id="demo" class="com.su.test.Demo"></bean> 
</beans>

  4)编写测试代码

public class Test { 
    public static void main(String[] args) {
        //原始方法
        // Demo demo = new Demo(); 
        // demo.demo1();
        // demo.demo2(); 
        // demo.demo3(); 
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 
        Demo demo = ac.getBean("demo",Demo.class); 
        demo.demo1(); 
        demo.demo2(); 
        demo.demo3(); 
    } 
}

  5) 运行结果

demo1
执行前置通知
demo2
执行后置通知
demo3

(二)异常通知(Schema-based 方式)

1、新建一个类实现 throwsAdvice 接口
  1)必须自己写方法,且必须叫 afterThrowing
  2)有两种参数方式
    必须是 1 个或 4 个
  3)异常类型要与切点报的异常类型一致

public class MyThrow implements ThrowsAdvice{ 
    // public void afterThrowing(Method m, Object[] args, Object target, Exception ex) {
        // System.out.println("执行异常通知"); 
    // } 
    public void afterThrowing(Exception ex) throws Throwable { 
        System.out.println("执行异常通过-schema-base 方式 "); 
    } 
}

2、在 ApplicationContext.xml 配置

<bean id="mythrow" class="com.su.advice.MyThrow"></bean> 
<aop:config> 
    <aop:pointcut expression="execution(* com.su.test.Demo.demo1())" id="mypoint"/> 
    <aop:advisor advice-ref="mythrow" pointcut-ref="mypoint" /> 
</aop:config> 
<bean id="demo" class="com.su.test.Demo"></bean>

(三)环绕通知(Schema-based 方式)

1、把前置通知和后置通知都写到一个通知中,组成了环绕通知
2、实现步骤
  1)新建一个类实现 MethodInterceptor

public class MyArround implements MethodInterceptor { 
    @Override 
    public Object invoke(MethodInvocation arg0) throws Throwable { 
        System.out.println("环绕-前置"); 
        Object result = arg0.proceed();//放行,调用切点方式
        System.out.println("环绕-后置"); 
        return result;
    }
}

  2)配置 applicationContext.xml

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

三. AspectJ 实现

(一)前置、后置、环绕通知(AspectJ 方式)

1、新建类,不用实现
  1)类中方法名任意

public class MyAdvice { 
    public void mybefore(String name1,int age1){
        System.out.println("前置"+name1 );
    } 
    public void mybefore1(String name1){ 
        System.out.println("前置:"+name1); 
    } 
    public void myaftering(){ 
        System.out.println("后置 2"); 
    } 
    public void myafter(){ 
        System.out.println("后置 1"); 
    } 
    public void mythrow(){ 
        System.out.println("异常"); 
    } 
    public Object myarround(ProceedingJoinPoint p) throws Throwable{ 
        System.out.println("执行环绕"); 
        System.out.println("环绕-前置"); 
        Object result = p.proceed(); 
        System.out.println("环绕后置"); 
        return result; 
    }
}

  2)配置 spring 配置文件
    <aop:after/> 后置通知,是否出现异常都执行
    <aop:after-returing/> 后置通知,只有当切点正确执行时执行
    <aop:after/> 和 <aop:after-returing/> 和<aop:after-throwing/>执行顺序和配置顺序有关
    execution() 括号不能扩上 args
    中间使用 and 不能使用&& 由 spring 把 and 解析成&&
    args(名称) 名称自定义的.顺序和 demo1(参数,参数)对应
    <aop:before/> arg-names="名称" 名称来源于expression="" 中 args(),名称必须一样
      args() 有几个参数,arg-names 里面必须有几个参数
      arg-names="" 里面名称必须和通知方法参数名对应

<?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">
    <bean id="demo" class="com.su.test.Demo"></bean>
    <bean id="myadvice" class="com.su.advice.MyAdvice"></bean>
    <aop:config>
        <aop:aspect ref="myadvice">
            <aop:pointcut expression="execution(* com.su.test.Demo.demo1(String,int)) and args(name1,age1)" id="mypoint"/>
            <aop:pointcut expression="execution(* com.su.test.Demo.demo1(String)) and args(name1)" id="mypoint1"/>
            <aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1"/>
            <aop:before method="mybefore1" pointcut-ref="mypoint1" arg-names="name1"/>
            <!-- <aop:after method="myafter" pointcut-ref="mypoint"/>
            <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>
</beans>

(二)异常通知(AspectJ 方式)

1、 只有当切点报异常才能触发异常通知
2、在 spring 中有 AspectJ 方式提供了异常通知的办法
3、实现步骤:
  1)新建类,在类写任意名称的方法

public class MyThrowAdvice{ 
    public void myexception(Exception e1){ 
        System.out.println("执行异常通知 "+e1.getMessage()); 
    } 
}

  2)在 spring 配置文件中配置
    <aop:aspect>的 ref 属性表示:方法在哪个类中.
    <aop:xxxx/> 表示什么通知
    method: 当触发这个通知时,调用哪个方法
    throwing: 异常对象名,必须和通知中方法参数名相同(可以不在通知中声明异常对象)

<bean id="mythrow" class="com.su.advice.MyThrowAdvice"></bean> 
<aop:config> 
    <aop:aspect ref="mythrow"> 
        <aop:pointcut expression="execution(* com.su.test.Demo.demo1())" id="mypoint"/> 
        <aop:after-throwing method="myexception" pointcut-ref="mypoint" throwing="e1"/>
    </aop:aspect> 
</aop:config> 
<bean id="demo" class="com.su.test.Demo"></bean>

 四.使用注解(基于 Aspect)

1、spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解
  1)引入 xmlns:context

<context:component-scan base-package="com.su.advice"></context:component-scan>

2、@Component
  1)相当于<bean/>
  2)如果没有参数,把类名首字母变小写,相当于<bean id=""/>
  3)@Component(“自定义名称”)
3、实现步骤:
  1)在 spring 配置文件中设置注解在哪些包中

<context:component-scan base-package="com.su.advice,com.su.test"></context:component-scan>

  2)在 Demo 类中添加@Componet
    在方法上添加@Pointcut("") 定义切点

@Component 
public class Demo { 
    @Pointcut("execution(* com.su.test.Demo.demo1())") 
    public void demo1() throws Exception{ 
        // int i = 5/0; 
        System.out.println("demo1"); 
    } 
}

  3)在通知类中配置
    @Component 类被 spring 管理
    @Aspect 相当于<aop:aspect/>表示通知方法在当前类中

@Component 
@Aspect 
public class MyAdvice { 
    @Before("com.su.test.Demo.demo1()") 
    public void mybefore(){ 
        System.out.println("前置"); 
    } 
    @After("com.su.test.Demo.demo1()") 
    public void myafter(){ 
        System.out.println("后置通知"); 
    } 
    @AfterThrowing("com.su.test.Demo.demo1()") 
    public void mythrow(){ 
        System.out.println("异常通知"); 
    } 
    @Around("com.su.test.Demo.demo1()") 
    public Object myarround(ProceedingJoinPoint p) throws Throwable{ 
        System.out.println("环绕-前置");
        Object result = p.proceed(); 
        System.out.println("环绕-后置"); 
        return result;
    }
}

猜你喜欢

转载自www.cnblogs.com/sunny-sml/p/12664161.html