JDK动态代理用处
以下一段代码,需要加入权限验证和日志记录
public AirticleService{
public List<Airticle> getAirticleList(){... }
public Airticle getAirticle(String rtid){... }
public String deleteAirticle(String rtid){... }
}
面对这种需求,我们常规的处理方案是:直接在类方法中编写权限验证和日志记录的代码。
但是,如果我们有很多Java类都需要作权限验证和日志记录呢?
我们是否要在每一个有需求的Java类中都加入权限验证的日志记录的代码呢(很多时候,每个类中的日志和权限验证的代码都是相同的)?
常规的方式,虽然能完成需求,但是带来了很多重复的工作,加上在业务代码中加入了很多非业务的代码(比如日志记录的代码就不是业务代码),使得代码的维护难度大大提升。
再者,如果某一天,用户要求我们将权限验证和日志记录的代码移除掉呢?那么,我们是否得一个个从Java类中把相关代码删掉或者注释掉呢?
JDK动态代理,就可以完美地解决上述的问题。
JDK动态代理实现原理
1、动态代理开发步骤
1 、定义接口
2 、编写接口实现类
3 、编写代理工厂类
代理类具体实现
3.1 生成目标对象(可以通过对数直接传递目标对象或者目标对象Class )
3.2 编写切面对象(切面对象包含目标对象)
3.3 生成代理对象(代理对象其实是目标对象所对应的接口的子类,与目标对象拥有相同的父类)
4、使用代理工厂获取目标对象
2、动态代理实现原理
我们回想一下动态代理的使用步骤,为什么动态代理要返回与目标对象有相同父类的对象,而不是直接返回目标对象?
以下是调用流程图
user ---> proxy ---> 切面 --- > targer
以下是伪代
interface { 接口方法 }
目标类 implements interface { }
代理工厂{
创建代理对象方法(目标类型)
目标对象 = 目标类型[反射创建实例];
切面类对象 = 构造器(目标对象);
代理对象 = JDK动态代理创建(目标类型接口参数,切面对象);
}
切面类{
构造器(目标对象);
执行(代理对象,执行方法,执行参数){
[自定义代码]
[反射执行]目标对象;
[自定义代码]
}
}
}
Interface 是Target 的父类,ProxyTarget 也是Interface 的子类,Target 是用户编码的代码,动态代理无法必定Target 类,但是ProxyTarget 是动态代理生成的类,其代码完全由代理自己控制。用户调用proxytarget ,proxytarget 调用切面代码,切面代码再调用target 。这样子,用户调用代理对象时候就相当于调用目标对象一样。
源码例子
1、定义接口
package com.example.springboot.demo.protxy;
public interface LoginService {
public String login (String loginname, String password);
public String unLogin (String credit);
public Long getOnLineTime (String credit);
}
2、实现接口
package com.example.springboot.demo.protxy;
public class LoginServiceImpl implements LoginService {
@Override
public String login (String loginname, String password) {
return "credit0001" ;
}
@Override
public String unLogin (String credit) {
return "unlogin sucesss" ;
}
@Override
public Long getOnLineTime (String credit) {
return 6000000 L;
}
}
3、编写代理类
package com.example.springboot.demo.protxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class LoginProxy {
@SuppressWarnings ("unchecked" )
public static <T> T getLoginService (Class<T> cls) throws InstantiationException, IllegalAccessException {
T newInstance = cls.newInstance();
LoginInvocationHandler h = new LoginInvocationHandler(newInstance);
return (T) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
}
static class LoginInvocationHandler implements InvocationHandler {
private Object target;
public LoginInvocationHandler (Object t) {
target = t;
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(target, args);
return invoke;
}
}
}
4、编写测试类
package com .example .springboot .demo .protxy
public class MainTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
LoginService loginService = LoginProxy.getLoginService (LoginServiceImpl.class )
System.out .println (loginService.login ("923339832" , "winway" ))
System.out .println (loginService.unLogin ("credit0001" ))
System.out .println (loginService.getOnLineTime ("credit0001" ))
System.out .println (loginService)
System.out .println (loginService.getClass ())
System.out .println (loginService.getClass ().getSuperclass ())
System.out .println (loginService.getClass ().getSuperclass ().getSuperclass ())
}
}