在开发中,我们尽量不要改动别人已经完成的代码。这样,可以保证代码的独立性,并同时保证了代码的完整性。另外,在使用别人的程序包的时候,我们也无法对别人的代码进行修改。但是,有时候我们需要对别人或自己已经完成的代码模块进行一些功能的增强,这时就需要用到代理模式。
代理模式有三类主要对象:
代理模式是指对已经完成的功能模块进行代理(同时可以进行功能的增强),通过访问代理对象,完成对功能模块的访问,这个访问过程并没有造成对被代理对象的修改,但同时引入了功能的增强。
静态代理通过将目标对象和代理对象实现同一接口(或继承同一父类)完成功能。主要原理是通过实现同一接口,在原本访问目标对象的位置,即可通过访问代理对象,在添加增强功能访问之后,进行原有功能的访问。在各种框架中,常见于事务的添加、AOP模式开发中。
统一接口:
package xx.xxx.xxx.service.proxy;
/**
* 接口对象
*/
public interface IWorkObject {
/**
* 打印对象信息
*/
void sout();
/**
* 执行代理对象任务
*/
void work();
}
目标对象:
package xx.xxx.xxx.service.proxy;
import org.apache.log4j.Logger;
/**
* 目标对象,实现了接口内容
*/
public class TargetObject implements IWorkObject {
private Logger LOGGER = Logger.getLogger(TargetObject.class);
public void sout() {
LOGGER.info("目标对象日志打印");
}
public void work() {
LOGGER.info("目标对象工作...");
}
}
代理对象:
package xx.xxx.xxx.service.proxy;
import org.apache.log4j.Logger;
/**
* 代理对象,对目标对象进行功能增强
*/
public class ProxyObject implements IWorkObject{
private static Logger LOGGER = Logger.getLogger(ProxyObject.class);
private IWorkObject targetObj;
public ProxyObject(IWorkObject iWorkObject) {
this.targetObj = iWorkObject;
}
public void sout() {
LOGGER.info("代理对象功能增强");
targetObj.sout();
}
public void work() {
LOGGER.info("代理对象功能增强");
targetObj.work();
}
}
测试程序:
package xx.xxx.xxx;
import xx.xxx.xxx.service.proxy.IWorkObject;
import xx.xxx.xxx.service.proxy.ProxyObject;
import xx.xxx.xxx.service.proxy.TargetObject;
import org.junit.Test;
/**
* 测试程序
*/
public class ProxyModeTest {
@Test
public void staticProxyTest() {
// 创建目标对象
IWorkObject iWorkObject = new TargetObject();
// 创建代理对象
IWorkObject proxyObject = new ProxyObject(iWorkObject);
proxyObject.work();
proxyObject.sout();
}
}
可以看到,在代理对象中实现了目标对象的功能增强,同时添加了一部分功能,但由于实现了同一个接口,没有对目标对象进行侵扰。最终输出如下:
[INFO ] 2020-03-30 16:58:12,036 method:xx.xxx.xxx.service.proxy.ProxyObject.work(ProxyObject.java:24)
代理对象功能增强
[INFO ] 2020-03-30 16:58:12,040 method:xx.xxx.xxx.service.proxy.TargetObject.work(TargetObject.java:16)
目标对象工作...
[INFO ] 2020-03-30 16:58:12,041 method:xx.xxx.xxx.service.proxy.ProxyObject.sout(ProxyObject.java:19)
代理对象功能增强
[INFO ] 2020-03-30 16:58:12,041 method:xx.xxx.xxx.service.proxy.TargetObject.sout(TargetObject.java:12)
目标对象日志打印
Process finished with exit code 0
动态代理在生成代理的过程中,不通过静态编码生成。通过使用Proxy.newProxyInstance方法生成代理对象。newProxyInstance的函数声明如下:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
其中,
接口文件:
package xx.xxx.xxx.service.dynamicProxy;
/**
* 接口文件
*/
public interface IWork {
// 开发代码
void coding();
// 调试代码
void debuging();
}
目标对象:
package xx.xxx.xxx.service.dynamicProxy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.log4j.Logger;
/**
* 目标对象
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WorkTarget implements IWork {
private String type;
private static Logger LOGGER = Logger.getLogger(WorkTarget.class);
public void coding() {
LOGGER.info("正常编码工作...");
}
public void debuging() {
LOGGER.info("正常调试工作...");
}
}
代理生成方法:
package xx.xxx.xxx.service.dynamicProxy;
import org.apache.log4j.Logger;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂
*/
public class WorkProxy {
private static final Logger LOGGER = Logger.getLogger(WorkProxy.class);
private static IWork worker = new WorkTarget("JAVA");
// 对worker目标对象进行代理增强
private static InvocationHandler javaHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("coding")) {
LOGGER.info("Java Code Designing...");
return method.invoke(worker, args);
}
if (method.getName().equals("debuging")) {
LOGGER.info("Java Code Debuging...");
return method.invoke(worker, args);
}
return null;
}
};
public static IWork workerProxyFactory(IWork worker) {
// 如果传入的worker.type是JAVA,则进行代理增强
if (((WorkTarget) worker).getType().equals("JAVA")) {
IWork iWork = (IWork) Proxy.newProxyInstance(
worker.getClass().getClassLoader(),
worker.getClass().getInterfaces(),
javaHandler);
return iWork;
}
// 反之,则不进行加强代理
return worker;
}
}
测试方法1(目标对象type为JAVA):
@Test
public void dynamicProxyTest() {
IWork work = new WorkTarget("JAVA");
IWork iWork= WorkProxy.workerProxyFactory(work);
if (iWork != null) {
iWork.coding();
iWork.debuging();
}
}
对应输出1:
[INFO ] 2020-03-30 22:19:52,636 method:xx.xxx.xxx.service.dynamicProxy.WorkProxy$1.invoke(WorkProxy.java:20)
Java Code Designing...
[INFO ] 2020-03-30 22:19:52,639 method:xx.xxx.xxx.service.dynamicProxy.WorkTarget.coding(WorkTarget.java:19)
正常编码工作...
[INFO ] 2020-03-30 22:19:52,640 method:xx.xxx.xxx.service.dynamicProxy.WorkProxy$1.invoke(WorkProxy.java:24)
Java Code Debuging...
[INFO ] 2020-03-30 22:19:52,640 method:xx.xxx.xxx.service.dynamicProxy.WorkTarget.debuging(WorkTarget.java:23)
正常调试工作...
测试方法2(目标对象type为其他):
@Test
public void dynamicProxyTest() {
IWork work = new WorkTarget("HTML");
IWork iWork= WorkProxy.workerProxyFactory(work);
if (iWork != null) {
iWork.coding();
iWork.debuging();
}
}
对应输出2:
[INFO ] 2020-03-30 22:22:22,760 method:xx.xxx.xxx.service.dynamicProxy.WorkTarget.coding(WorkTarget.java:19)
正常编码工作...
[INFO ] 2020-03-30 22:22:22,763 method:xx.xxx.xxx.service.dynamicProxy.WorkTarget.debuging(WorkTarget.java:23)
正常调试工作...
观察上面的输出,我们可以看到,第一个测试用例type为JAVA,进行了增强,而type为HTML的测试用例,则没有进行代理工作。
理解了上述内容,对于MyBatis中的动态代理就可以轻松理解其大致工作原理。针对每一个Mapper.xml文件,我们需要对他定义一个对应的接口。接口中定义好DAO层对象(目标对象)需要完成的功能。MyBatis使用这些目标对象,通过InvocationHandler,完成对这些接口中方法的增强,从而生成了对应的实现方法。
如果您觉得我的文章对您有所帮助,欢迎扫码进行赞赏!
如果你看到了这篇文章的最后,并且觉得有帮助的话,麻烦点个赞,谢谢了!也欢迎和我进行讨论!