AOP前言+动态代理

静态代理角色分析–运行前编译好的类


狂神的AOP链接

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

代理角色 被代理角色(真实对象) 共同接口 调用代理角色方法的客户

  • 流程大概 被代理类和代理类实现统一接口,代理类的构造函数参数是被代理类,代理类的接口实现方法中包含被代理类的实现方法,并且增加新功能

静态代理分析

  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
    以上两点是因为
    1.代理类是独立的 增加了被代理类的方法 所以实现分工
    2.集中方便是可以代理类可以通过传递构造函数中的参数 随意的改变被代理的对象
  • 缺点 :
    类多了,一个被代理对象对应着一个代理类 ,代理类都要根据新的被代理对象在构造函数中现new, 打代码工作量变大了 . 开发效率降低 .
  • 不改变原有真实对象的方法想要扩展一些功能 用代理模式非常好

基于接口的动态代理–边运行边生成

静态代理 不改变原码 分工拓展了新的功能 但是代码效率低

  • 每新增一个被代理对象 都会重新写代理类的创建 调用函数这几个语句
    代理类是随着被代理对象 成倍增加的
  • 方法大量重复 要在代理类中重复的写一遍真实对象的所有方法
  • 一个代理对象只能代理一种类型的
Host host=new Host();
Proxy proxy=new Proxy(host);
proxy.rent();

Host host2=new Host();
Proxy proxy2=new Proxy(host2);
proxy2.rent();
;;;;
Host hostN=new Host();
Proxy proxyN=new Proxy(hostN);
proxyN.rent();

比如刚才那个房东的 如果想买两个房子,第二套房子 又来个房东 那你又要new 一个代理类 因为代理类的构造函数是只对应这一个代理对象的 如果我们不能总是手动的去写类,代码量太高,怎么办?

  • 反射 反射可以动态的加载一个类
  • 动态代理和静态代理的角色还是一样,但是我们不用再手动的去写代理类了
    他会动态生成代理类
  • 动态代理分基于接口的动态代理和基于类的动态代理

InvocationHandler

唯一函数invoke()的作用就是对 真实对象 的 方法改造(处理代理实例并返回结果)
在这里插入图片描述

  • 每一个代理对象都有一个与自己关联的调用处理程序(就是代理类),这个程序(类)实现了InvocationHandler接口 能自动生成代理类

  • 不再根据具体的对象来实例化一个代理类 而是根据接口不同 返回这个代理对象
    代理对象再去调用接口方法

  • InvocationHandler接口的方法invoke通过反射机制 method.invoke();

  • 我们在增加真实的对象功能的时候,要调用真实对象的方法 这里采用反射机制来调用 而不是对象.的方式

visit();          
host.rent();
fee();

在这里插入图片描述
这个invoke()的参数
proxy:真实对象
method:真实对象的方法
args:向方法穿的参数
说明执行method.invoke()之前得把上面这三个参数获得
下面来看如何获得代理对象

Proxy

在这里插入图片描述
提供了静态方法,说明直接用类名.静态方法就能返回我们想要的东西
比如动态代理类实例对象~~~~
在这里插入图片描述
在这里插入图片描述
方法:
返回一个程序/代理类:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

第一个参数:类加载器一共三种 根类加载器 系统类加载器 用户类加载器
第二个参数:代理类和被代理对象 共同实现的接口
第三个参数:刚才学的那个接口InvocationHandler new一个传进去就行
我们要一一获得这三个参数 传进去

例子

动态代理类要实现 InvocationHandler接口,因为该接口功能是标志当前类为代理调用处理程序

真正生成代理类的是:Proxy 他的newProxyInstance()可以通过反射返回一个代理类对象 参数前面也说过了需要当前类的加载器,接口,InvocationHandler实现类
最后一个参数就是接口的实现类–自己所以写this就行
这个返回proxy的函数唯一需要变得就是中间的接口,其余都不用变
在这里插入图片描述
在这里插入图片描述

invoke(接口,参数)这个方法通过反射机制调用真是对象的rent方法
我们不用再代理类中传真实对象host了 也不用把它注入到proxy的构造函数
现在彻底不用new了 而是反射invoke方法去调用host的rent方法

在这里插入图片描述
动态代理类有两种实现方式
1.第一种是直接显示的写一个 关联程序实现inovacationHandler接口实现类
其中newProxyInstance的参数第一个和第三个是固定的this.
就是当前该方法执行器的接口实现类
2.第二种是匿名内部类形式的写inovacationHandler的实现类
这样第一个和第二个就是i固定的了 都写成真实角色的对象
在这里插入图片描述

基于子类的动态代理

当真是角色不实现任何接口 如何代理
在这里插入图片描述

还是那个赚钱的操作 故意放了一个i/0 使编译报错 结果转账的减 收账的没有增没有更新
在这里插入图片描述
如果希望所有操作都一起成功一起失败我们要使用事务回滚,曾经的简单的转账Dao和service层要求 两者公用同一连接 且在并发运行时线程隔离
现在出现问题我们猜测下原因:
1.这几个sql语句有谁没有事物,和从前Service一样,需要开启事务
2.有事物但是使用的不是同一个事物,无法做到原子性和一致性

  • 在i/0这个编译错误未添加之前 的增删改操作都正常进行了 如果没有事物是无法提交的 说明有事物 问题不是因为没有事物造成的 不像service
  • 在bean.xml中每一次执行Sql都会从数据源里获取一个新的连接< dataSource >,每个sql都有自己的连接 自己的事物 有4个连接 4个事物

需要使用线程容器,同一线程公用同一事物,把连接和当前线程绑定 从而使一个线程中只有一个能控制事物的对象

我发现你好像没明白什么是同一线程,
完整的执行完这些语句 一次执行 这叫同一线程下 
现在是同一线程里有这么多sql语句确都是不同的事物
 就像当初的service和Dao你就没太理解同一线程 
 他是指你一次运行 不是Service是一个线程dao 是一个
 也不是一个语句是一个线程
 线程的隔离是指两次执行 或者多次执行比如说页面上有俩人都点击转账这个业务 
 那你执行A和B的D和C的两个线程是互相隔离的不会影响 
 你可别以为是几条sql语句互相隔离他们都在同一线程下他们隔离什么

ThreadLocal: 同一线程下一个事物(公共参数的传递) 不同线程间做到线程隔离

实验需要 (全程在bean.xml中注入 不用注解)

  • 1.在pom.xml中添加依赖
    spring-context
    spring-test
    mysql-driver
    dbutils
    c3p0
    junit

  • 2.在bean.xml中配置环境+注入基本的操作对象
    配置数据库源dataSource 数据库连接池的密码用户名的配置等

  • 3.编写三层+实体类 进行转账操作

  • dao
    需要用到query 和 数据库连接 因为上面已经分析过要同一数据库连接
    所以这里面需要封装类 封装类里的函数 返回的是从线程容器中取出来的与本线程绑定的连接

  • 工具类之获取与当前线程绑定的连接
    函数需要做到 :
    创建线程容器-》每次先从线程容器中取连接-》如果取出的连接为空=》说明线程容器没有当前线程A的连接-》从数据源中获取连接放入线程容器当中-》返回线程容器存的当前线程A的连接-》当前线程A运行结束线程容器释放已失效的连接

  • 如果需要在那个函数中调用其它bean对象的时候 就用set方法注入

  • 现在我们需要在返回连接的函数里添加dataSource数据源对象来获取连接所以要加一个setDataSource dataSource.getConnection()
    -QueryRunner怎么用

查询的时候是runner.query
返回list:runner.query(conn,sql,new BeanListHandler<?>(?.class))
返回对象:runner.query(conn,sql,new BeanHandler<?>(?.class),sql占位符参数)

查询全部
   try{
            return runner.query(connectionUtils.getConnection(),sql,new BeanListHandler<Account>(Account.class));
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
 byId查询一个
   try{
            return runner.query(connectionUtils.getConnection(),sql,new BeanHandler<Account>(Account.class),id);
        }catch (Exception e) {
            throw new RuntimeException(e); 
        } 

增删改的时候是query.update()
runner.update(conn,sql,占位符操作数传参)
容器放完了已经保证了 同一线程下共用同一个连接

  • 事物的控制都是在业务层 MYBATIS当中就是在业务层进行的事物控制
    在Service中同一使用一个事物,事物有开启事务,回滚事务,提交事务,结束事物等控制 封装一个事物类
  • 在service的每一个Sql操作中都应该开启事务 如果提交失败回滚事务 然后释放
    -在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 在bean.xml中把刚才操作的注入对象都放里
  • 在这里插入图片描述
    -在这里插入图片描述
    总结:使用jdbc事物来需要一个封装类写事物的机制 回滚 开启 提交 结束
    使用connection封装类确保一个线程一个连接都放在线程容器里了 并且保证并发
    dao层使用jdbc的简单封装方法queryRunner来进行sql语句 会调用工具类返回连接 的函数
    service层一开始有4个事物现在 会调用工具类返回事物(还是那个connection只是为了看着方便我们写成Transaction其实还是和dao一样的那个conn)的函数

以上的操作方法依赖太严重 都需要从工具类里获得事物对象和连接 方法依赖太严重 保证方法独立解耦

在**加粗样式**这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值