欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring-AOP(面向切面编程)

程序员文章站 2022-04-15 11:13:31
AOP是针对面向对象编程的一种补充,有时使用面向对象不能很好完成一些额外的功能业务时,可以采用AOP来进行补充。 AOP术语: 切面(Aspect) 切面是用于编写切面逻辑的一个类,这个类很类似于JDK动态代理中的回调处理器或者cglib中的方法拦截器,主要就是将需要增强目标对象的功能代码编写在这个 ......

aop是针对面向对象编程的一种补充,有时使用面向对象不能很好完成一些额外的功能业务时,可以采用aop来进行补充。

aop术语:

  1. 切面(aspect)

    切面是用于编写切面逻辑的一个类,这个类很类似于jdk动态代理中的回调处理器或者cglib中的方法拦截器,主要就是将需要增强目标对象的功能代码编写在这个类中,而这些功能增强的代码就是切面逻辑。

  2. 切入点(pointcut)

    切入点类似一个切入的坐标,目的就是要找到目标对象的哪些方法。

    通常切入点都是以一种表达式的形式来描述

  3. 通知/增强(advice)

    通知就是切面中具体的增强逻辑,总共分为五种:

    1)前置通知(在目标方法调用之前执行)

    2)后置通知(在目标方法正确返回之后执行)

    3)环绕通知(在目标方法调用前后执行)

    4)异常通知(当目标方法抛出异常时执行,并且不会执行后置通知)

    5)最终通知(不管目标方法有无异常都会执行)

  4. 连接点(joinpoint)

    目标对象的方法就称之为连接点,一个切入点可以对应目标对象的的多个连接点。

  5. 代理(proxy)

    在运行时动态创建的对象,称之为代理对象,负责调用目标对象的方法,并执行增强功能

  6. 目标(target)

    被代理的对象就是目标对象

  7. 织入(weaver)

    将切面中的通知应用到目标对象上并且产生代理的过程称之为织入。

    因此通常描述为“将通知织入到具体的目标”。


 

项目结构:

spring-AOP(面向切面编程)

 

代码示例:

applicationcontext.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">

    <!-- 装备userserviceimpl -->
    <bean id="userservice" class="edu.nf.ch11.service.impl.userserviceimpl"/>

    <!-- 装配自定义的切面-->
    <bean id="userserviceaspect" class="edu.nf.ch11.service.aspect.userserviceaspect"/>

    <!-- 配置aop, proxy-target-class用于设置是否强制使用cglib进行动态代理
         true表示强制使用,false则表示spring会根据目标对象有无实现接口来决定
         使用jdk动态代理还是cglib代理。默认值就是false-->
    <aop:config>
        <!-- 配置切入点,id属性给切入点定义一个唯一标识,expression用于编写切入点表达式-->
        <!-- execution表达式的使用:切入范围是在方法级别-->
        <!-- 表达式语法[访问修饰符] 返回值类型 [完整类名].方法名(参数)-->
        <!-- *号表示通配所有,方法的参数可以使用".."来代表任意个数和类型的参数-->
        <aop:pointcut id="mycut" expression="execution(* edu.nf.ch11.service.*.*(..))"/>

        <!-- within表达式的使用:切入范围是在类级别-->
        <!--<aop:pointcut id="mycut" expression="within(edu.nf.ch11.service.impl.*)"/>-->

        <!-- 引用上面装配的切面类的id -->
        <aop:aspect ref="userserviceaspect">
            <!-- 配置具体的通知方法,method指定通知的方法名
            pointcut-ref引用上面定的切入点的id,也可以通过pointcut来编写相应的切入点表达式 -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="mycut"/>
            <!-- 环绕通知-->
            <aop:around method="around" pointcut-ref="mycut"/>
            <!-- 后置通知,returning指定后置通知的参数名,用于获取目标方法的返回值-->
            <aop:after-returning method="afterreturn" pointcut-ref="mycut" returning="returnval"/>
            <!-- 异常通知,如果要获取目标方法抛出的异常对象,需要指定throwing属性,value对应异常通知的参数名-->
            <aop:after-throwing method="throwadvice" pointcut-ref="mycut" throwing="e"/>
            <!-- 最终通知-->
            <aop:after method="after" pointcut-ref="mycut"/>
        </aop:aspect>

    </aop:config>

</beans>

 

切面(增强)类:

package edu.nf.ch11.service.aspect;

import org.aspectj.lang.joinpoint;
import org.aspectj.lang.proceedingjoinpoint;
import org.aspectj.lang.signature;
import org.aspectj.lang.reflect.methodsignature;

import javax.xml.crypto.dsig.signaturemethod;

/**
 * @author wangl
 * @date 2018/10/23
 * 编写一个userservice的切面
 * 在切面中编写的方法都称之为通知或者是增强
 * 在aop当中,通知有五种类型
 * 1. 前置通知
 * 2. 后置通知
 * 3. 环绕通知
 * 4. 最终通知
 * 5. 异常通知
 * 在一个切面中,任何一种类型的通知都可以定义多个,当有多个通知存在的时候
 * 就会形成一个通知栈
 */
public class userserviceaspect {

    /**
     * 前置通知
     * 在执行目标方法之前执行,拦截目标方法的参数
     * 可以通过joinpoint获得目标方法的参数信息
     */
    public void before(joinpoint joinpoint){
        system.out.println("前置通知...");
        //通过连接点获得目标对象
        //joinpoint.gettarget();
        //获取目标方法的参数信息
        object[] params = joinpoint.getargs();
        for (object param : params) {
            system.out.println(param);
        }
    }

    /**
     * 后置通知
     * 在目标方法执行完并且return之后执行
     */
    public void afterreturn(string returnval){
        system.out.println("后置通知..."+returnval);
    }

    /**
     * 环绕通知
     * @param pjp 连接点处理器,由它负责调用目标对象的具体方法
     *
     */
    public object around(proceedingjoinpoint pjp) throws throwable {
        system.out.println("环绕通知前...");
        //调用目标对象的方法
        object returnval = pjp.proceed();
        //获取目标方法的参数
        system.out.println(pjp.getargs()[0]);
        //通过methodsignature获取连接点方法信息
        methodsignature ms = (methodsignature)pjp.getsignature();
        //获取正在调用的目标方法
        system.out.println(ms.getmethod().getname());
        //获取目标方法的返回类型
        system.out.println(ms.getreturntype());
        system.out.println("环绕通知后...");
        return returnval;
    }

    /**
     * 异常通知
     * @param e 获取目标方法抛出的异常对象
     */
    public void throwadvice(throwable e){
        system.out.println("异常通知..."+e.getmessage());
    }

    /**
     * 最终通知
     */
    public void after(){
        system.out.println("最终通知...");
    }
}

 

 

 userservice接口:

package edu.nf.ch11.service;

/**
 * @author wangl
 * @date 2018/10/23
 */
public interface userservice {

    /**
     * 添加用户
     */
    void adduser();

    /**
     * 删除用户
     * @param uid
     */
    void deleteuser(string uid);

    /**
     * 查询用户
     * @param uid
     * @return
     */
    string getusernamebyid(string uid);
}

 

userserviceimpl实现类:

package edu.nf.ch11.service.impl;

import edu.nf.ch11.service.userservice;

/**
 * @author wangl
 * @date 2018/10/23
 * 目标对象(被代理的对象)
 * spring的aop中需要代理的所有目标对象都应该归纳在ioc容器中管理
 */
public class userserviceimpl implements userservice {

    @override
    public void adduser() {
        system.out.println("保存用户信息");
    }

    @override
    public void deleteuser(string uid) {
        system.out.println("删除用户,id:" + uid);
    }

    @override
    public string getusernamebyid(string uid) {
        system.out.println("根据id查询用户名");
        //system.out.println(10/0);
        return "user1";
    }
}

 

程序测试类:

package edu.nf.ch11.test;

import edu.nf.ch11.service.userservice;
import org.junit.test;
import org.springframework.context.applicationcontext;
import org.springframework.context.support.classpathxmlapplicationcontext;

/**
 * @author wangl
 * @date 2018/10/23
 */
public class userserviceaspecttest {

    @test
    public void testadduser(){
        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");
        //这里从容器中获取的对象是一个代理对象
        userservice service = context.getbean("userservice", userservice.class);
        //service.adduser();
        //system.out.println("----------------------");
        //service.deleteuser("10001");
        //system.out.println("----------------------");
        service.getusernamebyid("10001");
    }
}

 

 运行结果

spring-AOP(面向切面编程)