SpringAOP基础学习笔记

引子:在实际给别人做业务时,对业务的的要求是一直存在变化的

  • 代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀。每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。
  • 代码分散:以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码。如果日志需求发生变化,必须修改所有模块。

可以使用代理的方式解决上述问题:
代理设计模式原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
两种代理方式:

  • 静态代理:
    优点:通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。
    缺点:虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
    1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
    (1)只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
    (2)新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
    2、当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。

  • 动态代理:根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。

动态代理分两种:

1.通过实现接口的方式 ->JDK动态代理

案例:对计算器进行动态代理
在这里插入图片描述
JiSuanQi:

package 计算器;

public interface JiSuanQi {
    public int add(int a,int b);
    public int jian(int a,int b);
    public int cheng(int a,int b);
    public int chu(int a,int b);
}

JiSuanQiImpl:

package 计算器;

public class JiSuanQiImpl implements JiSuanQi {
    public int add(int a, int b) {
        return a+b;
    }

    public int jian(int a, int b) {
        return a-b;
    }

    public int cheng(int a, int b) {
        return a*b;
    }

    public int chu(int a, int b) {
        return a/b;
    }
}

JiSuanQiProxy:

package 计算器;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JiSuanQiProxy {
    //被代理的对象
    Object target;
    public JiSuanQiProxy(Object target){
        this.target=target;
    }
    //通过方法返回一个代理对象
    public Object getObject() {
        //获取被代理的对象的类加载器
        ClassLoader loader = target.getClass().getClassLoader();
        //JDK动态代理可以代理多个接口
        Class[] interfaces={JiSuanQi.class};
        //在匿名内部类中,可以对方法进行标注等处理,比如写日志
        InvocationHandler h=new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName()+"被执行之前");
                Object o=method.invoke(target,args);
                System.out.println(method.getName()+"被执行之后");
                return o;
            }
        };
        Object o = Proxy.newProxyInstance(loader, interfaces, h);
        return o;
    }
}

在这里插入图片描述

2.通过继承类的方式 -> CGLIB动态代理

在这里插入图片描述
PersonService:

package aop.cglib;

public class PersonService {

    public PersonService(){}
    public void setPerson(){

    }
    public void getPerson(){

    }
}

CglibProxyIntercepter:

package aop.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyIntercepter implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//        Object o是代理对象
        System.out.println("方法执行前");
//        method.invoke(o,objects);
//        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("方法执行后");

        return null;
    }
}

在这里插入图片描述

应用AOP技术:

AOP(面向切面(aspect)编程)是对传统OOP(面向对象编程)的补充。

AOP的好处:

1、每个事务逻辑位于同一位置,不分散,便于维护和升级
2、业务模块更简洁,只包含核心业务代码

在计算器案例中应用AOP技术:

在web.xml中导入所需jar包

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.9.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.13</version>
        </dependency>

在这里插入图片描述
JiSuanQi:

package aop;
public interface JiSuanQi {
    public int add(int a, int b);
    public int mul(int a, int b);
    public int cheng(int a, int b);
    public int chu(int a, int b);
}

JiSuanQiImpl:

package aop;
import org.springframework.stereotype.Service;
//将类放在IOC容器中
@Service
public class JiSuanQiImpl implements JiSuanQi {

    public int add(int a, int b) {
        int result=a+b;
        return result;
    }

    public int mul(int a, int b) {
        int result=a-b;
        return result;
    }

    public int cheng(int a, int b) {
        int result=a*b;
        return result;
    }

    public int chu(int a, int b) {
        int result=a/b;
        return result;
    }
}

AspectjTest:

package aop;

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

//将切面放在IOC容器中
@Component
//声明这是一个切面类
@Aspect
public class AspectjTest {

    //添加Before注解,在目标方法执行之前执行此方法
    @Before(value = "execution(public int aop.JiSuanQiImpl.add(int,int))")
    public  void before(){
        System.out.println("目标方法执行之前");
    }
    //添加After注解,在目标方法执行之后执行此方法
    @After(value = "execution(public int aop.JiSuanQiImpl.add(int,int))")
    public  void after(){
        System.out.println("目标方法执行之后");
    }


}

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

    <context:component-scan base-package="aop"></context:component-scan>
    <bean id="jiSuanQi" class="aop.JiSuanQiImpl"/>
    <!--启用spring 支持 aop注解-->
    <aop:aspectj-autoproxy/>
</beans>

在这里插入图片描述

AOP更多使用的技巧

优化@Before

对于此类注解

@Before(value = “execution(public int aop.JiSuanQiImpl.add(int,int))”)

当我们的目标方法不止一个时,我们可以使用通配符,方法类型和方法名用*来代替,传参类型用…来代替

@Before(value = “execution(* aop.JiSuanQiImpl.*(…))”)

要是在每个方法上面都要添加这个注解很麻烦,可以使用抽取切入点表达式的方法来简化代码

package aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//将切面放在IOC容器中
@Component
//声明这是一个切面类
@Aspect
public class AspectjTest {

    //抽取切入点表达式
    @Pointcut(value = "execution(* aop.JiSuanQiImpl.*(..))")
    public void cut(){}

    //添加Before注解,在目标方法执行之前执行此方法
    @Before(value = "cut()")
    public  void before(JoinPoint point){
        System.out.println(point.getSignature().getName()+"目标方法执行之前");
    }
    //添加After注解,在目标方法执行之后执行此方法
    @After(value = "cut()")
    public  void after(JoinPoint point){
        System.out.println(point.getSignature().getName()+"目标方法执行之后");
    }


}


在这里插入图片描述

返回通知和异常通知的注解

    @AfterReturning(value = "cut()",returning = "object")
    public void returnResult(JoinPoint joinPoint,Object object){
        System.out.println(joinPoint.getSignature().getName()+"返回结果后执行  返回值"+object);
    }
    @AfterThrowing(value = "cut()",throwing = "e")
    public void returnException(Exception e){
        System.out.println("异常通知:"+e);
    }

环绕通知:

环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是JoinPoint的子接口,允许控制何时执行,是否执行连接点。
在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用joinPoint.proceed();的返回值,否则会出现空指针异常。

设置切面优先级

@Order(1)

可以不在类上加注解,可以将类放在IOC容器中,在IOC容器中对类进行标注操作

    <bean id="aspectjTest2" class="aop.AspectjTest2"/>
    <aop:config>
        <aop:aspect ref="aspectjTest2">
            <aop:pointcut id="add" expression="execution(public int aop.JiSuanQiImpl.add(int,int))"/>
            <aop:before method="before" pointcut-ref="add"/>
            <aop:after method="after" pointcut-ref="add"/> 
        </aop:aspect>
    </aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值