Java_JDK动态代理学习笔记
昨天被问了个问题,问题的大意是这样的:为什么 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法的3个参数是这样的定义的?笔者一阵语塞,好生郁闷。在这里补充一下,记录下对这个问题的解答。
基本样例
接口类
package com.vavi.proxy;
public interface Sleepable {
void sleep();
void eat();
}
实现类
package com.vavi.proxy;
public class Person implements Sleepable {
public void sleep() {
System.out.println("He is sleeping");
}
public void eat() {
System.out.println("He is eating");
}
}
InvocationHandler实现类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class PersonDynamicJDKProxyHandler implements InvocationHandler {
private final Object targetObject;
public PersonDynamicJDKProxyHandler(Object targetObject) {
this.targetObject = targetObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getName().equals("eat")) {
System.out.println("wash hands before eating");
method.invoke(targetObject, args);
System.out.println("ready to sleep now..");
} else {
System.out.println("take off clothes");
method.invoke(targetObject, args);
System.out.println("sweet dream now..");
}
return null;
}
}
客户端类
import java.lang.reflect.Proxy;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import org.junit.Test;
import com.vavi.proxy.Person;
import com.vavi.proxy.Sleepable;
public class TestPersonDynamicJDKProxy {
@Test
public void testProxy() throws Exception {
// System.getProperties().put(
// "sun.misc.ProxyGenerator.saveGeneratedFiles", true);
Person person = new Person();
PersonDynamicJDKProxyHandler handler = new PersonDynamicJDKProxyHandler(
person);
Sleepable proxy = (Sleepable) Proxy.newProxyInstance(person.getClass()
.getClassLoader(), person.getClass().getInterfaces(), handler);
// 获取代理类的字节码
generateProxyClassFile();
proxy.eat();
proxy.sleep();
}
private void generateProxyClassFile() {
byte[] classFile = sun.misc.ProxyGenerator.generateProxyClass(
"$MyProxy", Person.class.getInterfaces());
FileOutputStream out = null;
String path = "/Users/ghj/startup/$MyProxy.class";
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
源码分析
执行上述程序后,系统打印如下结果:
wash hands before eating
He is eating
ready to sleep now..
take off clothes
He is sleeping
sweet dream now..
现在程序运行正常,成功实现了动态代理的效果。但是这个为什么能够得到这样的效果呢?我们跟着一步步跟踪代码执行,首先发现最重要的是(Sleepable) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), handler);
这段代码。
public static Object Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces); // 标记1
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);// 标记2
final InvocationHandler ih = h;
SecurityManager sm = System.getSecurityManager();
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);//标记3
}
});
} else {
return newInstance(cons, ih);//标记3
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
在上面代码中,标记1生成代理类的class对象,标记2获得带有这个参数constructorParams
的构造器,标记3完成对象实例化。 需要提前说明的是,由于在Proxy类中,硬编码了private final static Class[] constructorParams = { InvocationHandler.class };
这个属性值,所以这个一定程度了约束了我们必须要和InvocationHandler
打交道了。并且隐含在代理类中,有一个带有constructorParams
参数的构造器。这个在后文也会提及到。
下面接着看标记1内部实现(笔者删除了大量注释),中间进行了一些安全校验,接口个数校验,重复的接口名称等校验。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller
final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME);
final ClassLoader ccl = caller.getClassLoader();
checkProxyLoader(ccl, loader);
ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
}
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
Class<?> proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
// for detecting duplicates
Set<Class<?>> interfaceSet = new HashSet<>();
for (int i = 0; i < interfaces.length; i++) {
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
List<String> key = Arrays.asList(interfaceNames);
/*
* Find or create the proxy class cache for the class loader.
*/
Map<List<String>, Object> cache;
synchronized (loaderToCache) {
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
/*
* This mapping will remain valid for the duration of this
* method, without further synchronization, because the mapping
* will only be removed if the class loader becomes unreachable.
*/
}
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
try {
String proxyPkg = null; // package to define proxy class in
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
{
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num; // 标记1.1
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces); // 标记1.2
try {
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);// 标记1.3
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);
} finally {
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference<Class<?>>(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass;
}
标记1.1完成包名计算。 这里解释了为什么包名是类似com.sun.proxy.$Proxy.NUM
或者PKG.$Proxy.NUM
的形式了。
标记1.2完成字节码数组拼接,这个稍后分析。
标记1.3完成字节码数组拼接,最终返回Class对象。
标记1.2的代码如下:
public static byte[] ProxyGenerator.generateProxyClass(final String name,
Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
final byte[] classFile = gen.generateClassFile(); //标记1.2.1
if (saveGeneratedFiles) { //标记1.2.2
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
在上面代码的标记1.2.1中完成了实际的字节码拼接操作。
在上面代码的标记1.2.2中,使用了这个saveGeneratedFiles变量。而这个变量是这么定义的private final static boolean saveGeneratedFiles = java.security.AccessController.doPrivileged( new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();
。这个参数可以帮助我们把字节码写到文件中了。
现在,接着看下标记1.2.1处的代码。从下面的代码我们可以看到。该方法先后完成了hashCodeMethod,equalsMethod,toStringMethod的数据准备,所有接口的所有方法。在标记1.2.1.1处完成了带InvocationHandler参数的构造器和静态代码块的数据准备。
为避免正文过长,我把generateConstructor
和generateStaticInitializer
挪到附录章节。
private byte[] generateClassFile() {
* and toString methods of java.lang.Object. This is done before
* Step 1: Assemble ProxyMethod objects for all methods to
* generate proxy dispatching code for.
*/
/*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
/*
* Now record all of the methods from the proxy interfaces, giving
* earlier interfaces precedence over later ones with duplicate
* methods.
*/
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of
* fields and methods in the class we are generating.
*/
try {
methods.add(generateConstructor()); //标记1.2.1.1
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
methods.add(generateStaticInitializer()); //标记1.2.1.2
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* ============================================================
* Step 3: Write the final class file.
*/
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(
dotToSlash(interfaces[i].getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
return bout.toByteArray();
}
我们最后使用反编译工具JAD-UI看下生成的代理对象字节码。完整的代码见附录,这里关注下里面的eat()方法。该方法内部的this.h属性就是InvocationHandler的实现类。然后调到invoke方法,完成了最终的执行。
public final void eat()
{
try {
this.h.invoke(this, m3, null);
return;
} catch (Error localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
总结
- 最后,我们再回头看看本篇提的问题。这3个参数分别解决了如下几个问题:
ClassLoader loader
解决了使用什么classloader来加载这个代理类Class<?>[] interfaces
首先约束了JDK动态代理机制是基于接口实现的,它要求我们被代理的类必须实现相应的接口;其次JDK动态代理机制会帮我们完成接口方法的代理方法的实现,并通过硬编码把代理职责委托给了InvocationHandler h
这个参数。InvocationHandler h
完成了实际的代理职责。InvocationHandler.invoke(Object proxy, Method method, Object[] args)
的3个参数:- proxy是生成的代理实例,里面不包含target对象实例。所以,我们一般在实现InvocationHandler接口时,会通过构造方法传入target对象。
- method和args 分别对应了 target对象的方法和参数
- 在
InvocationHandler.invoke
内部再利用反射完成target对象的方法执行。
- 在源码面前,一切毫无遁形。
- 知其然,尽量要知其所以然。
附录
2.构造器数据准备
"<init>", "(Ljava/lang/reflect/InvocationHandler;)V",
MethodInfo minfo = new MethodInfo(
"<init>", "(Ljava/lang/reflect/InvocationHandler;)V",
ACC_PUBLIC);
DataOutputStream out = new DataOutputStream(minfo.code);
code_aload(0, out);
code_aload(1, out);
out.writeByte(opc_invokespecial);
out.writeShort(cp.getMethodRef(
superclassName,
"<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
out.writeByte(opc_return);
minfo.maxStack = 10;
minfo.maxLocals = 2;
minfo.declaredExceptions = new short[0];
return minfo;
}
3.静态块数据准备
cp.getClass("java/lang/NoSuchMethodException")));
MethodInfo minfo = new MethodInfo(
"<clinit>", "()V", ACC_STATIC);
int localSlot0 = 1;
short pc, tryBegin = 0, tryEnd;
DataOutputStream out = new DataOutputStream(minfo.code);
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
pm.codeFieldInitialization(out);
}
}
out.writeByte(opc_return);
tryEnd = pc = (short) minfo.code.size();
minfo.exceptionTable.add(new ExceptionTableEntry(
tryBegin, tryEnd, pc,
cp.getClass("java/lang/NoSuchMethodException")));
code_astore(localSlot0, out);
out.writeByte(opc_new);
out.writeShort(cp.getClass("java/lang/NoSuchMethodError"));
out.writeByte(opc_dup);
code_aload(localSlot0, out);
out.writeByte(opc_invokevirtual);
out.writeShort(cp.getMethodRef(
"java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
out.writeByte(opc_invokespecial);
out.writeShort(cp.getMethodRef(
"java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V"));
out.writeByte(opc_athrow);
pc = (short) minfo.code.size();
minfo.exceptionTable.add(new ExceptionTableEntry(
tryBegin, tryEnd, pc,
cp.getClass("java/lang/ClassNotFoundException")));
code_astore(localSlot0, out);
out.writeByte(opc_new);
out.writeShort(cp.getClass("java/lang/NoClassDefFoundError"));
out.writeByte(opc_dup);
code_aload(localSlot0, out);
out.writeByte(opc_invokevirtual);
out.writeShort(cp.getMethodRef(
"java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
out.writeByte(opc_invokespecial);
out.writeShort(cp.getMethodRef(
"java/lang/NoClassDefFoundError",
"<init>", "(Ljava/lang/String;)V"));
out.writeByte(opc_athrow);
if (minfo.code.size() > 65535) {
throw new IllegalArgumentException("code size limit exceeded");
}
minfo.maxStack = 10;
minfo.maxLocals = (short) (localSlot0 + 1);
minfo.declaredExceptions = new short[0];
return minfo;
}
4.代理类反编译后的源码
import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import com.vavi.proxy.Sleepable;
public final class MyProxy extends Proxy implements Sleepable {
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m4;
private static Method m2;
public MyProxy()
{
super(paramInvocationHandler);
}
public final boolean equals()
{
try {
return ((Boolean) this.h.invoke(this, m1,
new Object[] { paramObject })).booleanValue();
} catch (Error localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
@Override
public final void eat()
{
try {
this.h.invoke(this, m3, null);
return;
} catch (Error localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
@Override
public final int hashCode()
{
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Error localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
@Override
public final void sleep()
{
try {
this.h.invoke(this, m4, null);
return;
} catch (Error localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
@Override
public final String toString()
{
try {
return ((String) this.h.invoke(this, m2, null));
} catch (Error localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.vavi.proxy.Sleepable").getMethod("eat",
new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m4 = Class.forName("com.vavi.proxy.Sleepable").getMethod("sleep",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(
localClassNotFoundException.getMessage());
}
}
}
转自:http://my.oschina.net/geecoodeer/blog/204138
Java_JDK动态代理学习笔记的更多相关文章
- CgLib动态代理学习【Spring AOP基础之一】
如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...
- 动态代理学习(二)JDK动态代理源码分析
上篇文章我们学习了如何自己实现一个动态代理,这篇文章我们从源码角度来分析下JDK的动态代理 先看一个Demo: public class MyInvocationHandler implements ...
- 动态代理学习(一)自己动手模拟JDK动态代理
最近一直在学习Spring的源码,Spring底层大量使用了动态代理.所以花一些时间对动态代理的知识做一下总结. 我们自己动手模拟一个动态代理 对JDK动态代理的源码进行分析 文章目录 场景: 思路: ...
- jdk动态代理学习
在jdk的好多底层代码中很多都使用jdk的动态代理,下面就写写简单的代码来look look. 老规矩先上代码: public interface SayDao { public String say ...
- java 动态代理学习(Proxy,InvocationHandler)
前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...
- Java动态代理学习【Spring AOP基础之一】
Spring AOP使用的其中一个底层技术就是Java的动态代理技术.Java的动态代理技术主要围绕两个类进行的 java.lang.reflect.InvocationHandler java.la ...
- java jdk动态代理学习记录
转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...
- JAVA 动态代理学习记录
打算用JAVA实现一个简单的RPC框架,看完RPC参考代码之后,感觉RPC的实现主要用到了两个方面的JAVA知识:网络通信和动态代理.因此,先补补动态代理的知识.---多看看代码中写的注释 参考:Ja ...
- Java动态代理学习
动态代理类 Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类: 1.Interface InvocationHandler 该接口中仅定义了一个方法: Objec ...
随机推荐
- [荐]Js apply()和call()方法详解 - http://www.w3cfuns.com/article-5596443-1-1.html
本帖最后由 默默DE人生 于 2013-3-19 13:22 编辑 Js apply方法详解我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文 ...
- PHP中include和require(转)
昨天去面试一个php开发,看到笔试试卷上有这么一道题目: include和require有什么区别? 这个题目可以称得上php开发面试中的必考题目,网上也有各种答案和解释.但是我当时却真的想不起来了. ...
- python web编程-CGI帮助web服务器处理客户端编程
这几篇博客均来自python核心编程 如果你有任何疑问,欢迎联系我或者仔细查看这本书的地20章 另外推荐下这本书,希望对学习python的同学有所帮助 概念预热 eb客户端通过url请求web服务器里 ...
- ios程序后台运行设置(不是太懂)
文一 我从苹果文档中得知,一般的应用在进入后台的时候可以获取一定时间来运行相关任务,也就是说可以在后台运行一小段时间. 还有三种类型的可以运行在后以, 1.音乐 2.location 3.voip 文 ...
- Java学习随笔4:Java的IO操作
1. IO流的作用是读写设备上的数据,如硬盘文件.内存.键盘.网络等.根据数据走向,可分为:输入流和输出流:根据处理的数据类型,可分为:字节流和字符流.字节流可以处理所有类型的数据,如MP3.图片.视 ...
- C#实现序列化和反序列化
从我们面试准备上面,我知道了一个知识点,就是我们vs提供的序列化方法有两个,一个叫二进制序列化,一个叫做xml序列化,下面我们说一下二进制序列化的C#实现: 反序列化: public static T ...
- 封装JavaScript的AJAX
// 创建request对象 function createXMLHttpRequest() { try { return new XMLHttpRequest();//大多数浏览器 } catch ...
- java测试时常见的一些错误
1.解决警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' http://blo ...
- JMeter中的场景执行持续时间设置
jmeter之调度器配置 JMeter的线程组设置里有一个调配器设置,用于设置该线程组下脚本执行的开始时间.结束时间.持续时间及启动延迟时间.当需要半夜执行性能测试时会用到这个功能. 设置调度器配置, ...
- 从客户端(CourseIssueContent="<P>财务审计师岗位认证招生简章<BR>...")中检测到有潜在危险的 Request.Form 值。
说明: 请求验证过程检测到有潜在危险的客户端输入值,对请求的处理已经中止.该值可能指示危及应用程序安全的尝试,如跨站点的脚本攻击.通过在 Page 指令或 配置节中设置 validateRequest ...