使用动态代理增强对象的方法

一、什么是动态代理,动态代理能干什么?

简单的说,动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。

举个栗子,比如你创建了一个类1,里面定义了一个计算a+b的值的方法1,假如你需要在类2中计算a+b+c的值,这个时候你不想再new一个方法,因为前面的a+b的方法,可以做前面一部分的工作了。但是你又不能直接去修改前面那个a+b的方法,因为很多其他的地方用到过方法1,一旦修改了方法1,必然会牵扯到很多不必要的麻烦。这个时候你就可以用动态代理的方法去增强类1中的方法1实现a+b+c的计算然后在类2中使用。当然实际的应用场景可能比这复杂得多。

具体参考传智教育什么是动态代理?两种常用的动态代理方式

二、两种常用的动态代理方式

  1. 基于接口的动态代理(jdk动态代理)
  2. 基于类的动态代理(CGLib动态代理)

三、jdk动态代理

实现方法

只需要调用java.lang.reflect.Prxy类中的newProxyInstance(ClassLoader loader, class<?>[] interfaces, InvocationHandler h)方法

该方法是一个static方法,共有三个参数

第一个参数:类加载器

第二个参数:增强方法所在类实现的接口,支持多个接口(数组形式)。

第三个参数:实现InvocationHandler接口,创建代理对象,写增强的部分。

代码示例

  1. 创建接口,定义方法,这里我创建的是一个UserDao接口,定义了add(int a,int b)方法和update(String id)方法。

    package spring5;
    
    public interface UserDao {
        public int add(int a,int b);
    
        public  String  update(String id);
    }
    
  2. 创建接口实现类,实现定义的两个方法。

    package spring5;
    
    public class UserDaoImpl implements UserDao{
        @Override
        public int add(int a, int b) {
            return a+b;
        }
    
        @Override
        public String update(String id) {
            return id;
        }
    }
    
  3. 创建一个测试方法,增强方法,有两种写法

    (1)创建一个类去实现InvocationHandler接口

    在类中创建要增强类的对象,这里为了提高通用性将对象创建为了Object类。

    class UserDaoProxy implements InvocationHandler {
        private Object obj;
    
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //方法之前
            System.out.println("方法之前执行......"+method.getName()+"传递的参数..."+ Arrays.toString(args));
    
            //被增强的方法执行后再加上一个 c=1
                //method.invoke(obj,args)意思为执行该方法
            Object res = (Integer)method.invoke(obj,args)+1;
    
            return res;
        }
    }
    

    然后调用newProxyInstance方法,第三个参数直接new一个该类对象。

     @Test
     public void test1(){
         Class[] interfaces = {UserDao.class};
         UserDao dao =  (UserDao) Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(),interfaces,new UserDaoProxy(new UserDaoImpl()));
         int res = dao.add(1,2);
         System.out.println("res:"+res);
     }
    

    可以看到,调用该方法的结果变成了4,也就是说实现了a+b+c的计算。
    在这里插入图片描述

    (2)匿名内部类实现。

        @Test
        public void test1(){
            Class[] interfaces = {UserDao.class};
    //        UserDao dao =  (UserDao) Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(),interfaces,new UserDaoProxy(new UserDaoImpl()));
            UserDao dao = (UserDao)Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(), interfaces, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    proxy = new UserDaoImpl();
                    Object res = (Integer)method.invoke(proxy,args)+1;
                    return res;
                }
            });
            int res = dao.add(1,2);
            System.out.println("res:"+res);
        }
    

    该方式同样能实现一样的功能。

四、CGLib动态代理

什么是CGLib

CGLIB是一个强大的、高性能的代码生成代码库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

CGLib动态代理的优势

CGLIB动态代理相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

步骤
  1. 添加cgliib依赖
  2. 创建增强器Enhancer类对象
  3. 调用Enhancer类对象的setSuperclass(Class cls)方法设置需要增强类的对象,参数为需要增强的类。
  4. 调用Enhancer类对象的回调方法enhancer.setCallback(MethodInterceptor interceptor),与jdk动态代理一样,有两种方法。
  5. 获取增强之后的代理对象
示例

cgliib依赖,这里用的版本是我一直在用的,应该还算稳定。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

这是我需要增强的类,为了与jdk动态代理区别开来,是一个没有实现接口的类

package spring5;

public class Book {
    public int add(int a, int b) {
        return a+b;
    }
}

根据步骤测试代码

    @Test
    public void testCGLib(){
        //创建增强器Enhancer类对象
        Enhancer enhancer = new Enhancer();

        //设置需要增强类的对象
        enhancer.setSuperclass(Book.class);

        //调用回调方法,这里使用的是匿名内部类的方法
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object res = (Integer)methodProxy.invokeSuper(o,objects)+100;
                return res;
            }
        });
        
        //获取增强后的类对象
        Book book = (Book) enhancer.create();
        
        //调用方法
        System.out.println(book.add(1,2));
    }

运行结果
在这里插入图片描述

当然CGLib还有一些其他好用的功能,这里只是使用了它的动态代理。

以上就是动态代理的两种方法,如有错误欢迎指正。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值