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>

 

posted @ 2017-11-28 21:56  陆伟  阅读(176)  评论(0编辑  收藏  举报