JDK代理模式
/**
* @version 1.0
* @Description Bank的业务逻辑接口
* @Author chenmin
* @Date 2021/10/27 14:22
*/
public interface IBankService {
//登陆
public boolean login(Long id, String pwd);
//注册
public void register(Long id, String pwd, String name);
}
/**
* @version 1.0
* @Description 业务逻辑的实现类 - 目标类
* @Author chenmin
* @Date 2021/10/27 14:24
*/
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);
}
}
/**
* @version 1.0
* @Description 动态代理 - JDK动态代理
* @Author chenmin
* @Date 2021/10/27 15:08
*
* Spring的AOP原理 - 面向切口编程
* 默认使用的是JDK动态代理,但是若未发现接口,则会转而使用Cglib动态代理
*
* 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;
}
}
/**
* @version 1.0
* @Description 测试JDK动态代理
* @Author chenmin
* @Date 2021/10/27 15:28
*/
public class Test {
public static void main(String[] args) {
//目标对象
IBankService target = new BankService();
//com.igeek.javase.ch22.proxy.dynamic.BankService
System.out.println(target.getClass().getName());
//获取代理对象
IBankService proxy = new DynamicProxyBankLogService(target).getProxy();
//com.sun.proxy.$Proxy0 利用JDK的API,动态的在内存中构建代理对象
System.out.println(proxy.getClass().getName());
//测试登陆方法
proxy.login(1L,"123");
//测试注册方法
proxy.register(2L,"222","张二蛋");
}
}
运行结果
com.igeek.javase.ch22.proxy.dynamic.BankService
com.sun.proxy.$Proxy0
开启日志追踪:the method login begin with [1, 123]
结束日志追踪:the method login end with true
开启日志追踪:the method register begin with [2, 222, 张二蛋]
id = 2 , pwd = 222 , name = 张二蛋
结束日志追踪:the method register end with null
因为register 没有返回值所以最后为null
Cglib代理模式
/**
* @version 1.0
* @Description 业务逻辑的实现类 - 目标类
* @Author chenmin
* @Date 2021/10/27 14:24
*/
public /*final*/ class BankService {
//默认提供无参构造方法,所以省略不写
/*public BankService(int a){
}*/
//登陆
public /*final*/ /*static*/ 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;
}
//注册
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);
}
}
/**
* @version 1.0
* @Description 代理模式 - Cglib代理模式
* @Author chenmin
* @Date 2021/10/27 16:07
*
* 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;
}
}
/**
* @version 1.0
* @Description 测试
* @Author chenmin
* @Date 2021/10/27 16:07
*/
public class Test {
public static void main(String[] args) {
//目标对象
BankService target = new BankService();
//com.igeek.javase.ch22.proxy.cglib.BankService
System.out.println("target = "+target.getClass().getName());
//代理对象
BankService proxy = new CglibProxyBankLogService(target).getProxy();
//com.igeek.javase.ch22.proxy.cglib.BankService$$EnhancerByCGLIB$$2ff6b48a
System.out.println("proxy = "+proxy.getClass().getName());
//执行登陆方法
proxy.login(1L,"123");
//执行注册方法
proxy.register(2L,"222","陈二蛋");
}
}
运行结果
target = com.igeek.javase.ch22.proxy.cglib.BankService
proxy = com.igeek.javase.ch22.proxy.cglib.BankService E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBac9bd6b1
开启日志追踪:the method login begin with [1, 123]
结束日志追踪:the method login end with true , 此方法耗时:1.007s
开启日志追踪:the method register begin with [2, 222, 陈二蛋]
id = 2 , pwd = 222 , name = 陈二蛋
结束日志追踪:the method register end with null , 此方法耗时:1.007s