> scan(String... packages) throws Exception;
```
## **使用**
所以一般情况下,你可以直接这么使用:
```java
// 扫描两个包
Mock.scan("forte.test2.beans", "forte.test1.beans", ...);
// 然后直接获取
Mock.get(Xxxx.class);
// 使用
```
# **映射代理**
1.6.0版本后,我更新了**映射扫描**与**映射代理**功能。感谢提出建议的朋友。[Issue#I1CCMT](https://gitee.com/ForteScarlet/Mock.java/issues/I1CCMT)
首先看一下Issue上提出的模拟场景:
```java
// interface
public interface ServiceA{
VoA methodA();
}
// bean, can with @MockBean
public class VoA{
@MockValue("@cname")
private String p1;
}
```
此时,接口中的`methodA()`方法的返回值`VoA`恰好是一个MockBean,这时候,我想要得到`ServiceA`的一个代理对象,使其能够通过`methodA()`得到`VoA`的实例对象。
ok,因此我添加了方法`Mock.proxy(...)`及其重载。方法定义如下:
```java
/**
* 为一个接口提供一个代理对象。此接口中,所有的 抽象方法 都会被扫描,假如他的返回值存在与Mock中,则为其创建代理。
* 此方法默认不会为使用者保存单例,每次代理都会代理一个新的对象,因此如果有需要,请保存一个单例对象而不是频繁代理。
* @param type 要代理的接口类型。
* @param factory 接口代理处理器的获取工厂。可自行实现。
* @param 接口类型
* @return 代理结果
*/
public static T proxy(Class type, MockProxyHandlerFactory factory);
```
此方法传入一个接口类型`Class type` 和一个动态代理处理器`MockProxyHandlerFactory factory`,来获取一个代理对象。
`MockProxyHandlerFactory`是一个接口类型,只存在一个抽象方法:
```java
/**
* 获取代理处理接口{@link InvocationHandler}实例
* @param mockObjectFunction 传入一个类型和一个可能为null的name字符串,获取一个mockObject对象。如果存在name,则会尝试先用name获取
* @return JDK动态代理所需要的代理处理器示例。
* @see InvocationHandler
*/
InvocationHandler getHandler(BiFunction, String, MockObject>> mockObjectFunction);
```
此接口定义了如何创建一个代理类。`InvocationHandler`是JDK为动态代理的创建所提供的一个接口,知道动态代理的人对他应该不会很陌生。
但是如果不熟悉也没关系,我在内部提供了一个默认的实现`MockProxyHandlerFactoryImpl`,同时也为`Mock.proxy(...)`提供了一个重载方法:
```java
/**
* 为一个接口提供一个代理对象。此接口中,所有的 抽象方法 都会被扫描,假如他的返回值存在与Mock中,则为其创建代理。
* 此方法默认不会为使用者保存单例,每次代理都会代理一个新的对象,因此如果有需要,请保存一个单例对象而不是频繁代理。
* 使用默认的接口代理处理器工厂{@link MockProxyHandlerFactoryImpl}。
* 默认处理工厂中,代理接口时,被代理的方法需要:
* 不是default方法。default方法会根据其自己的逻辑执行。
* 没有参数
* 没有标注{@code @MockProxy(ignore=true) ignore=true的时候代表忽略}
*
* @see MockProxyHandlerFactoryImpl
* @param type 要代理的接口类型。
* @return 接口代理
*/
public static T proxy(Class type);
```
因此,一般情况下,你可以直接这么使用:
```java
MockTestInterface proxy = Mock.proxy(MockInterface.class);
```
让我们来创建一个示例接口来看看:
```java
public interface MockTestInterface {
/**
* 指定List的泛型类型为User类型,长度为2-4之间
*/
@MockProxy(size = {2, 4}, genericType = User.class)
List list();
/**
* 默认情况下,长度为1
*/
Teacher[] array();
/**
* 直接获取,不用注解。
*/
Admin admin();
/**
* 获取name为{@code mock_map}的mockObject, 基本上返回值都是Map类型。
*/
@MockProxy(name = "mock_map")
Map map();
/**
* 忽略, 返回值会默认为null
* @return null
*/
@MockProxy(ignore = true)
Admin justNullAdmin();
}
```
可以看到,上面的这些抽象方法中,有一部分方法标注了注解`@MockProxy`。此注解参数如下:
```java
/**
* 是否忽略此方法。如果为是,则接口的最终代理结果为返回一个null。
* 当然,如果获取不到对应的Mock类型,无论是否忽略都会返回null或者默认值。
* 如果是基础数据类型相关,数字类型,返回{@code 0 或 0.0}。
* 如果是基础数据类型相关,char类型,返回{@code ' '}。
* 如果是基础数据类型相关,boolean类型,返回{@code false}。
*/
boolean ignore() default false;
/**
* 如果此参数存在值,则会优先尝试通过name获取MockObject对象。一般应用在返回值为Map类型的时候。
*/
String name() default "";
/**
* 当接口返回值为数组或者集合的时候,此方法标记其返回值数量大小区间{@link [min, max], 即 max >= size >= min}。是数学上的闭区间。
* 如果此参数长度为0,则返回值为1。
* 如果参数长度为1,则相当于不是随机长度。
* 如果参数长度大于2,只取前两位。
*/
int[] size() default {1,1};
/**
* 指定返回值类型,三种可能类型:list类型,array类型,Object其他任意类型。默认值为Unknown类型。当为Unknown类型的时候,会根据返回值类型自动判断。
* 当类型为list与array类型的时候,需要通过{@link #genericType()}方法指定泛型的类型,获取mock类型的时候将会通过此方法得到的类型来获取。
*/
MockProxyType proxyType() default MockProxyType.UNKNOWN;
/**
* 假如类型为List或者数组类型,此处代表泛型的实际类型。
*/
Class> genericType() default Object.class;
```
简单汇总一下此注解的参数:
- ignore:忽略这个方法。
- name:指定mockObject的name。一般在返回值为Map类型的时候使用。
- size:指定大小区间。只有在返回值为array或者List类型的时候才有用。
- proxyType:指定返回值类型。一般情况下可以自动推断。
- genericType:当返回值为List类型的时候,此参数指定他的泛型类型。array类型可以进行推断。
`@MockProxy`并不是必须的,但也不是可能完全省略的,需要根据实际情况来。一般来讲,如果返回值是一个任意的bean类型,则基本上可以省略,而数组类型如果没有长度要求的话也可以省略,但是例如list、map类型基本上是需要配置部分参数的。
## **使用**
接着上面的`MockTestInterface`,在使用的时候就是这个样子的:
```java
public static void main(String[] args) throws Exception {
// 扫描包
Mock.scan("com.test");
// 注册一个map类型的mockbean
Mock.set("mock_map", new HashMap(){{
put("name_map", "@cname");
}});
// 获取接口代理
final MockTestInterface proxy = Mock.proxy(MockTestInterface.class);
// 输出测试
System.out.println("proxy.admin(): " + proxy.admin());
System.out.println("proxy.justNullAdmin(): " + proxy.justNullAdmin());
System.out.println("proxy.list(): " + proxy.list());
System.out.println("proxy.array(): " + Arrays.toString(proxy.array()));
System.out.println("proxy.map(): " + proxy.map());
}
```
# #函数
(v1.7.0更新,参阅wiki `8_#function&class parse`)
# List区间(v1.7.0)
(v1.7.0更新,参阅wiki `9_List interval parameter(v1.7.0)`)
## **注意事项**
- 尽可能别用泛型。
- 每次使用`Mock.proxy(...)`都会去生成动态代理对象,会影响性能,所以尽可能保存成一个单例使用。
# **使用依赖列表**
```xml
commons-beanutils
commons-beanutils
1.9.3
```
## 更新公告
### **v1.9.2(2020/10/24)**
- 追加一个区间参数`const`, 当区间参数为 `const`的时候,将不会对value值进行解析,而是直接原样赋值。
`const`区间参数常用于对`Map`类型的`MockObject`的某些字段指定默认值。
例如:
```java
Map map = new HashMap<>();
// 区间参数为 'const'
map.put("dataLevel|const", new int[]{51, 52, 53});
Mock.set("usermap", map);
MockObject