java中动态代理技术的两种实现方式

       动态代理是使用反射和字节码,在运行期间创建指定接口或类的子类以及它的实例对象的一项技术,通过这个技术可以对代码进行无侵入式的增强。java中动态代理技术有两种实现方式:JDK原生动态代理CGLIB动态代理。

一、JDK原生动态代理

       这一种主要是针对有接口实现的情况,它的底层是创建接口的实现代理类,实现扩展功能。也就是我们要增强的这个类实现了某个接口,那么我就可以使用这种方式。这种方式使用到一个类Proxy和一个接口InvocationHandler
       Proxy:所有动态类的父类,它提供一个静态方法来创建动态代理的class对象和实例,这个方法是newProxyInstance()。
       InvocationHandler:每个动态代理实例都有一个相关联的InvocationHandler方法,在代理实例调用时,方法调用将被转发到InvocationHandler的invole方法。
代码实现:
User:

public class User {
    private String name;
    private Integer age;
    //getter and setter 
}

UserService:

public interface UserService {
    public void addUser(User user);
}

UserServiceImpl:

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(User user) {
        System.out.println("新增用户成功,数据为:"+user.toString());
    }
}

UserServiceProxy:实现InvocationHandler,重写invoke方法,做前置增强,判断用户年龄是否大于0,如果小于等于0就报运行时异常。

public class UserServiceProxy implements InvocationHandler {
    private Object realObj;
    
    public Object getRealObj() {
        return realObj;
    }
    
    public void setRealObj(Object realObj) {
        this.realObj = realObj;
    }

    public UserServiceProxy(Object realObj) {
        super();
        this.realObj = realObj;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        if(objects!=null && objects.length>0 && objects[0] instanceof User){
            User user = (User)objects[0];
            if(user.getAge()<=0){
                throw new RuntimeException("用户年龄不符合");
            }
        }
        Object ret = method.invoke(realObj,objects);
        return ret;
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        User user  =new User();
        user.setName("张三");
        user.setAge(0);
        UserService us = new UserServiceImpl();
        UserServiceProxy usp = new UserServiceProxy(us);
        UserService proxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),usp);
        proxy.addUser(user);
    }
}

运行结果:

Exception in thread "main" java.lang.RuntimeException: 用户年龄不符合
	at DynamicProxy.JDK.UserServiceProxy.invoke(UserServiceProxy.java:30)
	at com.sun.proxy.$Proxy0.addUser(Unknown Source)
	at DynamicProxy.JDK.Client.main(Client.java:13)

过滤起了作用,将年龄改为大于0的数时,能够正常运行。

静态内部类实现方式:

public class JDKProxy {
    @Test
    public void fun01(){
        AccountDao accountDao = new AccountDaoImpl();
        AccountDao proxyDao =  (AccountDao) Proxy.newProxyInstance(accountDao.getClass().getClassLoader(), accountDao.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if("save".equals(method.getName())){
                    System.out.println("权限校验...");
                    return method.invoke(accountDao,args);
                }
                return method.invoke(accountDao, args);
            }
        });
        proxyDao.save();
    }
}

二、CGLIB动态代理

       CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。这是第三方的代理机制,不是jdk自带的. 没有实现接口的类产生代理,使用的是字节码的增强技术,其实就是产生这个类的子类。
       CGLIB通过继承方式实现代理:
       * Enhancer:来指定要代理的目标对象,实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象的所有非final方法的调用都会转发给MethodInterceptor;
       * MethodInterceptor:动态代理对象的方法调用都回转发到intercept方法进行增强。

代码实现:
UserServiceImpl类:无需接口

public class UserServiceImpl {
    public void addUser(User user) {
        System.out.println("新增用户成功,数据为:"+user.toString());
    }
}

UserServiceProxy:

public class UserServiceProxy implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects,MethodProxy proxy) throws Throwable {
        if(objects!=null && objects.length>0 && objects[0] instanceof User){
            User user = (User)objects[0];
            if(user.getAge()<=0){
                throw new RuntimeException("用户年龄不符合");
            }
        }
        Object ret = proxy.invokeSuper(realObj,objects);
        return ret;
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        User user  =new User();
        user.setName("张三");
        user.setAge(0);
        Enhancer enhancer = new EnHancer();
        enhancer.setSuperClass(UserServiceImpl.class);
        enhancer.setCallback(new UserServiceProxy());
        UserServiceImpl usi = (UserServiceImpl) enhancer.create();
        usi.addUser(user);
    }
}

静态内部类实现方式:

public class C_CglibProxy {
    @Test
    public void fun01(){
        AccountDaoImpl accountDao = new AccountDaoImpl();
        //创建enhancer对象
        Enhancer enhancer  = new Enhancer();
        //设置代理的父类
        enhancer.setSuperclass(AccountDaoImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if("save".equals(method.getName())){
                System.out.println("权限校验...");
                return method.invoke(accountDao, args);
                }
                return method.invoke(accountDao, args);
            }
        });
        AccountDaoImpl accountDaoProxy = (AccountDaoImpl) enhancer.create();
        accountDaoProxy.save();
    }
}

三、总结

  1. java的动态代理技术的实现主要有两种方式:
           * JDK原生动态代理
           * CGLIB动态代理
  2. JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理(需要代理的对象必须实现于某个接口);
  3. CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值