写一个注解使用类,如下:

public class Test {

    @Code(author = "mazhi",date="20170611")
    private void process() {

    }
}

使用Javac提供的api来运行Test类。在javac的Launcher类中编写如下代码:

JavaCompiler javac = ToolProvider.getSystemJavaCompiler();

String commonPath = "H:\\program workspace\\project\\Compiler_javac\\";
String str27 = commonPath+"test\\com\\test01\\Test.java";

javac.run(System.in, null, null,
        		//"-verbose",
        		//"-Xprint",
        		"-XprintRounds",
        		"-XprintProcessorInfo",
        		"-processor","com.test01.CodeProcessor",
        		"-d", "C:\\Users\\Administrator.PC-20160319ZQPU\\workspace\\tmp\\tmpdir\\",
        		str27);  

-Xprint表示打印出注解类,也就是Code类的代码。

-XprintRounds 打印出每一次Round的情况

-processor指定了注解处理器,注意这个包名必须在源代码目录下,不能在test目录或者其它路径下,否则不能加载到classpath中。

-d表示输出路径,CodeProcessor注解处理类生成的类路径,在运行CodeProcessor后生成的类名为GenerateTest如下:

package com.test01;

class TestAutogenerate {

    protected TestAutogenerate() {}
    protected final void message() {

//process()
//@com.test01.Code(date=20170611, author=mazhi)

        System.out.println("author:mazhi");

        System.out.println("date:20170611");
    }
}

下面来看javac的具体处理过程。

1、在javac的compile方法中会调用initProcessAnnotations(processors)方法

initProcessAnnotations(processors)方法的具体代码如下:

 /**
     * Check if we should process annotations.
     * If so, and if no scanner is yet registered, then set up the DocCommentScanner
     * to catch doc comments, and set keepComments so the parser records them in the compilation unit.
     *
     * @param processors user provided annotation processors to bypass
     * discovery, {@code null} means that no processors were provided
     */
    public void initProcessAnnotations(Iterable<? extends Processor> processors) {
        // Process annotations if processing is not disabled and there
        // is at least one Processor available.
        if (options.isSet(PROC, "none")) {
            processAnnotations = false;
        } else if (procEnvImpl == null) {
            procEnvImpl = new JavacProcessingEnvironment(context, processors);
            processAnnotations = procEnvImpl.atLeastOneProcessor();

            if (processAnnotations) {
                options.put("save-parameter-names", "save-parameter-names");
                reader.saveParameterNames = true;
                keepComments = true;
                genEndPos = true;
                if (taskListener != null)
                    taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
                log.deferDiagnostics = true;
            } else { // free resources
                procEnvImpl.close();
            }
        }
    }  

最主要的逻辑就是在创建JavacProcessingEnvironment对象,这个对象的构造函数中会调用initProcessorIterator(context, processors);

主要的代码如下:

private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
        Log   log   = Log.instance(context);
        Iterator<? extends Processor> processorIterator;

        if (options.isSet(XPRINT)) {
            try {
                Processor processor = PrintingProcessor.class.newInstance();
                processorIterator = List.of(processor).iterator();
            } catch (Throwable t) {
                AssertionError assertError = new AssertionError("Problem instantiating PrintingProcessor.");
                assertError.initCause(t);
                throw assertError;
            }
        } else if (processors != null) {
            processorIterator = processors.iterator();
        } else {
            String processorNames = options.get(PROCESSOR);
            JavaFileManager fileManager = context.get(JavaFileManager.class);
            try {
                // If processorpath is not explicitly set, use the classpath.
                processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
                    ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
                    : fileManager.getClassLoader(CLASS_PATH);

                /*
                 * If the "-processor" option is used, search the appropriate
                 * path for the named class.  Otherwise, use a service
                 * provider mechanism to create the processor iterator.
                 */
                if (processorNames != null) {
                    processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
                } else {
                    processorIterator = new ServiceIterator(processorClassLoader, log);
                }
            } catch (SecurityException e) {
                /*
                 * A security exception will occur if we can't create a classloader.
                 * Ignore the exception if, with hindsight, we didn't need it anyway
                 * (i.e. no processor was specified either explicitly, or implicitly,
                 * in service configuration file.) Otherwise, we cannot continue.
                 */
                processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
            }
        }
        discoveredProcs = new DiscoveredProcessors(processorIterator);
    }  

不难看出这个逻辑通过对-Xprint命令进行判断,如果设置了,那么就会打印注解类的代码(注意不是使用注解类的代码),其中打印已经有了默认的注解处理实现类PrintingProcessor。

最后一个else循环中对-processor命令进行了处理,得到的值就是我们指定的注解处理类CodeProcessor。接着往下面的逻辑看,从两处查找注解处理类Processor,这里用到了迭代器模式,有NameProcessIterator与ServiceIterator。

(1)NameProcessIterator主要处理-processor命令指定的注解处理器,指定多个时使用逗号分割,这样就可以通过这个注解处理器迭代获取

(2)ServiceIterator主要处理 /META-INF/services/下指定的注解处理器,在前面说过。

最后一段代码生成一个DiscoveredProcessors对象。

2、在initProcessAnnotations(processors)方法中调用JavacProcessingEnvironment的atLeastOneProcessor()方法

public boolean atLeastOneProcessor() {
        return discoveredProcs.iterator().hasNext();
}

DiscoveredProcessor是一个双层的迭代器,具体代码如下:

 // TODO: These two classes can probably be rewritten better...
    /**
     * This class holds information about the processors that have
     * been discoverd so far as well as the means to discover more, if
     * necessary.  A single iterator should be used per round of
     * annotation processing.  The iterator first visits already
     * discovered processors then fails over to the service provider
     * mechanism if additional queries are made.
     */
    class DiscoveredProcessors implements Iterable<ProcessorState> {

        // 省略内部的ProcessorStateIterator迭代器

        Iterator<? extends Processor> processorIterator;
        ArrayList<ProcessorState>  procStateList;

        public ProcessorStateIterator iterator() {
            return new ProcessorStateIterator(this); // 创建一个内部的迭代器ProcessorStateIterator类对象
        }

        DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
            this.processorIterator = processorIterator;
            this.procStateList = new ArrayList<ProcessorState>();
        }

        /**
         * Free jar files, etc. if using a service loader.
         */
        public void close() {
            if (processorIterator != null &&
                processorIterator instanceof ServiceIterator) {
                ((ServiceIterator) processorIterator).close();
            }
        }
    }

  

DiscoveredProcessors内部的迭代器如下:

class ProcessorStateIterator implements Iterator<ProcessorState> {
            DiscoveredProcessors psi;
            Iterator<ProcessorState> innerIter;
            boolean onProcInterator;

            ProcessorStateIterator(DiscoveredProcessors psi) {
                this.psi = psi;
                this.innerIter = psi.procStateList.iterator();
                this.onProcInterator = false;
            }

            public ProcessorState next() {
                if (!onProcInterator) {
                    if (innerIter.hasNext())
                        return innerIter.next();
                    else
                        onProcInterator = true;
                }

                if (psi.processorIterator.hasNext()) {
                    ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
                                                           log, source, JavacProcessingEnvironment.this);
                    psi.procStateList.add(ps);
                    return ps;
                } else
                    throw new NoSuchElementException();
            }

            public boolean hasNext() {
                if (onProcInterator)
                    return  psi.processorIterator.hasNext();
                else
                    return innerIter.hasNext() || psi.processorIterator.hasNext();
            }

            public void remove () {
                throw new UnsupportedOperationException();
            }

            /**
             * Run all remaining processors on the procStateList that
             * have not already run this round with an empty set of annotations.
             */
            public void runContributingProcs(RoundEnvironment re) {
                if (!onProcInterator) {
                    Set<TypeElement> emptyTypeElements = Collections.emptySet();
                    while(innerIter.hasNext()) {
                        ProcessorState ps = innerIter.next();
                        if (ps.contributed)
                            callProcessor(ps.processor, emptyTypeElements, re);
                    }
                }
            }
        }  

重点注意如上next()方法的一个处理逻辑,优先返回procStateList中的注解处理器。  

这个类涉及到另外一个重要的类ProcessorState,主要的代码逻辑段如下:

/**
     * State about how a processor has been used by the tool.  If a
     * processor has been used on a prior round, its process method is
     * called on all subsequent rounds, perhaps with an empty set of
     * annotations to process.  The {@code annotatedSupported} method
     * caches the supported annotation information from the first (and
     * only) getSupportedAnnotationTypes call to the processor.
     */
    static class ProcessorState {
        public Processor processor;
        public boolean   contributed;
        private ArrayList<Pattern> supportedAnnotationPatterns;
        private ArrayList<String>  supportedOptionNames;

        ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
            processor = p;
            contributed = false;

            try {
                processor.init(env);

                checkSourceVersionCompatibility(source, log);

                supportedAnnotationPatterns = new ArrayList<Pattern>();
                for (String importString : processor.getSupportedAnnotationTypes()) {
                    supportedAnnotationPatterns.add(importStringToPattern(importString,
                                                                          processor,
                                                                          log));
                }

                supportedOptionNames = new ArrayList<String>();
                for (String optionName : processor.getSupportedOptions() ) {
                    if (checkOptionName(optionName, log))
                        supportedOptionNames.add(optionName);
                }

            } catch (ClientCodeException e) {
                throw e;
            } catch (Throwable t) {
                throw new AnnotationProcessingError(t);
            }
        }
        // 省略一些方法
}

  

3、调用processAnnotations()方法运行注解处理器

这个方法的运行时机是完成词法和语法分析,并且输入符号表的过程完成后进行调用。

主要执行了代码:procEnvImpl.doProcessing(context, roots, classSymbols, pckSymbols)

主要的逻辑代码段如下:

Round round = new Round(context, roots, classSymbols);

        boolean errorStatus;
        boolean moreToDo;
        do {
            // Run processors for round n
            round.run(false, false);

            // Processors for round n have run to completion.
            // Check for errors and whether there is more work to do.
            errorStatus = round.unrecoverableError();
            moreToDo = moreToDo();

            round.showDiagnostics(errorStatus || showResolveErrors);

            // Set up next round.
            // Copy mutable collections returned from filer.
            round = round.next(
                    new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()),
                    new LinkedHashMap<String,JavaFileObject>(filer.getGeneratedClasses()));

             // Check for errors during setup.
            if (round.unrecoverableError())
                errorStatus = true;

        } while (moreToDo && !errorStatus);

        // run last round
        round.run(true, errorStatus);
        round.showDiagnostics(true);

3.1、创建一个Round类对像,主要的逻辑是调用了findAnnotationsPresent()方法,代码如下:

/** Find the set of annotations present in the set of top level
 *  classes and package info files to be processed this round. */
        void findAnnotationsPresent() {
            ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
            // Use annotation processing to compute the set of annotations present
            annotationsPresent = new LinkedHashSet<TypeElement>();
            for (ClassSymbol classSym : topLevelClasses)
                annotationComputer.scan(classSym, annotationsPresent);
            for (PackageSymbol pkgSym : packageInfoFiles)
                annotationComputer.scan(pkgSym, annotationsPresent);
}  

其中的topLevelClasses的值如下截图:

运行完成后的annotationsPresent值如下截图。

可以看到为process()方法找到了对应的注解类Code。下面来看查找Test()与process()方法注解的具体过程。 

涉及到一个类ComputeAnnotationSet,代码如下:

 /**
     * Computes the set of annotations on the symbol in question.
     * Leave class public for external testing purposes.
     */
    public static class ComputeAnnotationSet extends  ElementScanner7<Set<TypeElement>, Set<TypeElement>> {
        final Elements elements;

        public ComputeAnnotationSet(Elements elements) {
            super();
            this.elements = elements;
        }

        @Override
        public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
            // Don't scan enclosed elements of a package
            return p;
        }

        @Override
        public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
            for (AnnotationMirror annotationMirror : elements.getAllAnnotationMirrors(e) ) {
                Element e2 = annotationMirror.getAnnotationType().asElement();
                p.add((TypeElement) e2);
            }
            return super.scan(e, p);
        }
    }  

调用scan()方法时elements对象为JavacElements类对象,前面介绍过。由于Test的ClassSymbol没有注解,所以不走for循环。继续调用super.scan(e,p)方法继续执行查找注解元素。

关于Element中定义的访问者方法<R, P> R accept(ElementVisitor<R, P> v, P p);

但是只有TypeSymbol,PackageSymbol、ClassSymbol、VarSymbol、DelegatedSymbol实现了这个方法。

同样最终会使用scan方法来处理process()方法,其中e为MethodSymbol类型,如下截图:

其中getAllAnnotationMirrors()方法的代码如下:

/**
     * Returns all annotations of an element, whether inherited or directly present.
     * @param e  the element being examined
     * @return all annotations of the element
     */
    public List<com.sun.tools.javac.code.attribute.Compound> getAllAnnotationMirrors(Element e) {
        Symbol sym = cast(Symbol.class, e);
        // 返回Symbol中的attribute_field属性
        List<com.sun.tools.javac.code.attribute.Compound> annos = sym.getAnnotationMirrors();
        while (sym.getKind() == ElementKind.CLASS) {
            Type sup = ((ClassSymbol) sym).getSuperclass();
            if (sup.typeTag != TypeTags.CLASS || sup.isErroneous() ||
                    sup.typeSymbol == syms.objectType.typeSymbol) {
                break;
            }
            sym = sup.typeSymbol;
            List<com.sun.tools.javac.code.attribute.Compound> oldAnnos = annos;
            for (com.sun.tools.javac.code.attribute.Compound anno : sym.getAnnotationMirrors()) {
                if (isInherited(anno.type) &&
                        !containsAnnoOfType(oldAnnos, anno.type)) {
                    annos = annos.prepend(anno);
                }
            }
        }
        return annos;
    }

返回后进入循环获取的annotatonMirror如下截图:

最后调用这个对象的getAnnotationType()方法,返回对象后调用asElement()方法,也就是获取type的tsym属性值,从上图可看出是Code类。

  

3.2、调用Round类对象的round.run(false, false)方法

/** Run a processing round. */
void run(boolean lastRound, boolean errorStatus) {
            printRoundInfo(lastRound);

            try {
                if (lastRound) {
                    filer.setLastRound(true);
                    Set<Element> emptyRootElements = Collections.emptySet(); // immutable
                    RoundEnvironment renv = new JavacRoundEnvironment(true,
                            errorStatus,
                            emptyRootElements,
                            JavacProcessingEnvironment.this);
                    discoveredProcs.iterator().runContributingProcs(renv);
                } else {
                    discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
                }
            } finally {
                if (taskListener != null)
                    taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
            }

            nMessagerErrors = messager.errorCount();
}  

因为首次运行时lastRound值为false,所以运行discoverAndRunProcs()方法,具体代码如下:

  private void discoverAndRunProcs(Context context,
                                     Set<TypeElement> annotationsPresent,
                                     List<ClassSymbol> topLevelClasses,
                                     List<PackageSymbol> packageInfoFiles) {
        Map<String, TypeElement> unmatchedAnnotations = new HashMap<String, TypeElement>(annotationsPresent.size());

        for(TypeElement a  : annotationsPresent) {
                unmatchedAnnotations.put(a.getQualifiedName().toString(), a);
        }

        // Give "*" processors a chance to match
        if (unmatchedAnnotations.size() == 0)
            unmatchedAnnotations.put("", null);

        DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
        // TODO: Create proper argument values; need past round
        // information to fill in this constructor.  Note that the 1
        // st round of processing could be the last round if there
        // were parse errors on the initial source files; however, we
        // are not doing processing in that case.

        Set<Element> rootElements = new LinkedHashSet<Element>();
        rootElements.addAll(topLevelClasses);
        rootElements.addAll(packageInfoFiles);
        rootElements = Collections.unmodifiableSet(rootElements);

        RoundEnvironment renv = new JavacRoundEnvironment(false,
                                                          false,
                                                          rootElements,
                                                          JavacProcessingEnvironment.this);

        while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
            ProcessorState ps = psi.next();
            Set<String>  matchedNames = new HashSet<String>();
            Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();

            for (Map.Entry<String, TypeElement> entry: unmatchedAnnotations.entrySet()) {
                String unmatchedAnnotationName = entry.getKey();
                if (ps.annotationSupported(unmatchedAnnotationName) ) {
                    matchedNames.add(unmatchedAnnotationName);
                    TypeElement te = entry.getValue();
                    if (te != null)
                        typeElements.add(te);
                }
            }

            if (matchedNames.size() > 0 || ps.contributed) {
                boolean processingResult = callProcessor(ps.processor, typeElements, renv);
                ps.contributed = true;
                ps.removeSupportedOptions(unmatchedProcessorOptions);

                if (printProcessorInfo || verbose) {
                    log.printNoteLines("x.print.processor.info",
                            ps.processor.getClass().getName(),
                            matchedNames.toString(),
                            processingResult);
                }

                if (processingResult) {
                    unmatchedAnnotations.keySet().removeAll(matchedNames);
                }

            }
        }
        unmatchedAnnotations.remove("");

        if (lint && unmatchedAnnotations.size() > 0) {
            // Remove annotations processed by javac
            unmatchedAnnotations.keySet().removeAll(platformAnnotations);
            if (unmatchedAnnotations.size() > 0) {
                log = Log.instance(context);
                log.warning("proc.annotations.without.processors",unmatchedAnnotations.keySet());
            }
        }

        // Run contributing processors that haven't run yet
        psi.runContributingProcs(renv);

        // Debugging
        if (options.isSet("displayFilerState"))
            filer.displayState();
    }

3.2、调用next()方法生成新的Round对象

由于生成了新的源代码类GenerateTest,所以需要继续进行处理,但这次调用的构造方法与上次不同,如下:

  /** Create a new round. */
        private Round(Round prev,
                Set<JavaFileObject> newSourceFiles, Map<String,JavaFileObject> newClassFiles) {
            this(prev.nextContext(),
                    prev.number+1,
                    prev.nMessagerErrors,
                    prev.compiler.log.nwarnings);
            this.genClassFiles = prev.genClassFiles;

            List<JCCompilationUnit> parsedFiles = compiler.parseFiles(newSourceFiles);
            roots = cleanTrees(prev.roots).appendList(parsedFiles);

            // Check for errors after parsing
            if (unrecoverableError())
                return;

            enterClassFiles(genClassFiles);
            List<ClassSymbol> newClasses = enterClassFiles(newClassFiles);
            genClassFiles.putAll(newClassFiles);
            enterTrees(roots);

            if (unrecoverableError())
                return;

            topLevelClasses = join(
                    getTopLevelClasses(parsedFiles),
                    getTopLevelClassesFromClasses(newClasses));

            packageInfoFiles = join(
                    getPackageInfoFiles(parsedFiles),
                    getPackageInfoFilesFromClasses(newClasses));

            findAnnotationsPresent();
        }  

同样也调用了findAnnotationPresent()方法。这样创建了Round后继续调用run()方法。

上一篇提到了实现Annotation注解处理器的一些重要的类,如Message,Filer。

Message用来报告错误,警告和其他提示信息。

JavacFilter用来创建新的源文件,class文件以及辅助文件。

下面来看看javac中的具体实现。

(1)JavacFiler

首先看下JavacFiler的继承体系,其中主要包括两大块,操作流和表示的文件类型。

(2)JavacRoundEnvironment

 sdddddd

(3)JavacProcessingEnvironment

 

 

举个例子来看一看注解定义类在语法树中用哪些语法节点来表示以及如何组织的。

@Retention(RetentionPolicy.RUNTIME) // 注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)         // 目标是方法
@Documented   // 文档生成时,该注解将被包含在javadoc中,可去掉
public @interface TestAnnotation {
	public String name() default "helloworld";
}

查看JCClassDecl语法节点(注解类也被当作一个类来表示)的jcModifiers属性,截图如下。

jcModifiers中的flags属性值为8705,必写为这样的表示:

(1<<13)|(1<<0)|(1<<9)

查看Flags中关于各个Modifiers指定的常量,可知是public和interface且还有个专门表示annotation的静态常量,如下:

/** Flag that marks attribute interfaces 标记属性接口, added in classfile v49.0.
     *  应该只能修饰注解定义类
     */
    public static final int ANNOTATION   = 1<<13;

然后看一下JCMethodDecl属性,截图如下:

使用如上的注解,如下:

public class C {

	@TestAnnotation(name = "小明")
	public Integer test(int a, String name, Object c) {
		return 1;
	}
}

截图如下:

关于注解Annotation第二篇的更多相关文章

  1. 关于注解Annotation第一篇

    注解的定义格式如下: public @interface 注解名 {定义体} 定义体就是方法的集合,每个方法实则是声明了一个配置参数.方法的名称作为配置参数的名称,方法的返回值类型就是配置参数的类型. ...

  2. 【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  3. JUnit扩展:引入新注解Annotation

    发现问题 JUnit提供了Test Suite来帮助我们组织case,还提供了Category来帮助我们来给建立大的Test Set,比如BAT,MAT, Full Testing. 那么什么情况下, ...

  4. SSH整合 第二篇 工程初建

    SSH整合,第二篇. 创建工程 这里只是测试和理解hibernate.建立Java工程就好了. 1.hibernate-4.2.21.jar hibernate包下的required,即\hibern ...

  5. [转]Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇--开发工具库篇,主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多 ...

  6. Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇——开发工具库篇,**主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容 ...

  7. Java注解Annotation学习

    学习注解Annotation的原理,这篇讲的不错:http://blog.csdn.net/lylwo317/article/details/52163304 先自定义一个运行时注解 @Target( ...

  8. spring beans源码解读之--Bean的注解(annotation)

    随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...

  9. JAVA提高五:注解Annotation

    今天我们学习JDK5.0中一个非常重要的特性,叫做注解.是现在非常流行的一种方式,可以说因为配置XML 比较麻烦或者比容易查找出错误,现在越来越多的框架开始支持注解方式,比如注明的Spring 框架, ...

随机推荐

  1. Java中的I/O 线程 网络

    Java学习总结--I/O,线程,网络题目整理 I/O 1.有什么理由必须要用字符流? 答:处理字符数据的语法更方便.自动化字符编码 2.插入哪些代码可以让下面的代码正确编译? Console con ...

  2. [转自知乎] 从github上下载单个文件夹

    原文地址: 如何从 GitHub 上下载单个文件夹?  注意:如果是在公司网络环境的话需要配置可以访问外网的代理,否则 svn checkout 时会出错.

  3. [Zend Mail]发送中文名附件出现乱码解决方案

    Zend Framework 1.0.* “=?UTF-8?B?”.base64_encode($title).“?=” 发送中文名附件,结果如图: 英文名附件,结果截图: 解决办法就是将中文文件名拼 ...

  4. PHP Functions - arsort()

    <?php $characters = array('a','b','c','d','e','f'); arsort($characters); print_r($characters); /* ...

  5. Mina的ssl加密

    前面写的Mina的示例,都是可以通过telnet登录的,并且可以相互交互. 现在采用ssl加密的方式,对建立连接时,进行加密连接.这样,双方只有统一的加密,然后才可以连接. 密钥的生成之前有说过,里面 ...

  6. 解决使用ICsharpCode解压缩时候报错Size MisMatch4294967295;的错误

    如果是一个文件夹生成的zip文件,解压缩时候不会报错. 如果是一个文件夹里面包含着两个子文件夹,而且每个子文件夹里面都有着文件.生成的zip文件在解压时候就出报这个错误. 具体的解决办法,通过网上搜索 ...

  7. 使用filter进行登录验证,并解决多次重定向问题

    最近在做关于filter登录验证的功能,防止未登录的用户直接通过地址进入系统 LoginFilter类:继承Filter接口 package com.ss.filter; import java.io ...

  8. VisualStudio、NETFramework及C#版本关系

    1.Visual Studio..NET Framework 及C#版本搭载关系介绍 Visual Studio版本 .NET Framework版本 C#版本 增加功能 Visual Studio ...

  9. 使用node_redis进行redis数据库crud操作

    正在学习使用pomelo开发游戏服务器,碰到node.js操作redis,记录一下 假设应用场景是操作一个用户表的数据 引入node_redis库,创建客户端 var redis = require( ...

  10. day 104 luffy项目第二天

    一.前端配置 二.后端配置 一.前端配置 app.vue 二 . 后端配置 model模型配置 迁移数据 序列化 views.py文件配置 url路由 配置中间件解决跨域问题 重新设计下 model模 ...