使用Spring-AOP诠释代理模式

	代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,
	一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

要说代理模式,离我们最近的、也是面试问到最多的当属Spring-AOP

如果使用过 @point@befer@after 系列的注解就可以很简单的理解,就是在调用方法时,在其前后额外做一些事情,而你只需要关注被调用点的逻辑实现,其他操作都是通过代理的方式帮你进行处理,而你则不需要关注他帮你做的那些事情

而代理模式又可以分为以下:

1. 静态代理

首先我们需要明白的是几个概念:
	1. 真实对象(角色):上边提到的我们自己来实现的事情
	2. 代理对象(角色):帮我们隐藏做的一些附加事情
	3. 抽象对象(角色):通过接口或抽象类声明真实角色实现的业务方法。
同一个接口(抽象角色)有真实对象和代理对象两个实现类,当我们需要调用真实角色的时候,可以直接调用代理对象,
如果需要代理角色帮我们做一些事情的时候, 把真实对象通过构造函数放进代理角色就可以了,
这些代码都是我们写好的,编译后直接可以运行的,不需要jdk帮我们,这种就叫做静态代理,需要jdk帮我们做反射,则为动态代理

请查收代码:

// 同一个接口
public interface UserService {
    void registerUser();
}

@Service
@Transactional
// 实现的真实对象
public class UserImpl implements UserService {
    @Override
    public void registerUser() {
        System.out.println("数据库插入成功。。。");
    }
}
// 实现的代理对象
public class UserImplProxy implements UserService {

    private UserService userService;
    // 通过构造函数将真实对象放进来
    public UserImplProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void registerUser() {
        System.out.println("校验用户名。。。");
        userService.registerUser();
        System.out.println("创建用户成功。。。");
    }

@SpringBootTest
class DemoApplicationTests {
    @Test
    void staticProxy() {
    	// 测试  
    	// 将真实对象放进代理对象中
        UserImplProxy userImplProxy = new UserImplProxy(new UserImpl());
        userImplProxy.registerUser();
    }
}

静态代理输出

2. jdk动态代理(利用反射机制帮我们生成一个实现代理接口的匿名类)

在静态代理的基础上,我们可以认为,jdk帮我们动态组合一个代理对象(通过Proxy.newProxyInstance()动态生成字节码文件)
就可以称为动态代理

请查收代码:

public class UserJdkProxy {

    // 用来接受真实的User对象
    private Class<?> userImpl;
    
	// 同样使用构造函数将真实对象传进来
    public UserJdkProxy(Class<?> userImpl) {
        this.userImpl = userImpl;
    }

	// 让jdk帮我们动态生成一个代理实例(对象)
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(userImpl.getClassLoader(),
                userImpl.getInterfaces(), (proxy, method, args) -> {
                    System.out.println("校验用户名。。。");
                    //the object the underlying method is invoked from
                    Object invoke = method.invoke(userImpl.newInstance(), args);
                    System.out.println("创建用户成功。。。");
                    return invoke;
                });
    }
}

@SpringBootTest
class DemoApplicationTests {
	@Test
    void jdkProxy() {
        UserJdkProxy userJdkProxy = new UserJdkProxy(UserImpl.class);
        UserService user = (UserService) userJdkProxy.getProxyInstance();
        // 代理在真正调用的时候才会去初始化,此点可以通过debug进行验证
        user.registerUser();
    }
}

jdkproxy

3. cglib动态代理

CGLIB 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有
父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
public class UserCgLibProxy implements MethodInterceptor {

    private Object target;

    public Object getProxyInstance(Object target) {
        this.target = target;
        // Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
        Enhancer enhancer = new Enhancer();
        // 将被代理的对象设置成父类
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法,设置拦截器
        enhancer.setCallback(this);
        // 动态创建一个代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) 
  				throws Throwable {
        System.out.println("校验用户名。。。");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("创建用户成功。。。");
        return o1;
    }
}
@SpringBootTest
class DemoApplicationTests {
	@Test
    void cglibProxy() {
        UserService user = (UserService) new UserCgLibProxy().getProxyInstance(new UserImpl());
        user.registerUser();
    }
}

在这里插入图片描述

那我们在使用过程中应该如何进行选择?

总结以下:
cglib创建动态代理对象比jdk创建动态代理效率要高
cglib创建代理对象比jdk创建对象花费时间更长(因为cglib需要动态创建子类嘛)
所以对于单例对象,因为不需要频繁创建对象,使用cglib比较合适,否则 使用jdk
同时还要注意:
    jdk实现动态代理需要实现类通过接口定义业务方法
    cglib由于动态创建子类,对final修饰的方法无法进行代理

那么 spring 是如何进行选择的?


 public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
	 @Override
	 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
	     Class<?> targetClass = config.getTargetClass();
	     if (targetClass == null) {
	       throw new AopConfigException("TargetSource cannot determine target class: " +
	           "Either an interface or a target is required for proxy creation.");
	     }
	       // 判断目标类是否是接口或者目标类是否Proxy类型,若是则使用JDK动态代理
	     if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
	       return new JdkDynamicAopProxy(config);
	     }
	       // 配置了使用CGLIB进行动态代理或者目标类没有接口,那么使用CGLIB的方式创建代理对象
	     return new ObjenesisCglibAopProxy(config);
	   }
	   else {
	       // 上面的三个方法没有一个为true,那使用JDK的提供的代理方式生成代理对象
	     return new JdkDynamicAopProxy(config);
	   }
	 }
   //其他方法略……
}

可以看出, Spring AOP 中的代理使用逻辑:

  1. 如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;
  2. 如果目标对象没有实现接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换。

AOP 的日常使用场景

  • Authentication 权限
  • Transactions 事务
  • Error handling 错误处理
  • Synchronization 同步
  • logging, tracing, profiling and monitoring 记录跟踪 优化 校准
  • Caching 缓存
  • Context passing 内容传递
  • Lazy loading 懒加载
  • Debugging 调试
  • Performance optimization 性能优化
  • Persistence 持久化
  • Resource pooling 资源池
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值