Spring_AOP的实现机制-动态代理

Posted 奔跑的路奇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring_AOP的实现机制-动态代理相关的知识,希望对你有一定的参考价值。

1 Spring核心之AOP

1.1 AOP的概念

AOP是Aspect-Oriented Programming,即面向切面编程。
AOP的作用:不修改源码的情况下,程序运行期间对方法进行功能增强
开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。
通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。

1.2 AOP的相关术语

Target(目标对象)
要被增强的对象,一般是业务逻辑类的对象。

代理(Proxy)
一个类被 AOP 织入增强后,就产生一个结果代理类。

切面(Aspect)
表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。

连接点(Joinpoint)
所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法(一般是类中的业务方法),因为Spring只支持方法类型的连接点。

切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

Advice(通知/增强)
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执等。通知类型不同,切入时间不同。

Weaving(织入)
是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。

2 AspectJ 对 AOP 的实现

2.1 AspectJ的通知类型

AspectJ 中常用的通知有5种类型::
  前置通知(Before):在目标方法被调用之前调用通知功能;
  后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  返回通知(After-returning):在目标方法成功执行之后调用通知;
  异常通知(After-throwing):在目标方法抛出异常后调用通知;
  环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

2.2 AspectJ的切入点表达式

在这里插入图片描述
以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)。
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中就是方法的签名。
示例

execution(* com.kkb.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.kkb.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟 “*”,表示包、子包下的所有类。

execution(* com.kkb.service.IUserService+.*(..))
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类, 则为该类及其子类中的任意方法。

2.3 代码示例

1 目标类

//目标类
public class SomeServiceImpl{
    public void doThird() {
        System.out.println("执行业务方法doThird()");
    }
}

2 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码

/**
 * @Aspect : 是aspectj框架中的注解
 *      作用:表示当前类是切面类
 *      切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 */
@Aspect
public class MyAspect {

    @After(value = "myPointcut()")
    public void myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
    }

    @Before(value = "myPointcut()")
    public void myBefore(){
        System.out.println("执行前置通知,在目标方法执行之前执行");
    }

    /**
     * @Pointcut : 定义和管理切入点,如果项目中有多个切入点表达式是重复的,可以复用
     *
     * 特点:使用@Pointcut定义在方法上面,这个方法的名称就是切入点表达式的别名其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
     *              在
     */
    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void myPointcut(){
        //无需代码
    }
}

3 静态代理

/**
 静态代理类优缺点
	 优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
	 缺点:
		 1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
		 2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
 */

1:下面是一个去电影院看电影的例子

public interface Movie {
    //看电影
    void play();
}

2、RealMovie 是实现类

/**
 * 核心业务,只看电影
 */
public class RealMovie implements Movie {
    @Override
    public void play() {
        System.out.println("您正在观看电影 《肖申克的救赎》");
    }
}

3:Cinema是代理类

/**
 * 代理类
 */
public class Cinema implements Movie {

    private RealMovie movie;
    //代理角色,代理
    public Cinema(RealMovie movie) {
        super();
        this.movie = movie;
    }

    @Override
    public void play() {
        guanggao(true);(服务型业务)
        //你看电影(核心业务是看电影)
        movie.play();(服务型业务)
        guanggao(false);
    }
    //中介(电影院)的附属操作
    public void guanggao(boolean isStart){
        if ( isStart ) {
            System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
        } else {
            System.out.println("电影马上结束了,爆米花、可乐、口香糖8.8折,买回家吃吧!");
        }
    }

}

4:测试类

public class ProxyTest {

    public static void main(String[] args) {
        RealMovie realmovie = new RealMovie();

        //代理,中介还会添加一些附属操作
        Movie movie = new Cinema(realmovie);
        movie.play();
    }
}

5:控制台输出

电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!
您正在观看电影 《肖申克的救赎》
电影马上结束了,爆米花、可乐、口香糖8.8折,买回家吃吧!

4 AOP的实现机制-动态代理

4.1 JDK动态代理

/**
 *  基于接口的动态代理:实现InvocationHandler接口->调用处理程序Proxy->代理
 *
 * 具体步骤是:
 * a. 实现InvocationHandler接口创建自己的调用处理器
 * b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
 * c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
 * d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
 *
 * 好处:一个动态代理类可以代理多个类,只要是实现了同一个接口即可;复用性高
 *
 */

JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。

4.2 Cglib动态代理

Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。
DK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。

5 静态代理和动态代理的区别

1 静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
2 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
3 静态代理,在程序运行前,代理类的.class文件就已经存在了;动态代理,在程序运行时,运用反射机制动态创建而成。

6 两种动态代理方式区别

1 JDK动态代理只能对实现了接口的类生成代理,而不能针对类;Cglib是针 对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final 。
2 Cglib一个目标类方法会生成两个代理方法,一个重写目标方法,并实现代理逻辑,还有一个直接调用目标类方法。

最后,如果有问题,希望指正,一起进步。

以上是关于Spring_AOP的实现机制-动态代理的主要内容,如果未能解决你的问题,请参考以下文章

Spring_AOP容器

Spring_Aop的xml和注解的使用

spring_AOP编程

深入理解java动态代理的实现机制

java代理机制简单实现

反射机制-动态代理