一、JDK动态代理
JDK动态代理 也成为"接口代理"
1.在不改变目标类中方法的基础上,通过动态代理,实现对原有方法的增强
2.目标类要实现接口,否则不能用动态代理
3.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象优点:
1.在做目标类中方法的增强时,可以不去改变目标类原有方法的业务逻辑
2.在程序的拓展性能上更好了,维护更加方便缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的
public class DynamicProxyBankLogService {
//目标对象
private IBankService target;
public DynamicProxyBankLogService(IBankService target){
this.target = target;
}
//获得代理对象的方法
public IBankService getProxy(){
//代理对象
IBankService proxy = null;
//类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
//获取目标对象实现的所有接口
Class<?>[] interfaces = target.getClass().getInterfaces();
//执行器
InvocationHandler handler = new InvocationHandler() {
/**
* 将当前编写的增强功能,作用在目标对象的方法之上
* @param proxy 代理对象,一般不使用
* @param method 目标类中的所有方法
* @param args 目标类中的所有方法的形参列表
* @return 目标类中方法的返回值,若没有返回值则返回null
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//long l1 = System.currentTimeMillis();
//method 当前正在作用在哪一个方法之上 methodName方法名
String methodName = method.getName();
//日志追踪
System.out.println("开启日志追踪:the method "+methodName+" begin with "+ Arrays.asList(args));
//利用反射执行目标方法 invoke(执行哪一个对象的方法 , 方法所需要的形参)
//返回方法的执行结果 若目标方法没有返回值则返回null
Object result = method.invoke(target, args);
//计算耗时 +" , 此方法耗时:"+(l2-l1)/1000.0+"s"
//long l2 = System.currentTimeMillis();
System.out.println("结束日志追踪:the method "+methodName+" end with "+result );
return result;
}
};
proxy = (IBankService)Proxy.newProxyInstance(classLoader, interfaces, handler);
return proxy;
}
}
二、Cglib代理
Cglib代理模式
在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
优点:
1.若目标对象不需要实现接口,用Cglib代理。
2.做业务逻辑方法的增强时,不需要在原有方法上去做改变。
3.让程序拓展性更好,维护更加方便
缺点:
实现MethodInterceptor拦截器接口步骤:
1.当前代理类实现MethodInterceptor拦截器接口,重写拦截器方法
2.提供一个目标对象,提供一个获得代理对象的方法注意事项:
1.必须给目标类中提供公开的无参构造方法
IllegalArgumentException: Superclass has no null constructors but no arguments were given
2.目标类不可以使用final修饰,使用后将无法为其创建子类
IllegalArgumentException: Cannot subclass final class
3.目标类中的目标方法不可以使用final修饰,使用后,拦截器中的拦截方法,会拦截不到final的方法
4.目标类中的目标方法不可以使用static修饰,使用后,拦截器中的拦截方法,会拦截不到static的方法
public class CglibProxyBankLogService implements MethodInterceptor {
//目标对象
private BankService target;
public CglibProxyBankLogService(BankService target){
this.target = target;
}
//获取代理对象的方法
public BankService getProxy(){
BankService proxy = null;
//在内存中构建一个子类对象从而实现对目标对象功能扩展
Enhancer enhancer = new Enhancer();
//设置其父类
enhancer.setSuperclass(BankService.class);
//设置回调函数 this就是CglibProxyBankLogService,CglibProxyBankLogService implements MethodInterceptor extends Callback
enhancer.setCallback(this);
/*
* enhancer.create() 在内存中构建一个子类对象
* 1.找到其父类类型Superclass,利用其无参构造方法来创建子类对象
* 2.创建完毕,回调执行Callback,此处this是拦截器 --> 执行回调函数,就是在执行拦截方法intercept()
*/
proxy = (BankService)enhancer.create();
return proxy;
}
/**
* 拦截器方法中实现,目标类中希望增强的非业务逻辑
* @param proxy 代理对象,此处不用
* @param method 目标类中的方法对象
* @param args 目标类中的方法的形参列表
* @param methodProxy 方法的代理对象,此处不用
* @return 执行目标类中方法的返回值,若没有返回值则返回null
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long l1 = System.currentTimeMillis();
//method 当前正在作用在哪一个方法之上 methodName方法名
String methodName = method.getName();
//日志追踪
System.out.println("开启日志追踪:the method "+methodName+" begin with "+ Arrays.asList(args));
//利用反射执行目标方法 invoke(执行哪一个对象的方法 , 方法所需要的形参)
//返回方法的执行结果 若目标方法没有返回值则返回null
Object result = method.invoke(target, args);
//计算耗时
long l2 = System.currentTimeMillis();
System.out.println("结束日志追踪:the method "+methodName+" end with "+result +" , 此方法耗时:"+(l2-l1)/1000.0+"s" );
return result;
}
}
三、静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
使用前提:无论是目标对象还是代理对象,都必须一起实现相同的接口
优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
缺点:
1.目标类和代理类都需要实现接口,而且是相同接口
2.若采用静态代理模式,代理类会急剧膨胀
3.一旦接口中有新增方法,无论是目标类还是代理类,都需要新增方法,拓展性和维护性很差
public class StaticProxyBankLogService implements IBankService{
//目标对象
private IBankService target;
public StaticProxyBankLogService(IBankService target){
this.target = target;
}
@Override
public boolean login(Long id, String pwd) {
System.out.println("开启日志追踪:the method login begin with [ "+id+" , "+pwd+" ]");
long l1 = System.currentTimeMillis();
//调用业务逻辑
boolean flag = target.login(id, pwd);
long l2 = System.currentTimeMillis();
System.out.println("结束日志追踪:the method login end with "+flag+" , login方法耗时:"+(l2-l1)/1000.0+"s");
return flag;
}
@Override
public void register(Long id, String pwd, String name) {
System.out.println("开启日志追踪:the method register begin with [ "+id+" , "+pwd+" , "+name+" ]");
long l1 = System.currentTimeMillis();
//调用业务逻辑
target.register(id,pwd,name);
long l2 = System.currentTimeMillis();
System.out.println("结束日志追踪:the method register end with "+" , register方法耗时:"+(l2-l1)/1000.0+"s");
}
}
在这里贴一个IBankService和BankService类,方便大家用于测试
//IBankService 接口
public interface IBankService {
//登陆
public boolean login(Long id, String pwd);
//注册
public void register(Long id, String pwd, String name);
}
//BankService 实现类
public class BankService implements IBankService {
//登陆
@Override
public boolean login(Long id, String pwd) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(id.equals(1L) && "123".equals(pwd)){
return true;
}
return false;
}
//注册
@Override
public void register(Long id, String pwd, String name) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("id = "+id+" , pwd = "+pwd+" , name = "+name);
}
}