Spring AOP(面向切面)

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

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

一、注解(Annotation)方式实现Spring面向切面

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

package com.entities;

public interface IHuman {
    public void eat();
    public void sleep();
}

  创建一个继承自IHuman接口的类

  为该类加上@Compoent注解

package com.entities;

import org.springframework.stereotype.Component;

@Component
public class Chinese implements IHuman {

    @Override
    public void eat() {
        System.out.println("中国人在吃饭");
        //int i =1/0;
    }

    @Override
    public void sleep() {
        System.out.println("中国人在睡觉");

    }

}

  定义一个切面类

  为该类加上@Aspect注解@Compoent注解

package com.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class HumanAOP {
    @Before("execution(* com.entities.*.eat(..))")//before是在方法执行之前
    public void beforeeat(){
        System.out.println("先洗 手");
    }
    @After(value = "execution(* com.entities.*.eat(..))")//after是在方法执行之后
    public void aftereat(){
        System.out.println("去漱口");
    }
    @AfterThrowing(value="execution(* com.entities.*.eat(..))",throwing="ex")//AfterThrowing是在方法报错抛异常时执行
    public void except(Exception ex){
        System.out.println(ex.getMessage());
        System.out.println("撑死了");
    }
    @AfterReturning(value="execution(* com.entities.*.eat(..))",returning="result")//AfterReturning在目标方法执行成功后执行的通知
    public void fanhui(Object result){
        System.out.println(result);
        System.out.println("吃完了");
    }
    @Before("execution(* com.entities.*.sleep(..))")
    public void beforesleesp(){
        System.out.println("先洗澡");
    }
}

  创建xml文件

  <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>自动为匹配的类生成代理对象

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

  定义一个main函数类进行效果实现

package com.test;

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

import com.entities.Chinese;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Chinese c= context.getBean(Chinese.class);
        c.eat();
        c.sleep();

    }

}
先洗 手
中国人在吃饭
去漱口
null
吃完了
先洗澡
中国人在睡觉

二、XML方式实现Spring面向切面

  同理,先定义一个接口

package com.entities;

public interface IHuman {
    public void eat();
    public void sleep();
}

  创建一个继承自IHuman接口的类

package com.entities;



public class Chinese implements IHuman {

    @Override
    public void eat() {
        System.out.println("中国人在吃饭");
        //int i =1/0;
    }

    @Override
    public void sleep() {
        System.out.println("中国人在睡觉");

    }

}

  定义一个切面类

package com.aop;



public class HumanAOP {
    
    public void beforeeat(){
        System.out.println("先洗 手");
    }
    
    public void aftereat(){
        System.out.println("去漱口");
    }
    
    public void except(Exception ex){
        System.out.println(ex.getMessage());
        System.out.println("撑死了");
    }
    
    public void fanhui(Object result){
        System.out.println(result);
        System.out.println("吃完了");
    }
    
    public void beforesleesp(){
        System.out.println("先洗澡");
    }
}

  创建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/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd"
        default-autowire="byName"
        >
        
        <bean id="chinese" class="com.entities.Chinese"></bean>
        <bean id="gou" class="com.entities.Gou"></bean>
        <bean id="humanaop" class="com.aop.HumanAOP"></bean>
        
        <aop:config>
            <aop:pointcut expression="execution(* com.entities.*.eat())" id="aftereat"/>
            <aop:pointcut expression="execution(* com.entities.*.eat())" id="beforeeat"/>
            
            
            <aop:aspect ref="humanaop">
                <aop:after method="aftereat()" pointcut-ref="aftereat"/>
                <aop:before method="beforeeat()" pointcut-ref="beforeeat"/>
            </aop:aspect>
        </aop:config>
        
</beans>

  定义一个main函数来执行

package com.test;

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

import com.entities.Chinese;
import com.entities.IHuman;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IHuman c= (IHuman)context.getBean("chinese");
        c.eat();
        

    }

}
先洗 手
中国人在吃饭
去漱口

通知分类:
前置通知(@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参数,用来获得目标方法的返回值(只对返回通知起作用)

原文地址:https://www.cnblogs.com/claricre/p/6636912.html