Spring的Aop

AOP的相关术语

Joinpoint(连接点): 目标类中的所有方法都是连接点
Pointcut(切入点): 目标类类中会被增强的方法都是切入点
Advice(通知): 所谓通知是指拦截到Pointcut之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Aspect(切面): 通知与切入点的结合(所谓的切面就是用来说明通知与切入点的关系,即:通知在切入点执行的什么时候执行)
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
Target(目标对象): 目标类对象(要增强的类)
Proxy(代理对象): 一个目标类被AOP织入增强后,就产生一个结果代理类
Weaving(织入): 是把增强功能应用到目标的过程,即:把advice应用到target的过程

Spring的AOP的基本配置步骤

需求:在不修改已有代码的前提下,在项目现有所有类的方法前后打印日志

接口

public interface AccountService {
    /**
     * 模拟保存账户
     */
    void saveAccount();

    /**
     * 模拟更新账户
     */
    void updateAccount(int i);

    /**
     * 模拟删除账户
     */
    int deleteAccount();
}

实现类

public class AccountServiceImpl implements AccountService {

    public void saveAccount() {
        System.out.println("执行了保存");
    }

    public void updateAccount(int i) {
        System.out.println("执行了更新");
    }

    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

通知类

/**
 * 日志打印类
 */
public class Logger {
    /**
     * 用于打印日志,计划让其在切入点方法执行前执行(切入点方法就是业务层方法)
     */
    public void printLog() {
        System.out.println("Logger的printLog()执行了");
    }
}

配置文件

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置spring的IOC,将业务层的AccountServiceImpl配置到容器中 -->
    <bean id="accountServiceImpl" class="com.itcast.service.impl.AccountServiceImpl"></bean>

    <!--
        spring中基于xml的AOP配置:
            1.将通知Bean配置到spring的IOC容器中
            2.使用<aop:config>标签表明开始AOP的配置
            3.使用<aop:aspect>标签表明开始配置切面
                    属性:
                        id:切面的唯一标识符
                        ref:用于指向通知bean的id
            4.在<aop:aspect>标签的内部使用对应的标签来配置通知的类型
                    前置通知:<aop:before>
                        method属性:用于指定Logger类中的哪个方法是前置通知
                        pointcut属性:用于指定切入点表达式,该表达式的含义是指对业务层的哪些方法增强
                            切入点表达式的语法形式:
                                关键字:execution(表达式)
                                表达式:
                                    访问权限 返回值 包名.包名...类名.方法名(参数列表)
                                        标准的表达式写法:
                                            public void com.itcast.service.impl.AccountServiceImpl.saveAccount()
                                        访问修饰符可以省略:
                                            void com.itcast.service.impl.AccountServiceImpl.saveAccount()
                                        返回值可以使用通配符,表示任意返回值
                                            * com.itcast.service.impl.AccountServiceImpl.saveAccount()
                                        包名可以使用通配符,表示任意包,但是有几级包,就需要写几个*.
                                            * *.*.*.*.AccountServiceImpl.saveAccount()
                                        包名可以使用..表示当前包及其子包
                                            * *..AccountServiceImpl.saveAccount()
                                        包名和方法名也可以使用*来实现通配
                                            * *..*.*()
                                        参数列表
                                            可以直接写数据类型
                                                基本类型直接写名称:* *..*.*(int)
                                                引用类型写包名.类名:* *..*.*(java.lang.String)
                                            可以使用通配符表示任意类型,但必须是有参数:* *..*.*(*)
                                            可以使用..表示有无参数均可:* *..*.*(..)
                                        全通配写法:
                                            * *..*.*(..)
                                        实际开发中实际切入点表达式的通常写法:切到业务层下的所有实现类
                                            * com.itcast.service.impl.*.*(..)
                    后置通知:<aop:after>
                    异常通知:<aop:after-throwing>
                    最终通知:<app:after>
                    环绕通知:<aop:around>
                    我们现在示例是让printLog()方法在切入点方法执行之前先执行,属于前置通知

    -->
    <bean id="logger" class="com.itcast.utils.Logger"></bean>

    <aop:config>
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联关系 -->
            <aop:before method="printLog" pointcut="execution(* com.itcast.service.impl.*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>
    
</beans>

测试类

public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = applicationContext.getBean("accountServiceImpl",AccountService.class);
        accountService.saveAccount();
        accountService.updateAccount(1);
        accountService.deleteAccount();
    }
}

测试结果

Logger的printLog()执行了
执行了保存
Logger的printLog()执行了
执行了更新
Logger的printLog()执行了
执行了删除

四种常用的通知类型

XML中的AOP配置

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置spring的IOC,将业务层的AccountServiceImpl配置到容器中 -->
    <bean id="accountServiceImpl" class="com.itcast.service.impl.AccountServiceImpl"></bean>

    <bean id="printLog" class="com.itcast.utils.Logger"></bean>

    <aop:config>
        <!--
            配置切入点表达式,id属性用于指定表达式的唯一标识符,expression属性用于指定表达式内容,此标签写在
            <aop:aspect>标签内部,只能当前切面内使用。它可以配置<aop:aspect>标签为,这样就可以所有的切面都可以复用
        -->
        <aop:pointcut id="pt1" expression="execution(* com.itcast.service.impl.*.*(..))"/>

        <aop:aspect id="logAdvice" ref="printLog">
			
			<!------------------------------- 使用<aop:pointcut>标签简化配置前 ------------------------------->
			<!-- 前置通知 -->
            <aop:before method="beforePrintLog" pointcut="execution(* com.itcast.service.impl.*.*(..))"></aop:before>

            <!-- 后置通知 -->
            <aop:after-returning method="afterReturningPrintLog" pointcut="execution(* com.itcast.service.impl.*.*(..))"></aop:after-returning>

            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(* com.itcast.service.impl.*.*(..))"></aop:after-throwing>

            <!-- 最终通知 -->
            <aop:after method="afterPrintLog" pointcut="execution(* com.itcast.service.impl.*.*(..))"></aop:after>

			<!------------------------------- 使用<aop:pointcut>标签简化配置后 ------------------------------->

            <!-- 前置通知 -->
            <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>

            <!-- 后置通知 -->
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>

            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>

            <!-- 最终通知 -->
            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
        </aop:aspect>
        
    </aop:config>

</beans>

测试类

public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = applicationContext.getBean("accountServiceImpl",AccountService.class);
        accountService.saveAccount();
    }
}

测试结果

前置通知:Logger的printLog()执行了
执行了保存
后置通知:Logger的printLog()执行了
最终通知:Logger的printLog()执行了

配置环绕通知

通知类

/**
 * 用于记录日志的工具类
 */
public class Logger {
    /**
     * 前置通知
     */
    public void beforePrintLog() {
        System.out.println("前置通知:Logger的printLog()执行了");
    }

    /**
     * 后置通知
     */
    public void afterReturningPrintLog() {
        System.out.println("后置通知:Logger的printLog()执行了");
    }

    /**
     * 异常通知
     */
    public void afterThrowingPrintLog() {
        System.out.println("异常通知:Logger的printLog()执行了");
    }

    /**
     * 最终通知
     */
    public void afterPrintLog() {
        System.out.println("最终通知:Logger的printLog()执行了");
    }

    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用
     * 解决:
     *      Spring框架为我们提供了一个接口,Pro
   */
    public Object aroundPrintLog(ProceedingJoinPoint joinPoint) {
        // 调用proceed()方法即调用切入点方法
        Object retValue = null;
        try {
            System.out.println("前置通知:Logger的aroundPrintLog()执行了");
            // 获取切入点方法执行时所需的参数
            Object args[] = joinPoint.getArgs();
            retValue = joinPoint.proceed(args);
            System.out.println("后置通知:Logger的aroundPrintLog()执行了");
            return retValue;
        }catch (Throwable throwable) {
            System.out.println("异常通知:Logger的aroundPrintLog()执行了");
            throw new RuntimeException(throwable);
        }finally {
            System.out.println("最终通知:Logger的aroundPrintLog()执行了");
        }
    }
}

XML中的AOP配置

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置spring的IOC,将业务层的AccountServiceImpl配置到容器中 -->
    <bean id="accountServiceImpl" class="com.itcast.service.impl.AccountServiceImpl"></bean>

    <bean id="printLog" class="com.itcast.utils.Logger"></bean>

    <aop:config>
        <!--
            配置切入点表达式,id属性用于指定表达式的唯一标识符,expression属性用于指定表达式内容,此标签写在
            <aop:aspect>标签内部,只能当前切面内使用。它可以配置<aop:aspect>标签为,这样就可以所有的切面都可以复用
        -->
        <aop:pointcut id="pt1" expression="execution(* com.itcast.service.impl.*.*(..))"/>

        <aop:aspect id="logAdvice" ref="printLog">
            <!-- 配置环绕通知,详细注释请查询Logger类 -->
            <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>

</beans>

测试类

public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = applicationContext.getBean("accountServiceImpl",AccountService.class);
        accountService.saveAccount();
    }
}

测试结果

前置通知:Logger的aroundPrintLog()执行了
执行了保存
后置通知:Logger的aroundPrintLog()执行了
最终通知:Logger的aroundPrintLog()执行了

基于注解的AOP配置

接口

/**
 * 账户的业务层接口
 */
public interface AccountService {

    /**
     * 模拟保存账户
     */
   void saveAccount();

    /**
     * 模拟更新账户
     * @param i
     */
   void updateAccount(int i);

    /**
     * 删除账户
     * @return
     */
   int  deleteAccount();
}

实现类

/**
 * 账户的业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements IAccountService{

    @Override
    public void saveAccount() {
        System.out.println("执行了保存");
    }

    @Override
    public void updateAccount(int i) {
        System.out.println("执行了更新");

    }

    @Override
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

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

    <!-- 配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>

    <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

通知类

在这里插入代码片/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 */
@Component("logger")
//表示当前类是一个切面类
@Aspect
public class Logger {

    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt1(){}

    /**
     * 前置通知
     */
    @Before("pt1()")
    public  void beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
    }

    /**
     * 后置通知
     */
    @AfterReturning("pt1()")
    public  void afterReturningPrintLog(){
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
    }
    /**
     * 异常通知
     */
    @AfterThrowing("pt1()")
    public  void afterThrowingPrintLog(){
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
    }

    /**
     * 最终通知
     */
    @After("pt1()")
    public  void afterPrintLog(){
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
    }
}

测试类

/**
 * 测试AOP的配置
 */
public class AOPTest {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        //3.执行方法
        as.saveAccount();
    }
}

测试结果

前置通知Logger类中的beforePrintLog方法开始记录日志了。。。
执行了保存
最终通知Logger类中的afterPrintLog方法开始记录日志了。。。
后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。

观察返回结果就会发现通知的输出顺序有问题,这个时spring在使用注解环绕通知时存在的问题

基于注解的AOP,使用环绕通知不会存在顺序问题

/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 */
@Component("logger")
//表示当前类是一个切面类
@Aspect
public class Logger {

    @Pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt1(){}
    
    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     *
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
     */
    @Around("pt1()")
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args = pjp.getArgs();//得到方法执行所需的参数

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");

            rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");

            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
        }
    }
}

测试类

/**
 * 测试AOP的配置
 */
public class AOPTest {
    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        //3.执行方法
        as.saveAccount();
    }
}

测试结果

Logger类中的aroundPringLog方法开始记录日志了。。。前置
执行了保存
Logger类中的aroundPringLog方法开始记录日志了。。。后置
Logger类中的aroundPringLog方法开始记录日志了。。。最终
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能做出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值