Spring AOP源码分析(二)JDK动态代理和CGLIB介绍

本篇是介绍java实现代理对象的两种方法,JDK动态代理和CGLIB。 
JDK动态代理:针对你所调用的方法是接口所定义的方法。动态的创建一个类,通过实现目标类的接口来实现代理。 
CGLIB:没有限制。通过继承目标类来创建代理类,实现代理。 
下面看案例: 

案例一,JDK动态代理: 
Person和Animals都实现了Say接口sayHello方法。现在就需要对他们的sayHello方法进行拦截。 
Say接口如下: 

?


1

2

3

4

public interface Say {

 

    public void sayHello();

}

Person类如下: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package com.lg.aop.base;

 

public class Person implements Say{

     

    private String name;

     

    public Person() {

        super();

    }

 

    public Person(String name) {

        super();

        this.name = name;

    }

 

    @Override

    public void sayHello() {

        System.out.println("My name is "+name+"!");

        throw new RuntimeException();

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

     

}

Animal类如下: 

?


1

2

3

4

5

6

7

8

public class Animals implements Say{

 

    @Override

    public void sayHello() {

        System.out.println("I am a animal");

    }

 

}

使用JDK动态代理来创建代理对象的工具类JDKDynamicProxy如下: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

package com.lg.aop.base;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

 

public class JDKDynamicProxy {

     

    public static  Object createProxy(final Object target){

        return Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler(){

 

            @Override

            public Object invoke(Object proxy, Method method, Object[] args)

                    throws Throwable {

                if(method.getName().equals("sayHello")){

                    doBefore();

                    try {

                        method.invoke(target, args);

                    } catch (Exception e) {

                        doThrowing();

                    }

                    doAfter();

                }

                return null;

            }

 

        });

    }

     

    private static void doThrowing() {

        System.out.println("AOP say throw a exception");

    }

     

    private static void doBefore() {

        System.out.println("AOP before say");

    }

 

    private static void doAfter() {

        System.out.println("AOP after say");

    }

 

}

JDK动态代理就是通过Proxy.newProxyInstance来创建代理对象的: 
第一个参数是ClassLoader:因为此次代理会创建一个Say接口的实现类,需要将这个类加载到jvm中,所以用到了ClassLoader。 
第二个参数是代理类要实现的所有接口:当你调用这些接口的方法时都会进行拦截。 
第三个参数是InvocationHandler,每次调用代理对象的方法时,都会先执行InvocationHandler的invoke方法,在该方法中实现我们的拦截逻辑。 
在本案例中,InvocationHandler的invoke方法中,我们的拦截逻辑是这样的,当调用sayHello方法时,进行doBefore、doAfter、doThrowing等拦截。 

测试类如下: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

package com.lg.aop.base;

 

 

public class ProxyTest {

 

    public static void main(String[] args){

        Say say1=new Person("lg");

        say1=(Say)JDKDynamicProxy.createProxy(say1);

        say1.sayHello();

         

        System.out.println("-------------------------------");

         

        Say say2=new Animals();

        say2=(Say) JDKDynamicProxy.createProxy(say2);

        say2.sayHello();

    }

}

测试结果如下: 

?


1

2

3

4

5

6

7

8

AOP before say

My name is lg!

AOP say throw a exception

AOP after say

-------------------------------

AOP before say

I am a animal

AOP after say

我们可以看到实现了相应的拦截。 
这里说明下几个概念, 
(1)Proxy.newProxyInstance的返回结果和目标对象Person实现了同样的接口,但他们之间不能相互转化。即say1=(Person)JDKDynamicProxy.createProxy(say1);是错误的。 
(2)InvocationHandler的invoke(Object proxy, Method method, Object[] args)方法中的Object proxy是Proxy.newProxyInstance的返回的代理对象,不是目标对象Person,因此希望执行原目标对象的sayHello方法时,method.invoke(target, args);所传对象是原目标对象,而不是代理对象proxy。 

这就是JDK动态代理的原理,目前这些拦截都是硬编码写死的,如果我们继续进一步改造,便也可以实现更加灵活的代理,有兴趣的可以实现自己的AOP。 

案例二,CGLIB代理: 
首先在pom文件中引入cglib库。如下: 

?


1

2

3

4

5

<dependency>

        <groupId>cglib</groupId>

        <artifactId>cglib</artifactId>

        <version>3.1</version>

    </dependency>

还是针对同样的Person、Animals、Say接口。只是这次创建代理对象的方式变了,如下: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

package com.lg.aop.base;

 

import java.lang.reflect.Method;

 

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.InvocationHandler;

 

public class CglibProxy {

 

    @SuppressWarnings("unchecked")

    public static <T> T createProxy(final T t){

        Enhancer enhancer=new Enhancer();

        enhancer.setClassLoader(CglibProxy.class.getClassLoader());

        enhancer.setSuperclass(t.getClass());

        enhancer.setCallback(new InvocationHandler(){

 

            @Override

            public Object invoke(Object proxy, Method method, Object[] args)

                    throws Throwable {

                Object ret=null;

                if(method.getName().equals("sayHello")){

                    doBefore();

                    try {

                        ret=method.invoke(t, args);

                    } catch (Exception e) {

                        doThrowing();

                    }

                    doAfter();

                }

                return ret;

            }

             

        });

        return (T)enhancer.create();

    }

     

    private static void doThrowing() {

        System.out.println("AOP say throw a exception");

    }

     

    private static void doBefore() {

        System.out.println("AOP before say");

    }

 

    private static void doAfter() {

        System.out.println("AOP after say");

    }

}

使用cglib的Enhancer来创建,创建出的代理对象继承了指定的class。 
(1)enhancer.setClassLoader:也是指定类加载器,将创建出来的新类加载到jvm中。 
(2)enhancer.setSuperclass(t.getClass()):设置目标类作为代理对象的父类。 
(3)enhancer.setCallback:设置一个回调函数,每次调用代理类的方法时,先执行该回调函数。 
然后就是测试: 

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public class ProxyTest {

 

    public static void main(String[] args){

        cglibTest();

    }

     

    public static void cglibTest(){

        Person p=new Person("lg");

        p=CglibProxy.createProxy(p);

        p.sayHello();

         

        System.out.println("-------------------------------");

         

        Animals animals=new Animals();

        animals=CglibProxy.createProxy(animals);

        animals.sayHello();

    }

}

p=CglibProxy.createProxy(p):由于创建出来的代理对象就是目标对象的子类,所以不用像JDK动态代理那样创建出的代理类只能是Say类型。 
运行效果如下: 

?


1

2

3

4

5

6

7

8

AOP before say

My name is lg!

AOP say throw a exception

AOP after say

-------------------------------

AOP before say

I am a animal

AOP after say

和JDK动态代理一样的效果,实现了拦截。 

总结一下: 
(1)当你所调用的目标对象的方法是接口所定义的方法时,可以使用JDK动态代理或者Cglib。即当你的目标类虽然实现了接口,但是所调用的方法却不是接口方法时,就无法使用JDK动态代理,因为JDK动态代理的原理就是实现和目标对象同样的接口,因此只能调用那些接口方法。 
(2)Cglib则没有此限制,因为它所创建出来的代理对象就是目标类的子类,因此可以调用目标类的任何方法(除去final方法,final方法不可继承),都会进行拦截。 

所以SpringAOP会优先选择JDK动态代理,当调用方法不是接口方法时,只能选择Cglib了。

时间: 2024-08-30 06:32:03

Spring AOP源码分析(二)JDK动态代理和CGLIB介绍的相关文章

Spring AOP源码分析(八)SpringAOP要注意的地方

SpringAOP要注意的地方有很多,下面就举一个,之后想到了再列出来:  (1)SpringAOP对于最外层的函数只拦截public方法,不拦截protected和private方法,另外不会对最外层的public方法内部调用的其他方法也进行拦截,即只停留于代理对象所调用的方法.如下案例:  B类有两个public方法,foo1()和foo2(),foo1内部调用了foo2,简单如下:  ? 1 2 3 4 5 6 7 8 public void foo2() {          Syste

Spring AOP源码分析(七)ProxyFactoryBean介绍

这篇文章里面就要说说Spring自己的AOP,搞清楚哪种方式是Spring自己实现的AOP,哪种方式是Spring引入aspectj的AOP.  Spring自己的AOP实现在于ProxyFactoryBean.先看下使用案例(仍和之前的案例是一样的):接口AService.实现类AServiceImpl.通知MyBeforeAdvice  ? 1 2 3 4 5 6 public interface AService {       public void fooA(String _msg);

spring源码学习【准备】之jdk动态代理和cglib动态代理的区别和性能

一:区别:---->JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了.--->JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理. 性能:--->jdk的动态代理由于jdk版本的升级,渐渐超越cglib  二:都说 Cglib 创建的动态代理

Spring AOP源码分析(四)Spring AOP的JDK动态代理

本篇文章将会介绍上一个例子中的源码执行情况,从中熟悉整个SpringAOP的一些概念和接口设计.  首先整个SpringAOP的分两大过程.  第一个过程:根据xml文件或者注解中配置的拦截信息,生成相应的代理对象,这个代理对象包含了对应的拦截器.  第二个过程:执行所拦截的方法时,就是调用代理对象的执行逻辑,完成各种拦截.  本文章先对第二个过程进行源码解析.  对第一个过程先做简单概述,如果拦截的类的对应方法是接口方法,则使用JDK的Proxy进行代理对象的创建否则使用CGLIB进行代理对象

Spring AOP源码分析(五)Spring AOP的Cglib代理

上一篇文章介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程,仍然是使用上一篇文章的工程案例.  JDK动态代理是由JdkDynamicAopProxy来生成代理对象的,Cglib则是由CglibAopProxy来生成代理对象的.JdkDynamicAopProxy.CglibAopProxy实现了AopProxy接口,如下:  ? 1 2 3 4 5 6 7 public interface AopProxy {       Obje

Spring AOP源码分析(一)AOP介绍和aspectj、SpringAOP入门

首先说说三个名词,面向对象.面向接口编程.面向切面编程(Aspect Oriented Programming 即AOP).  针对java来说  面向对象:是对现实世界的描述,是后两者的基础,大前提.  面向接口编程:接口就是一组规则的集合,这组规则可以有不同的实现方式.如JDBC,它本身仅仅是接口规范,不同的数据库有不同的实现,我们通过JDBC这样的接口编程,就无需关心我们使用的是哪种数据库,可以方便的实现数据库的切换而不必改动代码.  面向切面编程:是对面向对象的补充,主要应用的场景为: 

Spring AOP源码分析(三)Spring AOP中的一些基本接口及其概念

本文章对一些SpringAOP的接口设计进行下介绍,主要是区分三者的关系,AOP联盟.Aspectj.SpringAOP所做的工作.  主要内容:  (1)Advice接口设计  (2)MethodInterceptor接口设计  (3)Advisor和Pointcut接口设计  第一个:Advice接口设计  Advice:AOP联盟定义的通知接口,即拦截到相应的方法后执行的动作.  先看下它的类图:    (1)BeforeAdvice.AfterAdvice:SpringAOP自定义的通知

java动态代理和cglib动态代理示例分享_java

java动态代理类可以分为两种. 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译.在程序运行前,代理类的.class文件就已经存在了. 动态代理:在程序运行时,运用反射机制动态创建而成. 一.首先我们进行java动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: 复制代码 代码如下: package testAOP;public interface Saying {public void sayHello(String name);    public void ta

Spring中JDK的动态代理和CGLIB代理的区别

一,应用范围          1,如果目标对象实现了接口,在默认情况下会采用JDK的动态代理实现AOP        2,如果目标对象实现了接口,也可以强制使用CGLIB生成代理实现AOP        3,如果目标对象没有实现接口,必须引入CGLIB,Spring会在JDK的动态代理和CGLIB代理之间进行切换. 二,强制使用CGLIB代理           1,引入CGLIB的Jar: 2,Spring配置文件中加入强制使用CGLIB代理   <!-- 强制使用CGLIB代理 -->