Spring(学习笔记)-动态代理与AOP(源码入门!)

重点标识

Java中的代理,动态代理与静态代理

代理

动态代理

JDK动态代理,前提是一定要有接口,不然实现不了,看看那下面的例子就知道了。

Jdk动态代理

这里用一个简单的计算方法来做一下动态代理演示,首先,定义一个加法接口,如下


public interface ICalculator {
    int add(int a,int b);
}

然后,我们定义一个实现类,实现这个接口,


public class ICalculatorImpl implements ICalculator{
    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

最后,则是进行动态代理,注释已经写的很清楚了,这里我就不啰嗦了。


    public static void main(String[] args) {

        ICalculatorImpl iCalculatorimpl = new ICalculatorImpl();

        /**
         *
         * 1,类加载器
         * 2,代理对象要实现的接口
         * 3,真正的代理对象生成逻辑
         *
         */
        ICalculator iCalculator = (ICalculator)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{ICalculator.class}, new InvocationHandler() {

            /**
             *
             * @param proxy 生成的代理对象
             *
             * @param method 被代理的方法,这里就是add方法
             *
             * @param args 传入的参数
             *
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long l = System.currentTimeMillis();
                long endMillis = System.currentTimeMillis();
                //args是传入的参数,而invoke则是计算的方法,那在这里,我们可以new Object[]{2,5} 这样,不管你传什么,这里都给你改成2和5了
                Object invoke = method.invoke(iCalculatorimpl, args);
                System.out.println("代理方法耗时:" + (endMillis - l) + "毫秒!");
                //这里,相当于被代理方法的返回值,等于说,如果invoke被改了值,那不管传什么,都会是那个写死的值,如给他个999
                return invoke;
            }
        });

        int add = iCalculator.add(2, 4);
        System.out.println(add);
    }

想了下,这里还是稍微解释一下,为什么要强转成ICalculator,而不是他的实现类ICalculatorImpl,答案实际上很简单,这是一个动态代理,看似是采用接口的实现类ICalculatorImpl来进行的逻辑运算,但实际上,Java会动态生成一个代理类,可以理解为ICalculatorImpl#,然后去调用它里面的add方法,那么这样的话,如果我们强转成ICalculatorImpl,就会报一个类型不匹配的错误,有兴趣的同学可以试一下。

那如果没有借口,我们该如何进行动态代理呢,当然是Cglib动态代理。

Cglib动态代理

通过拦截器,把你的目标方法拦截下来,进行处理。

没有接口,没有实现类,生成一个子类。

准备一个需要增强的类


public class UserService {

    public void cat(){
        System.out.println("喵喵喵!");
    }
}

写一个增强类,这里有个点,Cglib是springframework里面的,所以要引入Spring依赖,注释很详细,就不多说了。


public class MyMethodInterceptor implements MethodInterceptor {
    /**
     *
     * @param obj 代理类
     * @param method 方法
     * @param args 参数
     * @param proxy 代理方法
     * @return 返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //这里要注意的是,Cglib动态代理,本质上就是生成一个子类来动态增强父类的方法
        long startMillis = System.currentTimeMillis();
        Object o = proxy.invokeSuper(obj, args);
        long endMillis = System.currentTimeMillis();

        System.out.println(proxy.getSuperName()+"方法执行耗时为:"+(endMillis - startMillis)+"毫秒!");
        return o;
    }
}

最后进行调用:

 public static void main(String[] args) {
        //这个实际上就是字节增强器
        Enhancer enhancer = new Enhancer();
        //先设置父类
        enhancer.setSuperclass(UserService.class);
        //设置拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        
        //如果有接口,可以使用下面的
        //enhancer.setInterfaces();
        //调用,生成代理对象,就是UserService的子类所产生的对象
        UserService userService =(UserService) enhancer.create();
        userService.cat();

    }

AOP

AOP的底层就是动态代理,默认有接口就是用jdk动态代理,没有接口就是用Cglib,但是如果是Spring Boot,版本2之前和Spring一样,从Spring Boot版本2以后,默认情况下都是Cglib。

还是和之前一样,用计算器来演示这个,注意,要使用AOP,我们除了Spring-Context依赖以外,还需要spring-aspects切面依赖,如下

 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.1.4</version>
        </dependency>

计算器接口和实现类这里就不写了,还是上面那部分代码,直接看调用的:


    public static void main(String[] args) {

        //new 大力工厂,这个是Spring提供的,底层就是cglib和jdk动态代理
        ProxyFactory proxyFactory = new ProxyFactory();
        //设置被代理的对象
        proxyFactory.setTarget(new ICalculatorImpl());
        //代理对象要实现的接口
        proxyFactory.addInterface(ICalculator.class);

        //设置为ture,则直走Cglib,设置为false则根据情况,
        // 有接口则使用jdk动态代理,没有接口则使用Cglib动态代理
         proxyFactory.setProxyTargetClass(true);
        //通知,增强
       proxyFactory.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                long startMillis = System.currentTimeMillis();
                Method method = invocation.getMethod();
                Object proceed = invocation.proceed();
                long endMillis = System.currentTimeMillis();
                System.out.println(method.getName()+ "方法耗时: " +(endMillis - startMillis)+" 毫秒!");
                return proceed;
            }
        });
        ICalculator iCalculator = (ICalculator)proxyFactory.getProxy();
        System.out.println(iCalculator.add(
                2, 3
        ));
    }

这里我们可以debug看一下,按照我现在设置的,必须走Cglib动态代理,在代理接口打个断点就可以看到:
在这里插入图片描述
确实是使用了Cglib动态代理。
如果不设置setProxyTargetClass这个,默认为true,我们再看一下,
在这里插入图片描述
就是$Proxy,jdk动态代理了。

结语

放弃很容易,坚持却很难!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值