资讯专栏INFORMATION COLUMN

浅入浅出Java代理的三种实现

lewif / 3085人阅读

摘要:代理模式的实现静态代理优缺点优点只对对需要的方法加代理逻辑。通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法处理的情况。

注意:本文所有的class使用的static修饰主要是为了能在一个类里面测试。实际项目中不应该这样做的,应该分包分class。
文字描述不是很多,还是看代码比较好理解吧...

1. Java代理的理解

代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。
使用场景:如在方法执行前后计算执行时间,记录日志等。在不改变原码的条件下实现这些功能的扩展。

2. 代理模式的实现 2.1 静态代理 2.1.0 优缺点

优点: 只对对需要的方法加代理逻辑。

缺点: 1.每次代理都需要实现一个代理类;2. 代理类功能固定,无法灵活改变。3. 项目会有一大批代理的代码,如果目标对象改变,代理类也需要对应改变,不利于代码的维护。

2.1.1 实现方式一:继承代理(继承方式实现代理)
public class StaticProxyByExtendTest {

    //目标对象
    public static class UserServiceImpl {
        public void login(String username, String pwd) {
            System.out.println("Welcome " + username);
        }
    }
    
    //代理对象
    public static class UserServiceImplProxy extends UserServiceImpl{
        public void login(String username, String pwd) {
            System.out.println("before.... ");//代理额外逻辑
            super.login(username, pwd);//调用原实现方法
            System.out.println("after.... ");//代理额外逻辑
        }
    }

    //测试
    public static void main(String[] args) {
        UserServiceImpl ee = new UserServiceImplProxy();
        ee.login("Stephen", "123");
    }

}
2.1.2 实现方式二:聚合方式(通过实现相同接口)
public class StaticProxyByGroupTest {

    public interface UserService {
        public void login(String username, String pwd);
    }
    
    //目标对象
    public static class UserServiceImpl implements UserService {

        @Override
        public void login(String username, String pwd) {
            System.out.println("Welcome " + username);
        }
        
    }
    
    //代理对象
    public static class UserServiceImplProxy implements UserService {

        private UserService userService;
        
        public UserServiceImplProxy(UserService userService) {
            this.userService = userService;
        }
        
        @Override
        public void login(String username, String pwd) {
            System.out.println("before.... ");//代理额外逻辑
            userService.login(username, pwd);//调用原实现方法
            System.out.println("after.... ");//代理额外逻辑
        }
        
    }
    
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService tt = new UserServiceImplProxy(target);
        tt.login("Stephen", "123");
    }

}

使用聚合方式我们还可以添加其他的代理对象来对已经代理的对象继续做增强代理。

2.2 动态代理(JDK代理)

JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理;

2.2.0 优缺点

优点: 动态代理所有接口。

缺点: 必须依赖使用接口方式来实现代理。

主要实现是使用JDK自带的Proxy类来实现

newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

动态代理不需要对方法逐一实现代理,通过反射循环所有的接口方法,统一动态的加上代理逻辑。我们也可以通过执行方法的名字来过滤当前方法是否需要代理。

2.2.1 基本实现方式
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyByJDKTest {
    
    public interface UserService {
        public void login(String username, String pwd);
    }
    
    //目标对象
    public static class UserServiceImpl implements UserService {

        @Override
        public void login(String username, String pwd) {
            System.out.println("Welcome " + username);
        }
        
    }
    
    //代理对象1
    public static class UserServiceImplProxy {

        private UserService userServiceProxy;
        
        public UserServiceImplProxy(UserService userService) {
        //通过JDK动态代理获取UserService的代理对象
            UserService proxy = (UserService) Proxy.newProxyInstance(
                    userService.getClass().getClassLoader(), //1. 类加载器
                    userService.getClass().getInterfaces(), // 2. 代理需要实现的接口,可以有多个
                    new InvocationHandler() { // 3. 方法调用的实际处理者

                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("before.... ");//代理额外逻辑
                            Object returnValue = method.invoke(userService, args);
                            System.out.println("after.... ");//代理额外逻辑
                            return returnValue;
                        }
                        
                    });
            
            this.userServiceProxy = proxy;
        }

        public UserService getProxy() {
            return this.userServiceProxy;
        }
        
    }
    
    //获取动态代理对象的公共方法
    @SuppressWarnings("unchecked")
    public static  T getJDKProxy(T t) {
        T proxy = (T) Proxy.newProxyInstance(
                t.getClass().getClassLoader(), 
                t.getClass().getInterfaces(), 
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("before.... ");//代理额外逻辑
                        Object returnValue = method.invoke(t, args);
                        System.out.println("after.... ");//代理额外逻辑
                        return returnValue;
                    }
                    
                });
        
        return proxy;
    }

    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        //代理对象1
        UserServiceImplProxy proxy = new UserServiceImplProxy(target);
        proxy.getProxy().login("Stephen", "123");
        
        //代理对象
        UserService jdkProxy = getJDKProxy(target);
        jdkProxy.login("JDK Stephen", "123");
    }

}
2.2.2 优化动态代理

JDK Proxy动态代理进一步优化:

抽象出代理父类

代理类实现自己的before和after方法

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyByJDKTest2 {
    //目标接口
    public interface UserService {
        public void login(String username, String pwd);
    }
    
    //目标对象
    public static class UserServiceImpl implements UserService {

        @Override
        public void login(String username, String pwd) {
            System.out.println("Welcome " + username);
        }
        
    }
    
    //代理对象
    public static class UserServiceImplProxy extends JDKProxy {
        
        public UserService userServiceProxy;

        public UserServiceImplProxy(UserService userService) {
            this.userServiceProxy = getJDKProxy(userService);
        }
        
        @Override
        protected void before(Method method) {
            System.out.println("before.... ");//代理额外逻辑
        }

        @Override
        protected void after(Method method) {
            System.out.println("after.... ");//代理额外逻辑
        }

    }
    
    //Common proxy object class
    public static abstract class JDKProxy {
        
        protected abstract void before(Method method);
        
        protected abstract void after(Method method);
        
        @SuppressWarnings("unchecked")
        protected  T getJDKProxy(T t) {
            T proxy = (T) Proxy.newProxyInstance(
                    t.getClass().getClassLoader(), 
                    t.getClass().getInterfaces(), 
                    new InvocationHandler() {

                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            before(method);//代理额外逻辑
                            Object returnValue = method.invoke(t, args);
                            after(method);//代理额外逻辑
                            return returnValue;
                        }
                        
                    });
            
            return proxy;
        }
    }
    
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        //代理对象
        UserServiceImplProxy proxy = new UserServiceImplProxy(target);
        proxy.userServiceProxy.login("Stephen", "123");
    }

}
2.3 Cglib代理

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。
CGLIB通过继承的方式进行代理,无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。

2.3.0 优缺点

优点:Cglib代理不依赖接口,JDK代理赖接口

缺点:

在Spring的AOP编程中:

如果加入容器的目标对象有实现接口,用JDK代理

如果目标对象没有实现接口,用Cglib代理

2.3.1 使用spring-core实现cglib代理

需要引用spring-core.jarSpring Core » 5.1.8.RELEASE

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class DynamicProxyBySpringCglibTest {

    //目标接口
    public interface UserService {
        public void login(String username, String pwd);
    }
    
    //目标对象
    public static class UserServiceImpl implements UserService {

        @Override
        public void login(String username, String pwd) {
            System.out.println("Welcome " + username);
        }
        
    }
    
    //代理对象
    public static class ProxyFactory implements MethodInterceptor {

        private Object target;
        
        public ProxyFactory(Object target) {
            this.target = target;
        }
        
         public Object getProxyInstance(){
             //1.工具类 Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象
             Enhancer en = new Enhancer();
             //2.设置父类
             en.setSuperclass(target.getClass());
             //3.设置回调函数
             en.setCallback(this);
             //4.创建子类(代理对象)
             return en.create();
         }
        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("before.... ");//代理额外逻辑
            Object returnValue = method.invoke(target, args);
            System.out.println("after.... ");//代理额外逻辑
            return returnValue;
        }

    }
    
    public static void main(String[] args) throws Exception {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) new ProxyFactory(target).getProxyInstance();
        proxy.login("Stephen", "123");
    }

}
2.3.1 使用spring-core实现cglib代理(优化版本)

主要优化点:

提取抽象公共的ProxyFactory类

具体代理类的实现继承自ProxyFactory

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class DynamicProxyBySpringCglibTest1 {

    //目标接口
    public interface UserService {
        public void login(String username, String pwd);
    }
    
    //目标对象
    public static class UserServiceImpl implements UserService {

        @Override
        public void login(String username, String pwd) {
            System.out.println("Welcome " + username);
        }
        
    }
    
    //Common代理对象
    public static abstract class ProxyFactory implements MethodInterceptor {

        private Object target;
        
        public ProxyFactory(Object target) {
            this.target = target;
        }
        
        public Object getProxyInstance(){
             //1.工具类
             Enhancer en = new Enhancer();
             //2.设置父类
             en.setSuperclass(target.getClass());
             //3.设置回调函数
             en.setCallback(this);
             //4.创建子类(代理对象)
             return en.create();
         }
         
        protected abstract void before(Method method);
            
        protected abstract void after(Method method);
        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            before(method);
            Object returnValue = method.invoke(target, args);
            after(method);
            return returnValue;
        }

    }
    
    //具体代理对象
    public static class UserServiceProxy extends ProxyFactory {

        public UserServiceProxy(Object target) {
            super(target);
        }

        @Override
        protected void before(Method method) {
            System.out.println("before.... " + method.getName());//代理额外逻辑
        }

        @Override
        protected void after(Method method) {
            System.out.println("after.... " + method.getName());//代理额外逻辑
        }
        
    }
    
    public static void main(String[] args) throws Exception {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) new UserServiceProxy(target).getProxyInstance();
        proxy.login("Stephen", "123");
    }

}
3.Refs

API java.lang.reflect.Proxy

Spring Core » 5.1.8.RELEASE

理解java的三种代理模式

java代理

Java代理和动态代理机制分析和应用

Java Proxy和CGLIB动态代理原理

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75438.html

相关文章

  • SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程

    摘要:函数式编程,一看这个词,简直就是学院派的典范。所以这期周刊,我们就重点引入的函数式编程,浅入浅出,一窥函数式编程的思想,可能让你对编程语言的理解更加融会贯通一些。但从根本上来说,函数式编程就是关于如使用通用的可复用函数进行组合编程。 showImg(https://segmentfault.com/img/bVGQuc); 函数式编程(Functional Programming),一...

    csRyan 评论0 收藏0
  • 信安 - 收藏集 - 掘金

    摘要:咱妈说别乱点链接之浅谈攻击阅读掘金作者马达编辑迷鹿马达,精通开发开发,擅长接口设计以及平台化建设,独自主导过多个产品。一题目购物应用分环境要求安全学习资料汇总掘金安全学习资料汇总安全学习网站收集 咱妈说别乱点链接之浅谈 CSRF 攻击 - 阅读 - 掘金作者 | 马达编辑 | 迷鹿 马达, 精通PHP开发、Web开发,擅长api接口设计以及平台化建设,独自主导过多个Web产品。目前就职...

    lushan 评论0 收藏0
  • 信安 - 收藏集 - 掘金

    摘要:咱妈说别乱点链接之浅谈攻击阅读掘金作者马达编辑迷鹿马达,精通开发开发,擅长接口设计以及平台化建设,独自主导过多个产品。一题目购物应用分环境要求安全学习资料汇总掘金安全学习资料汇总安全学习网站收集 咱妈说别乱点链接之浅谈 CSRF 攻击 - 阅读 - 掘金作者 | 马达编辑 | 迷鹿 马达, 精通PHP开发、Web开发,擅长api接口设计以及平台化建设,独自主导过多个Web产品。目前就职...

    codecraft 评论0 收藏0
  • 安全攻防战 - 收藏集 - 掘金

    摘要:咱妈说别乱点链接之浅谈攻击阅读掘金作者马达编辑迷鹿马达,精通开发开发,擅长接口设计以及平台化建设,独自主导过多个产品。一题目购物应用分环境要求安全学习资料汇总掘金安全学习资料汇总安全学习网站收集 咱妈说别乱点链接之浅谈 CSRF 攻击 - 阅读 - 掘金作者 | 马达编辑 | 迷鹿 马达, 精通PHP开发、Web开发,擅长api接口设计以及平台化建设,独自主导过多个Web产品。目前就职...

    chanthuang 评论0 收藏0
  • 浅入浅出FlowDroid(一): 简介&基本使用

    摘要:后文将围绕做一些介绍。尽管如此,的使用对新手而言仍然充满了困难。本系列文章基本为个人见解,难免有错误与误解,如有客观错误欢迎提出。 前言 说到Android的污点分析框架,网上的搜索结果大多指向静态的FlowDroid与动态的TaintDroid。尽管由于加固、混淆等技术使得针对Android的静态分析越来越困难,但静态分析的无先验分析能力无法被动态分析取代,使得静态分析仍有发挥空间。...

    wqj97 评论0 收藏0

发表评论

0条评论

lewif

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<