发布时间:2025-06-24 17:16:32  作者:北方职教升学中心  阅读量:095


这样就可以避免静态代理中代理类接口过多的问题。前言

二、

jdk动态代理的使用步骤如下:

  • 创建一个需要动态代理的接口,即Movie接口;

  • 创建一个需要动态代理接口的真实实现,即RealMovie类;

  • 创建一个动态代理处理器,实现InvocationHandler接口,并重写invoke方法去增强真实实现中的目标方法;

  • 在测试类中,生成动态代理的对象;

自定义一个类,实现InvocationHandler接口,并重写里面的invoke方法

public class ProxyInvocationHandler implements InvocationHandler {    //需要动态代理接口的真实实现类    private Object object;    //通过构造方法去给需要动态代理接口的真实实现类赋值    public ProxyInvocationHandler(Object object) {        this.object = object;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        //方法增强        System.out.println("方法执行前操作");        //object是真实实现,args是调用方法的参数        //当代理对象调用真实实现的方法,那么这里就会将真实实现和方法参数传递过去,去调用真实实现的方法        method.invoke(object,args);        //方法增强        System.out.println("方法执行后操作");        return null;    }}

编写测试类,使用自定义的handler生成代理对象并调用目标方法

public class DynamicProxyTest {    public static void main(String[] args) {        //需要动态代理接口的真实实现        LoginServiceImpl realMovie = new LoginServiceImpl();        //动态代理处理类        ProxyInvocationHandler handler = new ProxyInvocationHandler(realMovie);        //获取动态代理对象        //第一个参数:真实实现(被代理对象)的类加载器        //第二个参数:真实实现类(被代理对象)它所实现的所有接口的数组        //第三个参数:动态代理处理器        LoginService loginService = (LoginService) Proxy.newProxyInstance(realMovie.getClass().getClassLoader(),                realMovie.getClass().getInterfaces(),                handler);        loginService.login("user");    }}

执行结果

3.1.1 jdk动态代理模拟实现

由于jdk的代理是在运行期间动态产生字节码,所以很难看到运行期间代理产生的源码,我们可以模拟其原理进行一个简单的实现,代码如下:

定义一个接口,作为被代理的对象,实现类也一并贴出

interface MinuService {        int addMinu();        void reduceMinu();    }    //目标对象实现类    static class Target implements MinuService {        @Override        public int addMinu() {            System.out.println("tager do addMinu");            return 1;        }        @Override        public void reduceMinu() {            System.out.println("tager do reduceMinu");        }    }

定义一个handler,handler的作用可理解为作为代理对象中实际执行方法调用的一个入口

interface MyInvocationHandler {        Object invoke(Object proxy,Method method, Object[] args);    }

定义代理类,代理类是核心实现,在代理类中主要做的事情如下:

  • 引用自定义handler;

  • 引用代理类中待执行的目标方法(该方法其实是运行期间动态生成的);

  • 调用handler中的invoke反射调用目标方法,并得到执行结果;

import java.lang.reflect.Method;public class MyProxy implements JdkInner.MinuService {    private JdkInner.MyInvocationHandler handler;    public MyProxy(JdkInner.MyInvocationHandler handler) {        this.handler = handler;    }    static Method addMinu;    static Method reduceMinu;    static {        try {            addMinu = JdkInner.MinuService.class.getMethod("addMinu");            reduceMinu = JdkInner.MinuService.class.getMethod("reduceMinu");        } catch (NoSuchMethodException e) {            e.printStackTrace();        }    }    @Override    public int addMinu() {        Object invoke = null;        invoke = handler.invoke(this, addMinu, new Object[0]);        return Integer.parseInt(invoke.toString());    }    @Override    public void reduceMinu() {        handler.invoke(this, reduceMinu, new Object[0]);    }}

编写测试类,有没有发现这个写法和上述使用jdk的动态代理很像

public static void main(String[] args) {        MinuService minuService = new MyProxy(new MyInvocationHandler() {            @Override            public Object invoke(Object proxy,Method method, Object[] args) {                System.out.println("before  invoke method");                Object result = null;                try {                    result = method.invoke(new Target(), args);                    System.out.println("result :" + result);                } catch (IllegalAccessException e) {                    e.printStackTrace();                } catch (InvocationTargetException e) {                    e.printStackTrace();                }                return result;            }        });        System.out.println(minuService.getClass());        minuService.addMinu();    }

如果你像查看jdk动态代理时产生的源码,可以考虑使用arthars进行反编译查看

3.2 CGLIB 代理

CGLIB 代理是一个基于字节码操作的代理方式,它是一个强大的、

3.2.2 cglib代理源码模拟实现

上面的案例了解了如何使用cglib进行代理以及代码的实现,下面来模拟一下其底层源码的实现

定义一个目标类

public class CglibTarget {    public void add(){        System.out.println("add()");    }    public void add(int num){        System.out.println("add() :" + num);    }    public void reduce(int count){        System.out.println("reduce() :" + count);    }}

定义代理类

通过上面的介绍了解到,cglib的代理对象是通过生成目标对象的子类实现的,所以代理类需要继承目标类

import org.springframework.cglib.proxy.MethodInterceptor;import java.lang.reflect.Method;public class CglibProxy extends CglibTarget {    private MethodInterceptor methodInterceptor;    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {        this.methodInterceptor = methodInterceptor;    }    static Method add_1;    static Method add_2;    static {        try {            add_1 = CglibTarget.class.getMethod("add");            add_2 = CglibTarget.class.getMethod("add",int.class);        } catch (NoSuchMethodException e) {            e.printStackTrace();        }    }    @Override    public void add() {        try {            methodInterceptor.intercept(this,add_1,new Object[0],null);        } catch (Throwable throwable) {            throwable.printStackTrace();        }    }    public void add(int num){        try {            methodInterceptor.intercept(this,add_2,new Object[]{num},null);        } catch (Throwable throwable) {            throwable.printStackTrace();        }    }    public void reduce(int count){        super.reduce(count);    }}

测类类

import com.congge.aop.cglib.CglibProxy;import com.congge.aop.cglib.CglibTarget;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibTest {    public static void main(String[] args) {        CglibProxy proxy = new CglibProxy();        CglibTarget target = new CglibTarget();        proxy.setMethodInterceptor(new MethodInterceptor() {            @Override            public Object intercept(Object targetObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {                System.out.println("before handle");                Object result = method.invoke(target, args);                System.out.println("after handle");                return result;            }        });        proxy.add(10);    }}

运行代码,得到如下效果

 

3.2.3 cglib代理补充说明

在使用cglib代码编码实现中,注意到在intercept方法参数中有一个Method的参数,这个参数是做什么用的呢?

不妨将代码修改成下面这样

public static void main(String[] args) {        Target target = new Target();        //拿到代理的类        Target proxyTarget = (Target)Enhancer.create(Target.class, new MethodInterceptor() {            @Override            public Object intercept(Object proxyClass, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {                System.out.println("before....");                //Object result = method.invoke(target, args);                Object result = methodProxy.invoke(target, args);                System.out.println("after...");                return result;            }        });        //执行代理类的方法        proxyTarget.get();    }

再次执行,发现仍然可以得到正确的结果,为什么会这样呢?

cglib代理底层通过这种方式为调用者提供了多一种选择,当选择使用methodProxy的invoke方法时,将不反射,而是退化为直接使用原始目标对象去调用方法,某些情况下,可以获得比反射更高的性能。本文将详细介绍AOP的核心技术和底层实现原理。

4.2.1 语法结构

例1:对扫描包中的某个类的方法增强

execution(* com.xxx.xxxClass.方法名(..))

例2:对扫描包中的某个类的所有方法增强

execution(* com.xxx.xxxClass.* (..))

例3:对扫描包中所有类,类中所有方法增强

execution(* com.xxx.service.*.* (..))

例4:所有带有某个注解的方法或类

@annotation(com.xxx.annotation.xxxAnnotation)

上面举的例子都是去切入具体的某个方法、前言

spring 是一个流行的 Java 企业应用程序开发框架,其中的 aop(面向切面编程)是 spring 框架中一个非常重要的概念。比如,一个日志模块可以被称作日志的AOP切面。

四、如果目标对象实现了至少一个接口,则框架使用JDK动态代理;否则,使用 CGLIB 代理。

2.2 AOP中的一些概念

为了后续更全面深入的掌握aop的使用,有必要了解下spring aop的相关概念。

  • 公共业务发生扩展时变得更加集中和方便。可以说在spring框架以及生态体系下,随处可见aop编程思想的运用,借助这一编程思想,在很多特殊的业务场景下,AOP的使用让编码变得易扩展、jdk动态代理是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。spring aop源码探究

    spring底层在aop的代理上是怎么处理的呢,接下来让我们通过源码一探究竟。

    从getBean入手,一路往下走,通过getBean方法,最终获取到了代理对象

    public <T> T getBean(Class<T> requiredType) throws BeansException {        this.assertBeanFactoryActive();        return this.getBeanFactory().getBean(requiredType);    }

    通过getBean方法走到下面这个doGetBean方法里,在这个方法中,注意有一个关键的位置:getSingleton,很多同学在看spring源码的时候发现调用栈非常深,经常陷入一种不知道从何看起的状态,这里说过小技巧,你只需要关注你期望得到的目标即可。这种编程方式将应用程序分成许多独立的部分,称为切面。

    目录

    一、下面列举几种常用的几种自定义切点表达式的写法。  

    2.3.1 aop中的代理实现

    基于接口情况下,使用JDK动态代理

    JDK动态代理,创建一个接口实现类的代理对象,该接口实现类的代理对象会调用该接口的真实实现,并且在代理对象中调用真实的实现类的方法进行增强

    没有接口情况下,使用 CGLIB 动态代理

    CGLIB动态代理,创建一个类子类的代理对象,该子类的代理对象会去调用父类中的方法,并且在子类代理对象调用其父类方法后做增强

    2.4 静态代理与动态代理

    很多同学在理解aop的代理时,很容易对静态代理与动态代理这个概念搞混,顾名思义,静态代理,简单理解就是代理的类或方法定义已经提前定义好了,在需要使用的地方直接调用即可,而动态代理,关键是理解动态这个概念,从程序的角度来说,就是在程序运行过程中,动态生成了目标对象的代理对象。

    AOP是OOP(面向对象编程)的延续,主要用于实现横切关注点,比如:日志记录、事务处理等方面。根据需求的不同,一个应用程序可以有若干切面。

    4.1 环境准备

    4.1.1 引入aop依赖包

    只需要引入aspectjweaver即可

    <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.8.3</version>        </dependency>

    4.1.2 自定义aop配置类

    定义一个切面的配置类,这里扫描的是某个包路径下的所有方法,并且使用了环绕通知,在通知方法里,输出了方法实际执行耗时

    @Component@Aspectpublic class AspectConfig {    @Pointcut("execution(* com.congge.service.aop.*.*(..)))")    private void pointcut(){    }    @Around("pointcut()")    public Object around(ProceedingJoinPoint point) throws Throwable {        long start = System.currentTimeMillis();        Object proceed = point.proceed();        long end = System.currentTimeMillis();        System.out.println("消耗时间:" + (end-start));        return proceed;    }}

    4.1.3 测试方法

    编写测试方法,验证上述aop切面通知是否生效

    @ComponentScan("com.congge.service.aop")@EnableAspectJAutoProxypublic class SpringApp {    public static void main(String[] args) {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApp.class);        UserService userService = context.getBean(UserService.class);        userService.getUser();    }}

    运行之后效果如下,说明aop的环绕通知配置生效了

    4.2 spring aop的切点表达式

    在实际开发中,aop编程的一般步骤是,定义切面类,然后定义具体的切点表达式,然后再在具体的通知方法上面引入切点表达式即可。更灵活,同时也能很好的解决通用的业务问题,提升开发效率。具体来说,

    • 在编译阶段, Spring 会使用 AspectJ 编译器将切面代码编译成字节码文件;

    • 在运行阶段, Spring 会使用 Java 动态代理或 CGLIB 代理生成代理类,这些代理类会在目标对象方法执行前后插入切面代码,从而实现AOP增强的功能;

    Spring AOP 主要使用两种代理方式:JDK动态代理和 CGLIB 代理。写在文末

    本文通过较大的篇幅详细结束了AOP的原理,并通过debug的方式完整走了一遍底层的源码,AOP不仅是spring体系中的一个重要思想,同时在日常的开发中也有较多的应用,希望对看到的同学有用,本篇到此结束,感谢观看。安全控制、这些概念能够更好的指导我们在编码中对aop的原理的理解。 jdk动态代理与cglib代理

    3.1 jdk动态代理

    与静态代理不同,动态代理是根据代理对象,动态创建代理类。 jdk动态代理与cglib代理

    3.1 jdk动态代理

    3.1.1 jdk动态代理模拟实现

    3.2 CGLIB 代理

    3.2.1 cglib代理实现

    3.2.2 cglib代理源码模拟实现

    3.2.3 cglib代理补充说明

    四、在Spring AOP中,切面通过带有@Aspect注解的类实现。spring aop源码探究

    4.1 环境准备

    4.1.1 引入aop依赖包

    4.1.2 自定义aop配置类

    4.1.3 测试方法

    4.2 spring aop的切点表达式

    4.2.1 语法结构

    4.2.2 常见的切点表达式示例

    4.3 spring aop源码分析过程

    五、简单来说,针对应用程序中那些可以预见的,以及归总为公共的处理业务的场景均可以考虑使用aop的编程实现。比如在doGetBean这里,为了快速获取到userService,可以在此处设置条件断点,如下:

    以上述getSingleton为例来说,通过这个方法,可能会得到代理对象,需不需要进去看呢,可以先走一步,如果得到的是代理对象,那么说明getSingleton这个方法中是产生代理对象的地方,按照这个思路,我们走一步看看,发现此刻得到的对象就是一个代理对象,说明确实是在getSingleton这个方法中产生了代理对象;

    以getSingleton继续深入,进入该方法之后发现,从singletonObjects中拿到的就是代理对象了,这里不禁冒出一个问题来了,singletonObjects中是什么时候将代理对象放进去的呢?

    这里不得不说另一个源码的调试和阅读技巧,就是直接看debug中的调用栈,从下面的调用栈可以发现,上一步singletonObjects中存储的这个代理对象就是在其中的某一步产生并放到容器中的;

    这样一来,至少可以说明,产生代理对象的时机在当前这个getSingleton之前,从spring的bean的生命周期来看,要经历解析,创建,初始化,实例化等一些步骤,所以可以将目光定位到创建的过程,即createBean阶段,于是从getBean方法入手作为突破点即可,再次断点进入,来到getBean方法中

    通过getBean一路来到getSingleton方法中,该方法即创建初始的userService的核心代码,而且第一次进入的时候发现userService的bean对象还未创建出来,一直来到该方法的如下位置,利用上面的调试技巧发现就是在这个singletonFactory.getObject()方法得到了代理对象

    继续进入这个singletonFactory.getObject()方法,就来到创建bean的方法中,即createBean

    沿着该方法继续往下走,当走到doCreateBean这里时,发现这个方法执行结束后就成了代理对象

    继续进入doCreateBean方法中,当走完initializeBean方法之后,发现就产生了代理对象

    那么initializeBean里面发生了什么呢?可以断定这个方法的某个地方最终产生了代理对象,于是来到下面这个方法

    进入到applyBeanPostProcessorsAfterInitialization方法中

    来到postProcessAfterInitialization里面之后,沿着wrapIfNecessary继续进入

    历经千辛万苦,最终来到这里,这个方法就是最终产生代理对象的地方,核心创建代理对象的代码就是下面这段

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {            return bean;        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {            return bean;        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);            if (specificInterceptors != DO_NOT_PROXY) {                this.advisedBeans.put(cacheKey, Boolean.TRUE);                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));                this.proxyTypes.put(cacheKey, proxy.getClass());                return proxy;            } else {                this.advisedBeans.put(cacheKey, Boolean.FALSE);                return bean;            }        } else {            this.advisedBeans.put(cacheKey, Boolean.FALSE);            return bean;        }    }

    我们重点关注方法中createProxy这个方法即可

    跳过中间的步骤,最终来到createAopProxy方法中,在这个方法中,展示了如何创建代理对象,以及采用哪种方式创建代理对象,即使用jdk动态代理呢?还是cglib方式呢?

    从这段代码不难看出,这里做了一个判断,如果目标对象是接口,将采用jdk动态代理,如果目标对象是类,则使用cglib的代理,由于在前面的代码中目标对象是普通的类,所以将会产生一个cglib的代理对象

    补充说明:

    在上面判断使用哪种代理方式时,有一个很重要的参数,即判断下面的这个参数的布尔值,通过源码点击进去发现,默认情况下该参数初始值为false

    config.isProxyTargetClass()

    该参数的意义在于,开发者可以通过强制指定这个参数的值,从而改变代理的方式强制使用cglib,如何修改呢,只需要将下面的注解中改为true即可

    五、高质量的 Code 生成类库,可以在运行期扩展 Java 类与实现 Java 接口,可以为没有实现接口的类创建代理对象。高性能、

    4.2.2 常见的切点表达式示例

    下面列举常用的几种切点表达式示例,便于后续的参考

    所有方法执行

    execution(public * *(..))

    名以"XXX"开头的所有方法执行

    execution(* XXX*(..))

    XXX接口中所有方法执行

    execution(* com.xxx.XXX.*(..))

    service包下所有方法执行

    execution(* com.xxx.service..*.*(..))

    service包下的所有连接点

    (仅在Spring AOP中执行方法)

    within(com.xxx.service..*)

    代理实现XXX接口的任何连接点

    (仅在Spring AOP中执行方法)

    this(com.xxx.service.XXXService)

    所有带有@XXX注解的方法或类

    @annotation(com.xxx.annotation.XXX)

    4.3 spring aop源码分析过程

    通过断点,在真正执行userService.getUser()之前,我们可以看到,userService已经是一个代理对象,而且不难看出,这是一个jdk动态代理产生的对象(userService接口存在实现类)

    于是可以断定,在真正执行目标方法的时候,是代理对象在帮我们执行了,那么代理对象在哪里产生的呢?代理对象是什么时候产生的呢?这就是接下来需要搞清的重点所在。性能统计、

    连接点(Join Point)

    哪些方法需要被AOP增强,这些方法就叫做连接点

    通知(Advice)

    AOP在特定的切入点上执行的增强处理

    常用的通知类型有:

    • before,前置通知

    • after,后置通知

    • afterReturning,返回通知

    • afterThrowing,异常通知

    • around,环绕通知

    切入点(Pointcut)

    实际真正被增强的方法,称为切入点

    2.2.1 aop通知类型

    通知(advice)是你希望在横切关注点具体的实现方式,即方法之前触发?执行后触发...,aop中的Advice主要有以下5种类型:

    • 前置通知(Before Advice)
      • 在连接点之前执行的Advice,即方法执行前,使用@Before注解使用这个Advice
    • 返回之后通知(After Retuning Advice)
      • 在连接点正常结束之后执行的Advice,即你的方法正常执行完成之后执行,通过 @AfterReturning注解使用它,前提是你的方法没有抛出异常
    • 抛出(异常)后执行通知(After Throwing Advice)
      • 如果方法执行抛异常的时候,这个Advice就会被执行,通过 @AfterThrowing注解来使用
    • 后置通知(After Advice)
      • 无论连接点通过什么方式退出(方法正常返回或者抛出异常)都会执行在结束后执行这些Advice,通过 @After注解使用
    • 围绕通知(Around Advice)
      • 围绕连接点执行的Advice,方法执行前可以拦截参数,方法执行后可以拿到执行结果,属于几种通知中比较灵活的一种,通过@Around注解使用

    2.3 AOP实现原理

    Spring AOP 的实现原理是基于动态代理字节码操作的。这些切面可以在应用程序的不同位置进行编写和维护,从而提高了应用程序的可重用性和可维护性。CGLIB 代理会在运行时生成一个目标对象的子类,并覆盖其中的方法,以实现AOP的功能。AOP 概述

    2.1 什么是AOP

    AOP,即面向切面编程,AOP是一种编程范式,用于在不修改原始代码的情况下向现有应用程序添加新功能。更优雅、下面是 CGLIB 代理的实现代码:

    public class CglibAopProxy implements AopProxy {     private final AdvisedSupport advised;     public CglibAopProxy(AdvisedSupport advised) {        this.advised = advised;    }     @Override    public Object getProxy() {        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(advised.getTargetSource().getTargetClass());        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));        return enhancer.create();    }     private static class DynamicAdvisedInterceptor implements MethodInterceptor {         private final AdvisedSupport advised;         public DynamicAdvisedInterceptor(AdvisedSupport advised) {            this.advised = advised;        }         @Override        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {            MethodInvocation methodInvocation = new CglibMethodInvocation(                    advised.getTargetSource().getTarget(),                    method,                    args,                    proxy,                    advised.getMethodInterceptor(),                    advised.getTargetSource().getTargetClass()            );            return methodInvocation.proceed();        }    }}

    3.2.1 cglib代理实现

    下面是一段使用cglib进行代理的代码

    import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibTest {    static class Target {        public void get() {            System.out.println("target get");        }    }    public static void main(String[] args) {        Target target = new Target();        //拿到代理的类        Target proxyTarget = (Target)Enhancer.create(Target.class, new MethodInterceptor() {            @Override            public Object intercept(Object proxyClass, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {                System.out.println("before....");                Object result = method.invoke(target, args);                System.out.println("after...");                return result;            }        });        //执行代理类的方法        proxyTarget.get();    }}

    结果看起来和使用jdk代理类似

    与jdk代理不同的是,jdk的代理仅能针对接口代理,而cglib生成的代理对象是一个子类,所以需要注意,使用cglib进行代理的时候,父类不能是final的,并且目标类中的代理方法也不能是final的。

    二、AOP 概述

    2.1 什么是AOP

    2.2 AOP中的一些概念

    2.2.1 aop通知类型

    2.3 AOP实现原理

    2.3.1 aop中的代理实现

    2.4 静态代理与动态代理

    2.4.1 静态代理实现

    三、

  • 三、类,也可以切到某个包下所有的方法,也可以去切某包下带有某注解的方法等等。

  • 公共的业务由代理来完成---实现业务的分工。

  • 缺点:

    • 每一个代理类都必须实现一遍真实实现类(也就是realMovie)的接口,如果接口增加方法,则代理类也必须跟着修改;

    • 每一个代理类对应一个真实实现类(委托类),如果真实实现(委托类)非常多,则静态代理类就非常臃肿,难以胜任。

      2.4.1 静态代理实现

      定义一个接口

      public interface LoginService {    void login(String userId);}

      接口实现

      public class LoginServiceImpl implements LoginService {    @Override    public void login(String userId) {        System.out.println("登录成功,userId:"+userId);    }}

      实现了LoginService 接口的代理实现类

      public class ProxyLoginService implements LoginService {    private LoginServiceImpl loginServiceImpl;    public ProxyLoginService(LoginServiceImpl loginServiceImpl){        this.loginServiceImpl=loginServiceImpl;    }    @Override    public void login(String userId) {        System.out.println("执行登录之前操作");        loginServiceImpl.login("user");        System.out.println("执行登录方法后操作");    }}

      测试方法

      public static void main(String[] args) {        //创建电影院(静态代理)        ProxyLoginService proxyLoginService = new ProxyLoginService(new LoginServiceImpl());        proxyLoginService.login("user1");    }

      静态代理优点

      • 使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情。写在文末


        一、

        切面(Aspect)

        AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。