詳解java中動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制
代理模式是常用的java設(shè)計(jì)模式,它的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會(huì)存在關(guān)聯(lián)關(guān)系,一個(gè)代理類的對(duì)象與一個(gè)委托類的對(duì)象關(guān)聯(lián),代理類的對(duì)象本身并不真正實(shí)現(xiàn)服務(wù),而是通過調(diào)用委托類的對(duì)象的相關(guān)方法,來提供特定的服務(wù)。
JAVA各種動(dòng)態(tài)代理實(shí)現(xiàn)的比較

接口
interface AddInterface{
int add(int a, int b);
}
interface SubInterface{
int sub(int a, int b);
}
實(shí)現(xiàn)類
class Arithmetic implements AddInterface, SubInterface{
@Override
public int sub(int a, int b) {
return a-b;
}
@Override
public int add(int a, int b) {
return a+b;
}
}
方式1: JDK自帶的動(dòng)態(tài)代理
1、實(shí)現(xiàn)方式
Java在JDK1.3后引入的動(dòng)態(tài)代理機(jī)制,使我們可以在運(yùn)行期動(dòng)態(tài)的創(chuàng)建代理類。使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP需要有四個(gè)角色:被代理的類,被代理類的接口,織入器,和InvocationHandler,而織入器使用接口反射機(jī)制生成一個(gè)代理類,然后在這個(gè)代理類中織入代碼。被代理的類是AOP里所說的目標(biāo),InvocationHandler是切面,它包含了Advice和Pointcut。

2、vInvocationHandler接口的實(shí)現(xiàn)
class JdkDPQueryHandler implements InvocationHandler{
private Arithmetic real;
public JdkDPQueryHandler(Arithmetic real){
this.real = real;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println(method);
System.out.println("the method: " + methodName + "開始, 參數(shù): "+Arrays.asList(args));
Object result = method.invoke(real, args);
System.out.println("the method: "+methodName+"結(jié)束, 結(jié)果: " + result);
return result;
}
}
3、創(chuàng)建代理類并且調(diào)用代理類
public class Main{
private static int a = 4, b = 2;
public static Object createJDKProxy(Arithmetic real){
Object proxyArithmetic = Proxy.newProxyInstance(real.getClass().getClassLoader(),
real.getClass().getInterfaces(), new JdkDPQueryHandler(real));
return proxyArithmetic;
}
public static void main(String[] args){
Arithmetic real = new Arithmetic();
Object proxyArithmetic = createJDKProxy(real);
((AddInterface)proxyArithmetic).add(a, b);
((SubInterface)proxyArithmetic).sub(a, b);
}
}
方式2:動(dòng)態(tài)字節(jié)碼生成(cglib)
1、實(shí)現(xiàn)方式
Enhancer和MethodInterceptor。Enhancer可以用來動(dòng)態(tài)的生成一個(gè)類,這個(gè)類可以繼承指定的一個(gè)類,實(shí)現(xiàn)指定的一些接口。同時(shí),Enhancer在生成一個(gè)類之前需要指定一個(gè)Callback,當(dāng)類方法調(diào)用時(shí),方法的執(zhí)行被分配給這個(gè)Callback,MethodInterceptor是一個(gè)使用比較多的繼承自Callback的接口,它只有一個(gè)方法聲明。

2、接口InvocationHandler(jdk中)和接口MethodInterceptor(cglib中)對(duì)比
public interface MethodInterceptor extends Callback {
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
從參數(shù)構(gòu)成上,methodInterceptor的輸入?yún)?shù)比Invocationhandler多1個(gè),其實(shí)前3個(gè)參數(shù)對(duì)象的含義與Invocationhandler的含義是相同的。
第一個(gè)參數(shù)表示調(diào)用方法來自哪個(gè)對(duì)象;
第二個(gè)參數(shù)表示調(diào)用方法的Method對(duì)象;
第三個(gè)參數(shù)表示此次調(diào)用的輸入?yún)?shù)列表;
多出來的參數(shù)是MethodProxy 類型的,它應(yīng)該是cglib生成用來代替Method對(duì)象的一個(gè)對(duì)象,使用MethodProxy比調(diào)用JDK自身的Method直接執(zhí)行方法效率會(huì)有提升。
3、實(shí)現(xiàn)1
MethodInterceptor接口的實(shí)現(xiàn)
class CglibDPQueryInterceptor implements MethodInterceptor{
private Arithmetic real;
public CglibDPQueryInterceptor(Arithmetic real){
this.real = real;
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
System.out.println(method);
System.out.println("the method: " + methodName + "開始, 參數(shù): "+Arrays.asList(args));
//Object result = method.invoke(real, args);//兩種方式都是可以得
Object result = proxy.invoke(real, args);
System.out.println("the method: "+methodName+"結(jié)束, 結(jié)果: " + result);
return result;
}
}
創(chuàng)建代理類并調(diào)用代理類
public class Main{
private static int a = 4, b = 2;
public static Object createCglibProxy(Arithmetic real){
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new CglibDPQueryInterceptor(real));
enhancer.setInterfaces(real.getClass().getInterfaces());
return enhancer.create();
}
public static void main(String[] args){
Arithmetic real = new Arithmetic();
Object proxyArithmetic = createCglibProxy(real);
((AddInterface)proxyArithmetic).add(a, b);
((SubInterface)proxyArithmetic).sub(a, b);
}
}
注意了,MethodProxy在對(duì)執(zhí)行函數(shù)的時(shí)候,提供了2個(gè)方法
public Object invoke (Object obj, Object[] args) throws Throwable public Object invokeSuper(Object obj, Object[] args) throws Throwable
其中,javadoc上說這個(gè)invoke()方法可以用于相同類中的其他對(duì)象的方法執(zhí)行,也就是說這個(gè)方法中的obj需要傳入相同一個(gè)類的另一個(gè)對(duì)象(上述方法中就是傳入了Arithmetic類的不同對(duì)象),否則會(huì)進(jìn)入無限遞歸循環(huán)(測(cè)試之后還真是出現(xiàn)了StackOverflowError)。仔細(xì)的想一想就會(huì)發(fā)現(xiàn),public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)中的target是實(shí)現(xiàn)的代理類對(duì)象,通過target調(diào)用add()方法時(shí)會(huì)觸發(fā)intercept()方法被調(diào)用,如果在intercept()方法中再調(diào)用method.invoke(target, args),就相當(dāng)于add()方法中又調(diào)用add()方法,導(dǎo)致無限的遞歸循環(huán)。但是如果執(zhí)行method.invoke(real, args)則不會(huì),因?yàn)閞eal和target是同一個(gè)類不同對(duì)象,real是真實(shí)邏輯主題,target是真實(shí)主題real的代理。
下面一個(gè)例子來模擬一下:
interface SolveInterface{
void solve();
}
class Real implements SolveInterface{
public void solve(){
System.out.println("Real Solve!");
}
}
class Target extends Real{
private Object obj;
public void setObject(Object obj){
this.obj = obj;
}
private void invoke(){
try {
Method method = SolveInterface.class.getMethod("solve", new Class[]{});
method.invoke(obj, new Class[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
public void solve(){
System.out.println("Target Solve!");
invoke();
}
}
public class Main{public static void main(String[] args) throws Exception{
Target target = new Target();
target.setObject(new Real());//正確
//target.setObject(target);//發(fā)生循環(huán)調(diào)用
target.solve();
}
}
其實(shí)Method的invoke()方法會(huì)根據(jù)obj的類型去調(diào)用對(duì)應(yīng)的solve()方法,也就是多態(tài)性。
4、實(shí)現(xiàn)2
MethodInterceptor接口的實(shí)現(xiàn)
class CglibDPQueryInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
System.out.println(method);
System.out.println("the method: " + methodName + "開始, 參數(shù): "+Arrays.asList(args));
// 打印類信息 :target.getClass();省略
Object result = proxy.invokeSuper(target, args);
System.out.println("the method: "+methodName+"結(jié)束, 結(jié)果: " + result);
return result;
}
}
創(chuàng)建代理類并調(diào)用代理類
public class Main{
private static int a = 4, b = 2;
public static Object createCglibProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new CglibDPQueryInterceptor());
enhancer.setSuperclass(Arithmetic.class);
return enhancer.create();
}
public static void main(String[] args){
Arithmetic real = new Arithmetic();
Object proxyArithmetic = createCglibProxy();
((AddInterface)proxyArithmetic).add(a, b);
((SubInterface)proxyArithmetic).sub(a, b);
}
}
注意了,實(shí)現(xiàn)2中Enhancer 沒有設(shè)置接口,因?yàn)樵O(shè)置了Superclass了(也就是代理類的父類是Arithmetic),我們的代理類會(huì)繼承它,而Arithmetic已經(jīng)實(shí)現(xiàn)了我們的接口。為了證明這一點(diǎn),可以在MethodInterceptor的 intercept方法中打印 target.getClass()的類信息,你會(huì)發(fā)現(xiàn)cglib的兩種方式代理類的父類是不同的。如下:
實(shí)現(xiàn)1:
public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends java.lang.Object
實(shí)現(xiàn)2:
public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends com.test.Arithmetic
方式3:javassist生成動(dòng)態(tài)代理(代理工廠創(chuàng)建 或者 動(dòng)態(tài)代碼創(chuàng)建)
Javassist是一個(gè)編輯字節(jié)碼的框架,可以讓你很簡(jiǎn)單地操作字節(jié)碼。它可以在運(yùn)行期定義或修改Class。使用Javassist實(shí)現(xiàn)AOP的原理是在字節(jié)碼加載前直接修改需要切入的方法。這比使用Cglib實(shí)現(xiàn)AOP更加高效,并且沒太多限制,實(shí)現(xiàn)原理如下圖:


實(shí)現(xiàn)1:
接口的實(shí)現(xiàn)
class JavassistDPQueryHandler implements MethodHandler{
@Override
public Object invoke(Object target, Method method, Method proxy, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println(method);
System.out.println("the method: " + methodName + "開始, 參數(shù): "+Arrays.asList(args));
Object result = proxy.invoke(target, args);
System.out.println("the method: "+methodName+"結(jié)束, 結(jié)果: " + result);
return result;
}
}
創(chuàng)建代理類并調(diào)用代理類
public class Main{
private static int a = 4, b = 2;
public static Object createJavassistProxy() throws Exception{
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Arithmetic.class);
factory.setHandler(new JavassistDPQueryHandler());
return factory.createClass().newInstance();
}
public static void main(String[] args) throws Exception{
Arithmetic real = new Arithmetic();
Object proxyArithmetic = createJavassistProxy();
((AddInterface)proxyArithmetic).add(a, b);
((SubInterface)proxyArithmetic).sub(a, b);
}
}
注意:MethodHandler接口中invoke方法的定義,如下:
public Object invoke(Object target, Method method, Method proxy, Object[] args)
method代表調(diào)用方法的Method對(duì)象,proxy是代理類產(chǎn)生并代替method的對(duì)象,否則用method.invoke(target, args)會(huì)產(chǎn)生無限循環(huán)調(diào)用。
實(shí)現(xiàn)2:
javassist使用動(dòng)態(tài)java代碼常見代理過程和前文的方法略有不同。javassist內(nèi)部可以通過動(dòng)態(tài)java代碼,生成字節(jié)碼。這種方式創(chuàng)建的動(dòng)態(tài)代理可以非常靈活,甚至可以在運(yùn)行時(shí)產(chǎn)生業(yè)務(wù)邏輯。
//自定義攔截器接口
interface InterceptorHandler {
/**
* 調(diào)用動(dòng)態(tài)代理對(duì)象的方法將反射本方法,可在本方法實(shí)現(xiàn)中添加類似AOP的事前事后操作,只有在本方法體中加入如下代碼
* 被代理的方法才會(huì)被執(zhí)行,返回值將返回給代理最后返回給程序
* @param obj Object 被代理的對(duì)象
* @param method Method 被代理對(duì)象的方法
* @param args Object[] 被代理對(duì)象的方法的參數(shù)
* @return Object 被代理對(duì)象的方法執(zhí)行后的返回值
* @throws Throwable
*/
public Object invoke(Object obj, Method method, Object[] args) throws Throwable;
}
//攔截器的實(shí)現(xiàn)
class InterceptorHandlerImpl implements InterceptorHandler{
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println(method);
System.out.println("the method: " + methodName + "開始, 參數(shù): "+Arrays.asList(args));
Object result = method.invoke(obj, args);
System.out.println("the method: "+methodName+"結(jié)束, 結(jié)果: " + result);
return result;
}
}
class MyProxyImpl {
/** 動(dòng)態(tài)代理類的類名后綴 */
private final static String PROXY_CLASS_NAME_SUFFIX = "$MyProxy_";
/** 攔截器接口 */
private final static String INTERCEPTOR_HANDLER_INTERFACE = "com.test.InterceptorHandler";
/** 動(dòng)態(tài)代理類的類名索引,防止類名重復(fù) */
private static int proxyClassIndex = 1;
/**
* 暴露給用戶的動(dòng)態(tài)代理接口,返回某個(gè)接口的動(dòng)態(tài)代理對(duì)象,注意本代理實(shí)現(xiàn)需和com.cuishen.myAop.InterceptorHandler攔截器配合
* 使用,即用戶要使用本動(dòng)態(tài)代理,需先實(shí)現(xiàn)com.cuishen.myAop.InterceptorHandler攔截器接口
* @param interfaceClassName String 要?jiǎng)討B(tài)代理的接口類名, e.g test.StudentInfoService
* @param classToProxy String 要?jiǎng)討B(tài)代理的接口的實(shí)現(xiàn)類的類名, e.g test.StudentInfoServiceImpl
* @param interceptorHandlerImplClassName String 用戶提供的攔截器接口的實(shí)現(xiàn)類的類名
* @return Object 返回某個(gè)接口的動(dòng)態(tài)代理對(duì)象
* @throws InstantiationException
* @throws IllegalAccessException
* @throws NotFoundException
* @throws CannotCompileException
* @throws ClassNotFoundException
* @see com.cuishen.myAop.InterceptorHandler
*/
public static Object newProxyInstance(String interfaceClassName, String classToProxy, String interceptorHandlerImplClassName) throws InstantiationException, IllegalAccessException, NotFoundException, CannotCompileException, ClassNotFoundException {
Class interfaceClass = Class.forName(interfaceClassName);
Class interceptorHandlerImplClass = Class.forName(interceptorHandlerImplClassName);
return dynamicImplementsInterface(classToProxy, interfaceClass, interceptorHandlerImplClass);
}
/**
* 動(dòng)態(tài)實(shí)現(xiàn)要代理的接口
* @param classToProxy String 要?jiǎng)討B(tài)代理的接口的實(shí)現(xiàn)類的類名, e.g test.StudentInfoServiceImpl
* @param interfaceClass Class 要?jiǎng)討B(tài)代理的接口類, e.g test.StudentInfoService
* @param interceptorHandlerImplClass Class 用戶提供的攔截器接口的實(shí)現(xiàn)類
* @return Object 返回某個(gè)接口的動(dòng)態(tài)代理對(duì)象
* @throws NotFoundException
* @throws CannotCompileException
* @throws InstantiationException
* @throws IllegalAccessException
*/
private static Object dynamicImplementsInterface(String classToProxy, Class interfaceClass, Class interceptorHandlerImplClass) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {
ClassPool cp = ClassPool.getDefault();
String interfaceName = interfaceClass.getName();
//動(dòng)態(tài)指定代理類的類名
String proxyClassName = interfaceName + PROXY_CLASS_NAME_SUFFIX + proxyClassIndex++;
//要實(shí)現(xiàn)的接口的包名+接口名
String interfaceNamePath = interfaceName;
CtClass ctInterface = cp.getCtClass(interfaceNamePath);
CtClass cc = cp.makeClass(proxyClassName);
cc.addInterface(ctInterface);
Method [] methods = interfaceClass.getMethods();
for(int i = 0; i < methods.length; i++) {
Method method = methods[i];
dynamicImplementsMethodsFromInterface(classToProxy, cc, method, interceptorHandlerImplClass, i);
}
return (Object)cc.toClass().newInstance();
}
/**
* 動(dòng)態(tài)實(shí)現(xiàn)接口里的方法
* @param classToProxy String 要?jiǎng)討B(tài)代理的接口的實(shí)現(xiàn)類的類名, e.g test.StudentInfoServiceImpl
* @param implementer CtClass 動(dòng)態(tài)代理類的包裝
* @param methodToImpl Method 動(dòng)態(tài)代理類里面要實(shí)現(xiàn)的接口方法的包裝
* @param interceptorClass Class 用戶提供的攔截器實(shí)現(xiàn)類
* @param methodIndex int 要實(shí)現(xiàn)的方法的索引
* @throws CannotCompileException
*/
private static void dynamicImplementsMethodsFromInterface(String classToProxy, CtClass implementer, Method methodToImpl, Class interceptorClass, int methodIndex) throws CannotCompileException {
String methodCode = generateMethodCode(classToProxy, methodToImpl, interceptorClass, methodIndex);
CtMethod cm = CtNewMethod.make(methodCode, implementer);
implementer.addMethod(cm);
}
/**
* 動(dòng)態(tài)組裝方法體,當(dāng)然代理里面的方法實(shí)現(xiàn)并不是簡(jiǎn)單的方法拷貝,而是反射調(diào)用了攔截器里的invoke方法,并將接收到的參數(shù)進(jìn)行傳遞
* @param classToProxy String 要?jiǎng)討B(tài)代理的接口的實(shí)現(xiàn)類的類名, e.g test.StudentInfoServiceImpl
* @param methodToImpl Method 動(dòng)態(tài)代理類里面要實(shí)現(xiàn)的接口方法的包裝
* @param interceptorClass Class 用戶提供的攔截器實(shí)現(xiàn)類
* @param methodIndex int 要實(shí)現(xiàn)的方法的索引
* @return String 動(dòng)態(tài)組裝的方法的字符串
*/
private static String generateMethodCode(String classToProxy, Method methodToImpl, Class interceptorClass, int methodIndex) {
String methodName = methodToImpl.getName();
String methodReturnType = methodToImpl.getReturnType().getName();
Class[] parameters = methodToImpl.getParameterTypes();
Class[] exceptionTypes = methodToImpl.getExceptionTypes();
StringBuffer exceptionBuffer = new StringBuffer();
//組裝方法的Exception聲明
if(exceptionTypes.length > 0) exceptionBuffer.append(" throws ");
for(int i = 0; i < exceptionTypes.length; i++) {
if(i != exceptionTypes.length - 1) exceptionBuffer.append(exceptionTypes[i].getName()).append(",");
else exceptionBuffer.append(exceptionTypes[i].getName());
}
StringBuffer parameterBuffer = new StringBuffer();
//組裝方法的參數(shù)列表
for(int i = 0; i < parameters.length; i++) {
Class parameter = parameters[i];
String parameterType = parameter.getName();
//動(dòng)態(tài)指定方法參數(shù)的變量名
String refName = "a" + i;
if(i != parameters.length - 1) parameterBuffer.append(parameterType).append(" " + refName).append(",");
else parameterBuffer.append(parameterType).append(" " + refName);
}
StringBuffer methodDeclare = new StringBuffer();
//方法聲明,由于是實(shí)現(xiàn)接口的方法,所以是public
methodDeclare.append("public ").append(methodReturnType).append(" ").append(methodName).append("(").append(parameterBuffer).append(")").append(exceptionBuffer).append(" {\n");
String interceptorImplName = interceptorClass.getName();
//方法體
methodDeclare.append(INTERCEPTOR_HANDLER_INTERFACE).append(" interceptor = new ").append(interceptorImplName).append("();\n");
//反射調(diào)用用戶的攔截器接口
methodDeclare.append("Object returnObj = interceptor.invoke(Class.forName(\"" + classToProxy + "\").newInstance(), Class.forName(\"" + classToProxy + "\").getMethods()[" + methodIndex + "], ");
//傳遞方法里的參數(shù)
if(parameters.length > 0) methodDeclare.append("new Object[]{");
for(int i = 0; i < parameters.length; i++) {
//($w) converts from a primitive type to the corresponding wrapper type: e.g.
//Integer i = ($w)5;
if(i != parameters.length - 1) methodDeclare.append("($w)a" + i + ",");
else methodDeclare.append("($w)a" + i);
}
if(parameters.length > 0) methodDeclare.append("});\n");
else methodDeclare.append("null);\n");
//對(duì)調(diào)用攔截器的返回值進(jìn)行包裝
if(methodToImpl.getReturnType().isPrimitive()) {
if(methodToImpl.getReturnType().equals(Boolean.TYPE)) methodDeclare.append("return ((Boolean)returnObj).booleanValue();\n");
else if(methodToImpl.getReturnType().equals(Integer.TYPE)) methodDeclare.append("return ((Integer)returnObj).intValue();\n");
else if(methodToImpl.getReturnType().equals(Long.TYPE)) methodDeclare.append("return ((Long)returnObj).longValue();\n");
else if(methodToImpl.getReturnType().equals(Float.TYPE)) methodDeclare.append("return ((Float)returnObj).floatValue();\n");
else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return ((Double)returnObj).doubleValue();\n");
else if(methodToImpl.getReturnType().equals(Character.TYPE)) methodDeclare.append("return ((Character)returnObj).charValue();\n");
else if(methodToImpl.getReturnType().equals(Byte.TYPE)) methodDeclare.append("return ((Byte)returnObj).byteValue();\n");
else if(methodToImpl.getReturnType().equals(Short.TYPE)) methodDeclare.append("return ((Short)returnObj).shortValue();\n");
} else {
methodDeclare.append("return (" + methodReturnType + ")returnObj;\n");
}
methodDeclare.append("}");
System.out.println(methodDeclare.toString());
return methodDeclare.toString();
}
}
public class Main{
public static void main(String[] args) throws Exception{
//分別對(duì)應(yīng) 代理類要實(shí)現(xiàn)的接口類名, 需要代理類的類名, 用戶自定義攔截器實(shí)現(xiàn)類的類名
Object proxyArithmetic = MyProxyImpl.newProxyInstance("com.test.ArithmeticInterface", "com.test.Arithmetic",
"com.test.InterceptorHandlerImpl");
((ArithmeticInterface)proxyArithmetic).add(a, b);
((ArithmeticInterface)proxyArithmetic).sub(a, b);
}
}
打印一下動(dòng)態(tài)實(shí)現(xiàn)接口的代碼如下:
public int add(int a0,int a1) {
com.test.InterceptorHandler interceptor = new com.test.InterceptorHandlerImpl();
Object returnObj = interceptor.invoke(Class.forName("com.test.Arithmetic").newInstance(), Class.forName("com.test.Arithmetic").getMethods()[0], new Object[]{($w)a0,($w)a1});
return ((Integer)returnObj).intValue();
}
public int sub(int a0,int a1) {
com.test.InterceptorHandler interceptor = new com.test.InterceptorHandlerImpl();
Object returnObj = interceptor.invoke(Class.forName("com.test.Arithmetic").newInstance(), Class.forName("com.test.Arithmetic").getMethods()[1], new Object[]{($w)a0,($w)a1});
return ((Integer)returnObj).intValue();
}
以上就是關(guān)于java中動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制的詳細(xì)介紹,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
Spring Cloud Gateway重試機(jī)制原理解析
這篇文章主要介紹了Spring Cloud Gateway重試機(jī)制原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
Java后端向前端返回文件流實(shí)現(xiàn)下載功能
后端可以使用Java中servlet提供的HttpServletResponse,核心步驟是要設(shè)置響應(yīng)的數(shù)據(jù)類型,設(shè)置為某一類文件類型或二進(jìn)制格式,以及響應(yīng)頭,然后用ServletOutputStream將文件以流的形式發(fā)送到前端,本文介紹Java后端向前端返回文件流實(shí)現(xiàn)下載功能,感興趣的朋友一起看看吧2023-12-12
Java將字節(jié)轉(zhuǎn)換為十六進(jìn)制代碼分享
我們知道,在java中,一個(gè)byte 就是一個(gè)字節(jié),也就是八個(gè)二進(jìn)制位;而4個(gè)二進(jìn)制位就可以表示一個(gè)十六進(jìn)制位,所以一個(gè)byte可以轉(zhuǎn)化為2個(gè)十六進(jìn)制位。下面我們就來詳細(xì)看下具體方法吧。2016-01-01
SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法
這篇文章主要介紹了SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
Java Web基于Session的登錄實(shí)現(xiàn)方法
這篇文章主要介紹了Java Web基于Session的登錄實(shí)現(xiàn)方法,涉及Java針對(duì)session的操作及表單提交與驗(yàn)證技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
利用Java搭建個(gè)簡(jiǎn)單的Netty通信實(shí)例教程
這篇文章主要給大家介紹了關(guān)于如何利用Java搭建個(gè)簡(jiǎn)單的Netty通信,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
java學(xué)習(xí)筆記_關(guān)于字符串概述
下面小編就為大家?guī)硪黄猨ava學(xué)習(xí)筆記_關(guān)于字符串概述。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
IDEA 非常重要的一些設(shè)置項(xiàng)(一連串的問題差點(diǎn)讓我重新用回 Eclipse)
這篇文章主要介紹了IDEA 非常重要的一些設(shè)置項(xiàng)(一連串的問題差點(diǎn)讓我重新用回 Eclipse),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
一文看懂springboot實(shí)現(xiàn)短信服務(wù)功能
項(xiàng)目中的短信服務(wù)基本上上都會(huì)用到,簡(jiǎn)單的注冊(cè)驗(yàn)證碼,消息通知等等都會(huì)用到。這篇文章主要介紹了springboot 實(shí)現(xiàn)短信服務(wù)功能,需要的朋友可以參考下2019-10-10

