Spring整合AOP


 

在Spring中使用AOP有2种方式

  • Spring AOP:Spring封装了动态代理实现AOP
  • AspectJ:专业的AOP框架,更强大

 

Spring AOP

依赖

  • spring-aop
     

目标接口、目标类

public interface UserService {
    void login();
    void logout();
}
@Service
public class UserServiceImpl implements UserService {

    @Override
    public void login() {
        System.out.println("正在执行login()...");
    }

    @Override
    public void logout() {
        System.out.println("正在执行logout()...");
    }
    
}

 

切面

新建包aspect放切面,包下新建切面类,实现对应的接口

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

@Component
public class UserServiceAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //前增强
        System.out.println("正在执行前增强...");

        //调用目标方法,返回值是Object类型
        Object object=methodInvocation.proceed();

        //后增强
        System.out.println("正在执行后增强...");

        //返回目标方法的返回值
        return object;
    }

}

导入MethodInterceptor 时不要导错了
 

根据增强时机实现对应的接口

通知类型增强时机需要实现的接口
环绕通知目标方法的整个调用流程,可实现前增强、后增强、异常处理等MethodInterceptor
前置通知目标方法执行之前MethodBeforeAdvice
后置通知目标方法执行之后AfterAdvice(空接口)
异常通知目标方法执行过程中抛出异常后ThrowsAdvice(空接口)
返回通知目标方法返回值后AfterReturningAdvice

一般不使用空接口,AfterReturningAdvice是返回通知,但很多时候都可以当作后置通知使用。

 

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

	<!-- 目标类、切面类要放到IOC容器中 -->
    <context:component-scan base-package="com.chy.mall" />

    <!-- UserService的代理。代理的是接口,name一般就使用接口名;代理的是目标类,name使用xxxProxy -->
    <bean name="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 指定要代理的接口,如果实现了多个接口,用子元素<list>来写 -->
        <property name="proxyInterfaces" value="service.UserService" />
        <!-- 指定目标对象 -->
        <property name="target" ref="userService" />
        <!-- 通过要使用的切面,通过bean的name指定,只能用value,不能用ref -->
        <property name="interceptorNames" value="userServiceAspect" />
        <!-- 是否直接代理目标类,默认false:代理接口 -->
        <!--<property name="proxyTargetClass" value="false" />-->
        <!-- 返回的代理对象是否是单例,默认为true -->
        <property name="singleton" value="true" />
    </bean>

</beans>

 

说明

示例写的是代理接口,会代理该接口所有的实现类。如果只代理目标类

  • 目标类不用实现接口
  • 代理中不必配置 proxyInterfaces ,需要将 proxyTargetClass 设置为true
    true:直接代理目标类,使用的是cglib动态代理
    false:默认值,代理接口,目标类必须实现接口,使用的是jdk动态代理

spring aop需要在配置文件中给每个目标类|接口代理,很繁琐,且配置文件会很庞大。

 

AspectJ

依赖

  • spring-aop
  • spring-aspects
     

目标接口、目标类

public interface UserService {
    void login();
    void logout();
}
@Service
public class UserServiceImpl implements UserService {

    @Override
    public void login() {
        System.out.println("正在执行login()...");
    }

    @Override
    public void logout() {
        System.out.println("正在执行logout()...");
    }

}

 

xml配置方式

切面

@Component
public class UserServiceAspect {

    /**
     * 前置通知要调用的方法
     */
    public void before(){
        System.out.println("正在执行前置通知...");
    }


    /**
     * 后置通知要调用的方法
     */
    public void after(){
        System.out.println("正在执行后置通知...");
    }


    /**
     * 返回通知要调用的方法
     */
    public void afterReturning(Object obj){
        System.out.println("正在执行返回通知...");
        System.out.println("目标方法的返回值是:"+obj);
    }


    /**
     * 异常通知要调用的方法
     */
    public void afterThrowing(JoinPoint point, Exception e){
        System.out.println("异常信息:"+e.getMessage());
    }

}
/**
 * 环绕通知
 */
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    //前增强
    System.out.println("正在执行前增强...");
    //调用目标方法
    Object object=proceedingJoinPoint.proceed();
    //后增强
    System.out.println("正在执行后增强...");
    return object;
}

 

spring-config.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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 目标类、切面类要放到IOC容器中 -->
    <context:component-scan base-package="com.chy.mall" />

    <!-- aop配置 -->
    <aop:config>
        <!-- 全局切入点,所有切面中都可引用。配置切入点只能用id,不能用name -->
        <!--<aop:pointcut id="pointCut" expression="execution(* com.chy.mall.service.*.*(..))" />-->

        <!-- 一个<aop:aspect>配置一个切面。指定切入点、增强 -->
        <aop:aspect ref="userServiceAspect">
            <!-- 局部切入点,只能在此切面中引用 -->
            <aop:pointcut id="pointCut" expression="execution(* com.chy.mall.service.UserService.*(..))" />

            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="pointCut" />

            <!-- 切入点也可以现配 -->
            <!-- <aop:before method="before" pointcut="execution(* com.chy.mall.service.UserService.*(..))"/>-->

            <!-- 后置通知 -->
            <aop:after method="after" pointcut-ref="pointCut" />

            <!-- 返回通知 -->
            <!-- 如果返回通知调用的方法中要使用目标方法的返回值,可以用returning指定将目标方法的返回值传递给哪个形参 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="obj"/>

            <!-- 异常通知 -->
            <!-- 如果要使用捕获的异常对象,可以用throwing指定将异常对象传递给哪个形参 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="e"/>
            
            <!-- 环绕通知 -->
            <!-- ProceedingJoinPoint是JoinPoint的子类,会自动传入 -->
            <!-- <aop:around method="around" pointcut-ref="pointCut" /> --> 
        </aop:aspect>

    </aop:config>

</beans>

 

注解配置方式

切面

@Component
@Aspect  //标识为切面
public class UserServiceAspect {

    /**
     * 配置切入点。此切入点是局部切入点,只能在当前切面中引用
     */
    @Pointcut("execution(* com.chy.mall.service.UserService.*(..))")
    private void pointCut(){}


    /**
     * 前置通知
     * 切入点均可现配  @Before("execution(* com.chy.mall.service.UserService.*(..))")
     */
    @Before("pointCut()")
    public void before(){
        System.out.println("正在执行前置通知...");
    }


    /**
     * 后置通知
     */
    @After("pointCut()")
    public void after(){
        System.out.println("正在执行后置通知...");
    }


    /**
     * 返回通知。可将目标方法的返回值传给指定形参
     */
    @AfterReturning(value = "pointCut()",returning = "obj" )
    public void afterReturning(Object obj){
        System.out.println("正在执行返回通知...");
        System.out.println("目标方法的返回值是:"+obj);
    }

    /**
     * 异常通知。可将目标方法的抛出的异常传给指定形参
     */
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void afterThrowing(JoinPoint point,Exception e){
        System.out.println("异常信息:"+e.getMessage());
    }

}
/**
 * 环绕通知
 */
@Around("pointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    //前增强
    System.out.println("正在执行前增强...");
    //调用目标方法
    Object object=proceedingJoinPoint.proceed();
    //后增强
    System.out.println("正在执行后增强...");
    return object;
}

 

spring-config.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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 组件扫描,扫猫标注bean的注解-->
    <context:component-scan base-package="com.chy.mall" />

    <!-- 启用AspectJ的注解 -->
    <aop:aspectj-autoproxy />
    
</beans>

 

总结

1、声明是声明为目标类型,注入是注入代理

@Resource(name="userService")  //注入的是代理,按名称注入
private UserService userService;  //如果代理的是接口,声明为接口类型;如果代理的目标类,声明为目标类

 
2、spring aop是组装目标、切面,aspectj是以切面为单位配置增强,将切面应用到多个切入点
 

3、 常见术语

  • Joinpoint 连接点:目标类中的所有方法都是连接点
  • Pointcut 切入点:目标类中被增强的方法
  • Weaving 织入:把增强应用到目标对象创建代理对象的过程。spring aop使用动态代理织入,aspectj在编译期、类加载期织入。
     

4、常见问题

<context:component-scan base-package=" " />

没有配置组件扫描,或者包未写全,出现bean未定义异常

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页