spring中 aop及aspect 代理使用

版权声明:本文为博主原创文章,转载请注明出处!!!!!!!!!! https://blog.csdn.net/qq_21434959/article/details/83120258

1. Spring 的AOP介绍

Spring 中 的AOP联盟为通知Advice定义了org.aoplliance.aop.Advice

Spring按照通知Advice在目标方法的连接点位置,可以分为5类:

  • 前置通知 org.springframework.aop.MethodBeforeAdvice
    在目标方法执行前实施增强
  • 后置通知 org.springframework.aop.AfterReturningAdvice
    在目标方法执行后实施增强
  • 环绕通知 org.aopalliance.intercept.MethodInterceptor
    在目标方法执行前后实施增强
  • 异常抛出通知 org.springframework.aop.ThrowsAdvice
    在方法抛出异常后实施增强
  • 引介通知 org.springframework.aop.IntroductionInterceptor
    在目标类中添加一些新的方法和属性

1.1 MethodBeforeAdvice源码分析:

/**
 * Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}.
 * Used internally by the AOP framework; application developers should not need
 * to use this class directly.
 *
 * @author Rod Johnson
 */
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

    private MethodBeforeAdvice advice;


    /**
     * Create a new MethodBeforeAdviceInterceptor for the given advice.
     * @param advice the MethodBeforeAdvice to wrap
     */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }

}

如上图源码:
invoke方法中先执行了advice 通知的方法,而后执行了目标方法。

  • 环绕通知,需要手动执行目标方法:
try{
   //前置通知
   //执行目标方法
   //后置通知
} catch(){
   //抛出异常通知
}    

其他通知请自行查看源码!!!

2. 基于aop 的切面编程

  • 让spring 创建代理对象,从spring容器中手动的获取代理对象。

2.1 demo样例如下:

需要导入的包如下:

核心:4 + 1

aop联盟(规范)、spring-aop(实现)
输入图片说明

2.2 需要编写的文件

  • beans.xml
  • AopTest.java
  • MyAspect.java
  • UserService.java
  • UserServiceImp.java

主要说下UserServiceImpl、beans.xml 、 AopTest.java、MyAspect.java:(如有不清楚,请自行查看码云源码 )

UserServiceImpl.java

public class UserServiceImp implements UserService {

    public void addUser() {
        System.out.println("d_aspect.a_xml.............a_jdk");
    }

    public void updateUser() {
        int i= 1/0;
        System.out.println("d_aspect.a_xml.............a_jdk");
    }

    public void deleteUser() {
        System.out.println("d_aspect.a_xml.............a_jdk");
    }

}

MyAspect切面类如下:

/**
 * @author liupenghao 切面类
 */
public class MyAspect implements MethodInterceptor{

    public Object invoke(MethodInvocation mi) throws Throwable {
        
        System.out.println("前3");
        Object obj = mi.proceed();
        System.out.println("后3");
        return obj;
    }
}

beans.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"
       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 definitions here -->
    
    <!--配置目标类  -->
    <bean id="UserService" class ="com.uu.c_spring_aop.UserServiceImp"></bean>
    
    <!-- 配置切面类 -->
    <bean id="myaspect" class="com.uu.c_spring_aop.MyAspect"> </bean>
    
    <!--  
    aop 编程
    1.  导入命名空间
    2. 使用<app:config> 进行配置
        proxy-target-class       声明是否使用cglib
        aop:pointcut    切入点,从哪目标对象获得具体方法
        aop:advisor     特殊的切面,只有一个通知和一个切入点
            advice-ref 通知引入
            pointcut-ref 切入点引用
    
    3. 切入点表达式
        execution(* com.uu.c_spring_aop.*.*(..))
        选择方法    返回值任意               包
            
            
    
    
    
    -->
    <aop:config  proxy-target-class="true">
        <aop:pointcut expression="execution(* com.uu.c_spring_aop.*.*(..))" id="myPointCut"></aop:pointcut>
        <aop:advisor advice-ref="myaspect" pointcut-ref="myPointCut"/>
    </aop:config>
</beans>

AopTest 如下:


public class AopTest {

    @Test
    public void Test() {

        String xmlPath = "com/uu/c_spring_aop/beans.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                xmlPath);
        UserService service = context.getBean("UserService",
                UserService.class);
        service.addUser();
        service.updateUser();
        service.deleteUser();
    }
}

2.3 基于AspectJ 的切面编程

与基于AOP编程不同的是,AspectJ提供了 按照通知Advice在目标方法的连接点位置的不同 而生成的实现类(如:AfterReturningAdviceInterceptor),用的时候只需配置通知方法即可。

可以简单的理解为:根据用户要在目标方法 实时操作的位置的不同,而实现的方法拦截器。

2.3.1 demo小案例

程序的实现需要的文件如下:

  • MyAspect.java
  • UserService.java
  • UserServiceImp.java
  • xmlTest.java
  • beans.xml

UserSerice、UserServiceImpl、xmlTest 文件和2.2的一致,不做赘述

MyAspect.javaq 如下:

public class MyAspect {

    public void myBefore(JoinPoint joinPoint) {
        System.out.println("前置通知:" + joinPoint.getSignature().getName());
    }

    public void MyAfterRetruning(JoinPoint joinPoint, Object ret) {
        System.out.println("后置通知: " + joinPoint.getSignature().getName() + ","
                + ret);
    }

    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println(" 前");
        Object obj = joinPoint.proceed();
        System.out.println("后");
        return obj;
    }

    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("抛出异常 通知:"+ e.getMessage());
    }
    
    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知");
    }
    
}

beans.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"
    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">
    <!-- 1 创建目标类 -->
    <bean id="userServiceId" class="com.uu.d_aspect.a_xml.UserServiceImp"></bean>
    <!-- 2 创建切面类(通知) -->
    <bean id="myAspectId" class="com.uu.d_aspect.a_xml.MyAspect"></bean>
    <!-- 3 aop编程 <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法) ref 切面类引用 <aop:pointcut> 
        声明一个切入点,所有的通知都可以使用。 expression 切入点表达式 id 名称,用于其它通知引用 -->

    <aop:config>
        <aop:aspect ref="myAspectId">
            <aop:pointcut
                expression="execution(* com.uu.d_aspect.a_xml.UserServiceImp.*(..))"
                id="myPointCut" />
            <!-- 前置通知: method: 通知,及方法名 pointcut: 切入点表达式 pointcut-ref: 切入点引用 -->
            <!-- <aop:before method="myBefore" pointcut-ref="myPointCut" /> -->

            <!-- <aop:after-returning method="MyAfterRetruning" pointcut-ref="myPointCut" 
                returning="ret"/> -->

            <!-- <aop:around method="myAround" pointcut-ref="myPointCut" /> -->

            <aop:after-throwing method="myAfterThrowing"
                pointcut-ref="myPointCut" throwing="e" />
            <aop:after method="myAfter" pointcut-ref="myPointCut" />
        </aop:aspect>
    </aop:config>
</beans>

2.4 基于注解的配置

与基于xml配合相比,注解配置较为简单,可以做到强耦合,基于method进行切面的编程。再加上spring 强大的IOC(控制反转),DI(依赖注入),代码在实现功能的基础上,变得简洁明了,完美!!!

2.4.1demo小案例

所需的文件如下:

  • beans.xml
  • AnnoTest.java
  • MyAspect.java
  • UserService.java
  • UserServiceImp.java

UserServiceImpl.java

@Service("userServiceId")
public class UserServiceImp implements UserService {

    public void addUser() {
        System.out.println("d_aspect.b_anno.............a_jdk");
    }

    public void updateUser() {
        //int i= 1/0;
        System.out.println("d_aspect.b_anno.............a_jdk");
    }

    public void deleteUser() {
        System.out.println("d_aspect.b_anno.............a_jdk");
    }

}

MyAspect.java

@Component
@Aspect
public class MyAspect {
    
    @Pointcut("execution(public * com.uu.d_aspect.b_anno.UserServiceImp.*(..))")
    public void myPointCut(){
        
    }

    @After("myPointCut()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知");
    }
    
}

beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 1. 扫描 注解类 -->
    <context:component-scan base-package="com.uu.d_aspect.b_anno"></context:component-scan>
    <!-- 2. 确定 aop 注解生效 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

3. 源码地址:

GitHub-下载地址

猜你喜欢

转载自blog.csdn.net/qq_21434959/article/details/83120258
今日推荐