反射和动态代理

原文
https://zhuanlan.zhihu.com/p/62534874
https://zhuanlan.zhihu.com/p/63126398

尽管动态代理看起来似乎有一定难度,但是这一块必须给我拿下嗷铁汁。因为Spring的事务控制依赖于AOP,AOP底层实现便是动态代理,环环相扣。到最后,还是看基本功。(IOC用了反射?)

代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求。

静态代理的实现比较简单:编写一个代理类,实现与目标对象相同的接口,并在内部维护一个目标对象的引用。通过构造器塞入目标对象,在代理对象中调用目标对象的同名方法,并添加前拦截,后拦截等所需的业务功能。

静态代理和直接修改没有多大区别,甚至,我认为,静态代理是脱裤子放屁地直接修改。

原文写法(这和@Autowired有关联么)
延伸阅读:@Autowired写在变量上和构造器上的区别
https://blog.csdn.net/kane0409/article/details/78865964

public class CalculatorProxy implements Calculator {
        //代理对象内部维护一个目标对象引用
	private Calculator target;     
        //构造方法,传入目标对象
	public CalculatorProxy(Calculator target) {
		this.target = target;
	}
 //调用目标对象的add,并在前后打印日志
	@Override
	public int add(int a, int b) {
		System.out.println("add方法开始...");
		int result = target.add(a, b);
		System.out.println("add方法结束...");
		return result;
	}
        //调用目标对象的subtract,并在前后打印日志
	@Override
	public int subtract(int a, int b) {
		System.out.println("subtract方法开始...");
		int result = target.subtract(a, b);
		System.out.println("subtract方法结束...");
		return result;
	}

	//乘法、除法...
}

我的菜逼写法:

public class CalculatorProxy implements Calculator {
    public static Calculator target=new CalculatorImpl();  //幼儿园水平写法  //不写new,报空指针异常(没有初始化,堆内存开辟空间)

   /* public CalculatorProxy(Calculator target) {
        this.target = target;
    }*/

    //调用目标对象的add,并在前后打印日志
    @Override
    public int add(int a, int b) {
        System.out.println("add方法开始...");
        int result = target.add(a, b);
        System.out.println("add方法结束...");
        return result;
    }
    //调用目标对象的subtract,并在前后打印日志
    @Override
    public int subtract(int a, int b) {
        System.out.println("subtract方法开始...");
        int result = target.subtract(a, b);
        System.out.println("subtract方法结束...");
        return result;
    }
    //乘法、除法...
}

目标对象都是
public class CalculatorImpl implements Calculator {…}

区别:
抽象类不能被直接实例化,他选择用构造器来初始化,优雅。虽然我们都是最终都还是要知道是调Calculator的哪一个实现类,但是他是Test才知道(那他就可以传点其他实现类啊什么的,CalculatorImpl()或者其他实现了那个接口的实现类(跑到代理类代码时可以直接顺畅转换:父包子、大包小)),而我在代理类代码里就给写死了(CalculatorImpl())【他符合开、闭原则。我修得在源码修,不好。而他只要是在扩展里也就是Test里动就行了】,他有源代码气质。
我和他的写法的差别,这感觉其实就有点类似于反射的好处了——最终用时(运行时)才知道传的什么类,这样上游代码就没写死,多么解耦合哦。

静态代理的优点:
并未直接修改源程序,符合开闭原则:对扩展开放,对修改关闭

静态代理的缺点:
如果Calculator有几十个、上百个方法,修改量太大 ×
存在重复代码(都是在核心代码前后打印日志) ×
日志打印硬编码在代理类中,不利于后期维护:比如你花了一上午终于写完了,组长告诉你这个功能取消,于是你又要打开Calculator花十分钟删除全部新增代码!×

代理类是我们事先编写的,而且要和目标对象类实现相同接口。(这里是手动找到接口名,可是我们的主角是目标对象类/被代理类,面向对象嗷。那么,如何能动态由类找到它实现接口名呢——反射?~ 好像没有,也不必,弄个接口是逃不掉喽【重点不是接口不接口,而是代理对象要和目标对象有相同方法。你说的不用接口,那就是自己写方法呗。或者实现有同样方法的其他接口。】,配合下多态吧~ 。接口名用时传吧,也是反射呢~)

代理对象构造器的参数类型是Calculator,这意味着它只能接受Calculator的实现类对象,亦即我们写的代理类CalculatorProxy只能给Calculator做代理,它们绑定死了。(嘤嘤嘤,他倒利用了一下继承多态的性质,(然后还不满足吗?[因为,如果现在我们系统需要全面改造,(实际开发中往往需要)给其他类也添加日志打印功能,就得为其他几百个接口都各自写一份代理类。所以,显然确定接口类型名的写法不行!]还能改善么?用反射?)。而我写的幼儿园代码还特么只能一态 -_-||)

为了实现代理,(我们编写了个接口),我们编写了CalculatorProxy(代理对象),并通过构造器传入CalculatorImpl(目标对象),调用目标对象同名方法的同时添加增强代码。

言语修正:
代理类 :
代理对象: 代理类的对象 calculatorProxy(用实现接口类型接收(大包小,自动类型转换),写作calculator)
目标对象:被代理的类的对象

//把目标对象通过构造器塞入代理对象
		Calculator calculator = new CalculatorProxy(new CalculatorImpl());
		//代理对象调用目标对象方法完成计算,并在前后打印日志
		calculator.add(1, 2);
		calculator.subtract(2, 1);

由代码可知,我们其实想要的并不是代理类,而是代理对象!(类比一下理解,并不完全如上代码所写)那么,能否让JVM根据接口自动生成代理对象呢?比如,有没有一个方法,我传入接口,它就给我自动返回代理对象呢?


动态代理
要用动态代理完成事务管理,还需要自己编写一个通知类(Spring:TransactionManager),并把通知对象传入代理对象,通知负责事务的开启和提交,并在代理对象内部调用目标对象同名方法完成业务功能。

invocationHandler 调用处理器

invoke 调用

我们能想到的方案,Spring肯定也知道。同样地,Spring为了实现事务,也编写了一个通知类,TransactionManager。利用动态代理创建代理对象时(怎么用?),Spring会把transactionManager(@Transaction?)织入代理对象,然后将代理对象注入到UserController。

所以我们在UserController中使用的userService其实是代理对象,而代理对象才支持事务。

原先不用AOP时,交叉业务直接写在方法内部的前后,用了AOP交叉业务写在方法调用前后。这与AOP的底层实现方式有关:动态代理其实就是代理对象调用目标对象的同名方法,并在调用前后加增强代码。不过这两种最终运行效果是一样的。

而所谓的模块化,我个人的理解是将切面代码做成一个可管理的状态。比如日志打印,不再是直接硬编码在方法中的零散语句(硬编码:就是写死,无法通过配置文件替换),而是做成一个通知类,通过通知去执行切面代码。

所以,现在需求已经很明确,我们需要一个通知类(TransactionManager)执行事务,一个代理工厂帮助生成代理对象,然后利用动态代理将事务代码织入代理对象的各个方法中。

代理对象方法 = 事务 + 目标对象方法。

事务操作,必须使用同一个Connection对象。如何保证?第一次从数据源获取Connection对象并开启事务后,将它存入当前线程的ThreadLocal中,等到了DAO层,还是从ThreadLocal中取,这样就能保证开启事务和操作数据库使用的Connection对象是同一个。

玩耍:
AOP事务具体代码实现
https://pan.baidu.com/share/init?surl=32yohBEUszAqjH66bXkfjA
提取码:7icy

补充:
Java 动态代理作用是什么?
https://www.zhihu.com/question/20794107/answer/658139129
“能否不写代理类(Me:不写死,没有事(运行)前/事先产生.class,而是运行时才产生Class),而直接得到代理Class对象,然后根据它创建代理实例(反射)。”

该问题下的其他回答:
动态代理的作用是什么:
Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;
可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;
解耦,通过参数就可以判断真实类,不需要事先实例化,更加灵活多变。

java反射与动态代理的理解
https://blog.csdn.net/familyshizhouna/article/details/78905997#_150
https://www.cnblogs.com/aspirant/p/9036805.html

这个说的好:
原文:
https://www.cnblogs.com/jacksontao/p/8552357.html
反射的作用:
1、动态地创建类的实例,将类绑定到现有的对象中,或从现有的对象中获取类型(两个是同义句?就倒过来说,换了个角度嘛,同义替换)。
2、应用程序需要在运行时从某个特定的程序集中载入一个特定的类
反射 一般使用 Class.forName()方法;
动态代理就是实现InvocationHandler 接口;
要想理解反射的原理,首先要了解什么是类型信息。Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。
动态代理
代理模式是为了提供额外或不同的操作,而插入的用来替代”实际”对象的对象,这些操作涉及到与”实际”对象的通信,因此代理通常充当中间人角色。Java的动态代理比代理的思想更前进了一步,它可以动态地创建并代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。
学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,它利用的是反射机制,依赖注入就不用多说了,而对于Spring的核心AOP来说,使用了动态代理,其实底层也是反射。
……

待回答:
java反射在动态代理中的应用?
Spring的核心AOP来说,使用了动态代理,其实底层也是反射。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值