IT虾米网

Spring AOP(面向切面示例)详解

sanshao 2019年09月30日 编程语言 147 0

什么是AOP?
基本概念
切面(aspect):横切关注点被模块化的特殊对象。
通知(advice):切面必须要完成的工作。切面中的每个方向称之为通知。通知是在切面对象中的。
目标(target):被通知的对象。
代理(proxy):向目标对象应用通知后创建的对象。

连接点(joinpoint):目标对象的程序执行的某个特定位置。如某个方法调用前,调用后的位置。包括两个信息:1.目标程序的哪个方法?2.方法执行 
前还是执行后?
切点(pointcut):每个类会有多个连接点,AOP通过切点定位到特定的边接点。
类比,连接点相当于数所成方圆 中的记录,而切点相当于查询条件。一个切点匹配多个连接点。

使用XML配置的方法:

1.导入 spring包和aspectj的两个jar包

2.目标对象(要切入的对象)

首先为了不违反开闭原则和更好的可扩展性,目标对象实际上是实现了已定义好的某个接口

接口:

package com.itnba.test; 
 
public interface IHuman { 
    public void chifan(); 
    public void shuijiao(); 
 
}

实现接口的两个类:

package com.itnba.test; 
 
import org.springframework.stereotype.Component; 
 
public class Chinese implements IHuman { 
 
    @Override 
    public void chifan() { 
        // TODO 自动生成的方法存根 
        System.out.println("中国人吃饭"); 
 
    } 
     
    @Override 
    public void shuijiao() { 
        // TODO 自动生成的方法存根 
        System.out.println("中国人睡觉"); 
    } 
 
}
package com.itnba.test; 
 
import org.springframework.stereotype.Component; 
 
 
public class American implements IHuman { 
 
    @Override 
    public void chifan() { 
        // TODO 自动生成的方法存根 
        System.out.println("美国人吃饭"); 
 
    } 
 
    @Override 
    public void shuijiao() { 
        // TODO 自动生成的方法存根 
        System.out.println("美国人睡觉"); 
    } 
 
 
}

3.定义一个切面类

package com.itnba.test; 
 
import org.aspectj.lang.annotation.After; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.springframework.stereotype.Component; 
public class Qiemian { 
     
    public void chifanqian(){ 
        System.out.println("洗手"); 
    } 
    public void chifanhou(){ 
        System.out.println("漱口"); 
    } 
    public void shuijiaoqian(){ 
        System.out.println("洗澡"); 
         
    } 
 
}

4.XML文件配置

<?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" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-4.3.xsd 
        http://www.springframework.org/schema/aop  
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" 
        default-autowire="byName" 
        > 
         <!-- 首相要实例化目标对象类和切面类 --> 
        <bean id="chinese" class="com.itnba.test.Chinese"></bean> 
        <bean id="american" class="com.itnba.test.American"></bean> 
        <bean id="qiemian" class="com.itnba.test.Qiemian"></bean> 
         
        <aop:config> 
        <!-- 要切入的对象 -->                  
        <aop:pointcut expression="execution(* com.itnba.test.*.chifan(..))" id="chifan"/> 
        <aop:pointcut expression="execution(* com.itnba.test.*.shijiao(..))" id="shijiao"/> 
        <!-- 切入点 --> 
        <aop:aspect id="ha" ref="qiemian"><!-- 切面类  --> 
            <!--  <aop:之前before、之后after... method="切面类中的方法" pointcut-ref="上面的切入的对象"/>  --> 
            <aop:before method="chifanqian()" pointcut-ref="chifan"/><!-- 之前通知 --> 
            <aop:after method="chifanhou()()" pointcut-ref="chifan"/><!-- 之后通知 --> 
            <aop:before method="shuijiaoqian()" pointcut-ref="shijiao"/> 
        </aop:aspect> 
    </aop:config> 
</beans>

5.运行

package com.itnba.test; 
 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
 
public class Test { 
 
    public static void main(String[] args) { 
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 
        //要用接口接收 
        IHuman c= (IHuman)context.getBean("chinese"); 
        c.chifan(); 
        c.shuijiao(); 
        System.out.println("*********************************"); 
        //要用接口接收 
        IHuman a= (IHuman) context.getBean("american"); 
        a.chifan(); 
        a.shuijiao(); 
 
    } 
 
}

结果:

 

使用注解的方法:

1.导如 spring包和aspectj的两个jar包

 


2.配置文件中加入aop命名空间

<?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" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-4.3.xsd 
        http://www.springframework.org/schema/aop  
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > 
 
        <!-- 自动扫描包下的类,并将其实例化。多个包之间用,隔开 --> 
        <context:component-scan base-package="com.itnba.test"></context:component-scan> 
        <!-- 配置文件中启动AspectJ的注解功能 ,默认是false,要将其改为true --> 
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>  
</beans>

3.目标对象(要切入的对象)

首先为了不违反开闭原则和更好的可扩展性,目标对象实际上是实现了已定义好的某个接口

接口:

package com.itnba.test; 
 
public interface IHuman { 
    public void chifan(); 
    public void shuijiao(); 
 
}

实现接口的两个类:

package com.itnba.test; 
 
import org.springframework.stereotype.Component; 
 
@Component 
public class Chinese implements IHuman { 
 
    @Override 
    public void chifan() { 
        // TODO 自动生成的方法存根 
        System.out.println("中国人吃饭"); 
 
    } 
     
    @Override 
    public void shuijiao() { 
        // TODO 自动生成的方法存根 
        System.out.println("中国人睡觉"); 
    } 
 
}
package com.itnba.test; 
 
import org.springframework.stereotype.Component; 
 
@Component 
public class American implements IHuman { 
 
    @Override 
    public void chifan() { 
        // TODO 自动生成的方法存根 
        System.out.println("美国人吃饭"); 
 
    } 
 
    @Override 
    public void shuijiao() { 
        // TODO 自动生成的方法存根 
        System.out.println("美国人睡觉"); 
    } 
 
 
}

4.定义一个切面类,直接在里面注解

package com.itnba.test; 
 
import org.aspectj.lang.annotation.After; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.springframework.stereotype.Component; 
@Aspect //声明该类是切面类 
@Component//配置文件中启动自动扫描功能 
public class Qiemian { 
     
    @Before("execution(* com.itnba.test.*.chifan(..))")//在那个方法的之前通知    *代表全部..代表所有形参,不管有多少 
    public void chifanqian(){ 
        System.out.println("洗手"); 
    } 
    @After("execution(* com.itnba.test.*.chifan(..))")//在那个方法之后通知 
    public void chifanhou(){ 
        System.out.println("漱口"); 
    } 
    @Before("execution(* com.itnba.test.*.shuijiao(..))") 
    public void shuijiaoqian(){ 
        System.out.println("洗澡"); 
         
    } 
 
}

5.写好后用main函数来运行一下

package com.itnba.test; 
 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
 
public class Test { 
 
    public static void main(String[] args) { 
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 
        Chinese c= context.getBean(Chinese.class); 
        c.chifan(); 
        c.shuijiao(); 
        System.out.println("*********************************"); 
        American a= (American) context.getBean("american"); 
        a.chifan(); 
        a.shuijiao(); 
 
    } 
 
}

结果:

这样,Spring的面向切面编程就简单介绍完了。

通知分类:
前置通知(@Before) -- 在目标方法执行前执行的通知。
后置通知(@After) -- 在目标方法执行后执行的通知。无论是否发生异常。后置通知中,无法读取目标方法返回的结果。
返回通知(@AfterReturnning) --在目标方法执行成功后执行的通知。在返回通知中可以访问目标返回结果的。 
@AfterReturnning(value="execution(* com.itnba..*(..))",returning="result")
public void afterRun(Object result){
System.out.println(result);
}

异常通知(@AfterThrowing) -- 在目标方法执行出错后执行的通知。
@AfterThrowing(value="execution(* com.itnba..*(..))",throwing="ex")
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage());
}

环绕通知(@Around) -- 需要切面方法携带ProceedingJoinPoion参数,类似于一个完整的动态代理,环绕通知必须要有一个返回值,是目标方法的返 
回值。

@Around("execution(* com.itnba..*(..))")
public object aroundAdvice( ProceedingJoinPoint pjp){

object obj = null;
try{
//做前置通知
obj = pjp.proceed();
//做返回通知
}
catch(Exception ex){
//做异常通知
}
//做后置通知
return obj;
}

 

添加日志:

切面方法可以加入参数(JoinPoint) joinPost.getSignature().getXXX()获得相关方法信息
切面方法中可以加入Object参数,用来获得目标方法的返回值(只对返回通知起作用)

 

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Spring属性注入、构造方法注入、工厂注入以及注入参数详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。