问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

一个程序问题,广大大牛们可不可以用动态代理模式实现(调用method方法后...

发布网友 发布时间:2022-04-29 00:47

我来回答

2个回答

懂视网 时间:2022-05-03 00:08

  接着上一篇的教程,本章我们继续讲SmartSql。今天的主题是动态仓储。

  老规矩,先上一个项目结构

技术图片

  从第二章开始。我们将原来的单一项目做了一个分离。方便之后的更新。

  在这个结构中。原本上一章的DataAccess没有了。取而代之的是Repository。这个就是动态仓储的项目。接下来我们从这个Repository项目开始说。这也是动态仓储的核心。

二、Repository项目

1. Nuget依赖

  SmartSql有一个独立的动态仓储库,即:SmartSql.DyRepository。如果你想使用动态仓储,引用它就行啦。

2. 第一个仓储接口

  引用完库,接下来就是创建我们的第一个仓储接口—IArticleRepository。废话不到,先上代码再一一解释。

技术图片
 1 using SmartSql.DyRepository;
 2 using SmartSql.DyRepository.Annotations;
 3 using SmartSqlSampleChapterTwo.Entity;
 4 using System.Data;
 5 
 6 namespace SmartSqlSampleChapterTwo.Repository
 7 {
 8 [SqlMap(Scope = "CustomScope")]
 9 public interface IArticleRepository : IRepository<T_Article, long>
10  {
11  [Statement(CommandType = CommandType.Text, Execute = ExecuteBehavior.ExecuteScalar, Id = "Offline")]
12  int OfflineArticle([Param("Id", FieldType = typeof(long))] long articleId);
13 
14  [Statement(Sql = "Update T_Article Set Status = 1 Where Id = @Id")]
15  int OnlineArticle([Param("Id")] long article);
16  }
17 }
IArticleRepository

2.1 默认接口 IRepository

  看完代码是不是发现和上一章的DataAccess有很大的区别,那些CURD的方法都没有了。

  这是SmartSql内置的一些默认接口,它包括以下这些接口,这些接口基本可以满足大部分普通业务场景了。

 1 int Insert(TEntity entity);
 2 
 3 int Update(TEntity entity);
 4 
 5 [Statement(Id = "Update")]
 6 int DyUpdate(object dyObj);
 7 
 8 int Delete(object reqParams);
 9 
10 [Statement(Id = "Delete")]
11 int DeleteById([Param("Id")] TPrimary id);
12 
13 TEntity GetEntity(object reqParams);
14 
15 [Statement(Id = "GetEntity")]
16 TEntity GetById([Param("Id")] TPrimary id);
17 
18 [Statement(Execute = ExecuteBehavior.ExecuteScalar)]
19 int GetRecord(object reqParams);
20 
21 IList<TEntity> QueryByPage(object reqParams);
22 
23 IList<TEntity> Query(object reqParams);
24 
25 [Statement(Execute = ExecuteBehavior.ExecuteScalar)]
26 bool IsExist(object reqParams);

2.2 SqlMap特性

  这个特性是用于指定Scope的配置。这个对应于Map中的Scope属性。这里我定义了“CustomScope”。那对应的Map中也将与之对应。如下图:

  技术图片

2.3 Statement特性

  这个特性略微有点复杂,其中包含了6个属性,接下来我们一个个看。

2.3.1 Scope

  这个特性和SqlMap的Scope作用是一样的。区别在于Statement的级别更高。

2.3.2 Id

  指定此函数所使用的Statement。依据是Id。例:

// 接口定义
[Statement(Id = "TestId")]
int CustomStatementId();
<!-- Statement定义 -->
<Statement Id="TestId">
 db script...
</Statement>

2.3.3 Execute

   Execute是一个ExecuteBehavior枚举,用于指定此函数执行Sql脚本的方式。

ExecuteBehavior
Auto ORM自动识别
Execute 返回影响行数,主要用于执行写操作。
ExecuteScalar 返回第一行第一列的数据,主要用于返回自增主键和获取结果数
Query 返回List
QuerySingle 返回第一行数据
GetDataTable 返回DataTable
GetDataSet 返回DataSet

 

  

 

 

 

 

 

 

 

 

 

2.3.4 Sql

  特殊场景下,可以直接使用此属性定义Sql脚本,而不用配置SqlMap。如IArticleRepository的OnlineArticle定义。

2.3.5 CommandType

  这个属性是ADO.NET的CommandType枚举。作用也完全相同

2.3.6 SourceChoice

  指定数据源,可以指定Write或Read。

3. Startup

  在上一章节中,我们在Startup中注册了SmartSql,现在我们要继续注册动态仓储。代码也很简单,只要在AddSmart方法完成后继续调用AddRepositoryFromAssembly即可。如下:

services.AddSmartSql(builder =>
{
 builder.UseAlias("SmartSqlSampleChapterTwo"); // 定义实例别名,在多库场景下适用。
 //.UseXmlConfig(ResourceType.File,"MyConfig.xml");
}).AddRepositoryFromAssembly(options =>
{
 // SmartSql实例的别名
 options.SmartSqlAlias = "SmartSqlSampleChapterTwo";
 // 仓储接口所在的程序集全称
 options.AssemblyString = "SmartSqlSampleChapterTwo.Repository";
 // 筛选器,根据接口的Type筛选需要的仓储
 options.Filter = type => type.FullName.Contains("Sample");
 // Scope模板,默认是"I{Scope}Repository"
 options.ScopeTemplate = "I{Scope}Repository";
});

  这个方法中会抛出一个AssemblyAutoRegisterOptions,方便用户注册指定的仓储。

4. Controller的变化

  在Sample中,我们直接让Controller引用了Repository,实际场景中。我们可以在任何需要仓储的地方引用仓储。代码如下:

技术图片
using Microsoft.AspNetCore.Mvc;
using SmartSqlSampleChapterTwo.Entity;
using SmartSqlSampleChapterTwo.Repository;
using System.Collections.Generic;

namespace SmartSqlSampleChapterTwo.Api.Controllers
{
 /// <summary>
 /// 
 /// </summary>
 [Route("[controller]/[action]")]
 public class ArticleController : Controller
 {
 private readonly IArticleRepository _articleRepository;

 /// <summary>
 /// constructor
 /// </summary>
 /// <param name="articleRepository"></param>
 public ArticleController(IArticleRepository articleRepository)
 {
  _articleRepository = articleRepository;
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="article"></param>
 /// <returns></returns>
 [HttpPost]
 public T_Article Add([FromBody] T_Article article)
 {
  article.Id = _articleRepository.Insert(article);
  return article;
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="id"></param>
 /// <returns></returns>
 [HttpGet]
 public T_Article Get([FromQuery] long id)
 {
  return _articleRepository.GetById(id);
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="article"></param>
 /// <returns></returns>
 [HttpPost]
 public bool Update([FromBody] T_Article article)
 {
  return _articleRepository.Update(article) > 0;
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="id"></param>
 /// <param name="status"></param>
 /// <returns></returns>
 [HttpPost]
 public bool UpdateStatus([FromQuery] long id, [FromQuery] int status)
 {
  return _articleRepository.DyUpdate(new
  {
  Id = id,
  Status = status
  }) > 0;
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="id"></param>
 /// <returns></returns>
 [HttpGet]
 public bool IsExist([FromQuery] long id)
 {
  return _articleRepository.IsExist(new
  {
  Id = id
  });
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="key"></param>
 /// <returns></returns>
 [HttpGet]
 public IEnumerable<T_Article> Query([FromQuery] string key = "")
 {
  return _articleRepository.Query(new
  {
  Title = key
  });
 }

 /// <summary>
 /// 
 /// </summary>
 /// <param name="id"></param>
 /// <returns></returns>
 [HttpGet]
 public int Offline([FromQuery] long id)
 {
  return _articleRepository.OfflineArticle(id);
 }
 
 /// <summary>
 /// 
 /// </summary>
 /// <param name="id"></param>
 /// <returns></returns>
 [HttpGet]
 public int Online([FromQuery] long id)
 {
  return _articleRepository.OnlineArticle(id);
 }
 }
}
ArticleController

  可以注意到的是,除了把DataAccess变成了Repository。其他的代码几乎没有改动。最后我还添加了仓储自定义的接口的调用。

5. 结语

  今天,我们了解了动态仓储的使用。它是一个非常方便的特性,可以非常显著的提升我们写代码的效率,减少一定的代码量,避免了很多“体力活”。让我们专注于业务!

示例代码链接在这里

 

下期预告:SmartSql中的事务,及AOP的使用

 

SmartSql使用教程(2)—使用动态代理实现CURD

标签:read   options   创建   用户注册   ext   自增   splay   closed   仓储   

热心网友 时间:2022-05-02 21:16

当然代理模式中,用的最广泛的,用的最多的是 动态代理模式。

动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理。

抽象接口的类图如下:

      --图来自设计模式之禅

所以动态代理模式要有一个InvocationHandler接口 和 GamePlayerIH实现类。其中 InvocationHandler是JD提供的动态代理接口,对被代理类的方法进行代理。

代码实现如下

抽象主题类或者接口:

复制代码
1 package com.yemaozi.proxy.dynamic;
2
3 /*
4 * 动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理。
5 */
6 public interface IGamePlayer {
7 //登录游戏
8 public void login(String username, String password);
9
10 //击杀Boss
11 public void killBoss();
12
13 //升级
14 public void upGrade();
15 }
复制代码

需要被代理类:

复制代码
1 package com.yemaozi.proxy.dynamic;
2
3 public class GamePlayer implements IGamePlayer {
4
5 private String name = "";
6
7 public GamePlayer(String name){
8 this.name = name;
9 }
10
11 public void login(String username, String password) {
12 System.out.println("登录名为 "+username+" 进入游戏," + name + " 登录成功!");
13 }
14
15 public void killBoss() {
16 System.out.println(this.name + " 击杀了Boss!");
17 }
18
19 public void upGrade() {
20 System.out.println(this.name + "升级了!");
21 }
22
23 }
复制代码

动态代理处理器类:

复制代码
1 package com.yemaozi.proxy.dynamic;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 public class GamePlayerInvocationHandler implements InvocationHandler{
7
8 //被代理的对象
9 private Object obj;
10
11 //将需要代理的实例通过处理器类的构造方法传递给代理。
12 public GamePlayerInvocationHandler(Object obj){
13 this.obj = obj;
14 }
15
16 public Object invoke(Object proxy, Method method, Object[] args)
17 throws Throwable {
18 Object result = null;
19 if("login".equalsIgnoreCase(method.getName())){
20 //这个在主题方法不受任何影响的情况下,在主题方法前后添加新的功能,或者增强主题方法,
21 //从侧面切入从而达到扩展的效果的编程,就是面向切面编程(AOP Aspect Oriented Programming)。
22 //AOP并不是新技术,而是相对于面向对象编程的一种新的编程思想。在日志,事务,权限等方面使用较多。
23 System.out.println("代理登录游戏!");
24 result = method.invoke(this.obj, args);
25 return result;
26 }
27 result = method.invoke(this.obj, args);
28 return result;
29 }
30
31 }
复制代码
由于代理是动态产生的,所以不需要再声明代理类。

动态代理场景类:

复制代码
package com.yemaozi.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client {
public static void main(String[] args) {
IGamePlayer gp = new GamePlayer("张三");
InvocationHandler gpHandler = new GamePlayerInvocationHandler(gp);
//获取真实主题类的ClassLoader
ClassLoader classLoader = gp.getClass().getClassLoader();
//动态产生一个代理者。
Class<?>[] cls = new Class[]{IGamePlayer.class};
IGamePlayer proxyGp = (IGamePlayer) Proxy.newProxyInstance(classLoader, cls, gpHandler);
proxyGp.login("zhangsan", "123456");
proxyGp.killBoss();
proxyGp.upGrade();
}
}

执行结果:
代理登录游戏!
登录名为 zhangsan 进入游戏,张三 登录成功!
张三 击杀了Boss!
张三升级了!

//在此,我们没有创建代理类,但是确实有代理类帮我们完成事情。
复制代码
其中,在此代理模式中,不仅代理是动态产生的(即在运行的时候生成),而且还在代理的时候,也增加了一些处理。在此处增加的处理,其实就是另一种编程思想-----面向切面编程思想(AOP Aspect Oriented Programming)。

带有AOP的动态代理模式类图:

        --图来自设计模式之禅

从上图中,可以看出有两个相对独立的模块(Subject和InvocationHandler)。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。然而,通知Advice从另一个切面切入,最终在上层模块就是Client耦合,完成逻辑的封装。

代码清单如下

抽象主题或者接口:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 public interface Subject {
4 public void doSomething(String str);
5 //...可以多个逻辑处理方法。。。
6 }
复制代码

真实主题:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 public class RealSubject implements Subject{
4
5 public void doSomething(String str) {
6 //do something...
7 System.out.println("do something..." + str);
8 }
9
10 }
复制代码

通知接口:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 //通知接口及定义、
4 public interface IAdvice {
5 public void exec();
6 }
复制代码

前置通知:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 public class BeforeAdvice implements IAdvice {
4 //在被代理的方法前来执行,从而达到扩展功能。
5 public void exec() {
6 System.out.println("前置通知被执行!");
7 }
8 }
复制代码

后置通知:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 public class AfterAdvice implements IAdvice {
4
5 //在被代理的方法后来执行,从而达到扩展功能。
6 public void exec() {
7 System.out.println("后置通知被执行!");
8 }
9 }
复制代码

动态代理的处理器类:

所有的方法通过invoke方法类实现。

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 public class MyInvocationHandler implements InvocationHandler {
7
8 //被代理的对象
9 private Subject realSubject;
10 //通过MyInvocationHandler的构造方法将被代理对象传递过来。
11 public MyInvocationHandler(Subject realSubject){
12 this.realSubject = realSubject;
13 }
14 //执行被代理类的方法。
15 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
16 //在执行方法前,执行前置通知。
17 IAdvice beforeAdvice = new BeforeAdvice();
18 beforeAdvice.exec();
19 Object result = method.invoke(this.realSubject, args);
20 //在执行方法后,执行后置通知。
21 IAdvice afterAdvice = new AfterAdvice();
22 afterAdvice.exec();
23 //前置通知,和后置通知,都是要看具体实际的业务需求来进行添加。
24 return result;
25 }
26
27 }
复制代码

动态代理类:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 public class DynamicProxy {
7
8 /**
9 * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler handler)
10 * loader:
11 * 一个ClassLoader对象,定义了由哪个ClassLoader对象,来对生产的代理进行加载。
12 * interfaces:
13 * 一个Interfaces数组,表示我将要给我所代理的对象提供一组什么样的接口,
14 * 如果提供一组接口给它,那么该代理对象就宣称实现了该接口,从而可以调用接口中的方法。
15 * 即,查找出真是主题类的所实现的所有的接口。
16 * handler:
17 * 一个InvocationHandler对象,表示当我这个动态代理对象在调用方法时,会关联到该InvocationHandler对象。
18 * 该InvocationHandler与主题类有着关联。
19 */
20 public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler){
21 @SuppressWarnings("unchecked")
22 T t = (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
23 return t;
24 }
25 }
复制代码
从动态的产生动态代理类。

动态代理场景类:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 import java.lang.reflect.InvocationHandler;
4
5 public class AOPClient {
6
7 public static void main(String[] args) {
8 Subject realSubject = new RealSubject();
9 InvocationHandler handler = new MyInvocationHandler(realSubject);
10 ClassLoader classLoader = realSubject.getClass().getClassLoader();
11 Class<?>[] interfaces = realSubject.getClass().getInterfaces();
12 Subject proxySubect = DynamicProxy.newProxyInstance(classLoader, interfaces, handler);
13 proxySubect.doSomething("这是一个Dynamic AOP示例!!!");
14 }
15 }
16
17 执行结果:
18 前置通知被执行!
19 do something...这是一个Dynamic AOP示例!!!
20 后置通知被执行!
复制代码
动态代理中invoke的动态调用:

动态代理类DynamicProxy是个纯粹的动态创建代理类通用类。

所以在具体业务中,可以在进一步封装具体的具有业务逻辑意义的DynamicProxy类。

代码如下

具体业务的动态代理:

复制代码
1 package com.yemaozi.proxy.dynamic_aop;
2
3 import java.lang.reflect.InvocationHandler;
4 //具体业务的动态代理。
5 public class SubjectDynamicProxy extends DynamicProxy {
6 public static <T> T newProxyInstance(Subject subject){
7 ClassLoader classLoader = subject.getClass().getClassLoader();
8 Class<?>[] interfaces = subject.getClass().getInterfaces();
9 InvocationHandler handler = new MyInvocationHandler(subject);
10 T t = newProxyInstance(classLoader, interfaces, handler);
11 return t;
12 }
13 }
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
抖音弹幕怎么关掉?怎么关闭抖音弹幕? 惠普LaserJet P3005D是否支持B5纸的双面打印? word打印出图片总是缺一部分怎么办-word打印图片不完整怎么解决_百度... 理想one哪里产的车辆? 抚州抚州ONE在哪里? one地址在哪里? 如何在图片上写字(如何在图片上添加文字) 网商贷为什么钱没到账 高级经济师职称怎么评 高级经济师需要评审吗 怎么让地暖更省钱? 淘宝真实买家评价也是没有问题为什么会提示异常评价疑似无意义?_百度... 怎样使用地暖节能又省钱? 华秦申购什么时候上市 java动态代理是什么 电脑新设置了个账户,而且是管理员的身份,怎么删除我创的那个帐户 地暖如何设置最省钱 今天某宝突然收到说异常评价是怎么回事?我很少给差评,基本都是默认好评 如何使用地暖省气? 电地暖与水地暖在安装上想要节省些钱,应该怎么做呢? 地暖只能控制水流量的大小怎么用省钱 地暖怎么用,才能又省钱又舒服? 在电脑中设置的账户怎么删除啊 关于地暖如何节能省钱的攻略(一) 地暖怎么开省钱 nvidia geforce experience 可以截屏嘛 在哪 我只找到了录像功能 英伟达n卡怎么吧视频保存d盘 《巫师3:狂猎》如何在游戏中截图 英伟达图库在哪 谁有PHOTOSHOP滤镜下载地址? 如何使用地暖最省钱? 什么是java代理模式,具体相关的动态代理和静态代理分别是什么?举例更... 秋冬取暖费用高,地暖怎么使用更节能?哪位能说说看? 如何使用Proxy模式及Java内建的动态代理机制 怎样用暖气省钱? 淘宝评价提示系统异常是咋回事? 谁能说说java框架spring的ioc和代理模式有什么关系?spring都哪里用了代理模式 淘宝评论怎么显示待评价状态不正确什么意思? 易方达丰和债券002969可以赎回吗 地暖是24小时开着省钱,还是分时段开更省钱? Spring框架的动态代理是干什么用的 系统无法识别显示器? 淘宝自检评价!怎么处理啊! 如何改变电脑识别到的显示器型号? 001496基金今天净值 收到淘宝邮件说我违反信用评价管理,删除评价,怎么没删啊 刚买的组装电脑显示器显示不出来型号,还有分辨率问题 Spring的Aop是采用的什么设计模式? Java的动态代理模式,在高并发的应用中能使用吗? Spring是什么平生