Spring --- AOP相关笔记

概述

AOP:Aspect Oriented Programming
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性的,同时提高开发效率。
AOP作用及优势
作用:在程序运行期间,不修改源码对已有方法进行增强
优势:减少重复代码,提高开发效率,维护方便
原理:采用动态代理的方式(基于接口的动态代理和基于子类的动态代理)

/**
 * 一个演员
 */
public class Actor implements IActor {
    /**
     * 基本的表演
     *
     * @param money
     */
    public void basicAct(float money) {
        System.out.println("拿到钱,开始基本的表演:" + money);
    }

    /**
     * 危险的表演
     *
     * @param money
     */
    public void dangerAct(float money) {
        System.out.println("拿到钱,开始危险的表演:" + money);
    }
}
------------------
基于接口的动态代理
/**
 * 模拟一个剧组
 */
public class Client {
    public static void main(String[] args) {
        final Actor actor = new Actor();
//        actor.basicAct(100f);
//        actor.dangerAct(500f);
        /**
         * 动态代理:
         *      作用:不改变源码的基础上,对己方已有的方法增强。(它是AOP思想的实现技术)
         *      分类:
         *          基于接口的动态代理:
         *                  要求:被代理类最少实现一个接口
         *                  提供者:JDk官方
         *                  涉及的类:Proxy
         *              创建代理对象的方法:newProxyInstance(ClassLoader,Class[],InvocationHandler)
         *                  参数含义:
         *                ClassLoader:类加载器。和被代理对象使用相同的类加载器。一般都是固定写法。
         *                    Class[]:字节码数组。被代理类实现的接口。(要求被代理对象和代理对象具有相同的行为)。一般都是固定写法。
         *          InvocationHandler:它是一个接口,就是用于我们提供增强代码的。我们一般都是写一个该接口的实现类。实现类可以是匿名内部类。
         *                                        它的含义是:如何代理。此处的代码只能是谁用谁提供
         *                                        策略模式:
         *                                            使用要求:数据已经有了
         *                                                     目的明确
         *                                                     达成目标的过程就是策略。
         *                                            在dbutils中的ResultSetHandler就是策略模式的具体应用
         *           基于子类的动态代理
         */
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
                actor.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     *
                     * 执行被代理对象的任何方法都会经过该方法,该方法有拦截的功能
                     * 方法的参数:
                     *      Object proxy:代理对象的引用。不一定每次都会有
                     *      Method method:当前执行的方法
                     *      Object[] args:当前执行方法所需的参数
                     * 返回值:
                     *      当前执行方法的返回值
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object rtValue = null;
                        //1.取出执行方法中的参数
                        Float money = (Float)args[0];
                        //2.判断当前执行的是什么方法
                        if("basicAct".equals(method.getName())){
                            //基本演出
                            if(money > 10000){
                                //执行方法(开始表演)
                                rtValue = method.invoke(actor, money/2);
                            }
                        }
                        if("dangerAct".equals(method.getName())){
                            //危险演出
                            if(money > 50000){
                                //执行方法
                                rtValue = method.invoke(actor, money/2);
                            }
                        }

                        return rtValue;
                    }

                });
        proxyActor.basicAct(20000);
        proxyActor.dangerAct(60000);
    }
}

-------------
基于子类的动态代理
/**
 * 模拟一个剧组
 */
public class Client {
    public static void main(String[] args) {
        final Actor actor = new Actor();
//        actor.basicAct(100f);
//        actor.dangerAct(500f);
        /* *
         * 动态代理:
         *      作用:不改变源码的基础上,对己方已有的方法增强。(它是AOP思想的实现技术)
         *      分类:
         *              基于接口的动态代理:
         *
         *              基于子类的动态代理:
         *                  要求:被代理类不能是最终类。不能被final修饰
         *                  提供者:第三方CGLIB
         *                  涉及的类:Enhancer
         *                  创建代理对象的方法:creat(Class,Callback);
         *                  参数的含义:
         *                      Class:被代理对象的字节码
         *                      Callback:如何代理。他和InvocationHandler的作用是一样的他也是一个接口,我们一般使用该接口的子接口MethodInterceptor
         *                              在使用时我们也是创建该接口的匿名内部类。
         */
        Actor cglibAct = (Actor) Enhancer.create(actor.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法,都会经过该方法。他和基于接口动态代理的invoke方法方法的作用是一模一样的。
             * 方法参数:
             *      前面三个和invoke方法的参数含义都一样。
             *      MethodProxy methodProxy:当前执行方法的代理对象。一般不用。
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object rtValue = null;
                //1.取出执行方法中的参数
                Float money = (Float) args[0];
                //2.判断当前执行的是什么方法
                if ("basicAct".equals(method.getName())) {
                    //基本演出
                    if (money > 10000) {
                        //执行方法(开始表演)
                        rtValue = method.invoke(actor, money / 2);
                    }
                }
                if ("dangerAct".equals(method.getName())) {
                    //危险演出
                    if (money > 50000) {
                        //执行方法
                        rtValue = method.invoke(actor, money / 2);
                    }
                }

                return rtValue;
            }
        });

        cglibAct.basicAct(20000);
        cglibAct.dangerAct(60000);
    }
}

**关于代理的选择 : ** 在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式
**AOP相关术语 : **

JoinPoint(连接点):所谓连接点就是指那些被拦截到的点。在spring中,这些点指的就是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点就是指我们要对那些JoinPoint进行拦截的定义。(被增强的就是切入点)
Advice(通知/增强):所谓通知是指拦截到JoinPoint之后所要做的事情就是通知。通知类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期间为类动态地添加一些方法或field。
Target(目标对象):代理目标对象。
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而aspectJ采用编译期织入和类装载期织入。
Proxy(代理):一个类AOP织入增强后,就产生一个结果代理类
Aspect(切面):是切入点和通知(引介)的结合。

使用xml配置AOP

导入坐标 —> 配置好IoC —> 按照四步骤进行配置

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

    <!-- 配置service -->
    <bean id="customerService" class="com.itheima.service.impl.CustomerServiceImpl"></bean>


    <!-- 基于xml的aop配置步骤 : 要想使用spring的aop,必须向导入aop的jar包-->
    <!-- 第一步:把通知bean交给spring来管理 -->
    <bean id="logger" class="com.itheima.utils.Logger"></bean>


    <!-- 第二步:导入AOP名称空间,并且使用 aop:config 标签 开始aop的配置 -->
    <aop:config>

        <!-- 定义通用切入点表达式,如果写在aop:aspect标签外部,则表示所有切面可用 -->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>

        <!-- 第三步:使用 aop:aspect 标签 配置切面  id属性:用于给切面提供一个唯一标识。ref属性:用于应用通知的Bean的id-->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 第四步:在aspect标签内部 配置通知的类型,指定增强的方法何时执行。
                    method属性:用于指定增强的方法名称 。
                    pointcut属性:用于指定切入点表达式-->
            <!-- 切入点表达式:
            关键字:execution(表达式)
            表达式写法:
            访问修饰符 返回值 包名.包名..类名.方法名(参数列表)
            全匹配方式:
                public void com.itheima1515.service.impl.CustomerServiceImpl.saveCustomer()
            访问修饰符可以省略
                void com.itheima1515.service.impl.CustomerServiceImpl.saveCustomer()
            返回值可以使用通配符,表示任意返回值。通配符是*
                * com.itheima1515.service.impl.CustomerServiceImpl.saveCustomer()
            包名可以使用通配符,表示任意符,但是,有几个包就需要写几个*
                * *.*.*.*.CustomerServiceImpl.saveCustomer()
            包名可以使用..表示当前包及其子包
                * com..CustomerServiceImpl.saveCustomer()
            类名和方法名都可以使用通配符
                * com..*.*()
            参数类别可以使用基本类型,表示参数类型
            基本类型直接写类型名称:int
            引用类型必须是包名.类名。java.lang.Integer
            参数列表可以使用通配符,表示任意参数类型,但是必须有参
                * com..*.*(*)
            参数列表可以使用..表示有无参数均可,有参数可以是任意类型
                * com..*.*(..)
            全通配方式:
                * *..*.*(..)
            实际开发中,我们都是对业务层方法进行增强:
            所以写法:* com.itheima1515.service.impl.*.*(..)


            -->
            <aop:before method="printLog" pointcut-ref="pt1"></aop:before>

            <!-- 定义通用切入点表达式:如果写在了aop:aspect标签内部,则表示只有当前切面可用
            <aop:pointcut id="pt1" expression="execution(* com.itheima1515.service.impl.*.*(..))"></aop:pointcut>
            -->
        </aop:aspect>
    </aop:config>
</beans>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值