zoukankan      html  css  js  c++  java
  • spring AOP

    什么叫AOP?

      这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

     面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。
    实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。 但是人们也发现,在分散代码的同时,也增加了代码的
    重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象
    的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。 也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两
    个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置
    上的编程思想就是面向切面的编程。 一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一
    个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP
    变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。

    1.代理模式readMe:

     1 代理设计模式: 是java中常用的设计模式!
     2 
     3    特点:
     4        01.委托类和代理类有相同的接口或者共同的父类!
     5        02.代理类为委托类负责处理消息,并将消息转发给委托类!
     6        03.委托类和代理类对象通常存在关联关系!
     7           一个代理类对象与一个委托类对象关联!
     8        04.代理类本身并不是真正的实现者!而是通过调用委托类的方法,
     9           来实现功能!
    10 
    11 
    12    按照代理类创建的时机,代理类分为两种:
    13    01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,
    14                  在程序运行之前,.class文件已经存在了!
    15       (serviceImpl 调用了 dao层的方法! 真正的实现是Dao)
    16    02.动态代理:在程序运行期间,通过反射的方式动态的创建出来!
    17 
    18 
    19    按照我们的使用方式:  是由共同的接口还是公共的父类?
    20 
    21      01.jdk动态代理 (接口)
    22           必须知道一个类和一个接口
    23            001.InvocationHandler接口只有一个方法
    24 
    25             public Object invoke(Object proxy, Method method, Object[] args)
    26                    throws Throwable;
    27 
    28            proxy:代理类对象
    29            method:被代理的方法
    30            args:被代理的方法的参数列表
    31 
    32           002.Proxy 类:
    33            public static Object newProxyInstance(ClassLoader loader,
    34                   Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
    35               loader:类加载器
    36               interfaces:代理类实现的所有接口
    37               h:InvocationHandler接口的一个实例   this当前对象
    38                  因为我们想使用jdk动态代理 必须是 代理类 实现 InvocationHandler!
    39                   它让我们传递父接口 我们传递 自身!
    40 
    41      02.cglib动态代理(接口+父类)
    42          必须知道一个类和一个接口
    43            001.MethodInterceptor接口
    44 
    45            public Object intercept(Object obj,
    46               Method method,Object[] args,MethodProxy proxy) throws Throwable;
    47 
    48         intercept是所有拦截器执行的方法,类似于jdk动态代理中的invoke
    49 
    50 
    51           002. Enhancer类
    52 
    53            设置委托类和代理类的公共接口或者公共的父类
    54            public void setSuperclass(Class superclass) {
    55                   if (superclass != null && superclass.isInterface()) {
    56                       setInterfaces(new Class[]{ superclass });
    57                   } else if (superclass != null && superclass.equals(Object.class)) {
    58                       // affects choice of ClassLoader
    59                       this.superclass = null;
    60                   } else {
    61                       this.superclass = superclass;
    62                   }
    63               }
    64 
    65                代理类执行完毕 通知委托类
    66                public void setCallback(final Callback callback) {
    67                       setCallbacks(new Callback[]{ callback });
    68                   }
    69 
    70 
    71                 在Enhancer类的父类AbstractClassGenerator中有一个方法
    72                   创建我们需要的代理类
    73                   protected Object create(Object key)

    2.静态代理:

    01.接口代码:

    package cn.pb.dao;
    
    /**
     * 动物类 父接口
     */
    public interface Animal {
        //主业务
        void eat();
        void sleep();
    }

    02.实现类代码:

    package cn.pb.dao.impl;
    /**
     * 狗狗类 实现了Animal接口
     */
    
    import cn.pb.dao.Animal;
    
    public class Dog implements Animal {
        public void eat() {
            System.out.println("狗狗在啃骨头!");
        }
    
        public void sleep() {
            System.out.println("狗狗在午休!");
        }
    }

    03.静态代理类:

    package cn.pb.staticproxy;
    
    import cn.pb.dao.Animal;
    import cn.pb.dao.impl.Dog;
    
    /**
     * 狗狗的静态代理类
     */
    public class AnimalStaticProxy implements Animal {
    
        private Dog dog;
    
    
        public void sleep() {
            System.out.println("主人在召唤");  //系统级业务
            dog.sleep();
            System.out.println("主人离开"); //系统级业务
        }
    
        public void eat() {
            System.out.println("主人在召唤"); //系统级业务
            dog.eat();
            System.out.println("主人离开"); //系统级业务
        }
    
        public Dog getDog() {
            return dog;
        }
    
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    
    
        /**
         * 我们发现的问题
         * 01:代码冗余
         * 02:把冗余的代码提取成公共的方法
         * 03:有可能小猫咪也有这些方法
         * 04:提取成一个工具类中的方法
         * 05:现在有一个小猫咪 也需要执行 sleep和eat 以及系统级业务方法
         * 06:我们又得创建一个小猫咪对应的代理类
         * 07:动物有很多  ,难道需要我们创建N个代理类吗??肯定!
         */
    }

    04.测试类代码:

    /**
         * 静态代理的测试方法
         */
        @Test
        public void testStaticProxy(){
            AnimalStaticProxy proxy = new AnimalStaticProxy();
            Dog dog=new Dog();
            proxy.setDog(dog);
    
    
            proxy.eat();
            System.out.println("*************");
            proxy.sleep();
        }

    3.JDK动态代理:

    01.接口代码:

    package cn.pb.dao;
    
    /**
     * 动物类 父接口
     */
    public interface Animal {
        //主业务
        void eat();
        void sleep();
    }

    02.实现类代码:

    package cn.pb.dao.impl;
    /**
     * 狗狗类 实现了Animal接口
     */
    
    import cn.pb.dao.Animal;
    
    public class Dog implements Animal {
        public void eat() {
            System.out.println("狗狗在啃骨头!");
        }
    
        public void sleep() {
            System.out.println("狗狗在午休!");
        }
    }

    03.动态代理类代码:

    package cn.pb.jdkdynamicproxy;
    /**
     * JDK的动态代理类
     */
    
    import cn.pb.dao.Animal;
    import cn.pb.dao.impl.Dog;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class AnimalJdkDynamicProxy implements InvocationHandler {
    
        /**
         *  01.我们不确定委托类是谁?委托类的类型 是Object
         *   和委托类建立关联关系
         */
        private Object target;
    
    
        /**
         * 02.给我一个委托类,我返回一个代理类对象
         */
        public Object createProxy(Object target){
            //根据传递的参数 进行对象的关联
            this.target=target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    
        /**
         *
         * @param proxy :代理对象
         * @param method :方法名
         * @param args : 参数列表
         * @return
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("主人在召唤"); //系统级业务   开始事务
            Object result= method.invoke(target,args);  // 主业务
            System.out.println("主人离开"); //系统级业务     日志处理  关闭事务
            return result;
        }
    
    
        //创建测试方法
        public static void main(String[] args) {
            AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
            Animal dog= (Animal) proxy.createProxy(new Dog());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
    
        }
    }

    04.测试代码:

    @Test
        public void testJdkDynamicProxy(){
            AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
            Animal dog= (Animal) proxy.createProxy(new Dog());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
        }

    4.cglib动态代理:

    01.接口代码:

    package cn.pb.dao;
    
    /**
     * 动物类 父接口
     */
    public interface Animal {
        //主业务
        void eat();
        void sleep();
    }

    02.实现类代码:

    package cn.pb.dao.impl;
    /**
     * 狗狗类 实现了Animal接口
     */
    
    import cn.pb.dao.Animal;
    
    public class Dog implements Animal {
        public void eat() {
            System.out.println("狗狗在啃骨头!");
        }
    
        public void sleep() {
            System.out.println("狗狗在午休!");
        }
    }

    03.动态代理类代码:

    package cn.pb.cglibdynamicproxy;
    
    
    /**
     * Cglib动态代理
     */
    import cn.pb.dao.Animal;
    import cn.pb.dao.impl.Dog;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class AnimalCglibDynamicProxy implements MethodInterceptor {
    
        /**
         * 在enhancer中有一个setCallBack(this)
         * 这样就实现了代理类和委托类的关联
         */
        private Enhancer enhancer=new Enhancer();
    
        /**
         *  创建代理类对象
         */
        public  Object  createProxy(Class clazz){
            //设置公共的接口或者公共的类
            enhancer.setSuperclass(clazz);
            //建立关联关系
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        /**
         * 类似于我们jdk中的invoke
         */
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("主人在召唤"); //系统级业务   开始事务
            Object result= proxy.invokeSuper(obj,args);  // 主业务
            System.out.println("主人离开"); //系统级业务     日志处理  关闭事务
            return result;
        }
    
    
        //创建测试方法
        public static void main(String[] args) {
            AnimalCglibDynamicProxy proxy=new AnimalCglibDynamicProxy();
            //这里的参数可以传三种形式01:new Dog().getClass()
            // 02:Class.forName("cn.pb.dao.impl.Dog") 03.Dog.class
            Animal dog= (Animal) proxy.createProxy(new Dog().getClass());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
        }
    }

    04.测试代码:

    @Test
        public void testCglibDynamicProxy(){
            AnimalJdkDynamicProxy proxy=new AnimalJdkDynamicProxy();
            Animal dog= (Animal) proxy.createProxy(new Dog());
            dog.eat();
            System.out.println("**************************");
            dog.sleep();
        }

    就是在方法前或后要做的一些事情。AOP中官方叫法为“切面” 如果写到方法中。后期维护很难。

    1:核心代码

     1 package com.spring.aop;
     2 
     3 public interface ArithmeticCalculator {
     4 
     5     int add(int i, int j);
     6     int sub(int i, int j);
     7     
     8     int mul(int i, int j);
     9     int div(int i, int j);
    10     
    11 }
     1 package com.spring.aop;
     2 
     3 import org.springframework.stereotype.Component;
     4 
     5 @Component("arithmeticCalculator")
     6 public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
     7 
     8     public int add(int i, int j) {
     9         int result = i + j;
    10         return result;
    11     }
    12 
    13     public int sub(int i, int j) {
    14         int result = i - j;
    15         return result;
    16     }
    17 
    18     public int mul(int i, int j) {
    19         int result = i * j;
    20         return result;
    21     }
    22 
    23     public int div(int i, int j) {
    24         int result = i / j;
    25         return result;
    26     }
    27 
    28 }

    2:写一个切面的类

    package com.spring.aop;

    import java.util.Arrays;

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;

    /**
     * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
     */
    @Order(1)
    @Aspect
    @Component
    public class LoggingAspect {
        
        /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
         * 使用 @Pointcut 来声明切入点表达式.
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式.
         */
        @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
        
        /**
         * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
         */
        @Before("declareJointPointExpression()")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            Object [] args = joinPoint.getArgs();
            
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }
        
        /**
         * 在方法执行之后执行的代码. 无论该方法是否出现异常
         */
        @After("declareJointPointExpression()")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends");
        }
        
        /**
         * 在方法法正常结束受执行的代码
         * 返回通知是可以访问到方法的返回值的!
         */
        @AfterReturning(value="declareJointPointExpression()",
                returning="result")
        public void afterReturning(JoinPoint joinPoint, Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends with " + result);
        }
        
        /**
         * 在目标方法出现异常时会执行的代码.
         * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
         */
        @AfterThrowing(value="declareJointPointExpression()",
                throwing="e")
        public void afterThrowing(JoinPoint joinPoint, Exception e){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " occurs excetion:" + e);
        }
        
        /**
         * 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
         * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
         * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
         */
        /*
        @Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public Object aroundMethod(ProceedingJoinPoint pjd){
            
            Object result = null;
            String methodName = pjd.getSignature().getName();
            
            try {
                //前置通知
                System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
                //执行目标方法
                result = pjd.proceed();
                //返回通知
                System.out.println("The method " + methodName + " ends with " + result);
            } catch (Throwable e) {
                //异常通知
                System.out.println("The method " + methodName + " occurs exception:" + e);
                throw new RuntimeException(e);
            }
            //后置通知
            System.out.println("The method " + methodName + " ends");
            
            return result;
        }
        */
    }

    3:配置文件中配置上使用注解和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:p="http://www.springframework.org/schema/p"
        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-4.0.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
                            ">
        <!-- 自动扫描的包 -->
        <context:component-scan base-package="com.spring.aop"></context:component-scan>
    
        <!-- 使 AspectJ 的注解起作用 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

     ***:小知识:先执行哪个切面类

    @Order(1)  值越小越先 执行

     4:基于xml文件配置的方式

    <!-- 配置目标方法的类的bean -->
            <bean id="arithmeticCalculator"
                    class="com.spring.aop.ArithmeticCalculatorImpl"
            ></bean>
            <!-- 配置切面类的bean -->
            <bean id="laoggingAspect"
                    class="com.spring.aop.LoggingAspect"
            ></bean>
            <!-- 配置AOP -->
            <aop:config>
            <!-- 切点表达式  就是配置哪个方法是切面 -->
            <aop:pointcut expression="execution(* com.spring.aop.ArithmeticCalculator.*(int, int))" id="pointcut"/>
            <!-- 调用方法的时候先执行切面 -->
            <aop:aspect ref="laoggingAspect">
                <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
            </aop:aspect>
            </aop:config>
  • 相关阅读:
    Java学习:冒泡排序和选择排序
    Java学习:多态
    Java学习:抽象类与接口
    Java学习:继承
    Java学习:标准类
    Java学习:方法简介
    传参的本质
    new 关键字做的事
    一个引用类型的对象占多大堆空间
    栈中空间大小
  • 原文地址:https://www.cnblogs.com/bulrush/p/7912356.html
Copyright © 2011-2022 走看看