【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(下)

简介: 【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)(下)

AspectJProxyFactory


其实ProxyFactory拥有的功能AspectjProxyFactory都有,它可以使用编程的方式去创建代理


在低版本Spring中定义一个切面是比较麻烦的,需要实现特定的接口,并进行一些较为复杂的配置,低版本Spring AOP的配置是被批评最多的地方。Spring听取这方面的批评声音,并下决心彻底改变这一现状。在Spring2.0中,Spring AOP已经焕然一新,你可以使用@AspectJ注解非常容易的定义一个切面,不需要实现任何的接口


AspectJ是目前大家最常用的 起到集成AspectJ和Spring,也就是我们平时长谈的:自动代理模式。它整个代理的过程全部交给Spring内部去完成,无侵入。


我们只需要配置切面、通知、切点表达式就能自动的实现切入的效果,使用起来极其方便,对调用者可以说是非常透明化的。相信这也是为何当下最流行这种方式的原因~


Demo体验

@Aspect
class MyAspect {
    @Pointcut("execution(* hello(..))")
    private void beforeAdd() {
    }
    @Before("beforeAdd()")
    public void before1() {
        System.out.println("-----------before-----------");
    }
}



在上述切面类定义中我们定义了一个Advisor(采用注解@Aspect标注的),其对应了一个MethodBeforeAdvice,实际上是一个AspectJMethodBeforeAdvice,该Advice对应的是上面的before1()方法,还对应了一个Pointcut:是一个AspectJExpressionPointcut。

该Advisor的语义拦截所有的方法名为**“hello”**的方法在它之前执行MyAspect.before1()方法。


如果我们现在需要创建一个代理对象,其需要绑定的Advisor逻辑跟上面定义的切面类中定义的Advisor类似。则我们可以进行如下编程:

    public static void main(String[] args) {
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new HelloServiceImpl());
    // 注意:此处得MyAspect类上面的@Aspect注解必不可少
        proxyFactory.addAspect(MyAspect.class);
        //proxyFactory.setProxyTargetClass(true);//是否需要使用CGLIB代理
        HelloService proxy = proxyFactory.getProxy();
        proxy.hello();
        System.out.println(proxy.getClass()); //class com.sun.proxy.$Proxy6
    }
输出:
-----------before-----------
this is my method~~
class com.sun.proxy.$Proxy6


这里面很有意思的地方在于:我们只是proxyFactory.addAspect(MyAspect.class);,就自动帮我们完成了方法、通知的绑定工作。


原理


public class AspectJProxyFactory extends ProxyCreatorSupport {
  /** Cache for singleton aspect instances */
  private static final Map<Class<?>, Object> aspectCache = new ConcurrentHashMap<>();
  //基于AspectJ时,创建Spring AOP的Advice  下面详说
  private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();
  public AspectJProxyFactory() {
  }
  public AspectJProxyFactory(Object target) {
    Assert.notNull(target, "Target object must not be null");
    setInterfaces(ClassUtils.getAllInterfaces(target));
    setTarget(target);
  }
  public AspectJProxyFactory(Class<?>... interfaces) {
    setInterfaces(interfaces);
  }
  // 这两个addAspect方法是最重要的:我们可以把一个现有的aspectInstance传进去,当然也可以是一个Class(下面)======
  public void addAspect(Object aspectInstance) {
    Class<?> aspectClass = aspectInstance.getClass();
    String aspectName = aspectClass.getName();
    AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
    // 显然这种直接传实例进来的,默认就是单例的。不是单例我们就报错了~~~~
    if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
      throw new IllegalArgumentException(
          "Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
    }
    // 这个方法就非常的关键了~~~ Singleton...是它MetadataAwareAspectInstanceFactory的子类
    addAdvisorsFromAspectInstanceFactory(
        new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
  }
  public void addAspect(Class<?> aspectClass) {
    String aspectName = aspectClass.getName();
    AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
    MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
    addAdvisorsFromAspectInstanceFactory(instanceFactory);
  }
  // 从切面工厂里,把对应切面实例里面的增强器(通知)都获取到~~~
  private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
    // 从切面工厂里,先拿到所有的增强器们~~~
    List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
    Class<?> targetClass = getTargetClass();
    Assert.state(targetClass != null, "Unresolvable target class");
    advisors = AopUtils.findAdvisorsThatCanApply(advisors, targetClass);
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
    AnnotationAwareOrderComparator.sort(advisors);
    addAdvisors(advisors);
  }
}



注意:


  • 需要注意的是在使用AspectjProxyFactory基于切面类创建代理对象时,我们指定的切面类上必须包含@Aspect注解。
  • 虽然我们自己通过编程的方式可以通过AspectjProxyFactory创建基于@Aspect标注的切面类的代理,但是通过配置<aop:aspectj-autoproxy/>(@EnableAspectJAutoProxy)使用基于注解的Aspectj风格的Aop时,Spring内部不是通过AspectjProxyFactory创建的代理对象,而是通过ProxyFactory(这个在分析自动代理源码的时候有说到过~~~~)


总结


这三个类本身没有什么关系,但都继承自:ProxyCreatorSupport,创建代理对象的核心逻辑都是在ProxyCreatorSupport中实现的


AspectJProxyFactory,ProxyFactoryBean,ProxyFactory 大体逻辑都是:


1.填充AdvisedSupport(ProxyCreatorSupport是其子类)的,然后交给父类ProxyCreatorSupport。


2.得到JDK或者CGLIB的AopProxy


3.代理调用时候被invoke或者intercept方法拦截 (分别在JdkDynamicAopProxy和ObjenesisCglibAopProxy(CglibAopProxy的子类)的中)

并且在这两个方法中调用ProxyCreatorSupport的getInterceptorsAndDynamicInterceptionAdvice方法去初始化advice和各个方法直接映射关系并缓存

相关文章
|
2月前
|
人工智能 监控 Java
面向切面编程(AOP)介绍--这是我见过最易理解的文章
这是我见过的最容易理解的文章,由浅入深介绍AOP面向切面编程,用科普版和专家版分别解说,有概念,有代码,有总结。
|
3月前
|
Java Spring
JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: ● JDK动态代理只提供接口的代理,不支持类的代理Proxy.newProxyInstance(类加载器, 代理对象实现的所有接口, 代理执行器) ● CGLIB是通过继承的方式做的动态代理 , 如果某个类被标记为final,那么它是无法使用 CGLIB做动态代理的。Enhancer.create(父类的字节码对象, 代理执行器)
|
3月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类方法的执行。主要通过两种方式实现:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口,利用`java.lang.reflect.Proxy`类和`InvocationHandler`接口;CGLIB则通过字节码技术生成目标类的子类作为代理,适用于未实现接口的类。两者均用于在方法执行前后添加额外逻辑,如日志记录、权限控制等,广泛应用于AOP框架中。
103 2
|
4月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类的方法调用,无需修改原代码。它有两种主要实现方式:JDK动态代理和CGLIB动态代理。 - **JDK动态代理**:通过`java.lang.reflect.Proxy`类和`InvocationHandler`接口实现,适用于实现了接口的类。它在方法调用前后插入额外逻辑,如日志记录、权限控制等。 - **CGLIB动态代理**:基于字节码技术,为未实现接口的类生成子类作为代理,重写父类方法以添加增强逻辑。适用于没有接口的类,但要求目标类不能是`final`类或方法。
|
4月前
|
Java API 数据安全/隐私保护
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
112 0
探索Java动态代理的奥秘:JDK vs CGLIB
|
7月前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
170 53
|
7月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
1004 2
什么是AOP面向切面编程?怎么简单理解?
|
7月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
504 5
|
8月前
|
Java 关系型数据库 MySQL
【编程基础知识】Eclipse连接MySQL 8.0时的JDK版本和驱动问题全解析
本文详细解析了在使用Eclipse连接MySQL 8.0时常见的JDK版本不兼容、驱动类错误和时区设置问题,并提供了清晰的解决方案。通过正确配置JDK版本、选择合适的驱动类和设置时区,确保Java应用能够顺利连接MySQL 8.0。
766 1
|
8月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
93 1