├── song-parser-mp3 ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── com.chenshuyi.demo.Parser │ │ └── java │ │ └── com │ │ └── xiaohei │ │ └── demo │ │ └── Mp3Parser.java └── pom.xml ├── song-parser-mp4 ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── com.chenshuyi.demo.Parser │ │ └── java │ │ └── com │ │ └── xiaoshu │ │ └── demo │ │ └── Mp4Parser.java └── pom.xml ├── song-parser-rmvb ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── com.chenshuyi.demo.Parser │ │ └── java │ │ └── com │ │ └── anonymous │ │ └── demo │ │ └── RmvbParser.java └── pom.xml ├── song-parser ├── src │ └── main │ │ └── java │ │ └── com │ │ └── chenshuyi │ │ └── demo │ │ ├── Parser.java │ │ ├── ParserInfo.java │ │ ├── ParserNotFoundException.java │ │ ├── Song.java │ │ └── ParserManager.java └── pom.xml ├── song-parser-demo ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── chenshuyi │ │ │ └── demo │ │ │ └── AppTest.java │ └── main │ │ └── java │ │ └── com │ │ └── chenshuyi │ │ └── demo │ │ └── App.java └── pom.xml ├── .gitignore ├── LICENSE ├── pom.xml ├── README-zh.md ├── README.md └── docs └── index.html /song-parser-mp3/src/main/resources/META-INF/services/com.chenshuyi.demo.Parser: -------------------------------------------------------------------------------- 1 | com.xiaohei.demo.Mp3Parser -------------------------------------------------------------------------------- /song-parser-mp4/src/main/resources/META-INF/services/com.chenshuyi.demo.Parser: -------------------------------------------------------------------------------- 1 | com.xiaoshu.demo.Mp4Parser -------------------------------------------------------------------------------- /song-parser-rmvb/src/main/resources/META-INF/services/com.chenshuyi.demo.Parser: -------------------------------------------------------------------------------- 1 | com.anonymous.demo.RmvbParser -------------------------------------------------------------------------------- /song-parser/src/main/java/com/chenshuyi/demo/Parser.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | /** 4 | * @author chenyr 5 | * @date 2018.04.19 6 | */ 7 | public interface Parser { 8 | 9 | byte[] getByteFormate(); 10 | 11 | Song parse(byte[] data) throws Exception; 12 | } 13 | -------------------------------------------------------------------------------- /song-parser/src/main/java/com/chenshuyi/demo/ParserInfo.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | /** 4 | * @author chenyr 5 | * @date 2018.04.19 6 | */ 7 | public class ParserInfo { 8 | final Parser parser; 9 | 10 | public ParserInfo(Parser parser) { 11 | this.parser = parser; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /song-parser-demo/src/test/java/com/chenshuyi/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /log/ 3 | *.iml 4 | /song-parser/target/ 5 | /song-parser/.idea/ 6 | /song-parser/*.iml 7 | /song-parser-demo/target/ 8 | /song-parser-demo/.idea/ 9 | /song-parser-demo/*.iml 10 | /song-parser-mp3/target/ 11 | /song-parser-mp3/.idea/ 12 | /song-parser-mp3/*.iml 13 | /song-parser-mp4/target/ 14 | /song-parser-mp4/.idea/ 15 | /song-parser-mp4/*.iml 16 | /song-parser-rmvb/target/ 17 | /song-parser-rmvb/.idea/ 18 | /song-parser-rmvb/*.iml -------------------------------------------------------------------------------- /song-parser/src/main/java/com/chenshuyi/demo/ParserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | /** 4 | * @author chenyr 5 | * @date 2018.04.19 6 | */ 7 | public class ParserNotFoundException extends RuntimeException{ 8 | private String code; 9 | 10 | private String message; 11 | 12 | public ParserNotFoundException(String code, String message) { 13 | this.code = code; 14 | this.message = message; 15 | } 16 | 17 | public String getCode() { 18 | return code; 19 | } 20 | 21 | public void setCode(String code) { 22 | this.code = code; 23 | } 24 | 25 | @Override 26 | public String getMessage() { 27 | return message; 28 | } 29 | 30 | public void setMessage(String message) { 31 | this.message = message; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 陈裕荣 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /song-parser/src/main/java/com/chenshuyi/demo/Song.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | /** 4 | * Service Interface 提供接口 5 | * @author chenyr 6 | * @date 2018.04.19 7 | */ 8 | public class Song { 9 | 10 | private String format; 11 | 12 | private String name; 13 | 14 | private String author; 15 | 16 | private long time; 17 | 18 | public Song() { 19 | } 20 | 21 | public Song(String author, String format, String name, long time) { 22 | this.author = author; 23 | this.format = format; 24 | this.name = name; 25 | this.time = time; 26 | } 27 | 28 | public String getFormat() { 29 | return format; 30 | } 31 | 32 | public void setFormat(String format) { 33 | this.format = format; 34 | } 35 | 36 | public String getAuthor() { 37 | return author; 38 | } 39 | 40 | public void setAuthor(String author) { 41 | this.author = author; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public long getTime() { 53 | return time; 54 | } 55 | 56 | public void setTime(long time) { 57 | this.time = time; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /song-parser-mp4/src/main/java/com/xiaoshu/demo/Mp4Parser.java: -------------------------------------------------------------------------------- 1 | package com.xiaoshu.demo; 2 | 3 | import com.chenshuyi.demo.Parser; 4 | import com.chenshuyi.demo.ParserManager; 5 | import com.chenshuyi.demo.Song; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @author chenyr 11 | * @date 2018.04.19 12 | */ 13 | public class Mp4Parser implements Parser { 14 | 15 | public final byte[] FORMAT = "MP4".getBytes(); 16 | 17 | public final int FORMAT_LENGTH = FORMAT.length; 18 | 19 | static 20 | { 21 | try 22 | { 23 | ParserManager.registerParser(new Mp4Parser()); 24 | } 25 | catch (Exception e) 26 | { 27 | throw new RuntimeException("Can't register parser!"); 28 | } 29 | } 30 | 31 | @Override 32 | public byte[] getByteFormate() { 33 | return this.FORMAT; 34 | } 35 | 36 | 37 | @Override 38 | public Song parse(byte[] data) throws Exception{ 39 | if (!isDataCompatible(data)) { 40 | throw new Exception("data format is wrong."); 41 | } 42 | //parse data by mp3 format type 43 | return new Song("陈楚生", "mp4", "《有没有人曾告诉你》", 320L); 44 | } 45 | 46 | private boolean isDataCompatible(byte[] data) { 47 | byte[] format = Arrays.copyOfRange(data, 0, FORMAT_LENGTH); 48 | return Arrays.equals(format, FORMAT); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /song-parser-rmvb/src/main/java/com/anonymous/demo/RmvbParser.java: -------------------------------------------------------------------------------- 1 | package com.anonymous.demo; 2 | 3 | import com.chenshuyi.demo.Parser; 4 | import com.chenshuyi.demo.ParserManager; 5 | import com.chenshuyi.demo.Song; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @author chenyr 11 | * @date 2018.04.19 12 | */ 13 | public class RmvbParser implements Parser { 14 | 15 | public final byte[] FORMAT = "RMVB".getBytes(); 16 | 17 | public final int FORMAT_LENGTH = FORMAT.length; 18 | 19 | static 20 | { 21 | try 22 | { 23 | ParserManager.registerParser(new RmvbParser()); 24 | } 25 | catch (Exception e) 26 | { 27 | throw new RuntimeException("Can't register parser!"); 28 | } 29 | } 30 | 31 | @Override 32 | public byte[] getByteFormate() { 33 | return this.FORMAT; 34 | } 35 | 36 | 37 | @Override 38 | public Song parse(byte[] data) throws Exception{ 39 | if (!isDataCompatible(data)) { 40 | throw new Exception("data format is wrong."); 41 | } 42 | //parse data by rmvb format type 43 | return new Song("AGA", "rmvb", "《Wonderful U》", 240L); 44 | } 45 | 46 | private boolean isDataCompatible(byte[] data) { 47 | byte[] format = Arrays.copyOfRange(data, 0, FORMAT_LENGTH); 48 | return Arrays.equals(format, FORMAT); 49 | } 50 | } -------------------------------------------------------------------------------- /song-parser-mp3/src/main/java/com/xiaohei/demo/Mp3Parser.java: -------------------------------------------------------------------------------- 1 | package com.xiaohei.demo; 2 | 3 | import com.chenshuyi.demo.ParserManager; 4 | import com.chenshuyi.demo.Song; 5 | import com.chenshuyi.demo.Parser; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @author chenyr 11 | * @date 2018.04.19 12 | */ 13 | public class Mp3Parser implements Parser { 14 | 15 | public final byte[] FORMAT = "MP3".getBytes(); 16 | 17 | public final int FORMAT_LENGTH = FORMAT.length; 18 | 19 | static 20 | { 21 | try 22 | { 23 | ParserManager.registerParser(new com.xiaohei.demo.Mp3Parser()); 24 | } 25 | catch (Exception e) 26 | { 27 | throw new RuntimeException("Can't register parser!"); 28 | } 29 | } 30 | 31 | @Override 32 | public byte[] getByteFormate() { 33 | return this.FORMAT; 34 | } 35 | 36 | 37 | @Override 38 | public Song parse(byte[] data) throws Exception{ 39 | if (!isDataCompatible(data)) { 40 | throw new Exception("data format is wrong."); 41 | } 42 | //parse data by mp3 format type 43 | return new Song("刘千楚", "mp3", "《北京东路的日子》", 220L); 44 | } 45 | 46 | private boolean isDataCompatible(byte[] data) { 47 | byte[] format = Arrays.copyOfRange(data, 0, FORMAT_LENGTH); 48 | return Arrays.equals(format, FORMAT); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /song-parser/src/main/java/com/chenshuyi/demo/ParserManager.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | import java.util.*; 4 | import java.util.concurrent.CopyOnWriteArrayList; 5 | 6 | /** 7 | * @author chenyr 8 | * @date 2018.04.19 9 | */ 10 | public class ParserManager { 11 | 12 | private final static Map registeredParsers = new HashMap<>(); 13 | 14 | static { 15 | loadInitialParsers(); 16 | System.out.println("SongParser initialized"); 17 | } 18 | 19 | private static void loadInitialParsers() { 20 | ServiceLoader loadedParsers = ServiceLoader.load(Parser.class); 21 | Iterator driversIterator = loadedParsers.iterator(); 22 | try{ 23 | while(driversIterator.hasNext()) { 24 | driversIterator.next(); 25 | } 26 | } catch(Throwable t) { 27 | // Do nothing 28 | } 29 | } 30 | 31 | public static synchronized void registerParser(Parser parser) { 32 | registeredParsers.put(Arrays.toString(parser.getByteFormate()),new ParserInfo(parser)); 33 | } 34 | 35 | public static Song getSong(byte[] data) { 36 | try { 37 | ParserInfo parserInfo = registeredParsers.get(Arrays.toString(data)); 38 | Song song = parserInfo.parser.parse(data); 39 | if (song != null) { 40 | return song; 41 | } 42 | } catch (Exception e) { 43 | //wrong parser, ignored it. 44 | } 45 | throw new ParserNotFoundException("10001", "Can not find corresponding data:" + new String(data)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /song-parser-demo/src/main/java/com/chenshuyi/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.chenshuyi.demo; 2 | 3 | /** 4 | * @author chenyr 5 | * @date 2018.04.19 6 | */ 7 | public class App { 8 | public static void main(String[] args) { 9 | testMp3Parser(); //success 10 | testMp4Parser(); //success 11 | testRmvbParser(); 12 | } 13 | 14 | /** 15 | * !! may throw ParserNotFoundException 16 | */ 17 | public static void testRmvbParser() { 18 | Song song = ParserManager.getSong(mockSongData("RMVB")); 19 | System.out.println("------------------------"); 20 | System.out.println("Name:" + song.getName()); 21 | System.out.println("Author:" + song.getAuthor()); 22 | System.out.println("Time:" + song.getTime()); 23 | System.out.println("Format:" + song.getFormat()); 24 | } 25 | 26 | public static void testMp4Parser() { 27 | Song song = ParserManager.getSong(mockSongData("MP4")); 28 | System.out.println("------------------------"); 29 | System.out.println("Name:" + song.getName()); 30 | System.out.println("Author:" + song.getAuthor()); 31 | System.out.println("Time:" + song.getTime()); 32 | System.out.println("Format:" + song.getFormat()); 33 | } 34 | 35 | public static void testMp3Parser() { 36 | Song song = ParserManager.getSong(mockSongData("MP3")); 37 | System.out.println("------------------------"); 38 | System.out.println("Name:" + song.getName()); 39 | System.out.println("Author:" + song.getAuthor()); 40 | System.out.println("Time:" + song.getTime()); 41 | System.out.println("Format:" + song.getFormat()); 42 | } 43 | 44 | /** 45 | * 制造歌曲数据 46 | * @param formatType 47 | * @return 48 | */ 49 | private static byte[] mockSongData(String formatType) { 50 | return new String(formatType).getBytes(); 51 | } 52 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.chenshuyi.demo 8 | song-parser-spi-demo 9 | pom 10 | 1.0.0 11 | 12 | song-parser 13 | song-parser-mp3 14 | song-parser-demo 15 | song-parser-mp4 16 | 17 | 18 | song-parser-spi-demo 19 | 20 | http://www.example.com 21 | 22 | 23 | UTF-8 24 | 1.7 25 | 1.7 26 | 27 | 28 | 29 | 30 | junit 31 | junit 32 | 4.11 33 | test 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | maven-clean-plugin 42 | 3.0.0 43 | 44 | 45 | 46 | maven-resources-plugin 47 | 3.0.2 48 | 49 | 50 | maven-compiler-plugin 51 | 3.7.0 52 | 53 | 54 | maven-surefire-plugin 55 | 2.20.1 56 | 57 | 58 | maven-jar-plugin 59 | 3.0.2 60 | 61 | 62 | maven-install-plugin 63 | 2.5.2 64 | 65 | 66 | maven-deploy-plugin 67 | 2.8.2 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /song-parser-mp3/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 4.0.0 7 | jar 8 | 9 | com.xiaohei.demo 10 | 1.0.0 11 | song-parser-mp3 12 | 13 | song-parser-mp3 14 | http://www.example.com 15 | 16 | 17 | UTF-8 18 | 1.7 19 | 1.7 20 | 21 | 22 | 23 | 24 | com.chenshuyi.demo 25 | song-parser 26 | 1.0.0 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | maven-clean-plugin 35 | 3.0.0 36 | 37 | 38 | 39 | maven-resources-plugin 40 | 3.0.2 41 | 42 | 43 | maven-compiler-plugin 44 | 3.7.0 45 | 46 | 47 | maven-surefire-plugin 48 | 2.20.1 49 | 50 | 51 | maven-jar-plugin 52 | 3.0.2 53 | 54 | 55 | maven-install-plugin 56 | 2.5.2 57 | 58 | 59 | maven-deploy-plugin 60 | 2.8.2 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /song-parser-mp4/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 1.0.0 7 | 8 | jar 9 | 10 | com.xiaoshu.demo 11 | song-parser-mp4 12 | 13 | song-parser-mp4 14 | 15 | http://www.example.com 16 | 17 | 18 | UTF-8 19 | 1.7 20 | 1.7 21 | 22 | 23 | 24 | 25 | com.chenshuyi.demo 26 | song-parser 27 | 1.0.0 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | maven-clean-plugin 36 | 3.0.0 37 | 38 | 39 | 40 | maven-resources-plugin 41 | 3.0.2 42 | 43 | 44 | maven-compiler-plugin 45 | 3.7.0 46 | 47 | 48 | maven-surefire-plugin 49 | 2.20.1 50 | 51 | 52 | maven-jar-plugin 53 | 3.0.2 54 | 55 | 56 | maven-install-plugin 57 | 2.5.2 58 | 59 | 60 | maven-deploy-plugin 61 | 2.8.2 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /song-parser/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | song-parser-spi-demo 7 | com.chenshuyi.demo 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | song-parser 13 | 14 | song-parser 15 | 16 | http://www.example.com 17 | 18 | 19 | UTF-8 20 | 1.7 21 | 1.7 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | 4.11 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | maven-clean-plugin 38 | 3.0.0 39 | 40 | 41 | 42 | maven-resources-plugin 43 | 3.0.2 44 | 45 | 46 | maven-compiler-plugin 47 | 3.7.0 48 | 49 | 50 | maven-surefire-plugin 51 | 2.20.1 52 | 53 | 54 | maven-jar-plugin 55 | 3.0.2 56 | 57 | 58 | maven-install-plugin 59 | 2.5.2 60 | 61 | 62 | maven-deploy-plugin 63 | 2.8.2 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /song-parser-rmvb/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.anonymous.demo 8 | song-parser-rmvb 9 | 1.0.0 10 | jar 11 | 12 | song-parser-rmvb 13 | 14 | http://www.example.com 15 | 16 | 17 | UTF-8 18 | 1.7 19 | 1.7 20 | 21 | 22 | 23 | 24 | com.chenshuyi.demo 25 | song-parser 26 | 1.0.0 27 | 28 | 29 | junit 30 | junit 31 | 4.11 32 | test 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | maven-clean-plugin 41 | 3.0.0 42 | 43 | 44 | 45 | maven-resources-plugin 46 | 3.0.2 47 | 48 | 49 | maven-compiler-plugin 50 | 3.7.0 51 | 52 | 53 | maven-surefire-plugin 54 | 2.20.1 55 | 56 | 57 | maven-jar-plugin 58 | 3.0.2 59 | 60 | 61 | maven-install-plugin 62 | 2.5.2 63 | 64 | 65 | maven-deploy-plugin 66 | 2.8.2 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /song-parser-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | song-parser-spi-demo 7 | com.chenshuyi.demo 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | song-parser-demo 13 | 14 | song-parser-demo 15 | 16 | http://www.example.com 17 | 18 | 19 | UTF-8 20 | 1.7 21 | 1.7 22 | 23 | 24 | 25 | 26 | com.chenshuyi.demo 27 | song-parser 28 | 1.0.0 29 | 30 | 31 | com.xiaohei.demo 32 | song-parser-mp3 33 | 1.0.0 34 | 35 | 36 | com.xiaoshu.demo 37 | song-parser-mp4 38 | 1.0.0 39 | 40 | 41 | com.anonymous.demo 42 | song-parser-rmvb 43 | 1.0.0 44 | 45 | 46 | junit 47 | junit 48 | 4.11 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | maven-clean-plugin 58 | 3.0.0 59 | 60 | 61 | 62 | maven-resources-plugin 63 | 3.0.2 64 | 65 | 66 | maven-compiler-plugin 67 | 3.7.0 68 | 69 | 70 | maven-surefire-plugin 71 | 2.20.1 72 | 73 | 74 | maven-jar-plugin 75 | 3.0.2 76 | 77 | 78 | maven-install-plugin 79 | 2.5.2 80 | 81 | 82 | maven-deploy-plugin 83 | 2.8.2 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # Java SPI 例子:歌曲解析器 2 | 3 | song-parser-api-demo 是 Java SPI 使用的一个简单示例。它可以加强你对 Java SPI 机制的理解。 4 | 5 | ## 背景 6 | 7 | 众所周知,歌曲格式有很多种,一首歌曲可以是MP3或MP4,甚至是RMBV格式。 我们想构建一个框架来解析歌曲信息,包括歌曲名称、歌曲作者、歌曲经过时间等。如果我们不使用Java SPI机制,我们可以这样开发框架: 8 | 9 | ``` 10 | public class ParseUtil{ 11 | public static Song parseMp3Song(byte[] data){ 12 | //parse song according to mp3 data format 13 | } 14 | } 15 | ``` 16 | 17 | 使用框架的人编写下面的代码来获取歌曲信息: 18 | 19 | ``` 20 | Song song = ParseUtil.parseMp3Song(data); 21 | ``` 22 | 23 | 如果还有一首mp4格式的歌曲,我们需要升级我们的框架并发布一个新版本,现在的框架是这样的: 24 | 25 | ``` 26 | public class ParseUtil{ 27 | public Song parseMp3Song(byte[] data){ 28 | //parse song according to mp3 data format 29 | } 30 | public Song parseMp4Song(byte[] data){ 31 | //parse song according to mp4 data format 32 | } 33 | } 34 | 35 | ``` 36 | 37 | 使用它的人需要像这样更改他们的代码: 38 | 39 | ``` 40 | //this song is mp3 format type, call the parseMp3Song() method. 41 | Song song = ParseUtil.parseMp3Song(data); 42 | //this song is mp4 format type, call the parseMp4Song() method. 43 | Song song = ParseUtil.parseMp4Song(data); 44 | ``` 45 | 46 | 每次我们添加一个新的歌曲格式解析器,我们都需要发布一个新版本的框架,每个想要使用我们新版本的人都需要升级框架。 更重要的是,使用框架的人需要知道歌曲格式并调用相应的方法。 太不方便了! 47 | 48 | ## 更好的框架与 SPI 49 | 50 | 但是如果我们使用 Java SPI 机制,每次我们添加一个新的歌曲格式解析器,使用歌曲解析器框架的人不需要升级框架,也不需要改变他们的任何代码。 51 | 52 | 当只有一种格式类型时,例如 mp3 格式类型。 我们编写下面的代码来解析歌曲信息。 53 | 54 | ``` 55 | Song song = ParserManager.getSong(data); //song stored with mp3 format 56 | ``` 57 | 58 | 而如果我们想解析mp4格式的歌曲,我们不需要改变调用方法,解析歌曲信息的代码还是这样的: 59 | 60 | ``` 61 | Song song = ParserManager.getSong(data); //song stored with mp3 format 62 | ``` 63 | 64 | 我们需要做的是导入 mp4 Parser 依赖,Java SPI 机制会找出所有的解析器并自动搜索正确的解析器来解析歌曲。 65 | 66 | ``` 67 | 68 | com.xiaoshu.demo 69 | song-parser-mp4 70 | 1.0.0 71 | 72 | ``` 73 | 74 | 真是太棒了! 75 | 76 | 我们不需要更改任何代码,只需导入 Parser 依赖项,它将返回正确的结果。 77 | 78 | 让我们进入 Song Parser Framework,看看它是如何工作的。 79 | 80 | ## 框架结构 81 | 82 | 该项目包含四个独立的模块,可分为三个主要部分。 83 | 84 | * "song-parser" 模块,定义每个解析器将使用的统一接口。 85 | * “song-parser-mp3”模块,定义mp3解析器。 86 | * "song-parser-demo" 模块,提供快速启动示例,您只需运行 "com.chenshuyi.demo.App.main" 即可获得结果。 87 | 88 | ## 如何添加新的解析器 89 | 90 | 如果要添加新的歌曲解析器,例如解析 RMVB 格式类型歌曲的解析器。 您需要创建一个新项目并添加“song-parser”的依赖项: 91 | 92 | ``` 93 | 94 | com.chenshuyi.demo 95 | song-parser 96 | 1.0.0 97 | 98 | ``` 99 | 100 | 然后在包的根目录中创建一个名为“Parser”的类: 101 | 102 | ``` 103 | package com.anonymous.demo; 104 | 105 | import com.chenshuyi.demo.ParserManager; 106 | 107 | /** 108 | * @author chenyr 109 | * @date 2018.04.19 110 | */ 111 | public class Parser extends RmvbParser implements com.chenshuyi.demo.Parser { 112 | static 113 | { 114 | try 115 | { 116 | ParserManager.registerParser(new Parser()); 117 | } 118 | catch (Exception e) 119 | { 120 | throw new RuntimeException("Can't register parser!"); 121 | } 122 | } 123 | } 124 | ``` 125 | 126 | 然后创建执行数据解析工作的 RmvbParser: 127 | 128 | ``` 129 | package com.anonymous.demo; 130 | 131 | import com.chenshuyi.demo.Song; 132 | 133 | import java.util.Arrays; 134 | 135 | /** 136 | * @author chenyr 137 | * @date 2018.04.19 138 | */ 139 | public class RmvbParser implements com.chenshuyi.demo.Parser { 140 | 141 | public final byte[] FORMAT = "RMVB".getBytes(); 142 | 143 | public final int FORMAT_LENGTH = FORMAT.length; 144 | 145 | @Override 146 | public Song parse(byte[] data) throws Exception{ 147 | if (!isDataCompatible(data)) { 148 | throw new Exception("data format is wrong."); 149 | } 150 | //parse data by rmvb format type 151 | return new Song("AGA", "rmvb", "《Wonderful U》", 240L); 152 | } 153 | 154 | private boolean isDataCompatible(byte[] data) { 155 | byte[] format = Arrays.copyOfRange(data, 0, FORMAT_LENGTH); 156 | return Arrays.equals(format, FORMAT); 157 | } 158 | } 159 | ``` 160 | 161 | 最后添加一个描述文件`resources/META-INF/services/com.chenshuyi.demo.Parser`: 162 | 163 | ``` 164 | com.anonymous.demo.Parser 165 | ``` 166 | 167 | 然后在模块“song-parser-demo”中导入新的 mp4 Parser: 168 | 169 | ``` 170 | 171 | com.anonymous.demo 172 | song-parser-rmvb 173 | 1.0.0 174 | 175 | ``` 176 | 177 | 然后尝试解析rmvb格式的歌曲: 178 | 179 | ``` 180 | Song song = ParserManager.getSong(data); //parse rmvb song 181 | ``` 182 | 183 | 成功了! 184 | 185 | ``` 186 | Name:《Wonderful U》 187 | Author:AGA 188 | Time:240 189 | Format:rmvb 190 | ``` 191 | 192 | 有关更多详细信息,您可以深入研究代码并感谢您的观看。 193 | 194 | 如果它有助于您理解Java SPI,请给我一个收藏,谢谢。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Song Parser Demo of Java SPI 2 | 3 | [中文版点击这里](README-zh.md) 4 | 5 | song-parser-spi-demo is a simple example of Java SPI usage. It can reinfoce your understanding of Java SPI mechanism. 6 | 7 | ## Background 8 | 9 | As we know, there is many kinds of song format, a song can be MP3 or MP4 or even RMBV format. We want to build a framework to parse the song information which contains song name, song author, song elasped time etc. If we don't use Java SPI mechanism, we can develop the framework like this: 10 | 11 | ``` 12 | public class ParseUtil{ 13 | public static Song parseMp3Song(byte[] data){ 14 | //parse song according to mp3 data format 15 | } 16 | } 17 | ``` 18 | 19 | people who use the framwork write the code below to get the song infomation: 20 | 21 | ``` 22 | Song song = ParseUtil.parseMp3Song(data); 23 | ``` 24 | 25 | and if there is another song which is stored by mp4 format, we need to upgrade our framework and release a new version, and the framework now like this: 26 | 27 | ``` 28 | public class ParseUtil{ 29 | public Song parseMp3Song(byte[] data){ 30 | //parse song according to mp3 data format 31 | } 32 | public Song parseMp4Song(byte[] data){ 33 | //parse song according to mp4 data format 34 | } 35 | } 36 | 37 | ``` 38 | 39 | and people who use it need to change their code like this: 40 | 41 | ``` 42 | //this song is mp3 format type, call the parseMp3Song() method. 43 | Song song = ParseUtil.parseMp3Song(data); 44 | //this song is mp4 format type, call the parseMp4Song() method. 45 | Song song = ParseUtil.parseMp4Song(data); 46 | ``` 47 | 48 | everytime we add a new song format parser, we need to release a new version of the framework, and everyone who want to use our new version need to upgrade the framework. What's more, people who use the framework need to know the song format and call the corresponding method. It's so unconvenient! 49 | 50 | ## Better Framework with SPI 51 | 52 | but if we use Java SPI mechanism, everytime we add a new song format parser, people who use the Song Parser Framework don't need to upgrade the framework and don't need to change any of their code. 53 | 54 | when there is only one kinds of format type, for example, mp3 format type. we write the code below to parse the song information. 55 | 56 | ``` 57 | Song song = ParserManager.getSong(data); //song stored with mp3 format 58 | ``` 59 | 60 | and if we want to parse song with mp4 format, we don't need to change the calling method, the code parse the song information is still like: 61 | 62 | ``` 63 | Song song = ParserManager.getSong(data); //song stored with mp3 format 64 | ``` 65 | 66 | what we need to do is import the mp4 Parser dependency, and the Java SPI mechanism will find out all the parser and auto search the right parser to parse the song. 67 | 68 | ``` 69 | 70 | com.xiaoshu.demo 71 | song-parser-mp4 72 | 1.0.0 73 | 74 | ``` 75 | 76 | It's really awesome! 77 | 78 | we don't need to change any of our code but just import the Parser dependency and it will return the right result. 79 | 80 | Let's go inside the Song Parser Framework and see how it works. 81 | 82 | ## Framework Structure 83 | 84 | This project contains four separate modules which can divide into three main part. 85 | 86 | * "song-parser" module, define the unified interaface that every parser will use. 87 | * "song-parser-mp3" module, define the mp3 parser. 88 | * "song-parser-demo" module, provide a quicks start example, you can just run the "com.chenshuyi.demo.App.main" and result comes. 89 | 90 | ## how to add a new Parser 91 | 92 | if you want to add a new song Parser, for example a Parser to parse RMVB format type song. You need to create a new project and add the dependency of "song-parser": 93 | 94 | ``` 95 | 96 | com.chenshuyi.demo 97 | song-parser 98 | 1.0.0 99 | 100 | ``` 101 | 102 | and then create a class called "Parser" in the root of package: 103 | 104 | ``` 105 | package com.anonymous.demo; 106 | 107 | import com.chenshuyi.demo.ParserManager; 108 | 109 | /** 110 | * @author chenyr 111 | * @date 2018.04.19 112 | */ 113 | public class Parser extends RmvbParser implements com.chenshuyi.demo.Parser { 114 | static 115 | { 116 | try 117 | { 118 | ParserManager.registerParser(new Parser()); 119 | } 120 | catch (Exception e) 121 | { 122 | throw new RuntimeException("Can't register parser!"); 123 | } 124 | } 125 | } 126 | ``` 127 | 128 | and then create the RmvbParser which do the data parse work: 129 | 130 | ``` 131 | package com.anonymous.demo; 132 | 133 | import com.chenshuyi.demo.Song; 134 | 135 | import java.util.Arrays; 136 | 137 | /** 138 | * @author chenyr 139 | * @date 2018.04.19 140 | */ 141 | public class RmvbParser implements com.chenshuyi.demo.Parser { 142 | 143 | public final byte[] FORMAT = "RMVB".getBytes(); 144 | 145 | public final int FORMAT_LENGTH = FORMAT.length; 146 | 147 | @Override 148 | public Song parse(byte[] data) throws Exception{ 149 | if (!isDataCompatible(data)) { 150 | throw new Exception("data format is wrong."); 151 | } 152 | //parse data by rmvb format type 153 | return new Song("AGA", "rmvb", "《Wonderful U》", 240L); 154 | } 155 | 156 | private boolean isDataCompatible(byte[] data) { 157 | byte[] format = Arrays.copyOfRange(data, 0, FORMAT_LENGTH); 158 | return Arrays.equals(format, FORMAT); 159 | } 160 | } 161 | ``` 162 | 163 | finanly add a description file `resources/META-INF/services/com.chenshuyi.demo.Parser`: 164 | 165 | ``` 166 | com.anonymous.demo.Parser 167 | ``` 168 | 169 | and then you import the new mp4 Parser in module "song-parser-demo": 170 | 171 | ``` 172 | 173 | com.anonymous.demo 174 | song-parser-rmvb 175 | 1.0.0 176 | 177 | ``` 178 | 179 | and then try to parse rmvb format song: 180 | 181 | ``` 182 | Song song = ParserManager.getSong(data); //parse rmvb song 183 | ``` 184 | 185 | and it works ! 186 | 187 | ``` 188 | Name:《Wonderful U》 189 | Author:AGA 190 | Time:240 191 | Format:rmvb 192 | ``` 193 | 194 | for more detail, you can dive into the code and thanks for watching. 195 | 196 | if it helps you understand the Java SPI, please give me a start, thank you. 197 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Song Parser Demo of Java SPI 881 | 882 |
883 |

Song Parser Demo of Java SPI

song-parser-spi-demo is a simple example of Java SPI usage. It can reinfoce your understanding of Java SPI mechanism.

Background

As we know, there is many kinds of song format, a song can be MP3 or MP4 or even RMBV format. We want to build a framework to parse the song information which contains song name, song author, song elasped time etc. If we don't use Java SPI mechanism, we can develop the framework like this:

people who use the framwork write the code below to get the song infomation:

and if there is another song which is stored by mp4 format, we need to upgrade our framework and release a new version, and the framework now like this:

and people who use it need to change their code like this:

everytime we add a new song format parser, we need to release a new version of the framework, and everyone who want to use our new version need to upgrade the framework. What's more, people who use the framework need to know the song format and call the corresponding method. It's so unconvenient!

Better Framework with SPI

but if we use Java SPI mechanism, everytime we add a new song format parser, people who use the Song Parser Framework don't need to upgrade the framework and don't need to change any of their code.

when there is only one kinds of format type, for example, mp3 format type. we write the code below to parse the song information.

and if we want to parse song with mp4 format, we don't need to change the calling method, the code parse the song information is still like:

what we need to do is import the mp4 Parser dependency, and the Java SPI mechanism will find out all the parser and auto search the right parser to parse the song.

It's really awesome!

we don't need to change any of our code but just import the Parser dependency and it will return the right result.

Let's go inside the Song Parser Framework and see how it works.

Framework Structure

This project contains four separate modules which can divide into three main part.

  • "song-parser" module, define the unified interaface that every parser will use.
  • "song-parser-mp3" module, define the mp3 parser.
  • "song-parser-demo" module, provide a quicks start example, you can just run the "com.chenshuyi.demo.App.main" and result comes.

how to add a new Parser

if you want to add a new song Parser, for example a Parser to parse RMVB format type song. You need to create a new project and add the dependency of "song-parser":

and then create a class called "Parser" in the root of package:

and then create the RmvbParser which do the data parse work:

finanly add a description file resources/META-INF/services/com.chenshuyi.demo.Parser:

and then you import the new mp4 Parser in module "song-parser-demo":

and then try to parse rmvb format song:

and it works !

for more detail, you can dive into the code and thanks for watching.

if it helps you understand the Java SPI, please give me a start, thank you.

894 | 895 | --------------------------------------------------------------------------------