接下来是我对 JDK与 CGLIB 动态代理的基本的讲解。
动态代理的主要作用: 不改变原有的代码基础上,对已有的方法进行增强。源于AOP思想中实现的技术。
1.JDK与CHLIB的不同之处
在学习这两种代理之前,我们先来对他们的差异进行一定的基本了解。
- JDK利用拦截器(InvocationHanlder)在调用方法之前先调用InvokeHandler来处理;
- CGLIB则是利用jar包对代理对象的类通过class文件加载进来处理。
- 使用JDK,就要对目标对象实现接口
- 使用CGLIB,可以不用对目标对象实现接口
本人用一个项目“登录”案例来对JDK与CGLIB动态代理的解释。
2.JDK动态代理
本次项目名【Dynammic_proxy】,JDK动态代理包,共有三个类与一个接口组成,分别为:
执行类(Client.java),接口(Interface_Login.java),目标对象(Login.java),代理类(Proxyer.java)。
Client.java类的代码
package com.yc.proxy;
/**
* JDK 动态代理
*
*/
public class Client {
public static void main(String[] args) {
Interface_Login login = new Login();
//创建被代理对象
Proxyer proxyer=new Proxyer();
//创建代理对象
Interface_Login proxyLogin=(Interface_Login)proxyer.getProxy(login);
//执行代理方法
proxyLogin.login("root", "123");
proxyLogin.login("root", "1234");
proxyLogin.login("root", "12345");
}
}
Interface_Login.java接口代码
package com.yc.proxy;
public interface Interface_Login {
public void login(String user,String pwd );
}
Login.java类的代码
package com.yc.proxy;
public class Login implements Interface_Login{
//要进行动态增强的方法
public void login(String user,String pwd ){
if(user.equals("root") && pwd.equals("123")){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
}
}
Proxyer.java代理类的代码。(主要:动态代理的代码基本了解与掌控)
最为主要的是对newProxyInstance()方法 与 invoke(Object proxy, Method method, Object[] args)方法的掌控。
package com.yc.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Proxyer implements InvocationHandler{
/**
* 目标对象(被代理对象)
*/
private Object target;
/**
* 创建代理对象
* newProxyInstance(参数一,参数二,参数三)
* 方法参数的含义:
* 参数一:类加载器,被代理对象的类加载器。(一般是固定写法)
* 参数二:字节码数组,被代理对象的接口数组。(一般是固定写法)
* 参数三:一个接口,如何代理。(此处代码只能是谁用谁写)
*/
public Object getProxy(Object target){
this.target=target;
/**
* Proxy是jdk提供的代理对象构建器
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), //被代理对象的接口数组
this);}
/**
* 执行被代理对象的任何方法都会经过该方法。(该方法具有拦截的功能)
* 方法参数的含义:
* Object proxy:代理对象的引用。(一般都不会用到)
* Method method:执行的方法。
* Object[] args:执行方法所需要的参数
* 返回值(return):当前执行方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret=null;
//获取密码
String pwd=(String)args[1];
//但凡密码为“12345” 的用户不能进行登录
if(pwd.equals("12345")){
//动态增强(不执行)
System.out.println("密码为12345的用户拦截成功");
}else{
//动态增强(执行方法)
ret = method.invoke(target, args);
}
return ret;
}
}
最后运行的结果是:
3.CGLIB动态代理
本次项目名【Dynammic_proxy】,CGLIB动态代理包,共有两个类组成,分别为:
执行类(CglibClient.java),目标对象(CglibLogin.java)。
在使用CGLIB前先加入jar包(spring-core-4.3.17.RELEASE.jar 或者 cglib-3.1.jar和asm-3.3.1.jar),本人使用的是第一种,第一种里面也包含了第二种的两个jar包。
CglibClient.java类代码
package com.yc.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* CGLIB动态代理
*
*/
public class CglibClient {
public static void main(String[] args) {
//CGLIB要涉及的类
Enhancer enhancer=new Enhancer();
//指定父类
enhancer.setSuperclass(CglibLogin.class);
//设置方法回调
enhancer.setCallback(new MethodInterceptor() {
CglibLogin login=new CglibLogin();
/**
* 执行被代理对象的任何方法都会经过该方法。(该方法具有拦截的功能)
* 方法参数的含义:
* 前面三个和invoke方法的参数含义和作用都是和“JDK方法的参数含义”一样的
* 返回值(return):当前执行方法的返回值
* 唯一不同的是MethodProxy:当前执行方法的代理对象。(这个也是一般不用的)
*/
@Override
public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
Object ret=null;
//获取密码
String pwd=(String)arg2[1];
//但凡密码为“12345” 的用户不能进行登录
if(pwd.equals("12345")){
//动态增强(不执行)
System.out.println("CGLIB:密码为12345的用户拦截成功");
}else{
//动态增强(执行方法)
ret = method.invoke(login, arg2);
}
return ret;
}
});
//创建代理对象的方法create()
CglibLogin cglibLogin=(CglibLogin) enhancer.create();
cglibLogin.login("root", "123");
cglibLogin.login("root", "1234");
cglibLogin.login("root", "12345");
}
}
CglibLogin.java类代码
package com.yc.cglib;
public class CglibLogin{
//要进行动态增强的方法
public void login(String user,String pwd ){
if(user.equals("root") && pwd.equals("123")){
System.out.println("CGLIB登录成功");
}else{
System.out.println("CGLIB登录失败");
}
}
}
最后运行的结果是: