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

Spring学习之路3-AOP及实现AOP的三种方式

程序员文章站 2022-07-09 20:23:31
AOP 面向切面的程序设计(Aspect oriented programming,AOP,又译作面向方面的程序设计、剖面导向程序设计)是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够 ......

aop

面向切面的程序设计(aspect-oriented programming,aop,又译作面向方面的程序设计、剖面导向程序设计)是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(advice)机制,能够对被声明为“切点(pointcut)”的代码块进行统一管理与装饰,如“对所有方法名以‘set*’开头的方法添加后台日志”。该思想使得开发人员能够将与代码核心业务逻辑关系不那么密切的功能(如日志功能)添加至程序中,同时又不降低业务代码的可读性。面向切面的程序设计思想也是面向切面软件开发的基础。

maven 依赖

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupid>org.aspectj</groupid>
    <artifactid>aspectjweaver</artifactid>
    <version>1.9.5</version>
</dependency>

实现方式一:原生 spring api 接口

public interface userservice {
    void add();
    int delete();
    void query();
    int update();
}

public class userserviceimpl implements userservice {
    @override
    public void add() {
        system.out.println("function:add()");
    }

    @override
    public int delete() {
        system.out.println("function:delete()");
        return 0;
    }

    @override
    public void query() {
        system.out.println("function:query()");
    }

    @override
    public int update() {
        system.out.println("function:update()");
        return 0;
    }
}

//方法执行前的log
public class beforelog implements methodbeforeadvice {

    @override
    public void before(method method, object[] args, object target) throws throwable {

        system.err.println("method before log" + target.getclass().getname() + method.getname());
    }
}

//方法执行后的log
public class afterlog implements afterreturningadvice {
    @override
    public void afterreturning(object returnvalue, method method, object[] args, object target) throws throwable {
        system.err.println("method before log" + target.getclass().getname() + "-->" + method.getname() + "---return:" + returnvalue);
    }
}
<?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 id="log1" class="com.youzi.log.beforelog"/>
    <bean id="log2" class="com.youzi.log.afterlog"/>
    <bean id="userservice" class="com.youzi.service.userserviceimpl"/>

    <!--配置aop-->
    <aop:config>
        <aop:pointcut id="pointcut1" expression="execution(int com.youzi.service.userserviceimpl.*(..))"/>
        <aop:pointcut id="pointcut2" expression="execution(void com.youzi.service.*.add(..))"/>
        <aop:advisor advice-ref="log1" pointcut-ref="pointcut1"/>
        <aop:advisor advice-ref="log2" pointcut-ref="pointcut2"/>
    </aop:config>
</beans>

execution() 切点函数语法:

execution(返回类型 包名.类名.方法名(参数类型))
  • 也可以用*号表示所有的类型。

  • 类名也可以用*代替表示包下面的所有类

  • *(..):这个星号表示方法名,*号表示所有的方法,括号中是方法的参数,两个点表示任何参数。

实现方式二:自定义类

第一种实现方式虽然实现了在方法前或者方法后添加日志,但是每实现一个功能就需要实现一个接口,通过下面的方法可以把这些方法写在一个类中

public class methodlog {
    public void beforemethod() {
        system.out.println("方法执行前");
    }

    public void aftermethod() {
        system.out.println("方法执行后");
    }
}
<?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 id="methodlog" class="com.youzi.log.methodlog"/>
    <bean id="userservice" class="com.youzi.service.userserviceimpl"/>

    <aop:config>
        <aop:aspect ref="methodlog">
            <aop:pointcut id="poindcut1" expression="execution(* com.youzi.service.*.*(..))"/>
            <aop:before method="beforemethod" pointcut-ref="poindcut1"/>
            <aop:after method="aftermethod" pointcut-ref="poindcut1"/>
        </aop:aspect>
    </aop:config>
</beans>

上面的方法虽然简化了代码,但是目前看来不能像第一种方式一样通过反射取到执行方法的信息

实现方式三:使用注解

<?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 id="methodlog" class="com.youzi.log.methodlog"/>
    <bean id="userservice" class="com.youzi.service.userserviceimpl"/>
    <!--在xml中声明使用注解的方式-->
    <aop:aspectj-autoproxy/>
</beans>
@aspect 
public class methodlog {
    @before("execution(* com.youzi.service.userserviceimpl.*(..))")
    public void beforemethod() {
        system.out.println("方法执行前1");
    }

    @after("execution(* com.youzi.service.userserviceimpl.*(..))")
    public void aftermethod() {
        system.out.println("方法执行后1");
    }

    @around("execution(* com.youzi.service.userserviceimpl.update(..))")
    public int aroundmethod(proceedingjoinpoint joinpoint) throws throwable {
        system.err.println("环绕前");
        system.err.println(joinpoint.getsignature());
        object proceed = joinpoint.proceed();
        system.err.println("环绕后");
        return integer.valueof((integer) proceed);
    }

}

测试代码统一为

applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");
userservice userservice = context.getbean("userservice", userservice.class);
userservice.query();
userservice.add();
userservice.delete();
userservice.update();