SpringFramework-AOP面向切面编程

AOP

对于代理模式而言,所创建的代理类是去代理执行和调用目标类的方法,而不是去重新实现目标类方法,它只是对目标类的一种增强。虽然在技术实现上代理类是通过继承目标类或实现目标类接口来创建的代理类,但是在实际调用过程当中,对于目标方法的最终执行依旧是所传入的目标对象

静态代理

代理模式实际上是通过调用代理的相关方法,再由代理去调用实际对象的方法

可以把代理模式看作是对实际对象的一种封装,由代理对象对实际对象进行一些功能上的增强(如添加日志)

根据这种设计思路,那么在设计代理类时,就需要将目标类的实例对象传递到代理类的实例中,通过调用代理实例的方法来间接调用目标类的方法。从而实现代理的功能

目标类的接口

public interface Calculator {
    public int add(int a, int b);
    public int subtract(int a, int b);
    public int multiply(int a, int b);
    public int divide(int a, int b);
}

目标类

public class CalculatorImpl implements Calculator {

    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int subtract(int a, int b) {
        return a-b;
    }

    @Override
    public int multiply(int a, int b) {
        return a*b;
    }

    @Override
    public int divide(int a, int b) {
        return a/b;
    }
}

代理类

public class CalculatorHandler implements Calculator {

    private Calculator calculator;

    public CalculatorHandler(CalculatorImpl calculator) {
        this.calculator = calculator;
    }

    @Override
    public int add(int a, int b) {
        System.out.println(a+"+"+b+"="+calculator.add(a,b));
        return calculator.add(a, b);
    }

    @Override
    public int subtract(int a, int b) {
        System.out.println(a+" -"+b+"="+calculator.subtract(a,b));
        return calculator.subtract(a, b);
    }

    @Override
    public int multiply(int a, int b) {
        System.out.println(a+" *"+b+"="+calculator.multiply(a,b));
        return calculator.multiply(a, b);
    }

    @Override
    public int divide(int a, int b) {
        System.out.println(a+" /"+b+"="+calculator.divide(a,b));
        return calculator.divide(a, b);
    }
}

测试类

public class CalculatorProxyTest {
    @Test
    public void testCalculatorProxy() {
        CalculatorImpl calculatorImpl = new CalculatorImpl();
        Calculator calculatorHandler = new CalculatorHandler(calculatorImpl);
        calculatorHandler.add(1,2);
        calculatorHandler.subtract(1,2);
        calculatorHandler.multiply(1,2);
        calculatorHandler.divide(1,2);
    }
}

动态代理

jdk动态代理

JDK动态代理实现的基本逻辑是实现目标类接口,然后利用Java的反射机制,在运行时动态生成代理类,通过代理类调用被代理对象的方法,并在invoke方法中添加额外的逻辑

需要代理类的接口

public interface UserService {
    public void work();
}

需要代理的类

public class UserServiceImpl implements UserService {
    @Override
    public void work() {
        System.out.print("i am work  ");
    }
}

代理处理器

public class UserHandler implements InvocationHandler {
    //将要代理的实体类对象
    private Object target;
    public UserHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.print("this is company  ");

        Object result = method.invoke(target, args);

        LocalDate currentDate = LocalDate.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String formattedDate = currentDate.format(formatter);
        System.out.println(formattedDate);
        return result;
    }
}

测试

public class UserHandlerTest {
    @Test
    public void test() {
        //创建需要代理的类的实例
        UserService userServiceImpl = new UserServiceImpl();
        //创建代理处理器的实例
        UserHandler userHandler = new UserHandler(userServiceImpl);
        //创建运行时代理类
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
                userServiceImpl.getClass().getClassLoader(),
                userServiceImpl.getClass().getInterfaces(),
                userHandler
        );
        userServiceProxy.work();
    }
}
  1. 创建代理类:当调用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)方法时,JDK会在运行时动态生成一个代理类,这个代理类会实现所指定的接口,并在方法调用时,将调用委托给InvocationHandler的实现对象
  2. 生成字节码:在运行时,JDK使用ProxyGenerator.generateProxyClass方法生成代理类的字节码。这个字节码包含了代理类的定义,以及方法调用
  3. 加载代理类:生成的字节码被加载到内存中,通过反射创建代理类实例
  4. 调用代理方法:当调用代理对象的方法时,实际上是调用了代理类中的方法。代理类中的方法将方法调用委托給InvocationHandlerinvoke方法。
  5. 调用处理器的invoke方法:在InvocationHandlerinvoke方法中,可以定义代理对象方法的行为。该方法接收三个参数:代理对象本身,被调用的方法对象和方法的参数数组。在invok方法中,可以对方法进行增强、修改或者添加额外的逻辑
  6. 执行原始方法:在invoke方法中,通常会调用Method对象的invoke方法来执行原始对象中对应的方法
  7. 返回结果:invoke方法返回的结果会称为代理对象方法的返回值

cglib动态代理

cglib的动态代理是在生成代理类时创建目标类的子类,并在子类中对目标方法进行拦截,在方法执行前后插入自定义逻辑实现的

目标类

public class Coder {
    public void work() {
        System.out.println("认真写bug……");
    }
}

代理类

public class AttendanceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("上班打卡……");

        Object result = proxy.invokeSuper(obj, args);

        System.out.println("下班打卡……");

        return result;
    }
}

测试类

public class CglibProxyTest {
  @Test
    public void Test() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Coder.class);
        enhancer.setCallback(new AttendanceMethodInterceptor());
        // 创建代理对象
        Object object = enhancer.create();

        Coder coder = (Coder) object;
        coder.work();
    }
}

AspectJ

是AOP思想的一种实现,本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。spring只是借用了AspectJ中的注解

基于注解的AOP

快速入门

配置文件

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

    <!--
        基于注解的aop的实现
        1.将目标对象和切面自动交给IOC容器管理(扫描+注解)
        2.开启AspectJ的自动代理,为目标对象自动生成代理
        3.将切面类通过注解@Aspect标识

    -->
    <context:component-scan base-package="com.atli.springaop"></context:component-scan>

    <!-- 开启aspectj自动代理,为目标对象生成代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

目标类接口

public interface Calculator {
    int add(int a, int b);
    int sub(int a, int b);
    int mul(int a, int b);
    int div(int a, int b);
}

目标类实现类

@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        System.out.println("this is add function running ");
        return a+b;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("this is sub function running: ");
        return a-b;
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("this is mul function running: ");
        return a*b;
    }

    @Override
    public int div(int a, int b) {
        System.out.println("this is div function running: ");
        return a/b;
    }
}

切面类

@Component//装入ioc
@Aspect//切面类
public class LogAspect {
    //设置切入点和通知类型
    //切入点表达式:execution(访问修饰符 增强方法返回类型 增强方法所在类全路径.方法名称(方法参数))

    //重用切点表达式
    @Pointcut(value = "execution(public int com.atli.springaop.CalculatorImpl.*(int,int))")
    public void pointCut() {}
    //  前置:@Before()
    @Before(value = "pointCut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("this is @Before " + joinPoint.getSignature().getName());
    }
    //  返回:@AfterReturning
    //JoinPoint获取方法名,参数等信息
    //result获取方法返回值信息
    @AfterReturning(value = "pointCut()"。returning="result")
    public void afterReturning(JoinPoint joinPoint,Object result) {
        System.out.println("this is @AfterReturning " + joinPoint.getSignature().getName());
    }
    //  异常:@AfterThrowing
    //  只有在抛出异常时才执行
    @AfterThrowing(value="pointCut()")
    public void afterThrowing(JoinPoint joinPoint) {
        System.out.println("this is @AfterThrowing " + joinPoint.getSignature().getName());
    }
    //  后置:@After()
    @After(value = "pointCut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("this is @After " + joinPoint.getSignature().getName());
    }
    // //  环绕:@Around()
    // //  注意,环绕通知和其他通知不能同时使用,
    // @Around(value = "pointCut()")
    // public void Around(JoinPoint joinPoint){
    //     System.out.println("this is @Around " + joinPoint.getSignature().getName());
    // }
}

测试类

public class AopTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Calculator calculatorImpl = (Calculator) context.getBean("calculatorImpl");
        int a = calculatorImpl.add(1, 2);
        System.out.println("this is @Test: "+a);
    }
}

相关术语

切面(Aspect):切面是横切关注点的模块化单元,它封装了与横切关注点相关的行为和逻辑。切面定义了在何处以及何时应用横切关注点。在 Spring AOP 中,切面通常是一个类,其中包含了一系列的通知(Advice)和切点(Pointcut)。

通知(Advice):通知定义了在切面的何处以及何时执行横切关注点的逻辑。在 AOP 中,有几种类型的通知,包括前置通知(Before advice)、后置通知(After advice)、返回通知(After-returning advice)、异常通知(After-throwing advice)和环绕通知(Around advice)。每种类型的通知在不同的时机执行横切关注点的逻辑。

切点(Pointcut):切点定义了在哪些连接点(Join Point)上应用横切关注点。连接点是程序执行过程中的特定位置,比如方法调用或异常抛出等。切点通过使用表达式或模式匹配规则来确定连接点的集合。

连接器(Join Point):连接器是程序执行过程中的特定位置,它是切点的实际实例。在 AOP 中,连接器表示方法调用、异常抛出等具体的执行点。

引入(Introduction):引入允许在现有的类中添加新的方法和属性。通过引入,我们可以在不修改原有代码的情况下,向现有类添加新的功能。

织入(Weaving):织入是将切面应用到目标对象中的过程。织入可以在编译时、类加载时或运行时进行。Spring AOP 使用运行时织入,它通过创建代理对象来将切面织入到目标对象中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值