Spring篇--AOP

AOP简介

AOP的概念

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

AOP思想的实现方案

动态代理技术,在运行期间,对目标对象 的方法进行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法

模拟AOP的基础代码

其实在之前学习BeanPostProcessor时,在BeanPostProcessor的after方法中使用动态代理对Bean进行了增强,实际存储到单例池singleObjects中的不是当前目标对象本身,而是当前目标对象的代理对象Proxy,这样在调用目标对象方法时,实际调用的是代理对象Proxy的同名方法,起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.zjy.service.impl包下的任何类的任何方法进行增强

java 复制代码
//自定义增强类
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("beforeAdvice ...");
    }
    public void afterAdvice(){
        System.out.println("afterAdvice ...");
    }
}
java 复制代码
public class MockAopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
    private ApplicationContext applicationContext;//注入Spring容器对象
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);//获得Advice对象
        String packageName = bean.getClass().getPackage().getName();
        if("com.zjy.service.impl".equals(packageName)){
            //对Bean进行动态代理,返回的是Proxy代理对象
            Object proxyBean = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (Object proxy, Method method, Object[] args) -> {
                    myAdvice.beforeAdvice();//执行Advice的before方法
                    Object result = method.invoke(bean, args);//执行目标
                    myAdvice.afterAdvice();//执行Advice的after方法
                    return result; });
            //返回代理对象
            return proxyBean; }
        return bean; }
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext; 
    }}

AOP相关概念

切面:增强部分加上被增强的部分

在xml中对AOP进行配置

xml标签去配置AOP的两种形式

1.< aop:aspect > 标签

xml 复制代码
<!--aop配置-->
    <aop:config>
        <!--配置切点表达式,目的:指定哪些方法被增强-->
        <aop:pointcut id="myPointcut" expression="execution(void com.zjy.service.impl.UserServiceImpl.show2())"/>
        <!--配置织入,目的:要执行哪些切点与哪些通知进行结合-->
        <aop:aspect ref="myAdvice">
            <!--前置通知-->
            <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
            <!--最终通知,无论是否异常都通知-->
            <aop:after method="afterAdvice" pointcut-ref="myPointcut"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="myPointcut"/>
            <!--异常通知-->
            <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="myPointcut"/>
            <!--后置通知,异常不通知-->
            <aop:after-returning method="afterReturningAdvice" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

通知(增强的方法)可以随意添加,相对较为灵活

2.< aop:advisor >标签

xml 复制代码
<!--aop配置-->
    <aop:config>
        <aop:pointcut id="myadvice2" expression="execution(void com.zjy.service.impl.UserServiceImpl.show1())"/>
        <aop:advisor advice-ref="myAdvice2" pointcut-ref="myadvice2"/>
    </aop:config>

通知所在的类需要继承指定接口,用到的地方较少例如事务的提交与回滚

java 复制代码
public class MyAdvice2 implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor {

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知");
    }

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知");
    }

    // 环绕通知
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("环绕前");
        // 执行目标方法
        Object invoke = methodInvocation.getMethod().invoke(methodInvocation.getThis(), methodInvocation.getArguments());
        System.out.println("环绕后");
        return invoke;
    }
}

用xml进行aop配置的五种通知模式

通知方法被调用时,spring可以为其传递一些必要参数

Spring框架的AOP的实现原理

利用AspectAwareAdvisorAutoProxyCreator(继承BeanPostProcessor也就是bean后处理器),对容器中的所有对象进行筛选,判断哪些需要增强,如果需要增强会创建一个新对象把原对象代理(采用的是jdk的动态代理去生成代理对象)

动态代理两种方式的选择

动态代理的实现的选择,在调用getProxy() 方法时,我们可选用的 AopProxy接口有两个实现类,如上图,这两种都是动态生成代理对象的方式,一种就是基于JDK的,一种是基于Cglib的

Cglib原理,就是通过目标对象生成一个子对象

JDK原理,就是继承原对象所继承的接口将需要增强的方法,重写成增强过的方法

Cglib底层原理

java 复制代码
Target target = new Target();//目标对象
Advices advices = new Advices();//通知对象
Enhancer enhancer = new Enhancer();//增强器对象
enhancer.setSuperclass(Target.class);//增强器设置父类
//增强器设置回调
enhancer.setCallback((MethodInterceptor )(o, method, objects, methodProxy) -> {
advices.before();
Object result = method.invoke(target, objects);
advices.afterReturning();
return result;
});
//创建代理对象
Target targetProxy = (Target) enhancer.create();
//测试
String result = targetProxy.show("haohao");

基于注解对AOP的配置

通过xml的方式使项目可以解析AOP注解

xml 复制代码
<!--组件扫描-->
<context:component-scan base-package="com.zjy"/>
<!--使用注解配置aop,需要开启aop自动代理-->
<aop:aspectj-autoproxy/>

通过配置类的方式使项目可以解析AOP注解

java 复制代码
@Configuration
// @ComponentScan("com.zjy") 用于替代标签形式的注解扫描
// <context:component-scan base-package="com.zjy"/>
@ComponentScan("com.zjy")
// @EnableAspectJAutoProxy 用于替代标签形式的开启aop
// <aop:aspectj-autoproxy/>
@EnableAspectJAutoProxy
public class SpringConfig {
}

注解配置AOP的使用方式

java 复制代码
@Component
@Aspect
//增强类,内部提供增强方法
public class MyAdvice {

    // 切点表达式的抽取
    @Pointcut("execution(void com.zjy.service.impl.UserServiceImpl.show2())")
    public void myPointcut(){ }

    // <aop:before method="beforeAdvice" pointcut="execution(void com.zjy.service.impl.UserServiceImpl.show2())"/>
    // 前置通知(前置通知切点)
    // @Before("execution(void com.zjy.service.impl.UserServiceImpl.show2())")
    @Before("MyAdvice.myPointcut()")
    public void beforeAdvice(){
        System.out.println("前置的增强...");
    }

    // @After("execution(void com.zjy.service.impl.UserServiceImpl.show2())")
    @After("MyAdvice.myPointcut()")
    public void afterAdvice(){
        System.out.println("最终的增强...");
    }

    // @AfterReturning("execution(void com.zjy.service.impl.UserServiceImpl.show2())")
    @AfterReturning("MyAdvice.myPointcut()")
    public void afterReturningAdvice(){
        System.out.println("后置的增强...");
    }

    // @Around("execution(void com.zjy.service.impl.UserServiceImpl.show2())")
    @Around("MyAdvice.myPointcut()")
    // 为什么注解中的属性是这样的?
    // 这是框架设计师自己规定的,在解析器解析注解时会以这种形式进行解析
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前增强...");
        //执行目标方法
        Object res = proceedingJoinPoint.proceed();
        System.out.println("环绕后增强...");
        return res;
    }

    // @AfterThrowing(value = "execution(void com.zjy.service.impl.UserServiceImpl.show2())",throwing = "ex")
    @AfterThrowing(value = "MyAdvice.myPointcut()",throwing = "ex")
    public void afterThrowingAdvice(Exception ex){
        System.out.println("当前异常信息是:"+ex);
    }
}

注解方式AOP原理解析

1.注解+xml配置的方式

AOP的流程就是,先通过解析AOP命名空间,生成xml解析器。在解析器初始化时,向Spring容器中注入一个继承bean后处理器的对象,用于处理容器中的对象,将他们编程AOP对象后放入Spring容器中

2.全注解方式

Spring 应用上下文启动时,Spring 会扫描所有的配置类,并识别出带有@EnableAspectJAutoProxy 注解的类。扫描到后会向Spring容器中注入一个继承bean后处理器的对象(BeanPostProcessor),将容器中的对象加工后放回到Spring容器中

相关推荐
老马啸西风3 小时前
Occlum 是一个内存安全的、支持多进程的 library OS,特别适用于 Intel SGX。
网络·后端·算法·阿里云·云原生·中间件·golang
她说彩礼65万5 小时前
WPF Binding方式详解
java·开发语言·wpf
天航星7 小时前
VSCode Java 单元测试没有运行按钮
java·vscode·单元测试
盖世英雄酱581367 小时前
编程 5 年之惑,AI 给出完美答案
java
二十雨辰7 小时前
[学成在线]07-视频转码
java·开发语言·mysql
冯浩(grow up)8 小时前
Spring Boot 连接 MySQL 配置参数详解
spring boot·后端·mysql
李贺梖梖8 小时前
DAY14 Lambda表达式、Stream流
java
whatever who cares8 小时前
java.util包之java.util.Collection介绍
java·服务器·windows
Hanson Huang8 小时前
23种设计模式-责任链(Chain of Responsibility)设计模式
java·设计模式·责任链模式·行为型设计模式
Asthenia04128 小时前
面试复盘:left join 底层算法(嵌套/哈希/分块) & 主从复制(异步/半同步/同步)
后端