什么是代理技术?
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。比如买房子找个中介来帮你进行买卖,中介如何操作你并不关心,而你关心的是最终的结果,房子是否买卖成功,中间过程有中介来处理。这里的中介就像我们理解的代理模式一样。我们不直接进行买卖,而是通过中介买卖。我们不直接访问某个对象,而是访问其代理对象
怎样实现代理?
实现代理,我们需要三个要素,分别是共同接口,真实对象和代理对象
一.静态代理
在service层中,我们处理事务的代码一般是这样的(伪代码)
public class ServiceEg {
public void biz1() {
System.out.println("事务开始");
try {
System.out.println("增加abc");
System.out.println("增加def");
System.out.println("增加ghi");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
}
}
public void biz2() {
try {
System.out.println("删除jkl");
System.out.println("删除mn");
System.out.println("删除opq");
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("回滚事务");
}
}
}
但是,我们会发现里面有好多代码都是重复的,比如事务的开始,回滚等,因此我们需要对代码进行简化,让关于事务的代码只需要写一遍即可,而不是每次执行方法时都要重复,此时就可以使用代理技术
以上述代码为例,使用静态代理实现事务的代码重用
思路:
1.提供一个代理类(Proxy)
调用通知类的invoke方法
获取方法对象和方法实际参数
与目标要实现相同的接口(目的是让使用者察觉不出是代理替换了原来的目标)
2.提供一个通知类(Advice)
实现了重复代码(事务的重复代码)
反射调用了目标对象的方法
把重复代码和目标方法联系在了一起
代码过程实现
UserService接口(这个是父类接口,以便代理对象替换真实对象)
public interface UserService {
public void biz1();
public void biz2();
}
UserServiceTarget类(实际类,是业务逻辑的实际执行者)
public class UserServiceTarget implements UserService {
//单一职责原则
public void biz1() {
System.out.println("增加abc");
System.out.println("增加def");
System.out.println("增加ghi");
}
public void biz2() {
System.out.println("删除jkl");
System.out.println("删除mn");
System.out.println("删除opq");
}
}
TrancationAdvice类(通知类,用于实现可重用的逻辑,并调用实际对象的方法)
import java.lang.reflect.Method;
//advice类,其中放事务之类可重用的逻辑
public class TrancationAdvice {
private UserServiceTarget target = new UserServiceTarget();
//method是任意方法的对象,args代表了方法的参数数组
//method就是biz1(),biz2()...,args就是biz1()里面的参数...
public Object invoke(Method method, Object[] args) {
System.out.println("事务开始");
Object obj = null;
try {
//通过反射调用方法 方法.invoke(对象,参数);
obj = method.invoke(target, args);
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("处理异常");
}
return obj;
}
}
UserProxy类(代理类,最后调用的方法,将通知类和需要执行的目标类结合)
import java.lang.reflect.Method;
//使用接口的目的就是为了UserProxy能够替换原来的UserServiceTarget
//能够以与原来一直的方式使用Proxy类,Proxy代理(把事务重复代码和目标业务类代码结合)
public class UserProxy implements UserService {
TrancationAdvice advice = new TrancationAdvice();
@Override
public void biz1() {
advice.invoke(biz1, new Object[] {});
}
@Override
public void biz2() {
advice.invoke(biz2, new Object[] {});
}
static Method biz1;
static Method biz2;
static {
Class<UserServiceTarget> c = UserServiceTarget.class;
try {
biz1 = c.getMethod("biz1");
biz2 = c.getMethod("biz2");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
测试(使用单元测试块进行测试)
public class TestProxy {
@Test
public void teat1() {
UserProxy userProxy = new UserProxy();
userProxy.biz1();
}
}
测试结果
二.动态代理
通过上面的静态代理(即我们自己写的代理),我们了解到代理技术的思想和具体实现过程的例子
但是,里面的代理类不具有通用性,只能解决事务类的可重用逻辑,但实际中,我们遇到会有更多的场景,那么就又要重新再写一个代理类,十分麻烦,所以,我们要针对这一问题进行改进,实现一个更加通用的代理类,即动态代理
什么是动态代理?
在Java中,会有一种技术,即在代码运行期间,jdk可以自己生成代理类和代理对象,而不用程序员去书写,我们只要告诉它通知是谁,接口是谁就可以
正常使用类:*.java ---> javac ---> *.class ---> java ---> 加载该class到虚拟机
动态代理: 直接生成 *.class 字节码 ---> 加载该class到虚拟机
如何使用动态代理?
要实现动态代理,就需要使用java自己的动态代理类,它位于java.lang.reflcet包下,主要涉及到两个类
1.Interface InvocationHandler : 该接口中仅仅定义了一个方法 Object invoke(Object obj, Method method, Object[] args)
参数1(obj)一般是指代理类
参数2(method)是被代理的方法
参数3(args)是该方法的参数数组
通知类需要实现这个借口,并实现里面的方法
2.Proxy :该类即为动态代理类,用于创建动态代理及对象
里面有一个方法newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHandler h);
参数1(loader) 是一个类加载器,获得方式是得到类对象,调用getClassLoader()方法就能得到类加载器啦
参数2(interfaces) 是告诉代理类需要实现哪些接口,直接 new Class[] {接口1.class, 接口2.class ...} 就可以了
参数3(h) 通知,规定代理类要执行哪些代码,也就是把通知类对象传进去就行了
代码实现
其中UserService类,UserServiceTarget类不改变
通知类 TrancationAdviceDynamic
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//实现InvocationHandler接口,并重写里面的方法
public class TrancationAdviceDynamic implements InvocationHandler {
private UserServiceTarget target = new UserServiceTarget();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("事务开始");
Object obj = null;
try {
//通过反射调用方法 方法.invoke(对象,参数);
obj = method.invoke(target, args);
System.out.println("提交事务");
} catch (Exception e) {
System.out.println("处理异常");
}
return obj;
}
}
测试类 TestDynamicProxy
import org.junit.Test;
import service.TrancationAdviceDynamic;
import service.UserService;
import java.lang.reflect.Proxy;
public class TestDynamicProxy {
@Test
public void test1() {
ClassLoader loader = TestDynamicProxy.class.getClassLoader();
UserService proxy = (UserService) Proxy.newProxyInstance(loader, new Class[]{UserService.class}, new TrancationAdviceDynamic());
proxy.biz2();
}
}
测试结果
到这里,静态代理和动态代理的基本知识和操作就可以啦!