一、代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强。加入一些非业务性代码,比如事务、日志、报警发邮件等操作。

二、jdk静态代理

1、业务接口

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 业务接口
 * @author pc
 *
 */
public interface UserService {
     
    // 增加一个用户
    public void addUser();
    // 编辑账户
    public void editUser();
 
}

2、业务实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 业务实现类
 * @author pc
 *
 */
public class UserServiceImpl implements UserService {
 
    public void addUser() {
        System.out.println("增加一个用户。。。");
    }
 
    public void editUser() {
        System.out.println("编辑一个用户。。。");
    }
 
}

3、代理类

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
/**
 * 代理类
 *
 * @author pc
 *
 */
public class UserServiceProxy implements UserService {
 
    private UserServiceImpl userImpl;
 
    public UserServiceProxy(UserServiceImpl countImpl) {
        this.userImpl = countImpl;
    }
 
    public void addUser() {
        System.out.println("代理类方法,进行了增强。。。");
        System.out.println("事务开始。。。");
        // 调用委托类的方法;
        userImpl.addUser();
        System.out.println("处理结束。。。");
    }
 
    public void editUser() {
        System.out.println("代理类方法,进行了增强。。。");
        System.out.println("事务开始。。。");
        // 调用委托类的方法;
        userImpl.editUser();
        System.out.println("事务结束。。。");
    }
 
}

  

4、测试类

1
2
3
4
5
6
7
public static void main(String[] args) {
    UserServiceImpl userImpl = new UserServiceImpl();
    UserServiceProxy proxy = new UserServiceProxy(userImpl);
    proxy.addUser();
    System.out.println("----------分割线----------");
    proxy.editUser();
}

5、结果

1
2
3
4
5
6
7
8
9
代理类方法,进行了增强。。。
事务开始。。。
增加一个用户。。。
处理结束。。。
----------分割线----------
代理类方法,进行了增强。。。
事务开始。。。
编辑一个用户。。。
事务结束。。。

  

三、jdk动态代理

1、业务接口

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 业务接口
 * @author pc
 *
 */
public interface UserService {
     
    // 增加一个用户
    public void addUser();
    // 编辑账户
    public void editUser();
 
}

2、业务接口实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * 业务接口实现类
 * @author pc
 *
 */
public class UserServiceImpl implements UserService {
 
    public void addUser() {
        System.out.println("增加一个用户。。。");
    }
 
    public void editUser() {
        System.out.println("编辑一个用户。。。");
    }
}

3、代理类

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
/**
 *
 * @author pc
 *
 */
public class ServiceInvocationHandler implements InvocationHandler {
 
    // 目标对象
    private Object target;
 
    public ServiceInvocationHandler(Object target) {
        super();
        this.target = target;
    }
 
    /**
     * 创建代理实例
     * @return
     * @throws Throwable
     */
    public Object getProxy() throws Throwable {
        return Proxy.newProxyInstance(Thread.currentThread()
                .getContextClassLoader(), this.target.getClass()
                .getInterfaces(), this);
        // 这样写只返回了目标对象,没有生成代理对象。
        // return target;
    }
 
    /**
     * 实现InvocationHandler接口方法
     * 执行目标对象的方法,并进行增强
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        System.out.println("代理类方法,进行了增强。。。");
        System.out.println("事务开始。。。");
        // 执行目标方法对象
        result = method.invoke(target, args);
        System.out.println("事务结束。。。");
        return result;
    }
 
}

  

4、测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
    /**
     * jdk动态代理会生成一个动态代理类,生成相应的字节码,然后通过ClassLoader加载字节码。
     * 该实例继承了Proxy类,并实现了业务接口,在实现的方法里通过反射调用了InvocationHandler接口实现类
     * 的invoke()回调方法。
     * @param args
     * @throws Throwable
     */
    public static void main(String[] args) throws Throwable {
        UserService userService = new UserServiceImpl();
        ServiceInvocationHandler handler = new ServiceInvocationHandler(userService);
        // 根据目标生成代理对象
        UserService proxy = (UserService) handler.getProxy();
        proxy.addUser();
//      proxy.editUser();
 
    }
 
}

5、测试结果

1
2
3
4
代理类方法,进行了增强。。。
事务开始。。。
增加一个用户。。。
事务结束。。。

  

四、cglib动态代理

需要引入cglib的jar包,

在pom.xml加入依赖:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

  

1、业务类,没有实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * 业务类
 * 没有实现接口
 * 如果类是final的,则没法生成代理对象,报错。
 * 如果方法是final的,代理无效
 * @author pc
 *
 */
public class UserServiceImpl {
 
    public void addUser() {
        System.out.println("增加一个用户。。。");
    }
 
    public void editUser() {
        System.out.println("编辑一个用户。。。");
    }
}

2、代理类

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
import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
/**
 * 使用Cglib动态代理
 * @author pc
 *
 */
public class UserServiceCglib implements MethodInterceptor{
 
    private Object target;
     
    /**
     * 创建代理实例
     * @param target
     * @return
     */
    public Object getInstance(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 设置回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }
     
    /**
     * 实现MethodInterceptor接口要重写的方法。
     * 回调方法
     */
    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;   
    }
 
}

  

3、测试类

1
2
3
4
5
6
7
8
9
public class TestCglib {
 
    public static void main(String[] args) {
        UserServiceCglib cglib = new UserServiceCglib();
        UserServiceImpl bookFacadeImpl = (UserServiceImpl)cglib.getInstance(new UserServiceImpl());
        bookFacadeImpl.addUser();
//      bookFacadeImpl.editUser();
    }
}

4、结果:

1
2
3
事务开始。。。
增加一个用户。。。
事务结束。。。

5、如果业务实现类被定义成final类,就会报以下错误

1
2
3
4
5
6
7
8
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class cn.xx.xx.cgilb.UserServiceImpl
    at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
    at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
    at cn.pconline.proxy.cgilb.UserServiceCglib.getInstance(UserServiceCglib.java:30)
    at cn.pconline.proxy.cgilb.TestCglib.main(TestCglib.java:7)

五、总结

1、原理

jdk静态代理实现比较简单,一般是直接代理对象直接包装了被代理对象。

  jdk动态代理是接口代理,被代理类A需要实现业务接口,业务代理类B需要实现InvocationHandler接口。

jdk动态代理会根据被代理对象生成一个继承了Proxy类,并实现了该业务接口的jdk代理类,该类的字节码会被传进去的ClassLoader加载,创建了jdk代理对象实例,

jdk代理对象实例在创建时,业务代理对象实例会被赋值给Proxy类,jdk代理对象实例也就有了业务代理对象实例,同时jdk代理对象实例通过反射根据被代理类的业务方法创建了相应的Method对象m(可能有多个)。当jdk代理对象实例调用业务方法,如proxy.addUser();这个会先把对应的m对象作为参数传给invoke()方法(就是invoke方法的第二个参数),调用了jdk代理对象实例的invoke()回调方法,在invoke方法里面再通过反射来调用被代理对象的因为方法,即result = method.invoke(target, args);。

cglib动态代理是继承代理,通过ASM字节码框架修改字节码生成新的子类,重写并增强方法的功能。

2、优缺点

jdk静态代理类只能为一个被代理类服务,如果需要代理的类比较多,那么会产生过多的代理类。jdk静态代理在编译时产生class文件,运行时无需产生,可直接使用,效率好。

jdk动态代理必须实现接口,通过反射来动态代理方法,消耗系统性能。但是无需产生过多的代理类,避免了重复代码的产生,系统更加灵活。

cglib动态代理无需实现接口,通过生成子类字节码来实现,比反射快一点,没有性能问题。但是由于cglib会继承被代理类,需要重写被代理方法,所以被代理类不能是final类,被代理方法不能是final。

因此,cglib的应用更加广泛一点。

参考:http://blog.csdn.net/fighterandknight/article/details/51200470

http://blog.csdn.net/jiankunking/article/details/52143504

[z]Java代理(jdk静态代理、动态代理和cglib动态代理)的更多相关文章

  1. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  2. 代理模式之静态代理,JDK动态代理和cglib动态代理

    代理模式,顾名思义,就是通过代理去完成某些功能.比如,你需要购买火车票,不想跑那么远到火车站售票窗口买,可以去附近的火车票代售点买,或者到携程等第三方网站买.这个时候,我们就把火车站叫做目标对象或者委 ...

  3. jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

    代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...

  4. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  5. 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  6. Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。

    借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...

  7. Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  8. jdk动态代理和cglib动态代理的区别

    一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...

  9. jdk 动态代理和 cglib 动态代理

    原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载 ...

  10. 动态代理:jdk动态代理和cglib动态代理

    /** * 动态代理类:先参考代理模式随笔,了解代理模式的概念,分为jdk动态代理和cglib,jdk动态代理是通过实现接口的方式创建代理类的,cglib是通过继承类的方式实现的代理类的 * jdk动 ...

随机推荐

  1. 通过rem编写自适应移动端要点

    直接上干货 1,dpr 苹果手机像素是2 普通安卓机是1 也就是说1像素下苹果需要的像素点是安卓机的两倍 所以一个需要15x15显示的图像安卓机仅需要提供15X15即可显示清楚 苹果手机需要要30X3 ...

  2. 【SSM 7】Mybatis底层封装思路

    一.基本概述 在前面的博客中介绍到Mybatis的逆向生成工具,为我们生成了每个实体的基本增删改查的代码,那么每个实体都是那么多的代码,我们很容易的发现,有很大的相似性.对于这部分代码,应该予以抽象封 ...

  3. 部署samba服务之后,在客户端用挂载访问的方式,错误信息:mount: block device //192.168.1.108/mysqldata is write-protected, mounting read-only mount: cannot mount block device //192.168.1.108/mysqldata read-only

    部署samba服务之后,在客户端用挂载访问的方式,错误信息:mount: block device //192.168.1.108/mysqldata is write-protected, moun ...

  4. python的optparse模块使用

    name or flags:就是参数的名称或标志 -f --file,-q --quit 等,其中-f表示option的缩写,--file表示option的全称 nargs:命令行参数的个数,一般使用 ...

  5. Vuejs使用笔记 --- 框架

    这两天学了vuejs的模板,于此纪录一下. 这是整个大文件夹的配置,现在我们看到的所有文件都不需要去管,说要关注也只需要关注“index.html” "index.html"里面是 ...

  6. Sublime Text 全程指引

    Sublime Text 全程指引 by Lucida 包含sublime 的常用快捷操作

  7. ubuntu 'Unable to correct problems, you have held broken packages' 错误

    在用apt 安装软件时,有时会用国内的源以加快下载速度. 但是在使用ubuntu 14.04的过程中,这一过程可能会导致错误“Unable to correct problems, you have ...

  8. 用CMake设置Visual Studio工程中预处理器定义值

    构建VS工程时预处理值是不可缺少的,如动态库的导出配置等.在通过CMake构建VS工程时,可以通过CMake命令进行定义,下面讲三种应用. 字符集:默认装填下VS工程是多字节字符集,如果需要使用Uni ...

  9. 创建Unicode格式的INI文件

    前段时间由于开发一个软件,需要调用别人的接口,虽然我的软件是Unicode编码,对方的模块也是Unicode编码,但是对方提供的接口却是Ansi接口,在非中文系统下,由于涉及到中文路径,导致Ansi和 ...

  10. ajax同步异步问题

    之前一直在写JQUERY代码的时候遇到AJAX加载数据都需要考虑代码运行顺序问题.最近的项目用了到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出去 ...