zoukankan      html  css  js  c++  java
  • Spring系统学习--3AOP

     解决什么样的问题?

    @Override 
    public void transfer(String sourceName,String targetName,Float money){ //1.根据名称查询转出账户 Account source=accountDao.findByName(sourceName); //2.根据名称查询转入账户 Account target=accountDao.findByName(targetName); //3.转出账户减钱 source.setMoney(source.getMoney()-money); //4.转入账户加钱 target.setMoney(target.getMoney()+money); //5.更新转出账户 accountDao.update(source); //模拟转账异常 int i=1/0;//6.更新转入账户 accountDao.update(target);

     动态代理

      1.基于接口

    /*创建一个被代理的类(这里加入是笔记本电脑生产厂家),被代理类要实现代理类接口*/
    public class ComputerFactory implements IAgent{
        public void sale(Float money){
            System.out.println("一手交钱"+money+"一手交货");
        }
        public void afterService(Float money){
            System.out.println("维想修,拿钱来"+money);
        }
    }
    /*创建一个代理类(接口),(这里假设是生产厂家的代理,帮着买电脑等...)*/
    
    public interface IAgent {
        public void sale(Float money);
        public void afterService(Float money);
    }
    /*下面是如何使用代理类代理处理业务的*/
    package spring.day03.agent;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class Demo {
        /**
         * 动态代理
         * 特点:字节码随用随创建,随用随加载
         * 分类:
         * 基于接口的
         * 基于子类的
         * 作用:
         * 在不改变源码的基础上对已有方法增强
         * 此类讲解的是基于接口的动态代理
         * 提供者:JDK官方
       * 涉及的类:Proxy * 创建代理对象的方法:newProxyInstance
       * 方法的参数: * ClassLoader:类加载器。和被代理象使用相同的类加载器。该参数是固定写法。 * Class[]:字节码数组。和被代理对象具有相同的行为,实现相同的接口。 * InvocationHandler:它是一个接口。提供如何代理的代码。也就是增强的代码。该参数一般都成名内部类。 * 并且它是策略模式的体现。 * 策略模式: * 要求:数据已经有了,目的明确。 * 达成目标的过程就是策略。 * 该参数是谁用谁写。 * 使用要求:被代理类最少实现一个接口。
    */ public static void main(String[] args) { final ComputerFactory factory = new ComputerFactory(); IAgent proxyFactory = (IAgent) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), new InvocationHandler() { /** *该方法的特征: *执行被代理对象的任何方法,都会经过该方法。该方法有拦截的特点 *方法的参数: *Object proxy:代理对象的引用。 *Method method:当前执行的方法 *Object[]args:当前执行方法所需的参数 *方法的返回值: *Object:和被代理对象的方法返回值一致。 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object rtValue = null; //1.获取正在执行方法的参数 Float money = (Float) args[0]; //2.判断正在执行的方法 if ("sale".equals(method.getName())) { //经销商最少一台电脑挣2000元钱 if (money > 7000) { rtValue = method.invoke(factory, money / 2); } } if ("afterservice".equals(method.getName())) { //经销商最少一台电脑正600元 if (money > 1600) { rtValue = method.invoke(factory, money / 4 * 3); } } return rtValue; // return method.invoke(factory,args); } }); // factory.sale(5000f); // factory.afterService(100f); proxyFactory.sale(10000f); proxyFactory.afterService(100f); } }

      2.基于子类

      
    <!--所需依赖-->
      <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>2.2.2</version>
        </dependency>
        <dependency>
          <groupId>asm</groupId>
          <artifactId>asm</artifactId>
          <version>3.3.1</version>
        </dependency>
    /*被代理的类*/
    public class ComputerFactory {
        public void sale(Float money){
            System.out.println("一手交钱"+money+"一手交货");
        }
        public void afterService(Float money){
            System.out.println("维想修,拿钱来"+money);
        }
    }
    /*执行代理的代码*/
    public class Demo {
        /**
         * 动态代理
         * 特点:字节码随用随创建,随用随加载
         * 分类:
         * 基于接口的
         * 基于子类的
         * 作用:
         * 在不改变源码的基础上对已有方法增强
         * 此类讲解的是基于子类的动态代理
         * 提供者:cglib
         * 依赖外部jar:cglib;asm;
         * 涉及的类:Enhancer
         * 创建代理对象的方法:create
         * 方法的参数:
         * Class:字节码。指定被代理文件的字节码文件
         * CallBack:如何代理。
         * <p>
         * 使用要求:被代理类不能是最终类
         */
        public static void main(String[] args) {
            ComputerFactory factory = new ComputerFactory();
            ComputerFactory cglibfactory = (ComputerFactory) Enhancer.create(factory.getClass(), new MethodInterceptor() {
                /**
                 *它和InvocationHandler中的invoke方法作用是一样的
                 *该方法的前3个参数和invoke方法的三个参数作用是一样的。
                 *该方法的返回值和invoke方法的返回值作用也是一样的。
                 *
                 *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 ("sale".equals(method.getName())) {
                        //经销商最少一台电脑挣2000元钱
                        if (money > 7000) {
                            rtValue = method.invoke(factory, money / 2);
                        }
                    }
                    if ("afterService".equals(method.getName())) {
                        //经销商最少一台电脑正600元
                        if (money > 1600) {
                            rtValue = method.invoke(factory, money / 4 * 3);
                        }
                    }
                    return rtValue;
    //                return method.invoke(factory,args);
                }
            });
            cglibfactory.sale(10000f);
            cglibfactory.afterService(5000f);
        }
    }

    AOP概述

    简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

    作用:
    在程运行期间:不修改源码对已有方法进行增强。
    优点:
    减少重复代码
    提高开发效案
    维护方便

     基于XML的AOP配置

    项目依赖:

            <!--    junit:方便我们进行方法的测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <!--      <scope>test</scope>-->
            </dependency>
            <!--  spring-context:导入spring核心jar包  -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.7.RELEASE</version>
                <!--      <scope>test</scope>-->
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.46</version>
            </dependency>
            <!--    导入日志jar包-->
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>jakarta.annotation</groupId>
                <artifactId>jakarta.annotation-api</artifactId>
                <version>1.3.4</version>
            </dependency>
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.2.2</version>
            </dependency>
            <dependency>
                <groupId>asm</groupId>
                <artifactId>asm</artifactId>
                <version>3.3.1</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version>
            </dependency>

    SpringXML配置文件:

    <?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">
        <!--    配置业务逻辑层-->
        <bean id="AccountService" class="main.java.spring.day03.aop.impl.AccountServiceImpl"></bean>
        <!--spring基于xml的aop配置步骤:
        前期准备:
        1.拷贝aop的jar包
        2.在配置文件中导入aop的约束
        配置步骤:
            1.把通知bean也交给spring来管理
            2.使用aop名称空间下的aop:config标签开始aop的配置
            3.使用aop:aspect标签,开始配置切面。
                id属性:用于给切面提供一个唯一标识
                ref属性:用于引用通知bean的id。
            4.使用aop:before标签配置前置通知
                method属性:用于指定通知类中的哪个方法是前置通知
                pointcut属性:用于指定切入点表达式。
                    切入点表达式:
                        关键字:execution(表达式)
                    表达式的写法:
                        访问修饰符返回值包名。包名...类名.方法名(参数列表)
                        全匹配方式:
                            public vdid main.java.spring.day03.aop.impl.AccountServiceImpl.saveAccount()
                        访问修饰符可以省略:
                                    void main.java.spring.day03.aop.impl.AccountServiceImpl.saveAccount()
                        返回值可以使用*,表示任意返回值类型
                            * main.java.spring.day03.aop.impl.AccountServiceImpl.saveAccount()
                        包名可以使用*,表示任意包。但是有几级包,需要写几个*
                            * *,*,*,*,*,*.AccountServiceImpl.saveAccount()
                        包名可以使用..表示当前包及其子包:
                            * *..AccountServiceImpl.saveAccount()
                        类名可以使用*,表示任意类
                            **..*.saveAccount()
                        方法名可以使用*,表示任意方法
                            *main.java.spring..*.*()
                        参数列表可以指定具体类型:
                            基本类型直接写类型名称:*main.java.spring..*.*(int)
                            引用类型必须是包名,类名的方式:*main.java.spring..*.*(java.lang.String)
                        参数列表可以使用*,表示任意参数类型,但是必须有参数
                                *main.java.spring..*.*(*)
                        参数列表可以使用..,表示有无参数均可:
                                *main.java.spring..*.*(..)
      -->
        <bean id="logger" class="main.java.spring.day03.aop.Logger"></bean>
        <aop:config>
            <!--使用aop:pointcut标签可以配置通用切入点表达式 ;写在切面aop:aspect标签内部,只能当前切面使用。
      如果要想所有切面使用,请写到aop:aspect标签外面-->
            <aop:pointcut expression="execution(* main.java.spring..*.*(..)) " id="pt"/>
            <aop:aspect id="logerAop" ref="logger">
                <!--        配置前置通知:她永远都会在切入点方法执行之前执行-->
                <aop:before method="beforeprintLog"
                            pointcut="execution(public void main.java.spring.day03.aop.impl.AccountServiceImpl.saveAccount())"></aop:before>
                <!--配置后置通知:当切入点方法正常执行之后,后置通知执行。它和异常通知只能有一个执行-->
                <aop:after-returning method="afterReturningPrintLog" pointcut="execution(* main.java.spring..*.*(..))"/>
                <!--配置异常通知:当切入点方法执行产生异常后执行。它和后置通知是互斥的-->
                <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(* main.java.spring..*.*(..))"/>
                <!--配置最终通知:无论切入点方法是否正常执行,它都会在其后面执行-->
                <aop:after method="afterPrintLog" pointcut="execution (* main.java.spring..*.*(..))"/>
                <!--配置环绕通知:详细注释写到Logger类里了-->
                <aop:around method="aroundPrintLog" pointcut-ref="pt"/>
            </aop:aspect>
    
        </aop:config>
    </beans>
    /*
    业务层代码
    */
    public interface AccountService {
        /**
         * 保存账户
         */
        void saveAccount();
    }
    
    /*
    业务层实现类
     */
    public class AccountServiceImpl implements AccountService {
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void saveAccount() {
            System.out.println("保存账户!!!");
    //        int a=1/0;//模拟异常
        }
    }
    /*****************切面类*************************/
    package main.java.spring.day03.aop;
    
    import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
    
    public class Logger {
        /**
         *计划让其在切入点方法执行之前执行
         */
        public void beforeprintLog() {
            System.out.println("Logger类中的beforePrintLog方法开始记录日志了。。。前置");
        }
    
        /**
         * 后置通知
         */
         public void afterReturningPrintLog(){
         System.out.println("Logger类中的afterReturningPrintLog方法开始记录日志了。。。后置");
         }
    
         /**
         *异常通知
         */
        public void afterThrowingPrintLog(){
            System.out.println("Logger类中的afterThrowingPrintLog方法开始记录日志了。。。异常");
        }
        /**
         *最终通知
         */
        public void afterPrintLog() {
            System.out.println("Logger类中的afterPrintLog方法开始记录日志了。。。最终");
    
        }
    
        /**
         *环绕通知
         * 问题:
         * 当我们配置了环绕通知之后,执行切入点方法时,最终的结果是环绕通知的代码执行了,而切入点方法却没有执行。
         * 分析:
         * 根据动态代理的代码分析,可以看到invoke方法中有一个明确调用切入点方法的代码。而我们spring中的环绕通知目前没有调用切入点方法
         * 解决办法:
         *  思路:我们也需要在环绕通知中明确调用一下切入点方法。
         *  Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口可以作为环绕通知的方法参数来使用。
         *  在程序运行时spring框架会为我们注入该接口的实现类供我们使用。I
         *  该接口有个方法:
         *             proceed()方法,它就相当于明确调用切入点方
         *   环绕通知:它是spring为我们提供的一种可以在代码中手动控制通知何时执行的方式方式。
         */
    
        public Object aroundPrintLog(MethodInvocationProceedingJoinPoint pjp) {
            //获取方法所需参数
            Object[] args = pjp.getArgs();
            Object rtValue = null;
            try {
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。前置");
                rtValue = pjp.proceed(args);
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。后置");
            } catch (Throwable throwable) {
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。异常");
                throwable.printStackTrace();
            }finally {
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。最终");
            }
            return rtValue;
        }
    
    }
    /******************测试类**************/
    public class Client {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config/bean_aop.xml");
            AccountServiceImpl accountService = context.getBean("AccountService", AccountServiceImpl.class);
            accountService.saveAccount();
            context.close();
        }
    }

    基于注解的AOP配置

    项目依赖:

     <!--    junit:方便我们进行方法的测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <!--      <scope>test</scope>-->
            </dependency>
            <!--  spring-context:导入spring核心jar包  -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.2.RELEASE</version>
                <!--      <scope>test</scope>-->
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.46</version>
            </dependency>
            <!--    导入日志jar包-->
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>jakarta.annotation</groupId>
                <artifactId>jakarta.annotation-api</artifactId>
                <version>1.3.4</version>
            </dependency>
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.2.2</version>
            </dependency>
            <dependency>
                <groupId>asm</groupId>
                <artifactId>asm</artifactId>
                <version>3.3.1</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version>
            </dependency>

    spring配置类

    /**
     * 创建spring的配置类,相当于原来的bean.xml
     */
    @Configuration
    @ComponentScan("spring")//配置要扫描的包
    @EnableAspectJAutoProxy//开启spring对注解AOP的支持
    public class SpringConfigtion {
    
    }

    业务层实现类

    /*
    业务层实现类
     */
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
    
    
        @Override
        public void saveAccount() {
            System.out.println("保存账户!!!");
    //        int a=1/0;//模拟异常
        }
    }

    切面类

    @Component("logger")
    @Aspect//把此类配置成一个切面
    public class Logger {
        @Pointcut("execution(* spring..*.*(..))")
        public void pt(){
    
        }
        /**
         * 前置通知
         *计划让其在切入点方法执行之前执行
         */
        @Before(value = "pt()")
        public void beforeprintLog() {
            System.out.println("Logger类中的beforePrintLog方法开始记录日志了。。。前置");
        }
    
        /**
         * 后置通知
         */
        @AfterReturning(value = "pt()")
         public void afterReturningPrintLog(){
         System.out.println("Logger类中的afterReturningPrintLog方法开始记录日志了。。。后置");
         }
    
         /**
         *异常通知
         */
         @AfterThrowing(value = "pt()")
        public void afterThrowingPrintLog(){
            System.out.println("Logger类中的afterThrowingPrintLog方法开始记录日志了。。。异常");
        }
        /**
         *最终通知
         */
        @After(value = "pt()")
        public void afterPrintLog() {
            System.out.println("Logger类中的afterPrintLog方法开始记录日志了。。。最终");
    
        }
    
    
    //    @Around(value = "pt()")
        public Object aroundPrintLog(ProceedingJoinPoint pjp) {
            //获取方法所需参数
            Object[] args = pjp.getArgs();
            Object rtValue = null;
            try {
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。前置");
                rtValue = pjp.proceed(args);
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。后置");
            } catch (Throwable throwable) {
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。异常");
                throwable.printStackTrace();
            }finally {
                System.out.println("Logger类中的aroundPrintLog方法开始记录日志了。。。最终");
            }
            return rtValue;
        }
    
    }

    测试类

    public class Client {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfigtion.class);
            AccountService accountService = (AccountService)context.getBean("accountService");
            accountService.saveAccount();
            context.close();
        }
    }
  • 相关阅读:
    Solr3.5安装测试指南yzn
    Babylon.js 构建 地球,支持切片地图 (四)
    arcgis 4 与deckgl 整合 (一)
    初遇Citymaker (一)
    arcgis 4 与deckgl 整合 (三)
    Babylon.js 构建 地球,支持切片地图 (一)
    Babylon.js 构建 地球,支持切片地图 (五)
    Babylon.js 构建 地球,支持切片地图 (三)
    Babylon.js 构建 地球,支持切片地图 (六)
    Babylon.js 构建 地球,支持切片地图 (二)
  • 原文地址:https://www.cnblogs.com/luzhanshi/p/13229352.html
Copyright © 2011-2022 走看看