首页 文章 精选 留言 我的

精选列表

搜索[官方镜像],共10000篇文章
优秀的个人博客,低调大师

Atom 1.58.0 发布,GitHub 官方文本编辑器

Atom 是由 GitHub 开发的开源文本编辑器,支持 macOS、Windows 和 Linux 操作系统,支持 Node.js 所写的插件,并内置由 Github 提供的 Git 版本控制系统。多数的延伸包皆为开放源代码许可,并由社区构建与维护。Atom 基于使用 Chromium 和 Node.js 的跨平台应用框架 Electron(最初名为 Atom Shell),并使用 CoffeeScript 和 Less 撰写。Atom 也可当作 IDE 使用。被开发者称为“21 世纪的高自定义性”文本编辑器(hackable text editor for the 21st Century)。自 2014 年 5 月 6 日起,Atom 的核心程序、包管理器以及 Atom 基于 Chromium 的桌面程序框架皆使用 MIT 许可协议发布。 Atom 1.58.0 正式发布,本次更新内容如下: 值得注意的变化: #22315- 更新至 macOS Big Sur 风格图标。 #22424- 修复在安全模式和开发模式下重新打开项目的问题。 #22123- 改进 Windows 应用程序图标的对比度。 atom/archive-view#73- 增加了折叠压缩文档目录的功能(zip、tar 等)。 atom/bracket-matcher#405- 处理括号内的多光标选择问题。 atom/find-and-replace#932- 增加 "在新标签页打开" 和 "在新窗口打开" 的右键菜单选项 更多详情可查看:https://github.com/atom/atom/releases/tag/v1.58.0

优秀的个人博客,低调大师

Hadoop 官方WordCount案例带你手把手的解析

2 --> 文章目录 1.需求 2.需求分析 3.项目结构图 4.项目依赖包 5.编写Mapper 6.编写Reducer 7.编写Driver 出现如下所示就欧克 ,接着看结果 1.需求 在给定的文本文件中统计输出每一个单词出现的总次数hello.txt hadoop hadoop ss ss cls cls jiao banzhang xue 2.需求分析 3.项目结构图 4.项目依赖包 <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>RELEASE</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.2</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-hdfs</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies> 5.编写Mapper package wordcount_hdfs; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> { // 0. 将创建对象的操作提取成变量,防止在 map 方法重复创建 private Text text = new Text(); private IntWritable i = new IntWritable(1); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { // 1. 将 Hadoop 内置的text 数据类型转换为string类型 // 方便操作 String string = value.toString(); // 2. 对字符串进行切分 String[] split = string.split(" "); // 3. 对字符串数组遍历,将单词映射成 (单词,1) for (String s : split) { text.set(s); context.write(text, i); } } } 6.编写Reducer package wordcount_hdfs; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; public class WordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable> { private IntWritable total = new IntWritable(); @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { // 定义一个 sum,用来对每个键值对的 值 做 累加操作 int sum = 0; for (IntWritable value : values) { int i = value.get(); sum+=i; } total.set(sum); // 最后写出到文件 context.write(key, total); } } 7.编写Driver package wordcount_hdfs; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.KeyValueLineRecordReader; import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.IOException; public class WordCountDriver { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { // 0 指定路径 这里路径有两种写法 : args = new String[]{"E:\\Hadoop\\src\\main\\resources\\input", "E:\\Hadoop\\src\\main\\resources\\ouput"}; //args = new String[]{"E:/Hadoop/src/main/resources/", "E:/Hadoop/src/main/resources/"}; // 1 获取配置信息configuration以及封装任务job Configuration configuration = new Configuration(); Job job = Job.getInstance(configuration); // 2 设置Driver加载路径 setJarByClass job.setJarByClass(WordCountDriver.class); // 3 设置map和reduce类 setMaper setReducer job.setMapperClass(WordCountMapper.class); job.setReducerClass(WordCountReducer.class); // 4 设置map输出 setmapoutputkey setmapoutputvalue job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(IntWritable.class); // 5 设置最终输出kv类型 (reducer的输出kv类型) setoutoutkey setoutputvalue job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); // 6 设置本地的输入和输出路径 fileinputformat.setinputpath FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); // 7 提交 boolean completion = job.waitForCompletion(true); System.exit(completion ? 0 : 1); } } 8.运行结果 出现如下所示就欧克 ,接着看结果 "D:\Program Files\Java\bin\java.exe" "-javaagent:D:\office\Program Files\IntelliJ2018.2.6\lib\idea_rt.jar=53527:D:\office\Program Files\IntelliJ2018.2.6\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jre\lib\charsets.jar;D:\Program Files\Java\jre\lib\deploy.jar;D:\Program Files\Java\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jre\lib\ext\localedata.jar;D:\Program Files\Java\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jre\lib\ext\sunec.jar;D:\Program Files\Java\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jre\lib\javaws.jar;D:\Program Files\Java\jre\lib\jce.jar;D:\Program Files\Java\jre\lib\jfr.jar;D:\Program Files\Java\jre\lib\jfxswt.jar;D:\Program Files\Java\jre\lib\jsse.jar;D:\Program Files\Java\jre\lib\management-agent.jar;D:\Program Files\Java\jre\lib\plugin.jar;D:\Program Files\Java\jre\lib\resources.jar;D:\Program Files\Java\jre\lib\rt.jar;E:\Hadoop\target\classes;C:\Users\Administrator\.m2\repository\junit\junit\4.13.1\junit-4.13.1.jar;C:\Users\Administrator\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-core\2.8.2\log4j-core-2.8.2.jar;C:\Users\Administrator\.m2\repository\org\apache\logging\log4j\log4j-api\2.8.2\log4j-api-2.8.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-common\2.7.2\hadoop-common-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-annotations\2.7.2\hadoop-annotations-2.7.2.jar;D:\Program Files\Java\lib\tools.jar;C:\Users\Administrator\.m2\repository\com\google\guava\guava\11.0.2\guava-11.0.2.jar;C:\Users\Administrator\.m2\repository\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\Users\Administrator\.m2\repository\org\apache\commons\commons-math3\3.1.1\commons-math3-3.1.1.jar;C:\Users\Administrator\.m2\repository\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\Users\Administrator\.m2\repository\commons-httpclient\commons-httpclient\3.1\commons-httpclient-3.1.jar;C:\Users\Administrator\.m2\repository\commons-codec\commons-codec\1.4\commons-codec-1.4.jar;C:\Users\Administrator\.m2\repository\commons-io\commons-io\2.4\commons-io-2.4.jar;C:\Users\Administrator\.m2\repository\commons-net\commons-net\3.1\commons-net-3.1.jar;C:\Users\Administrator\.m2\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;C:\Users\Administrator\.m2\repository\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar;C:\Users\Administrator\.m2\repository\org\mortbay\jetty\jetty\6.1.26\jetty-6.1.26.jar;C:\Users\Administrator\.m2\repository\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\Users\Administrator\.m2\repository\javax\servlet\jsp\jsp-api\2.1\jsp-api-2.1.jar;C:\Users\Administrator\.m2\repository\com\sun\jersey\jersey-core\1.9\jersey-core-1.9.jar;C:\Users\Administrator\.m2\repository\com\sun\jersey\jersey-json\1.9\jersey-json-1.9.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jettison\jettison\1.1\jettison-1.1.jar;C:\Users\Administrator\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.3-1\jaxb-impl-2.2.3-1.jar;C:\Users\Administrator\.m2\repository\javax\xml\bind\jaxb-api\2.2.2\jaxb-api-2.2.2.jar;C:\Users\Administrator\.m2\repository\javax\xml\stream\stax-api\1.0-2\stax-api-1.0-2.jar;C:\Users\Administrator\.m2\repository\javax\activation\activation\1.1\activation-1.1.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-jaxrs\1.8.3\jackson-jaxrs-1.8.3.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-xc\1.8.3\jackson-xc-1.8.3.jar;C:\Users\Administrator\.m2\repository\com\sun\jersey\jersey-server\1.9\jersey-server-1.9.jar;C:\Users\Administrator\.m2\repository\asm\asm\3.1\asm-3.1.jar;C:\Users\Administrator\.m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar;C:\Users\Administrator\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\Users\Administrator\.m2\repository\net\java\dev\jets3t\jets3t\0.9.0\jets3t-0.9.0.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpclient\4.1.2\httpclient-4.1.2.jar;C:\Users\Administrator\.m2\repository\org\apache\httpcomponents\httpcore\4.1.2\httpcore-4.1.2.jar;C:\Users\Administrator\.m2\repository\com\jamesmurty\utils\java-xmlbuilder\0.4\java-xmlbuilder-0.4.jar;C:\Users\Administrator\.m2\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;C:\Users\Administrator\.m2\repository\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\Users\Administrator\.m2\repository\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\Users\Administrator\.m2\repository\commons-beanutils\commons-beanutils\1.7.0\commons-beanutils-1.7.0.jar;C:\Users\Administrator\.m2\repository\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.10\slf4j-api-1.7.10.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-log4j12\1.7.10\slf4j-log4j12-1.7.10.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\Users\Administrator\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\Users\Administrator\.m2\repository\org\apache\avro\avro\1.7.4\avro-1.7.4.jar;C:\Users\Administrator\.m2\repository\com\thoughtworks\paranamer\paranamer\2.3\paranamer-2.3.jar;C:\Users\Administrator\.m2\repository\org\xerial\snappy\snappy-java\1.0.4.1\snappy-java-1.0.4.1.jar;C:\Users\Administrator\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Administrator\.m2\repository\com\google\code\gson\gson\2.2.4\gson-2.2.4.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-auth\2.7.2\hadoop-auth-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\directory\server\apacheds-kerberos-codec\2.0.0-M15\apacheds-kerberos-codec-2.0.0-M15.jar;C:\Users\Administrator\.m2\repository\org\apache\directory\server\apacheds-i18n\2.0.0-M15\apacheds-i18n-2.0.0-M15.jar;C:\Users\Administrator\.m2\repository\org\apache\directory\api\api-asn1-api\1.0.0-M20\api-asn1-api-1.0.0-M20.jar;C:\Users\Administrator\.m2\repository\org\apache\directory\api\api-util\1.0.0-M20\api-util-1.0.0-M20.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-framework\2.7.1\curator-framework-2.7.1.jar;C:\Users\Administrator\.m2\repository\com\jcraft\jsch\0.1.42\jsch-0.1.42.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-client\2.7.1\curator-client-2.7.1.jar;C:\Users\Administrator\.m2\repository\org\apache\curator\curator-recipes\2.7.1\curator-recipes-2.7.1.jar;C:\Users\Administrator\.m2\repository\com\google\code\findbugs\jsr305\3.0.0\jsr305-3.0.0.jar;C:\Users\Administrator\.m2\repository\org\apache\htrace\htrace-core\3.1.0-incubating\htrace-core-3.1.0-incubating.jar;C:\Users\Administrator\.m2\repository\org\apache\zookeeper\zookeeper\3.4.6\zookeeper-3.4.6.jar;C:\Users\Administrator\.m2\repository\org\apache\commons\commons-compress\1.4.1\commons-compress-1.4.1.jar;C:\Users\Administrator\.m2\repository\org\tukaani\xz\1.0\xz-1.0.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-client\2.7.2\hadoop-client-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-app\2.7.2\hadoop-mapreduce-client-app-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-common\2.7.2\hadoop-mapreduce-client-common-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-yarn-client\2.7.2\hadoop-yarn-client-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-yarn-server-common\2.7.2\hadoop-yarn-server-common-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.7.2\hadoop-mapreduce-client-shuffle-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-yarn-api\2.7.2\hadoop-yarn-api-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-core\2.7.2\hadoop-mapreduce-client-core-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-yarn-common\2.7.2\hadoop-yarn-common-2.7.2.jar;C:\Users\Administrator\.m2\repository\com\sun\jersey\jersey-client\1.9\jersey-client-1.9.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.7.2\hadoop-mapreduce-client-jobclient-2.7.2.jar;C:\Users\Administrator\.m2\repository\org\apache\hadoop\hadoop-hdfs\2.7.2\hadoop-hdfs-2.7.2.jar;C:\Users\Administrator\.m2\repository\commons-daemon\commons-daemon\1.0.13\commons-daemon-1.0.13.jar;C:\Users\Administrator\.m2\repository\io\netty\netty\3.6.2.Final\netty-3.6.2.Final.jar;C:\Users\Administrator\.m2\repository\io\netty\netty-all\4.0.23.Final\netty-all-4.0.23.Final.jar;C:\Users\Administrator\.m2\repository\xerces\xercesImpl\2.9.1\xercesImpl-2.9.1.jar;C:\Users\Administrator\.m2\repository\xml-apis\xml-apis\1.3.04\xml-apis-1.3.04.jar;C:\Users\Administrator\.m2\repository\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar" KVText.KVTextDriver 2020-10-21 14:41:01,541 INFO [org.apache.hadoop.conf.Configuration.deprecation] - session.id is deprecated. Instead, use dfs.metrics.session-id 2020-10-21 14:41:01,551 INFO [org.apache.hadoop.metrics.jvm.JvmMetrics] - Initializing JVM Metrics with processName=JobTracker, sessionId= 2020-10-21 14:41:02,916 WARN [org.apache.hadoop.mapreduce.JobResourceUploader] - Hadoop command-line option parsing not performed. Implement the Tool interface and execute your application with ToolRunner to remedy this. 2020-10-21 14:41:02,936 WARN [org.apache.hadoop.mapreduce.JobResourceUploader] - No job jar file set. User classes may not be found. See Job or Job#setJar(String). 2020-10-21 14:41:03,236 INFO [org.apache.hadoop.mapreduce.lib.input.FileInputFormat] - Total input paths to process : 1 2020-10-21 14:41:03,256 INFO [org.apache.hadoop.mapreduce.JobSubmitter] - number of splits:1 2020-10-21 14:41:03,326 INFO [org.apache.hadoop.mapreduce.JobSubmitter] - Submitting tokens for job: job_local297471183_0001 2020-10-21 14:41:03,476 INFO [org.apache.hadoop.mapreduce.Job] - The url to track the job: http://localhost:8080/ 2020-10-21 14:41:03,476 INFO [org.apache.hadoop.mapreduce.Job] - Running job: job_local297471183_0001 2020-10-21 14:41:03,476 INFO [org.apache.hadoop.mapred.LocalJobRunner] - OutputCommitter set in config null 2020-10-21 14:41:03,486 INFO [org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter] - File Output Committer Algorithm version is 1 2020-10-21 14:41:03,486 INFO [org.apache.hadoop.mapred.LocalJobRunner] - OutputCommitter is org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter 2020-10-21 14:41:03,536 INFO [org.apache.hadoop.mapred.LocalJobRunner] - Waiting for map tasks 2020-10-21 14:41:03,536 INFO [org.apache.hadoop.mapred.LocalJobRunner] - Starting task: attempt_local297471183_0001_m_000000_0 2020-10-21 14:41:03,566 INFO [org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter] - File Output Committer Algorithm version is 1 2020-10-21 14:41:03,576 INFO [org.apache.hadoop.yarn.util.ProcfsBasedProcessTree] - ProcfsBasedProcessTree currently is supported only on Linux. 2020-10-21 14:41:03,616 INFO [org.apache.hadoop.mapred.Task] - Using ResourceCalculatorProcessTree : org.apache.hadoop.yarn.util.WindowsBasedProcessTree@6cec6fbe 2020-10-21 14:41:03,626 INFO [org.apache.hadoop.mapred.MapTask] - Processing split: file:/E:/Hadoop/src/main/resources/input/englishconment.txt:0+80 2020-10-21 14:41:03,666 INFO [org.apache.hadoop.mapred.MapTask] - (EQUATOR) 0 kvi 26214396(104857584) 2020-10-21 14:41:03,666 INFO [org.apache.hadoop.mapred.MapTask] - mapreduce.task.io.sort.mb: 100 2020-10-21 14:41:03,666 INFO [org.apache.hadoop.mapred.MapTask] - soft limit at 83886080 2020-10-21 14:41:03,666 INFO [org.apache.hadoop.mapred.MapTask] - bufstart = 0; bufvoid = 104857600 2020-10-21 14:41:03,666 INFO [org.apache.hadoop.mapred.MapTask] - kvstart = 26214396; length = 6553600 2020-10-21 14:41:03,666 INFO [org.apache.hadoop.mapred.MapTask] - Map output collector class = org.apache.hadoop.mapred.MapTask$MapOutputBuffer 2020-10-21 14:41:03,676 INFO [org.apache.hadoop.mapred.LocalJobRunner] - 2020-10-21 14:41:03,676 INFO [org.apache.hadoop.mapred.MapTask] - Starting flush of map output 2020-10-21 14:41:03,676 INFO [org.apache.hadoop.mapred.MapTask] - Spilling map output 2020-10-21 14:41:03,676 INFO [org.apache.hadoop.mapred.MapTask] - bufstart = 0; bufend = 48; bufvoid = 104857600 2020-10-21 14:41:03,676 INFO [org.apache.hadoop.mapred.MapTask] - kvstart = 26214396(104857584); kvend = 26214384(104857536); length = 13/6553600 2020-10-21 14:41:03,796 INFO [org.apache.hadoop.mapred.MapTask] - Finished spill 0 2020-10-21 14:41:03,806 INFO [org.apache.hadoop.mapred.Task] - Task:attempt_local297471183_0001_m_000000_0 is done. And is in the process of committing 2020-10-21 14:41:03,826 INFO [org.apache.hadoop.mapred.LocalJobRunner] - file:/E:/Hadoop/src/main/resources/input/englishconment.txt:0+80 2020-10-21 14:41:03,826 INFO [org.apache.hadoop.mapred.Task] - Task 'attempt_local297471183_0001_m_000000_0' done. 2020-10-21 14:41:03,826 INFO [org.apache.hadoop.mapred.LocalJobRunner] - Finishing task: attempt_local297471183_0001_m_000000_0 2020-10-21 14:41:03,826 INFO [org.apache.hadoop.mapred.LocalJobRunner] - map task executor complete. 2020-10-21 14:41:03,826 INFO [org.apache.hadoop.mapred.LocalJobRunner] - Waiting for reduce tasks 2020-10-21 14:41:03,826 INFO [org.apache.hadoop.mapred.LocalJobRunner] - Starting task: attempt_local297471183_0001_r_000000_0 2020-10-21 14:41:03,836 INFO [org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter] - File Output Committer Algorithm version is 1 2020-10-21 14:41:03,836 INFO [org.apache.hadoop.yarn.util.ProcfsBasedProcessTree] - ProcfsBasedProcessTree currently is supported only on Linux. 2020-10-21 14:41:04,115 INFO [org.apache.hadoop.mapred.Task] - Using ResourceCalculatorProcessTree : org.apache.hadoop.yarn.util.WindowsBasedProcessTree@2f6f6193 2020-10-21 14:41:04,115 INFO [org.apache.hadoop.mapred.ReduceTask] - Using ShuffleConsumerPlugin: org.apache.hadoop.mapreduce.task.reduce.Shuffle@15648f8c 2020-10-21 14:41:04,135 INFO [org.apache.hadoop.mapreduce.task.reduce.MergeManagerImpl] - MergerManager: memoryLimit=648858816, maxSingleShuffleLimit=162214704, mergeThreshold=428246848, ioSortFactor=10, memToMemMergeOutputsThreshold=10 2020-10-21 14:41:04,135 INFO [org.apache.hadoop.mapreduce.task.reduce.EventFetcher] - attempt_local297471183_0001_r_000000_0 Thread started: EventFetcher for fetching Map Completion Events 2020-10-21 14:41:04,165 INFO [org.apache.hadoop.mapreduce.task.reduce.LocalFetcher] - localfetcher#1 about to shuffle output of map attempt_local297471183_0001_m_000000_0 decomp: 58 len: 62 to MEMORY 2020-10-21 14:41:04,175 INFO [org.apache.hadoop.mapreduce.task.reduce.InMemoryMapOutput] - Read 58 bytes from map-output for attempt_local297471183_0001_m_000000_0 2020-10-21 14:41:04,175 INFO [org.apache.hadoop.mapreduce.task.reduce.MergeManagerImpl] - closeInMemoryFile -> map-output of size: 58, inMemoryMapOutputs.size() -> 1, commitMemory -> 0, usedMemory ->58 2020-10-21 14:41:04,175 INFO [org.apache.hadoop.mapreduce.task.reduce.EventFetcher] - EventFetcher is interrupted.. Returning 2020-10-21 14:41:04,175 INFO [org.apache.hadoop.mapred.LocalJobRunner] - 1 / 1 copied. 2020-10-21 14:41:04,175 INFO [org.apache.hadoop.mapreduce.task.reduce.MergeManagerImpl] - finalMerge called with 1 in-memory map-outputs and 0 on-disk map-outputs 2020-10-21 14:41:04,195 INFO [org.apache.hadoop.mapred.Merger] - Merging 1 sorted segments 2020-10-21 14:41:04,195 INFO [org.apache.hadoop.mapred.Merger] - Down to the last merge-pass, with 1 segments left of total size: 47 bytes 2020-10-21 14:41:04,205 INFO [org.apache.hadoop.mapreduce.task.reduce.MergeManagerImpl] - Merged 1 segments, 58 bytes to disk to satisfy reduce memory limit 2020-10-21 14:41:04,215 INFO [org.apache.hadoop.mapreduce.task.reduce.MergeManagerImpl] - Merging 1 files, 62 bytes from disk 2020-10-21 14:41:04,215 INFO [org.apache.hadoop.mapreduce.task.reduce.MergeManagerImpl] - Merging 0 segments, 0 bytes from memory into reduce 2020-10-21 14:41:04,215 INFO [org.apache.hadoop.mapred.Merger] - Merging 1 sorted segments 2020-10-21 14:41:04,215 INFO [org.apache.hadoop.mapred.Merger] - Down to the last merge-pass, with 1 segments left of total size: 47 bytes 2020-10-21 14:41:04,215 INFO [org.apache.hadoop.mapred.LocalJobRunner] - 1 / 1 copied. 2020-10-21 14:41:04,215 INFO [org.apache.hadoop.conf.Configuration.deprecation] - mapred.skip.on is deprecated. Instead, use mapreduce.job.skiprecords 2020-10-21 14:41:04,225 INFO [org.apache.hadoop.mapred.Task] - Task:attempt_local297471183_0001_r_000000_0 is done. And is in the process of committing 2020-10-21 14:41:04,225 INFO [org.apache.hadoop.mapred.LocalJobRunner] - 1 / 1 copied. 2020-10-21 14:41:04,225 INFO [org.apache.hadoop.mapred.Task] - Task attempt_local297471183_0001_r_000000_0 is allowed to commit now 2020-10-21 14:41:04,235 INFO [org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter] - Saved output of task 'attempt_local297471183_0001_r_000000_0' to file:/E:/Hadoop/src/main/resources/ouput/_temporary/0/task_local297471183_0001_r_000000 2020-10-21 14:41:04,235 INFO [org.apache.hadoop.mapred.LocalJobRunner] - reduce > reduce 2020-10-21 14:41:04,235 INFO [org.apache.hadoop.mapred.Task] - Task 'attempt_local297471183_0001_r_000000_0' done. 2020-10-21 14:41:04,235 INFO [org.apache.hadoop.mapred.LocalJobRunner] - Finishing task: attempt_local297471183_0001_r_000000_0 2020-10-21 14:41:04,235 INFO [org.apache.hadoop.mapred.LocalJobRunner] - reduce task executor complete. 2020-10-21 14:41:04,485 INFO [org.apache.hadoop.mapreduce.Job] - Job job_local297471183_0001 running in uber mode : false 2020-10-21 14:41:04,485 INFO [org.apache.hadoop.mapreduce.Job] - map 100% reduce 100% 2020-10-21 14:41:04,485 INFO [org.apache.hadoop.mapreduce.Job] - Job job_local297471183_0001 completed successfully 2020-10-21 14:41:04,515 INFO [org.apache.hadoop.mapreduce.Job] - Counters: 30 File System Counters FILE: Number of bytes read=672 FILE: Number of bytes written=583822 FILE: Number of read operations=0 FILE: Number of large read operations=0 FILE: Number of write operations=0 Map-Reduce Framework Map input records=4 Map output records=4 Map output bytes=48 Map output materialized bytes=62 Input split bytes=124 Combine input records=0 Combine output records=0 Reduce input groups=2 Reduce shuffle bytes=62 Reduce input records=4 Reduce output records=2 Spilled Records=8 Shuffled Maps =1 Failed Shuffles=0 Merged Map outputs=1 GC time elapsed (ms)=12 Total committed heap usage (bytes)=374865920 Shuffle Errors BAD_ID=0 CONNECTION=0 IO_ERROR=0 WRONG_LENGTH=0 WRONG_MAP=0 WRONG_REDUCE=0 File Input Format Counters Bytes Read=80 File Output Format Counters Bytes Written=32 Process finished with exit code 0

优秀的个人博客,低调大师

Atom 1.57.0 发布,GitHub 官方文本编辑器

Atom 是由 GitHub 开发的自由及开放源代码的文字与代码编辑器,支持 macOS、Windows 和 Linux 操作系统,支持 Node.js 所写的插件,并内置由 Github 提供的 Git 版本控制系统。多数的延伸包皆为开放源代码许可,并由社区建置与维护。Atom 基于使用 Chromium 和 Node.js 的跨平台应用框架 Electron(最初名为Atom Shell),并使用 CoffeeScript 和 Less 撰写。Atom 也可当作 IDE 使用。被它的开发者称为“21 世纪的高自定义性”文本编辑器(hackable text editor for the 21st Century)。自 2014 年 5 月 6 日起,Atom 的核心程序、包管理器以及 Atom 基于 Chromium 的桌面程序框架皆使用 MIT 许可协议发布。 Atom 1.57.0 正式发布,本次更新内容如下: 值得注意的变化: #21847- 改进对不兼容的本地模块的检测 Atom Core: atom/atom#22019 -修复依赖性 Bump 脚本失败的问题 atom/atom#21927 -修复:直接要求 .node 文件以检测不兼容的本地模块 atom/atom#22046 -修复上下文菜单不工作的问题 atom/atom#22050 -修复上下文菜单右键不起作用的问题 atom/atom#22106 -将 y18n 从 3.2.1 升级到 3.2.2 atom/atom#22061 -focus-trap 升级至 6.3.0 atom/atom#22060 -chai 升级至 4.3.4 atom/atom#22063 -git-utils 升级至 5.7.1 atom/atom#22068 -normalize-package-data 升级至 3.0.2 atom/atom#22152 -settings-view 升级至 0.261.8 atom/atom#22159 -tree-view 升级至 0.228.3 setting-view: atom/settings-view#1176 -修复:更新异步依赖性 atom/settings-view#1182 -捕获 README 文件未找到的错误 tree-view: atom/tree-view#1377 -将 fs.realpathSync 包在 try-catch 中 更多详情可查看:https://github.com/atom/atom/releases/tag/v1.57.0

优秀的个人博客,低调大师

Atom 1.56.0 发布,GitHub 官方文本编辑器

Atom 是由 GitHub 开发的自由及开放源代码的文字与代码编辑器,支持 macOS、Windows 和 Linux 操作系统,支持 Node.js 所写的插件,并内置由 Github 提供的 Git 版本控制系统。多数的延伸包皆为开放源代码许可,并由社区建置与维护。Atom 基于使用 Chromium 和 Node.js 的跨平台应用框架 Electron(最初名为Atom Shell),并使用 CoffeeScript 和 Less 撰写。Atom 也可当作 IDE 使用。被它的开发者称为“21 世纪的高自定义性”文本编辑器(hackable text editor for the 21st Century)。自 2014 年 5 月 6 日起,Atom 的核心程序、包管理器以及 Atom 基于 Chromium 的桌面程序框架皆使用 MIT 许可协议发布。 Atom 1.56.0 正式发布,本次更新内容如下: 显著变化: #21847- 修复 macOS 上所有窗口关闭后退出的错误行为; #21852- 改进 Java 语法高亮显示; #21847- 增加禁用鼠标中键粘贴的设置; #21777- Electron 升级; Atom Core atom/atom#21753 -修正树状结构注入时"empty"语言的处理方式; atom/atom#21715 -检查 testRunner 是否为 es 模块; atom/atom#21848 -添加授权; atom/atom#21928 -修复了依赖性 bump 脚本; GitHub atom/github#2459 -使用 action-setup-atom 来实现; atom/github#2621 -package.json:将@babel/core 固定为 7.12.10 以下; atom/github#2625 -更新 shell.openExternal 为 Promise,因为 Atom 上的 Electron 更新; atom/github#2626 -更新 Electron API 中一些方法的 Promise api; atom/github#2631 -修复 GitHub 在 Atom electron 9.4.1 升级失败的测试; 拼写检查 atom/spell-check#357 -在配置中加入 enableDebug; atom/spell-check#359 -修复了无法加载包的问题; 更多详情可查看:https://github.com/atom/atom/releases/tag/v1.56.0

优秀的个人博客,低调大师

Atom 1.55.0 发布,GitHub 官方文本编辑器

Atom 是由 GitHub 开发的自由及开放源代码的文字与代码编辑器,支持 macOS、Windows 和 Linux 操作系统,支持 Node.js 所写的插件,并内置由 Github 提供的 Git 版本控制系统。多数的延伸包皆为开放源代码许可,并由社区建置与维护。Atom 基于使用 Chromium 和 Node.js 的跨平台应用框架 Electron(最初名为Atom Shell),并使用 CoffeeScript 和 Less 撰写。Atom 也可当作 IDE 使用。被它的开发者称为“21 世纪的高自定义性”文本编辑器(hackable text editor for the 21st Century)。自 2014 年 5 月 6 日起,Atom 的核心程序、包管理器以及 Atom 基于 Chromium 的桌面程序框架皆使用 MIT 许可协议发布。 显著变化 https://github.com/atom/github/pull/2564 - 在没有仓库的情况下读写 git 配置。 Atom Core atom/atom#21665 - 将 postcss 从 8.1.4 升级至 8.1.6 atom/atom#21762 - GitHub 软件包更新 atom/atom#21787 - 修复异步确认的问题 Github atom/github#2559 - GraphQL 模式更新 atom/github#2564 - 在没有仓库的情况下读写 git 配置 atom/github#2572 - 增加 GitHub 选项卡上登录提示的优先级 atom/github#2573 - 修正一个片状的测试 atom/github#2574 - 删减 issue 和 pull request 模板 atom/github#2583 - 将 superstring 从 2.4.2 升级至 2.4.3 atom/github#2584 - 将 whats-my-line 升级至 0.1.14 atom/github#2587 - Git 身份面板上的接受和取消按钮 atom/github#2592 - 将 ini 从 1.3.5 升级到 1.3.7 atom/github#2598 - 将 dompurify 从 2.0.7 升级到 2.0.17 atom/github#2617 - 测试:为 Atom CI 禁用失败的文件补丁测试 更多详情可查看:https://atom.io/releases

优秀的个人博客,低调大师

一起来读官方文档-----SpringIOC(08)

1.9。基于注解的容器配置 注解在配置Spring方面比XML更好吗? 基于注解的配置的引入提出了一个问题,即这种方法是否比XML“更好”。 简短的答案是“取决于情况”。 长话短说,每种方法都有其优缺点,通常,由开发人员决定哪种策略更适合他们。 由于定义方式的不同,注解在声明中提供了很多上下文,从而使配置更短,更简洁。 但是,XML擅长连接组件而不接触其源代码或重新编译它们。 一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注解的类不再是POJO, 而且,该配置变得分散且难以控制。 无论选择如何,Spring都可以容纳两种样式,甚至可以将它们混合在一起。 值得指出的是,通过其JavaConfig选项,Spring允许以非侵入方式使用注解, 而无需接触目标组件的源代码。 注解是XML配置的替代方法,该配置依赖字节码元数据来连接组件,而不是尖括号声明。 通过使用相关的 类,方法或字段 声明上的注解,开发人员无需使用XML来描述bean的连接,而是将配置移入组件类本身。 如示例中所述:将RequiredAnnotationBeanPostProcessor,通过BeanPostProcessor的方式与注解结合使用是扩展Spring IoC容器的常用方法。 例如,Spring 2.0引入了使用@Required注解强制执行必需属性的可能性。 Spring 2.5引入@Autowired注解,提供的功能与自动装配协作器中所述的功能相同,但具有更细粒度的控制和更广泛的适用性。 Spring 2.5还添加了对JSR-250批注(例如 @PostConstruct和@PreDestroy)的支持。 Spring 3.0增加了对javax.inject包中包含的JSR-330(Java依赖性注入)注解的支持,例如@Inject 和@Named。 注解注入在XML注入之前执行。因此,XML配置将覆盖通过注解注入的属性 与往常一样,您可以根据类名将它们注册为单独的bean定义,但也可以通过在基于XML的Spring配置中包含以下标记来隐式注册它们: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans> <context:annotation-config/> 隐式注册后置处理器包括 : AutowiredAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor PersistenceAnnotationBeanPostProcessor RequiredAnnotationBeanPostProcessor 并且当使用<context:component-scan/>后,即可将<context:annotation-config/>省去 context:annotation-config/只在定义它的相同应用程序上下文中查找关于bean的注解。 这意味着,如果你把context:annotation-config/定义在WebApplicationContext的DispatcherServlet中,它只是检查controllers中的@Autowired注解,而不是你的services。 上边的这段话意思不是很明确,需要解释一下以前用web.xml配置时的Spring启动流程 拿出几段配置 <!--配置开始 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/service/*</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:spring/spring-base.xml </param-value> </context-param> <!--配置结束 --> 上边的配置应该是多年前webi应用的基础配置,理一下tomcat启动后如何调用Spring的大概流程 1. tomcat读取web.xml文件(此处不管tomcat如何找到xml),解析内容并分组, 分成ServletContainerInitializer,servlet,listener,context-param等多个数组 2.逐个进行解析,先解析ServletContainerInitializer //这个就相当典型了 这个东西就是之前的文章讲过的ServletContainerInitializer //Tomcat启动会查找ServletContainerInitializer实现类并执行其中的onStartup方法。 //Spring-web模块存在ServletContainerInitializer实现类,所以Tomcat启动会调用Spring-web的代码。 //但是我们用Spring框架的话不需要实现这个接口,实现一个Spring的接口WebApplicationInitializer。 //就可以由Tomcat调用Spring-web的ServletContainerInitializer实现类 Iterator i$ = this.initializers.entrySet().iterator(); while(i$.hasNext()) { Entry entry = (Entry)i$.next(); try { ((ServletContainerInitializer)entry.getKey()).onStartup((Set)entry.getValue(), this.getServletContext()); } catch (ServletException var22) { log.error(sm.getString("standardContext.sciFail"), var22); ok = false; break; } } 但是这里我们并没有用这种方式而是用了listener的方式继续往下看 3. 解析listener,这里this.listenerStart()会解析我们配置的ContextLoaderListener if (ok && !this.listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); ok = false; } 就在这里tomcat关联上了Spring的ApplicationContext,会实例化XmlWebApplicationContext, 实例化时取出context-param中的name为contextConfigLocation的配置文件,来进行解析注册 4.解析servlet,this.loadOnStartup(this.findChildren())来解析servlet, if (ok && !this.loadOnStartup(this.findChildren())) { log.error(sm.getString("standardContext.servletFail")); ok = false; } 这里就会进入DispatcherServlet的init方法, init方法中会根据当前的ServletContext查找在此之前有没有初始化过Spring的ApplicationContext, 然后再判断当前DispatcherServlet有没有ApplicationContext, 如果没有就初始化一个并把之前初始化ApplicationContext的设置为父节点 总结一下,也就是说用了上边的配置的话,tomcat在启动过程中,会初始化两遍并生成两个ApplicationContext对象, 第一遍解析context-param中param-name 为contextConfigLocation的配置文件, 并以此配置文件生成一个ApplicationContext ROOT 第二遍是解析DispatcherServlet servlet的spring-mvc.xml配置文件, 再以此配置文件生成一个ApplicationContext,并将ROOT设置为父节点 因此就产生了一个问题,当你在两个ApplicationContext都可以扫描到同一个Bean的时候, 那么这个bean在连个ApplicationContext中都各存在一个实例,并且实例不一样 举一个之前遇到的问题: 之前想给某个controller加一个AOP,拦截某些方法进行特殊处理,但是我把 <aop:aspectj-autoproxy/>这个注解 放到了下面这个层次的配置文件中了 <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:spring/spring-base.xml </param-value> </context-param> 最后我的AOP并没有生效,后来又把注解挪到了spring-mvc.xml中,才生效。 之前百度搜到说:spring-mvc 的配置扫描优先于spring的配置文件 通过调试才理解这句话: 我的controller在spring的ApplicationContext中有一份被AOP代理的对象 在spring-mvc的ApplicationContext中还有一份没被代理的普通对象 因为spring-mvc配置加载的晚,所以用到的都是没有被代理的对象 1.9.1。@Required 该@Required注解适用于bean属性setter方法,如下面的例子: public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } 这个注解要求,必须在配置时通过bean定义中的显式属性值或自动装配来填充bean属性。 如果未填充bean属性,容器将抛出异常。 这样显式的失败,避免了实例在应用的时候出现NullPointerException的情况。 @Required注解在Spring Framework 5.1时正式被弃用, Spring更赞同使用构造函数注入来进行必需参数的设置 (或者使用InitializingBean.afterPropertiesSet()的自定义实现来进行bean属性的设置)。 1.9.2。@Autowired 在本节包含的示例中,JSR330的@Inject注释可以用来替代Spring的@Autowired注释。 您可以将@Autowired注解应用于构造函数,如以下示例所示: public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... } 从Spring Framework 4.3开始,@Autowired如果目标bean仅定义一个构造函数作为开始,则不再需要在此类构造函数上进行注解。 但是,如果有多个构造函数可用,并且没有主/默认构造函数,则必须至少注解一个构造函数,@Autowired以指示容器使用哪个构造函数。 您还可以将@Autowired注解应用于传统的setter方法,如以下示例所示: public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } 您还可以将注解应用于具有任意名称和多个参数的方法,如以下示例所示: public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } } 您也可以将其应用于@Autowired字段,甚至可以将其与构造函数混合使用,如以下示例所示: public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired private MovieCatalog movieCatalog; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... } 确保目标组件(例如MovieCatalog或CustomerPreferenceDao)与带@Autowired注解的注入点的类型一致地声明。 否则,注入可能会由于运行时出现“no type match found”错误而失败。 对于通过类路径扫描找到的xml定义的bean或组件类,容器通常预先知道具体的类型。 但是,对于@Bean工厂方法,您需要确保声明的返回类型具有足够的表达能力。 对于实现多个接口的组件,或者对于可能由其实现类型引用的组件, 考虑在您的工厂方法上声明最特定的返回类型(至少与引用您的bean的注入点所要求的那样特定)。 您还可以将@Autowired注解添加到需要该类型数组的字段或方法中,指示Spring提供特定类型的所有bean ,如以下示例所示: public class MovieRecommender { @Autowired private MovieCatalog[] movieCatalogs; // ... } 如以下示例所示,这同样适用于类型化集合: public class MovieRecommender { private Set<MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... } 如果希望数组或列表中的项目以特定顺序排序, 则目标bean可以实现该org.springframework.core.Ordered接口或使用@Order或标准@Priority注解。 否则,它们的顺序将遵循容器中相应目标bean定义的注册顺序。 您可以使用@Order在目标类级别和@Bean方法上声明注解。 @Order值可能会影响注入点的优先级,但请注意它们不会影响单例启动顺序, 这是由依赖关系和@DependsOn声明确定的正交关系。(举例:a,b,c三个bean设置的order分别为1,2,3, 但是a依赖c,所以a在初始化的时候会初始化c,导致c比b提前初始化) 请注意,标准javax.annotation.Priority注解在该@Bean级别不可用 ,因为无法在方法上声明它。 可以通过将@Order值与@Primary每个类型的单个bean结合使用来对其语义进行建模。 即使是类型化的Map实例也可以自动注入,键包含相应的bean名称是String类型,值是对应的bean实例,如下面的示例所示: public class MovieRecommender { private Map<String, MovieCatalog> movieCatalogs; @Autowired public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) { this.movieCatalogs = movieCatalogs; } } 默认情况下,当给定注入点没有匹配的候选bean可用时,自动装配将失败。对于声明的数组,集合或映射,至少应有一个匹配元素。 默认是将带注解的方法和字段视为必须要注入的依赖项。 你可以通过标记为非必需注入来改变这个行为(例如,通过在@Autowired中设置required属性为false): public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required = false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... } @Autowired(required = false)用在方法上时 当存在任何一个参数不可注入,则根本不会调用该方法。 在这种情况下,完全不需要填充非必需字段,而保留其默认值。 当方法有多个参数时,可以使用该注解标识其中的某个参数可以不被注入 public class ServiceController { private ServiceTwo serviceTwo; private CusService serviceOne; public ServiceController(CusService cusService, @Autowired(required = false) ServiceTwo serviceTwo){ this.serviceOne = cusService; this.serviceTwo = serviceTwo; } } 在任何给定bean类中,只有一个构造函数可以声明@Autowired,并将required属性设置为true,以指示当作为Spring bean使用时要自动装配的构造函数。 因此,如果required属性的默认值为true,那么只有一个构造函数可以使用@Autowired注解。 如果有多个构造函数声明注解,那么它们都必须声明required=false,才能被认为是自动装配的候选者(类似于XML中的autowire=constructor)。 通过在Spring容器中匹配bean可以满足的依赖关系最多的构造函数将被选择。 如果没有一个候选函数可以满足,那么将使用主/默认构造函数(如果存在)。 类似地,如果一个类声明了多个构造函数,但是没有一个是用@Autowired注解的,那么一个主/默认构造函数(如果有的话)将会被使用。 如果一个类只声明了一个构造函数,那么它将始终被使用,即使没有@Autowired注解。 请注意,带注解的构造函数不必是public的。 建议在setter方法上的已弃用的@Required注释上使用@Autowired属性。 将required属性设置为false表示该属性对于自动装配目的是不需要的,并且如果该属性不能自动装配,则忽略它。 另一方面,@Required更强制,因为它强制用容器支持的任何方法设置属性,如果没有定义值,则会引发相应的异常。 另外,您可以通过Java8来表达特定依赖项的非必需性质java.util.Optional,如以下示例所示: public class SimpleMovieLister { @Autowired public void setMovieFinder(Optional<MovieFinder> movieFinder) { ... } } 从Spring Framework 5.0开始,您还可以使用@Nullable注解(任何包中的Nullable注解,例如,javax.annotation.Nullable来自JSR-305的注解)。 使用此注解标识该参数不一定会被注入,有可能会是空值 public class SimpleMovieLister { @Autowired public void setMovieFinder(@Nullable MovieFinder movieFinder) { ... } } 您还可以对这些接口(BeanFactory,ApplicationContext,Environment,ResourceLoader, ApplicationEventPublisher,和MessageSource)使用@Autowired。 这些接口及其扩展接口(例如ConfigurableApplicationContext或ResourcePatternResolver)将自动解析,而无需进行特殊设置。 以下示例自动装配ApplicationContext对象: public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ... } 在@Autowired,@Inject,@Value,和@Resource注解由Spring注册的BeanPostProcessor实现。 这意味着您不能在自己的类型BeanPostProcessor或BeanFactoryPostProcessor类型(如果有)中应用这些注解。 必须通过使用XML或Spring@Bean方法显式地“连接”这些类型。 不仅相当上一章的内容: 您应该看到一条参考性日志消息: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)。 这条消息的意思大概就是说这个bean没有得到所有BeanPostProcessor的处理 如果您自定义的BeanPostProcessor或BeanFactoryPostProcessor在自动注入的BeanPostProcessor之前实例化那么就无法为您注入您想要的参数。 1.9.3。@Primary 由于按类型自动布线可能会导致多个候选对象,因此通常有必要对选择过程进行更多控制。 一种实现此目的的方法是使用Spring的 @Primary注解。 @Primary指示当多个bean是要自动装配到单值依赖项的候选对象时,应给予特定bean优先权。 如果候选中恰好存在一个主bean,则它将成为自动装配的值。 考虑以下定义firstMovieCatalog为主要配置的配置MovieCatalog: @Configuration public class MovieConfiguration { @Bean @Primary public MovieCatalog firstMovieCatalog() { ... } @Bean public MovieCatalog secondMovieCatalog() { ... } // ... } 使用前面的配置,以下内容MovieRecommender将自动注入到 firstMovieCatalog: public class MovieRecommender { @Autowired private MovieCatalog movieCatalog; // ... } 1.9.4。@Qualifier @Primary当可以确定一个主要候选对象时,它是在几种情况下按类型使用自动装配的有效方法。 当需要对选择过程进行更多控制时,可以使用Spring的@Qualifier注解。 您可以将限定符值与特定的参数相关联,从而缩小类型匹配的范围,以便为每个参数选择特定的bean。 在最简单的情况下,这可以是简单的描述性值,如以下示例所示: public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ... } 您还可以@Qualifier在各个构造函数参数或方法参数上指定注解,如以下示例所示: public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... } 下面的示例显示了相应的bean定义。 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier value="main"/> <!-- 指定qualifier属性 --> </bean> </beans> bean名称被认为是默认的qualifier值。 也可以不使用qualifier而是将该bean的id定义为main,达到相同的匹配效果。 然而,尽管您可以使用这种约定来按名称引用特定的bean,但@Autowired基本上是关于类型驱动的注入,qualifier只是在类型之上的可选选项,这意味着,即使使用了bean名称来进行qualifier的限定,qualifier 值也总是在类型匹配集中选择相同名称的bean。 qualifier 也适用于collections, 如前所述—例如 Set<MovieCatalog>,在这种情况下,根据声明的qualifier值,所有匹配的bean都作为一个集合注入。 这意味着qualifier不必是惟一的。相反,它们构成了过滤标准。例如,您可以定义具有相同qualifier值“action”的多个MovieCatalog bean, 所有这些bean都被注入到带有@Qualifier(“action”)注释的集合中。 public class ServiceController { @Autowired @Qualifier("main") private List<MovieCatalog> serviceList; } <bean class="example.SimpleMovieCatalogOne"> <qualifier value="main"/> <!-- 指定相同的qualifier属性 --> </bean> <bean class="example.SimpleMovieCatalogTwo"> <qualifier value="main"/> <!-- 指定相同的qualifier属性 --> </bean> <bean class="example.SimpleMovieCatalogThree"> <qualifier value="action"/> <!-- 指定相同的qualifier属性 --> </bean> 如果没有其他注解(例如qualifier或primary ), 对于非唯一依赖情况,Spring将注入点名称(即字段名称或参数名称)与目标bean名称或者bean id匹配, 并选择同名的候选对象(如果有同名的的话,没有同名的话则依然抛出异常)。 如果您打算通过名称进行依赖注入,请不要主要使用@Autowired,即使它能够通过bean名称在类型匹配的候选者中进行选择。 使用JSR-250 @Resource注解: 1. 如果同时指定了name和type,按照bean Name 和 bean 类型同时匹配 2. 如果指定了name,就按照bean Name 匹配 3. 如果指定了type,就按照类型匹配 4. 如果既没有指定name,又没有指定type,就先按照beanName匹配; 如果没有匹配,再按照类型进行匹配; 测试 @Resource的时候还发现一个有意思的东西, 当被注入的是个List的时候,不管是什么类型的List, 只要@Resource加了name条件,都能被注入进去, 比如 List<String> 会被注入到List<MovieCatalog> 大家可以试一下 @Autowired注解: 在按类型选择候选bean之后,再在候选者bean中选择相同名称的。 @Autowired适用于 字段,构造方法,和多参数方法,允许通过qualifier注解在参数级别上缩小范围。 相比之下,@Resource只支持具有单个参数的字段和bean属性设置器方法。 因此,如果注入目标是构造函数或多参数方法,则应该坚持使用qualifier。 您可以创建自己的自定义限定符注解。为此,请定义一个注解并在您的定义中提供该注解,如以下示例所示: @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Genre { String value(); } 然后,您可以在自动连接的字段和参数上提供自定义限定符,如以下示例所示: public class MovieRecommender { @Autowired @Genre("Action") private MovieCatalog actionCatalog; private MovieCatalog comedyCatalog; @Autowired public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) { this.comedyCatalog = comedyCatalog; } // ... } 接下来,您可以为候选bean定义提供信息。您可以添加<qualifier></qualifier>标记作为<bean></bean>标记的子元素,然后指定类型和值来匹配您的自定义qualifier注解。该类型与注释的全限定类名匹配。 另外,为了方便起见,如果不存在名称冲突的风险,您可以使用简短的类名。 下面的例子演示了这两种方法: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="Genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="example.Genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> <bean id="movieRecommender" class="example.MovieRecommender"/> </beans> 在某些情况下,使用没有值的注解就足够了。当注解用于更通用的目的,并且可以跨几种不同类型的依赖项应用时,这一点非常有用。例如,您可以提供一个脱机目录,当没有可用的Internet连接时可以搜索该目录。首先,定义简单注释,如下例所示: @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Offline { } 然后将注解添加到要自动装配的字段或属性,如以下示例所示: public class MovieRecommender { @Autowired @Offline private MovieCatalog offlineCatalog; // ... } 现在,bean定义仅需要一个限定符type,如以下示例所示: <bean class="example.SimpleMovieCatalog"> <qualifier type="Offline"/> <!-- inject any dependencies required by this bean --> </bean> 您还可以定义自定义qualifier注解,自定义的注解可以定义除了简单value属性之外的属性。 如果随后在要自动装配的字段或参数上指定了多个属性值,则bean定义必须与所有此类属性值匹配才能被视为自动装配候选。 例如,请考虑以下注解定义: @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String genre(); Format format(); } 在这种情况下Format是一个枚举,定义如下: public enum Format { VHS, DVD, BLURAY } 要自动装配的字段将用自定义qualifier进行注解,并包括这两个属性的值:genre和format,如以下示例所示: public class MovieRecommender { @Autowired @MovieQualifier(format=Format.VHS, genre="Action") private MovieCatalog actionVhsCatalog; @Autowired @MovieQualifier(format=Format.VHS, genre="Comedy") private MovieCatalog comedyVhsCatalog; @Autowired @MovieQualifier(format=Format.DVD, genre="Action") private MovieCatalog actionDvdCatalog; @Autowired @MovieQualifier(format=Format.BLURAY, genre="Comedy") private MovieCatalog comedyBluRayCatalog; // ... } 最后,bean定义应该包含匹配的限定符值。这个例子还演示了您可以使用bean元属性来代替<qualifier></qualifier>元素。 如果可用,<qualifier></qualifier>元素及其属性优先,但是如果没有这样的限定符,自动装配机制就会回到<meta>标签中提供的值上,就像下面例子中的最后两个bean定义一样: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Action"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <qualifier type="MovieQualifier"> <attribute key="format" value="VHS"/> <attribute key="genre" value="Comedy"/> </qualifier> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="DVD"/> <meta key="genre" value="Action"/> <!-- inject any dependencies required by this bean --> </bean> <bean class="example.SimpleMovieCatalog"> <meta key="format" value="BLURAY"/> <meta key="genre" value="Comedy"/> <!-- inject any dependencies required by this bean --> </bean> </beans> 1.9.5。将泛型用作自动装配限定符 除了@Qualifier注解之外,您还可以将Java泛型类型用作资格的隐式形式。例如,假设您具有以下配置: @Configuration public class MyConfiguration { @Bean public StringStore stringStore() { return new StringStore(); } @Bean public IntegerStore integerStore() { return new IntegerStore(); } } 假设前面的bean实现了一个通用接口(即Store<String>和 Store<Integer>) class StringStore implements Store<String>{ } class IntegerStore implements Store<Integer>{ } 则可以@Autowire将该Store接口和通用用作限定符,如以下示例所示: @Autowired private Store<String> s1; // <String> qualifier, 注入 stringStore bean @Autowired private Store<Integer> s2; // <Integer> qualifier, 注入 the integerStore bean 在自动装配列表,Map实例和数组时,通用限定符也适用。下面的示例自动连接泛型List: // 只注入 Store<Integer> 类型 // Store<String> 不会被注入 @Autowired private List<Store<Integer>> s; 1.9.6。使用CustomAutowireConfigurer CustomAutowireConfigurer 是一个BeanFactoryPostProcessor 您可以注册自己的自定义限定符注解类型,即使它们未使用Spring的@Qualifier注解进行注解。 像之前我们定义的注解 @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface MovieQualifier { String value(); } 这种写法主要就是托@Qualifier的福气 但我们也可以不依赖它 以下示例显示如何使用CustomAutowireConfigurer: <bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property> </bean> example.CustomQualifier Spring会根据这个类路径加载这个类, 并将这个类作为和@Qualifier同作用来对待 自动注入是如何处理候选对象的? bean definition 的 autowire-candidate 值,值为false表示该bean不参于候选 <beans/>元素default-autowire-candidates上可用的任何模式,值为false表示该组的bean不参与候选 @Qualifier注解 和 任何在customautowiresfigurer注册的自定义注解的存在会被优先使用 当多个bean符合自动装配候选条件时, 确定“primary”的步骤如下:如果候选中恰好有一个bean定义将primary属性设置为true,则将其选中。 1.9.7。@Resource Spring还通过在字段或bean属性设置器方法上使用JSR-250@Resource批注(javax.annotation.Resource)支持注入。 1. 如果同时指定了name和type,按照bean Name 和 bean 类型同时匹配 2. 如果指定了name,就按照bean Name 匹配 3. 如果指定了type,就按照类型匹配 4. 如果既没有指定name,又没有指定type,就先按照beanName匹配; 如果没有匹配,再按照类型进行匹配; @Resource具有name属性。默认情况下,Spring将该值解释为要注入的Bean名称。 换句话说,它遵循名称语义,如以下示例所示: public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } 如果未明确指定名称,则默认名称是从字段名称或setter方法派生的。 如果是字段,则采用字段名称。 在使用setter方法的情况下,它采用bean属性名称。 以下示例将名为 movieFinder的bean注入到setter方法: public class SimpleMovieLister { private MovieFinder movieFinder; @Resource public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } } 因此,在下例中,customerPreferenceDao字段首先查找名为“customerPreferenceDao”的bean,找不到的话然后返回到与类型customerPreferenceDao匹配的bean: public class MovieRecommender { @Resource private CustomerPreferenceDao customerPreferenceDao; @Resource private ApplicationContext context; public MovieRecommender() { } } 1.9.8。使用@Value @Value 通常用于注入外部属性: @Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name}") String catalog) { this.catalog = catalog; } } 使用以下配置: @Configuration @PropertySource("classpath:application.properties") public class AppConfig { } 和以下application.properties文件: catalog.name=MovieCatalog 在这种情况下,catalog参数和字段将等于MovieCatalog值。 Spring提供了一个默认的值解析器。 它将尝试解析属性值,如果无法解析, ${catalog.name} 则将被当做值注入到属性中。 例如:catalog="${catalog.name}" 如果要严格控制不存在的值,则应声明一个PropertySourcesPlaceholderConfigurerbean,如以下示例所示: @Configuration public class AppConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } } 当配置PropertySourcesPlaceholderConfigurer使用JavaConfig,该@Bean方法必须是static。 如果${} 无法解析任何占位符,则使用上述配置可确保Spring初始化失败。 默认情况下,Spring Boot配置一个PropertySourcesPlaceholderConfigurer 从application.properties和application.yml获取bean的属性。 Spring提供的内置转换器支持允许自动处理简单的类型转换(例如转换为Integer 或转换为简单的类型int)。 多个逗号分隔的值可以自动转换为String数组,而无需付出额外的努力。 可以像下边一样提供默认值: @Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) { this.catalog = catalog; } } Spring BeanPostProcessor在后台使用ConversionService来处理将@Value中的字符串值转换为目标类型的过程。如果你想为自己的自定义类型提供转换支持,你可以提供自己的ConversionService bean实例,如下面的例子所示: @Configuration public class AppConfig { @Bean public ConversionService conversionService() { DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(); conversionService.addConverter(new MyCustomConverter()); return conversionService; } } 当@Value包含SpEL表达式时,该值将在运行时动态计算,如以下示例所示: @Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) { this.catalog = catalog; } } SpEL还支持使用更复杂的数据结构: @Component public class MovieRecommender { private final Map<String, Integer> countOfMoviesPerCatalog; public MovieRecommender( @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) { this.countOfMoviesPerCatalog = countOfMoviesPerCatalog; } } 1.9.9。使用@PostConstruct和@PreDestroy CommonAnnotationBeanPostProcessor不仅处理@Resource注解 也处理javax.annotation.PostConstruct和 javax.annotation.PreDestroy。 在Spring 2.5中引入了对这些注解的支持,为初始化回调和销毁回调中描述的生命周期回调机制提供了一种替代方法。 如果在Spring ApplicationContext中注册了CommonAnnotationBeanPostProcessor,带有这两个注解的方法将会被回调执行。 在下面的例子中,缓存在初始化时被预填充,在销毁时被清除: public class CachingMovieLister { @PostConstruct public void populateMovieCache() { // populates the movie cache upon initialization... } @PreDestroy public void clearMovieCache() { // clears the movie cache upon destruction... } } 像@Resource一样,@PostConstruct和@PreDestroy注解是JDK6到8的标准Java库的一部分。 但是,整个javax.annotation 程序包都与JDK 9中的核心Java模块分开,并最终在JDK 11中删除了。 如果需要,需要对javax.annotation-api工件进行处理。 现在可以通过Maven Central获取,只需像其他任何库一样将其添加到应用程序的类路径中即可。

优秀的个人博客,低调大师

一起来读官方文档-----SpringIOC(07)

1.8。容器扩展点 通常,应用程序开发人员不需要对ApplicationContext 实现类进行子类化。相反,可以通过插入特殊集成接口的实现来扩展Spring IoC容器。接下来的几节描述了这些集成接口。 1.8.1。自定义bean实现BeanBeanPostProcessor接口 BeanPostProcessor接口定义了回调方法,您可以实现这些回调方法来修改默认的bean实例化的逻辑,依赖关系解析逻辑等。 如果您想在Spring容器完成实例化,配置和初始化bean之后实现一些自定义逻辑,则可以插入一个或多个自定义BeanPostProcessor。 您可以配置多个BeanPostProcessor实例,并且可以BeanPostProcessor通过实现Ordered 接口设置order属性来控制这些实例的运行顺序。 @Component public class MyBeanPostProcessor implements BeanPostProcessor, Ordered { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public int getOrder() { return 0; } } BeanPostProcessor实例操作的是bean的实例。 也就是说,Spring IoC容器实例化一个bean实例, 然后使用BeanPostProcessor对这些实例进行处理加工。 BeanPostProcessor实例是按容器划分作用域的。 仅在使用容器层次结构时,这才有意义。 如果BeanPostProcessor在一个容器中定义一个,它将仅对该容器中的bean进行后处理。 换句话说,一个容器中定义的bean不会被BeanPostProcessor另一个容器中的定义进行后处理, 即使这两个容器是同一层次结构的一部分也是如此。 BeanPostProcessor修改的是bean实例化之后的内容, 如果要更改实际的bean定义(即bean definition) 您需要使用 BeanFactoryPostProcessor接口. org.springframework.beans.factory.config.BeanPostProcessor接口恰好由两个回调方法组成。 当此类被注册为容器的post-processor时,对于容器创建的每个bean实例,post-processor都会在任何bean实例化之后并且在容器初始化方法(例如InitializingBean.afterPropertiesSet()或任何声明的init方法)被使用之前调用。 post-processor可以对bean实例执行任何操作,也可以完全忽略回调。 post-processor通常检查回调接口,或者可以用代理包装Bean。 一些Spring AOP基础结构类被实现为post-processor,以提供代理包装逻辑。 ApplicationContext自动检测实现BeanPostProcessor接口所有bean,注意是要注册成bean,仅仅实现接口是不可以的。 请注意,通过使用@Bean工厂方法声明BeanPostProcessor时,工厂方法的返回类型应该是实现类本身或至少是org.springframework.beans.factory.config.BeanPostProcessor 接口,以清楚地表明该bean的post-processor性质。 否则,ApplicationContext无法在完全创建之前按类型自动检测它。 由于BeanPostProcessor需要提前实例化以便应用于上下文中其他bean的初始化,因此这种早期类型检测至关重要。 @Bean public BeanPostProcessor myBeanPostProcessor(){ return new MyBeanPostProcessor(); } 以编程方式注册BeanPostProcessor实例 虽然推荐的BeanPostProcessor注册方法是通过ApplicationContext自动检测, 但是您可以ConfigurableBeanFactory使用addBeanPostProcessor方法通过编程方式对它们进行注册。 当您需要在注册之前评估条件逻辑(比如应用场景是xxx条件才注册,xxx条件不注册时), 甚至需要跨层次结构的上下文复制Bean post-processor时,这将非常有用。 但是请注意,以BeanPostProcessor编程方式添加的实例不遵守该Ordered接口。 在这里,注册的顺序决定了执行的顺序。 还要注意,以BeanPostProcessor编程方式注册的实例总是在通过自动检测注册的实例之前进行处理, 而不考虑任何明确的顺序。 BeanPostProcessor 实例和AOP自动代理 实现BeanPostProcessor接口的类是特殊的,并且容器对它们的处理方式有所不同。 BeanPostProcessor它们直接引用的所有实例和bean在启动时都会实例化, 作为ApplicationContext的特殊启动阶段的一部分。 接下来,BeanPostProcessor以排序方式注册所有实例,并将其应用于容器中的所有其他bean。 但是因为AOP自动代理的实现是通过BeanPostProcessor接口, 所以在AOP的BeanPostProcessor接口实例化之前的 BeanPostProcessor实例或BeanPostProcessor实例直接引用的bean都没有资格进行自动代理。 并且对于任何此类bean都没有任何处理切面的BeanPostProcessor指向他们。 您应该看到一条参考性日志消息: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)。 这条消息的意思大概就是说这个bean没有得到所有BeanPostProcessor的处理 下面分析一下这条日志的逻辑:我们不用AOP的BeanPostProcessor用AutowiredAnnotationBeanPostProcessor来看这个情况 首先这条日志是在BeanPostProcessorChecker类中打印的, 这个类本身就实现了BeanPostProcessor, Spring容器增加这个processor的代码如下: //获取所有的BeanPostProcessor类型的bean //第一个true表示包括非单例的bean //第二个false表示仅查找已经实例化完成的bean,如果是factory-bean则不算入内 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); //当前beanFactory内的所有post-processor数 + 1 + postBeanNames的数量 //这个数量在后续有个判断 //beanFactory.getBeanPostProcessorCount() 系统内置processor //1 就是BeanPostProcessorChecker //postProcessorNames.length 就是能扫描到的processor //这个数量之和就是目前系统能看到的所有processor //还有的就可能是解析完了某些bean又新增了processor那个不算在内 int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; //add BeanPostProcessorChecker 进入beanPostProcessor链 beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); BeanPostProcessorChecker中判断并打印上边那条日志的方法如下: @Override public Object postProcessAfterInitialization(Object bean, String beanName) { //如果当前bean不是postProcessor的实例 //并且不是内部使用的bean //并且this.beanFactory.getBeanPostProcessorCount()小于刚才相加的值 //三个都满足才会打印那行日志 if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { if (logger.isInfoEnabled()) { logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() + "] is not eligible for getting processed by all BeanPostProcessors " + "(for example: not eligible for auto-proxying)"); } } return bean; } //当前beanName不为空,并且对应的bean是容器内部使用的bean则返回true private boolean isInfrastructureBean(@Nullable String beanName) { if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) { BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName); return (bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE); } return false; } 在看Spring createBean时遍历postProcessor的代码 @Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; } 就是通过这么一个循环来执行后置方法applyBeanPostProcessorsAfterInitialization,前置方法也是这样的 现在假设我们有一个自定义的beanPostProcessor里面需要注入一个我们自定义的beanA, 那么在beanPostProcessor被实例化的时候肯定会要求注入我们自定义的beanA, 那么现在就有多种情况了: 1.我们用的set或者构造器注入那beanA会被实例化并注入 2.如果我们用的@Autowired,当我们自定义的beanPostProcessor实例化 在AutowiredAnnotationBeanPostProcessor实例化之前,那么beanA都无法被注入值 如果在之后,则还是可以被注入值 但是这两种情况都会打印这行日志 Bean 'beanA' of type [org.springframework.beanA] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 以下示例显示了如何在ApplicationContext中编写,注册和使用BeanPostProcessor实例。 示例:Hello World,BeanPostProcessor-style 第一个示例演示了基本用法。示例展示了一个自定义BeanPostProcessor实现,它在容器创建每个bean时调用该bean的toString()方法,并将结果字符串打印到系统控制台。 下面的清单显示了自定义的BeanPostProcessor实现类定义: package scripting; import org.springframework.beans.factory.config.BeanPostProcessor; public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor { // 只需按原样返回实例化的bean public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; // 我们可以返回任何对象引用 } public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("Bean '" + beanName + "' created : " + bean.toString()); return bean; } } 以下beans元素使用InstantiationTracingBeanPostProcessor: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd"> <lang:groovy id="messenger" script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy"> <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/> </lang:groovy> <!-- 当上述bean (messenger)被实例化时,这个自定义的BeanPostProcessor实现将事实输出到系统控制台 --> <bean class="scripting.InstantiationTracingBeanPostProcessor"/> </beans> 请注意实例化tracingbeanpostprocessor是如何定义的。它甚至没有名称,而且,因为它是一个bean,所以可以像其他bean一样进行依赖注入。 下面的Java应用程序运行前面的代码和配置: import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.scripting.Messenger; public final class Boot { public static void main(final String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml"); Messenger messenger = ctx.getBean("messenger", Messenger.class); System.out.println(messenger); } } 前面的应用程序的输出类似于以下内容: Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961 org.springframework.scripting.groovy.GroovyMessenger@272961 示例: RequiredAnnotationBeanPostProcessor 将回调接口或注解与自定义BeanPostProcessor实现结合使用是扩展Spring IoC容器的一种常见方法。 一个例子是Spring的AutowiredAnnotationBeanPostProcessor——一个随Spring发行版附带的BeanPostProcessor实现,它确保被注解(@Autowired,@Value, @Inject等注解)注释的属性会被注入一个bean实例。 1.8.2。自定义配置元数据BeanFactoryPostProcessor 我们要看的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor。 该接口与BeanPostProcessor主要区别在于:BeanFactoryPostProcessor对Bean配置元数据进行操作。 也就是说,Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据,并有可能在容器实例化实例任何bean之前更改元数据。 您可以配置多个BeanFactoryPostProcessor实例,并且可以BeanFactoryPostProcessor通过设置order属性来控制这些实例的运行顺序。但是,仅当BeanFactoryPostProcessor实现 Ordered接口时才能设置此属性。 如果希望更改实际bean实例(从配置元数据创建的对象),则需要使用BeanPostProcessor。 尽管在BeanFactoryPostProcessor中使用bean实例在技术上是可行的(例如,通过使用BeanFactory.getBean()), 但是这样做会导致过早的bean实例化,违反标准的容器生命周期。 这可能会导致负面的副作用,比如绕过bean的后处理。 另外,BeanFactoryPostProcessor实例的作用域为每个容器。 这只有在使用容器层次结构时才有用。 如果您在一个容器中定义了BeanFactoryPostProcessor,那么它只应用于该容器中的bean定义。 一个容器中的Bean定义不会被另一个容器中的BeanFactoryPostProcessor实例进行后处理,即使这两个容器属于同一层次结构。 当BeanFactoryPostProcessor在ApplicationContext中声明时,它将自动运行,以便对定义容器的配置元数据应用更改。 Spring包括许多预定义的bean工厂后处理器,如PropertyOverrideConfigurer和PropertySourcesPlaceholderConfigurer。 您还可以使用自定义BeanFactoryPostProcessor例如,用于注册自定义属性编辑器。 ApplicationContext自动检测部署其中实现BeanFactoryPostProcessor接口的任何bean。在适当的时候,这些bean会被bean factory post-processors来使用。 你也可以像部署任何其他bean一样部署这些自定义的bean factory post-processors。 示例:PropertySourcesPlaceholderConfigurer 您可以使用PropertySourcesPlaceholderConfigurer使用标准的Java属性格式将bean定义中的属性值外部化到单独的文件中。这样,部署应用程序的人员就可以自定义特定于环境的属性,比如数据库url和密码,而无需修改主XML定义文件或容器文件的复杂性或风险。 考虑以下基于xml的配置元数据片段,其中定义了具有占位符值的数据源: <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="locations" value="classpath:com/something/jdbc.properties"/> </bean> <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> 该示例显示了从外部Properties文件配置的属性。 在运行时,将 PropertySourcesPlaceholderConfigurer应用于替换数据源的某些属性的元数据。将要替换的值指定为形式的占位符,该形式${property-name}遵循Ant和log4j和JSP EL样式。 实际值来自标准Java Properties格式的另一个文件: jdbc.driverClassName = org.hsqldb.jdbcDriver jdbc.url = jdbc:hsqldb:hsql://production:9002 jdbc.username = sa jdbc.password = root 因此,${jdbc.username}在运行时将字符串替换为值“sa”,并且其他与属性文件中的键匹配的占位符值也适用。 在PropertySourcesPlaceholderConfigurer为大多数属性和bean定义的属性占位符检查。此外,您可以自定义占位符前缀和后缀。 <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="locations" value="classpath:jdbc.properties"/> //自定义前缀后缀 <property name="placeholderPrefix" value="${"/> <property name="placeholderSuffix" value="}"/> </bean> 1.8.3。自定义实例化逻辑FactoryBean 您可以org.springframework.beans.factory.FactoryBean为本身就是工厂的对象实现接口。 该FactoryBean接口是可插入Spring IoC容器的实例化逻辑的一点。 如果您有复杂的初始化代码,而不是(可能)冗长的XML,可以用Java更好地表达,则以创建自己的代码 FactoryBean, 在该类中编写复杂的初始化,然后将自定义FactoryBean插入容器。 该FactoryBean界面提供了三种方法: Object getObject():返回此工厂创建的对象的实例。实例可以共享,具体取决于该工厂是否返回单例或原型。 boolean isSingleton():true如果FactoryBean返回单例或false其他则返回 。 Class getObjectType():返回getObject()方法返回的对象类型,或者null如果类型未知,则返回该对象类型。 FactoryBeanSpring框架中的许多地方都使用了该概念和接口。Spring附带了50多种FactoryBean接口实现。Spring中的了解的少,但是Mybatis的MybatisSqlSessionFactoryBean很出名。 当您需要向容器询问FactoryBean本身而不是由它产生的bean的实际实例时,请在调用的方法时在该bean的id前面加上“&”符号(&)。 因此,对于给定id为myBean的一个FactoryBean ,调用getBean("myBean")返回的是FactoryBean生成的实例,getBean("&myBean")返回的是FactoryBean本身。 public class MyFactoryBean implements FactoryBean<MyBean> { @Override public MyBean getObject() throws Exception { return new MyBean(); } @Override public Class<?> getObjectType() { return MyBean.class; } } <bean id="myFactoryBean" class="org.springframework.example.factoryBean.MyFactoryBean"/> getBean("myFactoryBean") 返回的是MyBean实例 getBean("&myFactoryBean") 返回的是MyFactoryBean实例

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册