SpringAOP

AOP:Aspect Oriented Programming,意思为面向切面编程

面向切面编程:对于不同的模块,在具有相同共性的情况下,由切面的增强来负责统一处理;其本质就是动态代理

相对于OOP(面向对象)来说,AOP对于过程的管理更加的精细,能够进一步来完成解耦工作,在程序运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想,将不同的方法的同一个位置抽象成一个切面对象

AOP的特点:

  • 降低模块之间的耦合度。

  • 使系统更容易扩展。

  • 更好的代码复用。

  • 非业务代码更加集中,不分散,便于统一管理。

  • 业务代码更加简洁纯粹,不参杂其他代码的影响。

AOP相关术语:

  • 增强(Advice)
  • 切入点(Pointcut)
  • 连接点(Joinpoint)
  • 切面(Aspect)
  • 代理(Proxy)
  • 目标对象(Target)
  • 织入(Weaving)

动态代理

场景:苹果手机与苹果电脑,华为手机与华为电脑的销售

接口:

package com.m.staticproxy;

public interface Phone {
    public String salePhone();
}

package com.m.staticproxy;

public interface Computer {
    public String saleComputer();
}

实现类:

package com.m.staticproxy.impl;

import com.m.staticproxy.Computer;
import com.m.staticproxy.Phone;

public class Apple implements Phone,Computer {
    @Override
    public String salePhone() {
        return "销售Apple手机";
    }

    @Override
    public String saleComputer() {
        return "销售Apple电脑";
    }
}

package com.m.staticproxy.impl;

import com.m.staticproxy.Computer;
import com.m.staticproxy.Phone;

public class HuaWei implements Phone,Computer {
    @Override
    public String salePhone() {
        return "销售HuaWei手机";
    }

    @Override
    public String saleComputer() {
        return "销售HuaWei电脑";
    }
}

MyInvocationHandler类:

package com.m.staticproxy.proxy;

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

public class MyInvocationHandler implements InvocationHandler {
    private Object proObject;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object bind(Object object) {
        this.proObject = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.proObject);
        System.out.println(name+"卖的好,这个"+name+"物美价廉,赶快来抢购吧!");
        System.out.println(result);
        System.out.println(method.getName()+"方法的结果是"+result);
        return result;
    }
}

测试类:

package com.m.staticproxy;

import com.m.staticproxy.Phone;
import com.m.staticproxy.impl.Apple;
import com.m.staticproxy.impl.HuaWei;
import com.m.staticproxy.proxy.MyInvocationHandler;
import com.m.staticproxy.proxy.PhoneProxy;

public class Test {
    public static void main(String[] args) {
        Phone phone1 = new Apple();

        Phone phone2 = new HuaWei();

        Computer computer1 = new Apple();

        Computer computer2 = new HuaWei();

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();

        myInvocationHandler.setName(phone1.salePhone().substring(2));
        phone1 = (Phone) myInvocationHandler.bind(phone1);
        phone1.salePhone();

        myInvocationHandler.setName(phone2.salePhone().substring(2));
        phone2 = (Phone) myInvocationHandler.bind(phone2);
        phone2.salePhone();

        myInvocationHandler.setName(computer1.saleComputer().substring(2));
        computer1 = (Computer) myInvocationHandler.bind(computer1);
        computer1.saleComputer();

        myInvocationHandler.setName(computer2.saleComputer().substring(2));
        computer2 = (Computer) myInvocationHandler.bind(computer2);
        computer2.saleComputer();

    }
}

用静态代理得写手机代理类和电脑代理类,动态代理用的是多态思想,一个类搞定。

Spring-aop

在Spring框架中,我们不需要创建动态代理类,只需要创建一个切面类,由该切面类产生的对象就是切面对象,可以将非业务代码写入到切面对象中,再切入到业务方法中,Spring框架底层会自动根据切面类以及目标类生成一个代理对象。

1.配置pom环境

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.8.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.8.RELEASE</version>
    </dependency>

2.spring-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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.3.xsd
">

    <!-- 自动扫码 -->
    <context:component-scan base-package="com.m"></context:component-scan>

    <!-- 使Aspect注解生效,为委托类自动生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

目标类需要添加@Component注解

package com.m.action_proxy.impl;

import com.m.action_proxy.Cal;
import org.springframework.stereotype.Component;

@Component
public class CalImpl implements Cal {
    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public int sub(int num1, int num2) {
        return num1 - num2;
    }

    @Override
    public int mul(int num1, int num2) {
        return num1 * num2;
    }

    @Override
    public int div(int num1, int num2) {
        return num1 / num2;
    }
}

LoggerAspect类定义处添加了两个注解:

  • @Aspect:表示该类是切面类。
  • @Component:将该类注入到IoC容器中。

3.创建LoggerAspect类

package com.m.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LoggerAspect {

    @Before(value = "execution(public int com.m.action_proxy.impl.CalImpl.*(..))")
    public void before(JoinPoint joinPoint){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        //获取参数列表
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name+"的参数是:"+args);
    }


    @After("execution(public int com.m.action_proxy.impl.CalImpl.*(..))")
    public void after(JoinPoint joinPoint){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法结束");
       
    }

    @AfterReturning(value = "execution(public int com.m.action_proxy.impl.CalImpl.*(..)))",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法的结果是:"+result);
        System.out.println("---------------------------------");
    }

    @AfterThrowing(value = "execution(public int com.m.action_proxy.impl.CalImpl.*(..)))",throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint,Exception exception){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name+"方法抛出异常:"+exception);
    }
}

方法注解:

  • @Before:表示切面方法执行的时机是业务方法执行之前。

  • @After:表示切面方法执行的时机是业务方法执行之后。

  • @AfterReturning:表示切面方法执行的时机是业务方法return之后。

  • @AfterThrowing:表示切面方法执行的时机是业务方法抛出异常之后。

  • execution(public int com.southwind.util.CalImpl.*(..)):表示切入点是com.southwind.util包下CalImpl类中的所有方法,即CalImpl所有方法在执行时会优先执行切面方法。






备注:最近来手感,写了个类似Tomcat服务

github地址:https://github.com/cnamep001/my_tomcat.git