NO.10 Java代理模式
之前,我们一直学习的是面向对象编程,现在学习另外一种思想——面向切面编程AOP。
假如说学生数据Student是从控制层到服务层再到持久层(数据库),支付信息同样也存到数据库。现在对系统添加日志,权限的管理。需要分别在controller层,service层,dao层添加日志,说明信息的流转。现在在service层添加一个日志——开始学生信息服务层,同样也需要一个结束信息服务层。
倘若业务比较多,添加的日志也就多,那么,有没有一种方式将日志信息、或者权限管理不用写在业务代码中,也就是想写日志,配置一下写在另外的一个地方。这个时候就需要用到另外一种思想——面向切面。
在service层,用一条线切开,在切开地方编程,当经过此处,就留下日志信息,那么有没有一种方式将代码写到这条线上,而不是写在服务层里?这就是所谓的面向切面,Java里有一种方式去实现这个业务逻辑,就是Java代理模式。
StuCardService的实现类 StuCardServiceImpl
package com.xt.spring.aop.normal;
public class StuCardServiceImpl implements StuCardService{
public void deposit(String cardNo, String money) {
//一般日志
System.out.println("开始存钱");
System.out.println("向学生卡" + cardNo + "存钱:" + money);
System.out.println("存钱结束");
}
public void payMoney(String cardNo, String money) {
System.out.println("使用学生卡" + cardNo + "支付:" + money);
}
}
要代理服务层,这是一种实现,需要实现一个接口InvocationHandler
运用反射手动代理,代理的都是代理接口,通过接口代理实现类
package com.xt.spring.aop.normal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ServiceProxy implements InvocationHandler{
//被代理的对象
private Object target;
//proxy 代理类 method 需要执行的方法 args 方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始操作");
//invoke 调用这个方法invoke(被代理的对象,参数) 这个对象的这个方法要执行
Object returnValue = method.invoke(target, args);
System.out.println("操作结束");
//将方法的返回值返回
return returnValue;
}
//对象被创建代理的方法
public Object creatProxyInstance(Object target){
this.target = target;
//newProxyInstance(loader,interfaces,h)loader表示类加载器 interfaces 需要代理的类所实现的接口,h当前代理的这个对象
Object returnValue = Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
return returnValue;
}
}
想要添加日志,可以在代理类中添加,invoke方法要执行,可以在该方法里写日志,将日志由StuCardServiceImpl转到ServiceProxy。
而为什么会执行invoke方法?这时代理模式的固定方式。paymoney方法要执行,其实是调用了代理对象的invoke方法,paymoney就是执行了Object returnValue = method.invoke(target, args);这个操作。
package com.xt.spring.aop.normal;
import org.junit.Test;
public class AOPTest {
@Test
public void test(){
StuCardService scd = new StuCardServiceImpl();
scd.deposit("12132412", "400");
}
@Test
public void proxyTest(){
StuCardService scd = new StuCardServiceImpl();
//创建StuCardService的代理对象 通用代理类这个对象去创建想要去代理的对象 返回的是object,需要强转
StuCardService scdProxy = (StuCardService) new ServiceProxy().creatProxyInstance(scd);
//scdProxy是ServiceProxy这个代理对象依据scd创建的代理对象。
scdProxy.payMoney("454545", "200");
}
}
举个例子说,开办了一个公司,有财务,公司要记账,公司人员去税务局交税,去银行存款,公司要通过税务人员去交税存款,现在有一个代理机构,这个代理机构代表公司去交税,存款。这个代理机构可以代理很多家公司。要代理财务(财务支付—缴费,存钱两个功能),可以没有实现类,没有财务人员,由代理机构进行操作。
以上是最原始的代理方式。
综上所述:
java代理模式:
将日志写在切面内,而不直接显示在业务层
原理是java反射机制
需要实现一个接口InvocationHandler,实现invoke方法
创建代理对象–接口,通过代理接口,代理实现类
invoke(Object代理类,Method执行的方法,Object[]方法的参数)
method.invoke(代理对象,args)表示调用这个方法
method是代理对象的需要执行的方法
代理对象的创建:
Proxy.newProxyInstance(类加载器this.target.getclass.getClassLoader(),interface需要代理的这个类的接口,当前代理的对象)
将日志转移到invoke方法里