AOP面向切面编程

底层原理: 动态代理

动态代理的核心思想: 借助父类或接口的多态性特点, 在形式上, 通过调用原来存在的父类或接口中的方法, 而实际执行的是子类或实现类的代理类中的增强方法

JDK动态代理(针对接口)

public class JDKProxyMain {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();

        // Proxy.newProxyInstance()即为动态字节码技术,直接写入到JVM中,而不需要类加载器来加载字节码文件
        // 之所以这里仍然需要传入一个类加载器,是因为需要类加载器用于创建Class对象,任意一个类加载器即可
        // 而.class字节码会由JVM自动分配类加载器,但是动态代理则需要手动提供一个类加载器
        UserService proxy = (UserService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() {
            // proxy: 代表Proxy.newProxyInstance()所创建的代理对象,基本上不使用
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("JDKProxy做一些事情");
                Object result = method.invoke(userService, args);
                return result;
            }
        });

        proxy.login("root", "123456");

    }
}

CGLIB动态代理(针对类)

public class CglibProxyMain {
    public static void main(String[] args) {
        UserServiceImpl userServiceImpl = new UserServiceImpl();

        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(ClassLoader.getSystemClassLoader());
        // 设置原始类(假设其没有实现接口)
        enhancer.setSuperclass(userServiceImpl.getClass());
        // 设置方法增强(额外功能)
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglib输出");
                Object result = method.invoke(userServiceImpl, objects);
                return result;
            }
        });

        UserServiceImpl userServiceImplProxy = (UserServiceImpl) enhancer.create();
        userServiceImplProxy.login("root", "987654");
    }
}

切入点表达式

语法结构

  • execution(<权限修饰符和返回类型> <全类名>.<方法名称>(<参数列表>))
  • @annotation(<注解的全类名>)

注意第一个 * 同时表示权限修饰符和返回值类型没有要求

参数列表中使用 .. 来表示对参数没要求,而不是继续使用通配符 *
如果对参数有要求,只需要设置参数类型即可,例如 (String, int)(String, ..)。自定义的类型需要使用全限定名

全类名中使用 .. 来表示一级子包或多级子包

package dao;

public interface UserDao {
    private int add(int x, int y);
    public String update(String id);
}

案例1:方法切入点

com.atguigu.dao.UserDao类里面的add()进行增强

切入点表达式: execution(* com.atguigu.dao.UserDao.add(..))

*表示任意, 可以是任意修饰符, 任意返回类型, 任意类名, 任意方法…

案例2:类切入点

com.atguigu.dao.UserDao类里面的所有方法进行增强

切入点表达式: execution(* com.atguigu.dao.UserDao.*(..))

案例3:包切入点

com.atguigu.dao包里面的所有类, 类中的所有方法进行增强

切入点表达式: execution(* com.atguigu.dao.*.*(..))

重用切入点(优化)

  1. 新建方法, 使用@Pointcut来抽取出公共的切入点
  2. 在通知中为value属性赋值上面的方法即可提取该方法中的切入点表达式
    //用来保存切入点表达式的函数
    @Pointcut(value = "execution(* dao.UserDaoImpl.add())")
    private final void pointcut() {
    }


    /**
     * 通过方法名提取切入点b
     */
    @Before(value = "pointcut()")
    public void before() {
        System.out.println("before()前置增强执行中...");
    }

切入点案例演示

案例一:方法表达式

import lombok.Data;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;

import java.lang.reflect.Method;

public class MyPointCutV1 {

    @Data
    static class Person {
        private String name;
        private int age;

        public void hello() {
            System.out.println("hello, world");
        }

        public void hi() {
            System.out.println("hi, world");
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //1. 创建一种切入点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();

        //2. 设置要匹配的切入点表达式
        pointcut.setExpression("execution(* hello())");

        //3. 准备测试方法
        Method helloMethod = Person.class.getMethod("hello");
        Method hiMethod = Person.class.getMethod("hi");

        //4. 用切入点表达式去匹配方法
        boolean b1 = pointcut.matches(helloMethod, Person.class);
        boolean b2 = pointcut.matches(hiMethod, Person.class);

        //5. 查看匹配结果
        System.out.println(b1);//true
        System.out.println(b2);//false
    }
}

案例二:注解表达式

import lombok.Data;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

public class MyPointCutV2 {
    @Data
    static class Person {
        private String name;
        private int age;

        @Transactional
        public void hello() {
            System.out.println("hello, world");
        }

        public void hi() {
            System.out.println("hi, world");
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //1. 创建一种切入点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();

        //2. 设置要匹配的切入点表达式
        pointcut.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");

        //3. 准备测试方法
        Method helloMethod = Person.class.getMethod("hello");
        Method hiMethod = Person.class.getMethod("hi");

        //4. 用切入点表达式去匹配方法
        boolean b1 = pointcut.matches(helloMethod, Person.class);
        boolean b2 = pointcut.matches(hiMethod, Person.class);

        //5. 查看匹配结果
        System.out.println(b1);//true
        System.out.println(b2);//false
    }
}

案例三:自定义匹配规则

import lombok.Data;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

public class MyPointCutV3 {
    @Data
    @Transactional
    static class Person {
        private String name;
        private int age;

        public void hello() {
            System.out.println("hello, world");
        }

        public void hi() {
            System.out.println("hi, world");
        }
    }


    public static void main(String[] args) throws NoSuchMethodException {
        //1. 自定义匹配规则
        // 对于一个特定的注解就需要一个实现类
        StaticMethodMatcherPointcut pointcut = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 获取方法上的注解
                MergedAnnotations annotations = MergedAnnotations.from(method);
                //1.1 判断方法上是否有@Transactional注解
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                //1.2 判断类上是否有@Transactional注解
                //MergedAnnotations.SearchStrategy.TYPE_HIERARCHY是搜索继承树上的类和接口是否含有@Transactional注解
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }

                return false;
            }
        };


        //3. 准备测试方法
        Method helloMethod = Person.class.getMethod("hello");
        Method hiMethod = Person.class.getMethod("hi");

        //4. 用切入点表达式去匹配方法
        boolean b1 = pointcut.matches(helloMethod, Person.class);
        boolean b2 = pointcut.matches(hiMethod, Person.class);

        //5. 查看匹配结果
        System.out.println(b1);//true
        System.out.println(b2);//true
    }
}

切面案例演示

案例一

import lombok.Data;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 由@Aspect注解标注(高级切面)
 */
@Aspect
public class MyAspectV1 {

    @Configuration
    static class Config {

        /**
         * Advisor低级切面
         *
         * @param advice
         * @return
         */
        @Bean
        public Advisor advisor(MethodInterceptor advice) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* hello(..))");
            //通过切入点和增强来创建切面
            return new DefaultPointcutAdvisor(pointcut, advice);
        }

        @Bean
        public MethodInterceptor advice() {
            return invocation -> {
                System.out.println("########before##########");
                Object result = invocation.proceed();
                System.out.println("########after##########");
                return result;
            };
        }
    }

    @Data
    static class Person {
        private String name;
        private int age;

        public void hello() {
            System.out.println("hello, world");
        }

        public void hi() {
            System.out.println("hi, world");
        }
    }


    @Pointcut("execution(* hello(..))")
    private void helloPointCut() {
    }

    /**
     * 每个增强会对应一个低级切面Advisor(InstantiationModelAwarePointcutAdvisor)
     *
     * @param joinPoint
     * @return
     */
    @Around(value = "helloPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 原始方法之前执行
        System.out.println("around: 功能执行之前");

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        //原始方法之后执行
        System.out.println("around: 功能执行之后");

        return result;
    }

    /**
     * 每个增强对生成一个低级切面(InstantiationModelAwarePointcutAdvisor)
     */
    @Before(value = "helloPointCut()")
    public void before() {
        // 原始方法之前执行
        System.out.println("before: 功能执行之前");
    }

    public static void main(String[] args) throws Exception {
        GenericApplicationContext applicationContext = new GenericApplicationContext();
        applicationContext.registerBean("aspect", MyAspectV1.class);
        applicationContext.registerBean("person", Person.class);
        applicationContext.registerBean("config", Config.class);
        //识别@Configuration注解
        applicationContext.registerBean(ConfigurationClassPostProcessor.class);
        //识别@Aspect高级切面相关的注解, 两个连接点: 依赖注入之前或者初始化之后
        applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
        applicationContext.refresh();

        // 通过反射去调用protect方法
        AnnotationAwareAspectJAutoProxyCreator creator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method method = creator.getClass().getDeclaredMethod("findCandidateAdvisors");
        method.setAccessible(true);
        List<Advisor> advisors = (List<Advisor>) method.invoke(creator);
        advisors.forEach(System.out::println);
    }
}

案例二

import lombok.Data;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Method;

/**
 * 由@Aspect注解标注(高级切面)
 */
@Aspect
public class MyAspectV2 {

    @Configuration
    static class Config {

        /**
         * Advisor低级切面
         *
         * @param advice
         * @return
         */
        @Bean
        public Advisor advisor(MethodInterceptor advice) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* hello(..))");
            //通过切入点和增强来创建切面
            return new DefaultPointcutAdvisor(pointcut, advice);
        }

        @Bean
        public MethodInterceptor advice() {
            return invocation -> {
                System.out.println("########before##########");
                Object result = invocation.proceed();
                System.out.println("########after##########");
                return result;
            };
        }
    }

    @Data
    static class Person {
        private String name;
        private int age;

        public void hello() {
            System.out.println("hello, world");
        }

        public void hi() {
            System.out.println("hi, world");
        }
    }


    @Pointcut("execution(* hello(..))")
    private void helloPointCut() {
    }

    /**
     * 每个增强会对应一个低级切面Advisor(InstantiationModelAwarePointcutAdvisor)
     *
     * @param joinPoint
     * @return
     */
    @Around(value = "helloPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 原始方法之前执行
        System.out.println("around: 功能执行之前");

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }

        //原始方法之后执行
        System.out.println("around: 功能执行之后");

        return result;
    }

    /**
     * 每个增强对生成一个低级切面(InstantiationModelAwarePointcutAdvisor)
     */
    @Before(value = "helloPointCut()")
    public void before() {
        // 原始方法之前执行
        System.out.println("before: 功能执行之前");
    }

    public static void main(String[] args) throws Exception {
        GenericApplicationContext applicationContext = new GenericApplicationContext();
        applicationContext.registerBean("aspect", MyAspectV2.class);
        applicationContext.registerBean("person", Person.class);
        applicationContext.registerBean("config", Config.class);
        //识别@Configuration注解
        applicationContext.registerBean(ConfigurationClassPostProcessor.class);
        //识别@Aspect高级切面相关的注解, 两个连接点: 依赖注入之前或者初始化之后
        applicationContext.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
        applicationContext.refresh();

        // 通过反射去调用protect方法
        AnnotationAwareAspectJAutoProxyCreator creator = applicationContext.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method method = AbstractAutoProxyCreator.class.getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
        method.setAccessible(true);

        Object o1 = method.invoke(creator, new MyAspectV2(), "aspect-02", "aspect-02");
        Object o2 = method.invoke(creator, new Person(), "person-02", "person-02");
        System.out.println("o1.getClass() = " + o1.getClass());//不匹配切入点, 因此不会生成代理类
        System.out.println("o2.getClass() = " + o2.getClass());//匹配切入点,生成代理类
        Person person = (Person) o2;
        person.hello();//调用增强方法
    }
}

案例三:高级切面如何被解析成为低级切面

import org.aspectj.lang.annotation.*;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Aspect
public class MyAspectV3 {
    @Pointcut("execution(* hello())")
    public void helloPointCut() {
    }

    @Before(value = "helloPointCut()")
    public void before() {
        System.out.println("@Before: 方法执行前增强");
    }

    public static void main(String[] args) {
        SingletonAspectInstanceFactory factory = new SingletonAspectInstanceFactory(new MyAspectV3());

        List<Advisor> beforeAdvisors = new ArrayList<>();

        for (Method method : MyAspectV3.class.getMethods()) {
            if (method.isAnnotationPresent(Before.class)) {
                // 解析@Before注解
                Before annotation = method.getAnnotation(Before.class);
                String expression = annotation.value();

                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                // 增强
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
                // 切面类
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(advice);
                beforeAdvisors.add(advisor);
            } else if (method.isAnnotationPresent(After.class)) {
                // 解析@After注解
                // TODO
            } else if (method.isAnnotationPresent(Around.class)) {
                // 解析@Around注解
                // TODO
            }
        }

        beforeAdvisors.forEach(System.out::println);
    }
}

案例四:通知都会被转换为环绕通知(适配器模式+调用链模式)


   转载规则


《》 熊水斌 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
IOC容器xml解析 反射 工厂模式 实现流程 配置xml文件, 配置需要创建的bean对象 IOC容器通过xml解析获取属性值, 通过反射创建对象 IOC容器的两种接口 BeanFactory: 底层实现, 懒汉式创建对象 Appli
2022-11-11
下一篇 
Spring配置文件<?xml version="1.0" encoding="UTF-8"?> <!--xmlns="http://www.springframework.org/schema/beans"根据该值修改-->
2022-11-11
  目录