map = Splitter.on("&").withKeyValueSeparator("=").split(result);
116 | String accessToken = map.get("oauth_token");
117 | String accessTokenSecret = map.get("oauth_token_secret");
118 |
119 | // 直接將 accessToken 和 accessTokenSecret 展示在前端頁面,後面懶得做了...
120 | ModelAndView mv = new ModelAndView("index");
121 | mv.addObject("twitterUser", new TwitterUser(accessToken, accessTokenSecret));
122 | return mv;
123 | }
124 |
125 | private String generateSignature(String baseString, String tokenSecret) {
126 | HMACSha1SignatureService signatureService = new HMACSha1SignatureService();
127 | return signatureService.getSignature(baseString, CONSUMER_SECRET, tokenSecret);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth1a-twitter/src/main/java/com/kucw/controller/Oauth1aBindWithScribeController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.controller;
2 |
3 | import com.kucw.model.TwitterUser;
4 | import com.fasterxml.jackson.databind.DeserializationFeature;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import com.github.scribejava.apis.TwitterApi;
7 | import com.github.scribejava.core.builder.ServiceBuilder;
8 | import com.github.scribejava.core.model.*;
9 | import com.github.scribejava.core.oauth.OAuth10aService;
10 | import org.springframework.stereotype.Controller;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RequestParam;
13 | import org.springframework.web.servlet.ModelAndView;
14 |
15 | /**
16 | * Twitter OAuth1.0a 官方文件 https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens
17 | * 使用 scribe library 輔助我們進行 OAuth call
18 | */
19 | @RequestMapping("/scribe")
20 | @Controller
21 | public class Oauth1aBindWithScribeController {
22 |
23 | private static final String CONSUMER_KEY = "CIZ7N7mJWqztIrMiClu2WWpag";
24 | private static final String CONSUMER_SECRET = "aSgplBUypZPZPExRGfJoxtT03g0SynYhIOxn7ZkEvIImXKxy8n";
25 |
26 | private OAuth10aService twitterOAuthService = new ServiceBuilder(CONSUMER_KEY)
27 | .apiSecret(CONSUMER_SECRET)
28 | .callback("https://c4ba298b.ngrok.io/callback")
29 | .build(TwitterApi.instance());
30 |
31 | private OAuth1RequestToken requestToken;
32 |
33 | private ObjectMapper objectMapper = new ObjectMapper()
34 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
35 | .setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE);
36 |
37 | @RequestMapping("")
38 | public ModelAndView index() {
39 | return new ModelAndView("index");
40 | }
41 |
42 | @RequestMapping("/bind")
43 | public String bind() throws Exception {
44 | // 向 Twitter 獲取臨時token
45 | requestToken = twitterOAuthService.getRequestToken();
46 |
47 | // redirect 到 Twitter 授權頁
48 | return "redirect:" + twitterOAuthService.getAuthorizationUrl(requestToken);
49 | }
50 |
51 | @RequestMapping("/callback")
52 | public ModelAndView callback(@RequestParam(name = "oauth_token") String oauthToken,
53 | @RequestParam(name = "oauth_verifier") String oauthVerifier) throws Exception {
54 | // 當 user 在 Twitter 那裡按下確認按鈕後,Twitter 就會將 user 導回這個 callback url,順便告訴我們這是哪個oauthToken的verifier
55 |
56 | // 使用剛剛Twitter confirmed 的 verifier 和前面申請的 臨時token 去交換 accessToken
57 | OAuth1AccessToken accessToken = twitterOAuthService.getAccessToken(requestToken, oauthVerifier);
58 |
59 | // 拿到 accessToken 之後,向 Twitter api 取得該 user 的 data
60 | OAuthRequest request = new OAuthRequest(Verb.GET, "https://api.twitter.com/1.1/account/settings.json");
61 | twitterOAuthService.signRequest(accessToken, request);
62 | Response response = twitterOAuthService.execute(request);
63 | String json = response.getBody();
64 | TwitterUser twitterUser = objectMapper.readValue(json, TwitterUser.class);
65 |
66 | // 將 data 展示在前端頁面
67 | ModelAndView mv = new ModelAndView("index");
68 | mv.addObject("twitterUser", twitterUser);
69 | return mv;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth1a-twitter/src/main/java/com/kucw/model/TwitterUser.java:
--------------------------------------------------------------------------------
1 | package com.kucw.model;
2 |
3 | import java.io.Serializable;
4 |
5 | public class TwitterUser implements Serializable {
6 | private static final long serialVersionUID = 1L;
7 | String screenName;
8 | String language;
9 | String accessToken;
10 | String accessTokenSecret;
11 |
12 | public TwitterUser() {}
13 |
14 | public TwitterUser(String accessToken, String accessTokenSecret) {
15 | this.accessToken = accessToken;
16 | this.accessTokenSecret = accessTokenSecret;
17 | }
18 |
19 | public String getScreenName() {
20 | return screenName;
21 | }
22 |
23 | public void setScreenName(String screenName) {
24 | this.screenName = screenName;
25 | }
26 |
27 | public String getLanguage() {
28 | return language;
29 | }
30 |
31 | public void setLanguage(String language) {
32 | this.language = language;
33 | }
34 |
35 | public String getAccessToken() {
36 | return accessToken;
37 | }
38 |
39 | public void setAccessToken(String accessToken) {
40 | this.accessToken = accessToken;
41 | }
42 |
43 | public String getAccessTokenSecret() {
44 | return accessTokenSecret;
45 | }
46 |
47 | public void setAccessTokenSecret(String accessTokenSecret) {
48 | this.accessTokenSecret = accessTokenSecret;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth1a-twitter/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # freemarker setting
2 | spring.mvc.static-path-pattern=/static/**
3 | spring.freemarker.template-loader-path=classpath:/templates
4 | spring.freemarker.suffix=.ftl
5 | spring.freemarker.cache=false
6 | spring.freemarker.request-context-attribute=request
7 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth1a-twitter/src/main/resources/templates/index.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Bind Twitter OAuth 1.0a demo
7 | Click me to start binding
8 |
9 | Name: <#if twitterUser??>${twitterUser.screenName!}#if>
10 | Language: <#if twitterUser??>${twitterUser.language!}#if>
11 | accessToken: <#if twitterUser??>${twitterUser.accessToken}#if>
12 | accessTokenSecret: <#if twitterUser??>${twitterUser.accessTokenSecret}#if>
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/.gitignore:
--------------------------------------------------------------------------------
1 | # kdiff3 ignore
2 | *.orig
3 |
4 | # maven ignore
5 | target/
6 |
7 | # eclipse ignore
8 | .settings/
9 | .project
10 | .classpath
11 | .springBeans
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | *.log
21 | *.cache
22 | *.diff
23 | *.patch
24 | *.tmp
25 |
26 | # system ignore
27 | .DS_Store
28 | Thumbs.db
29 |
30 | # package ignore (optional)
31 | # *.jar
32 | # *.war
33 | # *.zip
34 | # *.tar
35 | # *.tar.gz
36 | /bin/
37 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.2.2.RELEASE
9 |
10 |
11 |
12 | com.kucw
13 | spring-boot-demo-oauth2-github
14 | 0.0.1-SNAPSHOT
15 | spring-boot-demo-oauth2-github
16 |
17 |
18 | 1.8
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-web
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-freemarker
29 |
30 |
31 | com.github.scribejava
32 | scribejava-apis
33 | 6.9.0
34 |
35 |
36 | org.apache.httpcomponents
37 | httpclient
38 | 4.5.5
39 |
40 |
41 | com.google.guava
42 | guava
43 | 28.1-jre
44 |
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-maven-plugin
52 |
53 | true
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/src/main/java/com/kucw/DemoOauth2GithubApplication.java:
--------------------------------------------------------------------------------
1 | package com.kucw;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class DemoOauth2GithubApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(DemoOauth2GithubApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/src/main/java/com/kucw/controller/Oauth2BindController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.controller;
2 |
3 | import com.kucw.model.GithubUser;
4 | import com.fasterxml.jackson.databind.DeserializationFeature;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import com.google.common.base.Splitter;
7 | import org.apache.http.HttpEntity;
8 | import org.apache.http.HttpResponse;
9 | import org.apache.http.client.HttpClient;
10 | import org.apache.http.client.entity.UrlEncodedFormEntity;
11 | import org.apache.http.client.methods.HttpGet;
12 | import org.apache.http.client.methods.HttpPost;
13 | import org.apache.http.impl.client.HttpClients;
14 | import org.apache.http.message.BasicNameValuePair;
15 | import org.apache.http.util.EntityUtils;
16 | import org.springframework.stereotype.Controller;
17 | import org.springframework.web.bind.annotation.RequestMapping;
18 | import org.springframework.web.bind.annotation.RequestParam;
19 | import org.springframework.web.servlet.ModelAndView;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 | import java.util.Map;
24 |
25 | /**
26 | * Github OAuth2 官方文件 https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
27 | */
28 | @RequestMapping("")
29 | @Controller
30 | public class Oauth2BindController {
31 |
32 | private static final String CLIENT_ID = "c84e3e988d00b01a3cba";
33 | private static final String CLIENT_SECRET = "ecd565c42e2ed0298e916e5d0dad8ad9733d4db1";
34 | private static final String CALLBACK_URL = "http://localhost:8080/callback";
35 |
36 | private ObjectMapper objectMapper = new ObjectMapper()
37 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
38 | .setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE);
39 |
40 | @RequestMapping("")
41 | public ModelAndView index() {
42 | return new ModelAndView("index");
43 | }
44 |
45 | @RequestMapping("/redirectToGithub")
46 | public String redirectToGithub() {
47 | return "redirect:https://github.com/login/oauth/authorize?client_id=" + CLIENT_ID + "&redirect_uri=" + CALLBACK_URL;
48 | }
49 |
50 | @RequestMapping("/callback")
51 | public ModelAndView callbackWithoutScribe(@RequestParam String code) throws Exception {
52 | // 向 Github 取得 accessToken
53 | HttpPost post = new HttpPost("https://github.com/login/oauth/access_token");
54 |
55 | List parameters = new ArrayList<>();
56 | parameters.add(new BasicNameValuePair("client_id", CLIENT_ID));
57 | parameters.add(new BasicNameValuePair("client_secret", CLIENT_SECRET));
58 | parameters.add(new BasicNameValuePair("code", code));
59 | post.setEntity(new UrlEncodedFormEntity(parameters, "utf-8"));
60 |
61 | // 取得 response 中的 accessToken
62 | HttpClient httpClient = HttpClients.createDefault();
63 | HttpResponse response = httpClient.execute(post);
64 | HttpEntity httpEntity = response.getEntity();
65 | String result = EntityUtils.toString(httpEntity, "utf-8");
66 | Map map = Splitter.on("&").withKeyValueSeparator("=").split(result);
67 | String accessToken = map.get("access_token");
68 |
69 | // 拿到 accessToken 之後,向 Github user api 取得該 user 的 data
70 | HttpGet get = new HttpGet("https://api.github.com/user");
71 | get.addHeader("Authorization", "token " + accessToken);
72 | HttpResponse getResponse = httpClient.execute(get);
73 | String json = EntityUtils.toString(getResponse.getEntity(), "utf-8");
74 | GithubUser githubUser = objectMapper.readValue(json, GithubUser.class);
75 |
76 | // 將 data 展示在前端頁面
77 | ModelAndView mv = new ModelAndView("index");
78 | mv.addObject("githubUser", githubUser);
79 | return mv;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/src/main/java/com/kucw/controller/Oauth2BindWithScribeController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.controller;
2 |
3 | import com.kucw.model.GithubUser;
4 | import com.fasterxml.jackson.databind.DeserializationFeature;
5 | import com.fasterxml.jackson.databind.ObjectMapper;
6 | import com.github.scribejava.apis.GitHubApi;
7 | import com.github.scribejava.core.builder.ServiceBuilder;
8 | import com.github.scribejava.core.model.OAuth2AccessToken;
9 | import com.github.scribejava.core.model.OAuthRequest;
10 | import com.github.scribejava.core.model.Response;
11 | import com.github.scribejava.core.model.Verb;
12 | import com.github.scribejava.core.oauth.OAuth20Service;
13 | import org.springframework.stereotype.Controller;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RequestParam;
16 | import org.springframework.web.servlet.ModelAndView;
17 |
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | /**
22 | * Github OAuth2 官方文件 https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
23 | * 使用 scribe library 輔助我們進行 OAuth call
24 | */
25 | @RequestMapping("/scribe")
26 | @Controller
27 | public class Oauth2BindWithScribeController {
28 |
29 | private static final String CLIENT_ID = "c84e3e988d00b01a3cba";
30 | private static final String CLIENT_SECRET = "ecd565c42e2ed0298e916e5d0dad8ad9733d4db1";
31 | private static final String CALLBACK_URL = "http://localhost:8080/callback";
32 |
33 | private OAuth20Service githubOAuthService = new ServiceBuilder(CLIENT_ID).apiSecret(CLIENT_SECRET).build(GitHubApi.instance());
34 |
35 | private ObjectMapper objectMapper = new ObjectMapper()
36 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
37 | .setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE);
38 |
39 | @RequestMapping("")
40 | public ModelAndView index() {
41 | return new ModelAndView("index");
42 | }
43 |
44 | @RequestMapping("/redirectToGithub")
45 | public String redirectToGithub() {
46 | Map paramMap = new HashMap<>();
47 | paramMap.put("client_id", CLIENT_ID);
48 | paramMap.put("redirect_uri", CALLBACK_URL);
49 | return "redirect:" + githubOAuthService.getAuthorizationUrl(paramMap);
50 | }
51 |
52 | @RequestMapping("/callback")
53 | public ModelAndView callback(@RequestParam String code) throws Exception {
54 | // 向 Github 取得 accessToken
55 | OAuth2AccessToken accessToken = githubOAuthService.getAccessToken(code);
56 |
57 | // 拿到 accessToken 之後,向 Github user api 取得該 user 的 data
58 | OAuthRequest request = new OAuthRequest(Verb.GET, "https://api.github.com/user");
59 | githubOAuthService.signRequest(accessToken, request);
60 | Response response = githubOAuthService.execute(request);
61 | String json = response.getBody();
62 | GithubUser githubUser = objectMapper.readValue(json, GithubUser.class);
63 |
64 | // 將 data 展示在前端頁面
65 | ModelAndView mv = new ModelAndView("index");
66 | mv.addObject("githubUser", githubUser);
67 | return mv;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/src/main/java/com/kucw/model/GithubUser.java:
--------------------------------------------------------------------------------
1 | package com.kucw.model;
2 |
3 | import java.io.Serializable;
4 |
5 | public class GithubUser implements Serializable {
6 | private static final long serialVersionUID = 1L;
7 | String login;
8 | int id;
9 | String nodeId;
10 | String avatarUrl;
11 | String gravatarId;
12 | String url;
13 | String htmlUrl;
14 | String followersUrl;
15 | String followingUrl;
16 | String gistsUrl;
17 | String starredUrl;
18 | String subscriptionsUrl;
19 | String organizationsUrl;
20 | String reposUrl;
21 | String eventsUrl;
22 | String receivedEventsUrl;
23 | String type;
24 | boolean siteAdmin;
25 | String name;
26 | String company;
27 | String blog;
28 | String location;
29 | String email;
30 | String hireable;
31 | String bio;
32 | int publicRepos;
33 | int publicGists;
34 | int followers;
35 | int following;
36 | String createdAt;
37 | String updatedAt;
38 |
39 | public String getLogin() {
40 | return login;
41 | }
42 |
43 | public void setLogin(String login) {
44 | this.login = login;
45 | }
46 |
47 | public int getId() {
48 | return id;
49 | }
50 |
51 | public void setId(int id) {
52 | this.id = id;
53 | }
54 |
55 | public String getNodeId() {
56 | return nodeId;
57 | }
58 |
59 | public void setNodeId(String nodeId) {
60 | this.nodeId = nodeId;
61 | }
62 |
63 | public String getAvatarUrl() {
64 | return avatarUrl;
65 | }
66 |
67 | public void setAvatarUrl(String avatarUrl) {
68 | this.avatarUrl = avatarUrl;
69 | }
70 |
71 | public String getGravatarId() {
72 | return gravatarId;
73 | }
74 |
75 | public void setGravatarId(String gravatarId) {
76 | this.gravatarId = gravatarId;
77 | }
78 |
79 | public String getUrl() {
80 | return url;
81 | }
82 |
83 | public void setUrl(String url) {
84 | this.url = url;
85 | }
86 |
87 | public String getHtmlUrl() {
88 | return htmlUrl;
89 | }
90 |
91 | public void setHtmlUrl(String htmlUrl) {
92 | this.htmlUrl = htmlUrl;
93 | }
94 |
95 | public String getFollowersUrl() {
96 | return followersUrl;
97 | }
98 |
99 | public void setFollowersUrl(String followersUrl) {
100 | this.followersUrl = followersUrl;
101 | }
102 |
103 | public String getFollowingUrl() {
104 | return followingUrl;
105 | }
106 |
107 | public void setFollowingUrl(String followingUrl) {
108 | this.followingUrl = followingUrl;
109 | }
110 |
111 | public String getGistsUrl() {
112 | return gistsUrl;
113 | }
114 |
115 | public void setGistsUrl(String gistsUrl) {
116 | this.gistsUrl = gistsUrl;
117 | }
118 |
119 | public String getStarredUrl() {
120 | return starredUrl;
121 | }
122 |
123 | public void setStarredUrl(String starredUrl) {
124 | this.starredUrl = starredUrl;
125 | }
126 |
127 | public String getSubscriptionsUrl() {
128 | return subscriptionsUrl;
129 | }
130 |
131 | public void setSubscriptionsUrl(String subscriptionsUrl) {
132 | this.subscriptionsUrl = subscriptionsUrl;
133 | }
134 |
135 | public String getOrganizationsUrl() {
136 | return organizationsUrl;
137 | }
138 |
139 | public void setOrganizationsUrl(String organizationsUrl) {
140 | this.organizationsUrl = organizationsUrl;
141 | }
142 |
143 | public String getReposUrl() {
144 | return reposUrl;
145 | }
146 |
147 | public void setReposUrl(String reposUrl) {
148 | this.reposUrl = reposUrl;
149 | }
150 |
151 | public String getEventsUrl() {
152 | return eventsUrl;
153 | }
154 |
155 | public void setEventsUrl(String eventsUrl) {
156 | this.eventsUrl = eventsUrl;
157 | }
158 |
159 | public String getReceivedEventsUrl() {
160 | return receivedEventsUrl;
161 | }
162 |
163 | public void setReceivedEventsUrl(String receivedEventsUrl) {
164 | this.receivedEventsUrl = receivedEventsUrl;
165 | }
166 |
167 | public String getType() {
168 | return type;
169 | }
170 |
171 | public void setType(String type) {
172 | this.type = type;
173 | }
174 |
175 | public boolean isSiteAdmin() {
176 | return siteAdmin;
177 | }
178 |
179 | public void setSiteAdmin(boolean siteAdmin) {
180 | this.siteAdmin = siteAdmin;
181 | }
182 |
183 | public String getName() {
184 | return name;
185 | }
186 |
187 | public void setName(String name) {
188 | this.name = name;
189 | }
190 |
191 | public String getCompany() {
192 | return company;
193 | }
194 |
195 | public void setCompany(String company) {
196 | this.company = company;
197 | }
198 |
199 | public String getBlog() {
200 | return blog;
201 | }
202 |
203 | public void setBlog(String blog) {
204 | this.blog = blog;
205 | }
206 |
207 | public String getLocation() {
208 | return location;
209 | }
210 |
211 | public void setLocation(String location) {
212 | this.location = location;
213 | }
214 |
215 | public String getEmail() {
216 | return email;
217 | }
218 |
219 | public void setEmail(String email) {
220 | this.email = email;
221 | }
222 |
223 | public String getHireable() {
224 | return hireable;
225 | }
226 |
227 | public void setHireable(String hireable) {
228 | this.hireable = hireable;
229 | }
230 |
231 | public String getBio() {
232 | return bio;
233 | }
234 |
235 | public void setBio(String bio) {
236 | this.bio = bio;
237 | }
238 |
239 | public int getPublicRepos() {
240 | return publicRepos;
241 | }
242 |
243 | public void setPublicRepos(int publicRepos) {
244 | this.publicRepos = publicRepos;
245 | }
246 |
247 | public int getPublicGists() {
248 | return publicGists;
249 | }
250 |
251 | public void setPublicGists(int publicGists) {
252 | this.publicGists = publicGists;
253 | }
254 |
255 | public int getFollowers() {
256 | return followers;
257 | }
258 |
259 | public void setFollowers(int followers) {
260 | this.followers = followers;
261 | }
262 |
263 | public int getFollowing() {
264 | return following;
265 | }
266 |
267 | public void setFollowing(int following) {
268 | this.following = following;
269 | }
270 |
271 | public String getCreatedAt() {
272 | return createdAt;
273 | }
274 |
275 | public void setCreatedAt(String createdAt) {
276 | this.createdAt = createdAt;
277 | }
278 |
279 | public String getUpdatedAt() {
280 | return updatedAt;
281 | }
282 |
283 | public void setUpdatedAt(String updatedAt) {
284 | this.updatedAt = updatedAt;
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # freemarker setting
2 | spring.mvc.static-path-pattern=/static/**
3 | spring.freemarker.template-loader-path=classpath:/templates
4 | spring.freemarker.suffix=.ftl
5 | spring.freemarker.cache=false
6 | spring.freemarker.request-context-attribute=request
--------------------------------------------------------------------------------
/spring-boot-demo-oauth2-github/src/main/resources/templates/index.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Bind Github OAuth 2.0 demo
7 | Click me to start binding
8 |
9 | Name: <#if githubUser??>${githubUser.name}#if>
10 | Image: <#if githubUser??>
#if>
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/.gitignore:
--------------------------------------------------------------------------------
1 | # kdiff3 ignore
2 | *.orig
3 |
4 | # maven ignore
5 | target/
6 |
7 | # eclipse ignore
8 | .settings/
9 | .project
10 | .classpath
11 | .springBeans
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | *.log
21 | *.cache
22 | *.diff
23 | *.patch
24 | *.tmp
25 |
26 | # system ignore
27 | .DS_Store
28 | Thumbs.db
29 |
30 | # package ignore (optional)
31 | # *.jar
32 | # *.war
33 | # *.zip
34 | # *.tar
35 | # *.tar.gz
36 | /bin/
37 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 2.2.8.RELEASE
11 |
12 |
13 |
14 | com.kucw
15 | spring-boot-demo-rabbitmq
16 | 0.0.1-SNAPSHOT
17 | spring-boot-demo-rabbitmq
18 |
19 |
20 | 1.8
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-amqp
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-maven-plugin
39 |
40 | true
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/DemoRabbitmqApplication.java:
--------------------------------------------------------------------------------
1 | package com.kucw;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class DemoRabbitmqApplication {
8 | public static void main(String[] args) {
9 | SpringApplication.run(DemoRabbitmqApplication.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/Student.java:
--------------------------------------------------------------------------------
1 | package com.kucw;
2 |
3 | import java.io.Serializable;
4 |
5 | public class Student implements Serializable {
6 |
7 | private static final long serialVersionUID = 11234877563926L;
8 |
9 | private Integer id;
10 | private String name;
11 |
12 | public Student(Integer id, String name) {
13 | this.id = id;
14 | this.name = name;
15 | }
16 |
17 | public Integer getId() {
18 | return id;
19 | }
20 |
21 | public void setId(Integer id) {
22 | this.id = id;
23 | }
24 |
25 | public String getName() {
26 | return name;
27 | }
28 |
29 | public void setName(String name) {
30 | this.name = name;
31 | }
32 |
33 | @Override
34 | public String toString() {
35 | return "Student{" +
36 | "id=" + id +
37 | ", name='" + name + '\'' +
38 | '}';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/direct/DirectConsumer.java:
--------------------------------------------------------------------------------
1 | package com.kucw.direct;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class DirectConsumer {
9 |
10 | @RabbitListener(queues = "DIRECT_QUEUE")
11 | public void listen(Student student) {
12 | System.out.println("receive message from DIRECT_QUEUE: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/direct/DirectController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.direct;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | public class DirectController {
10 |
11 | @Autowired
12 | private DirectProducer directProducer;
13 |
14 | @RequestMapping("/direct/send")
15 | public String send() {
16 | directProducer.send(new Student(1, "John"));
17 | return "success";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/direct/DirectProducer.java:
--------------------------------------------------------------------------------
1 | package com.kucw.direct;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.core.AmqpTemplate;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class DirectProducer {
10 |
11 | @Autowired
12 | private AmqpTemplate rabbitmqTemplate;
13 |
14 | public void send(Student student) {
15 | rabbitmqTemplate.convertAndSend("DIRECT_QUEUE", student);
16 | System.out.println("send message " + student + " to DIRECT_QUEUE successfully");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/routing/RoutingConsumer1.java:
--------------------------------------------------------------------------------
1 | package com.kucw.routing;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class RoutingConsumer1 {
9 |
10 | @RabbitListener(queues = "ROUTING_QUEUE_1")
11 | public void listen(Student student) {
12 | System.out.println("receive message from ROUTING_QUEUE_1: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/routing/RoutingConsumer2.java:
--------------------------------------------------------------------------------
1 | package com.kucw.routing;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class RoutingConsumer2 {
9 |
10 | @RabbitListener(queues = "ROUTING_QUEUE_2")
11 | public void listen(Student student) {
12 | System.out.println("receive message from ROUTING_QUEUE_2: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/routing/RoutingController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.routing;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | public class RoutingController {
10 |
11 | @Autowired
12 | private RoutingProducer routingProducer;
13 |
14 | @RequestMapping("/routing/send")
15 | public String send() {
16 | routingProducer.send(new Student(1, "John"));
17 | routingProducer.send(new Student(2, "Amy"));
18 | routingProducer.send(new Student(3, "Bob"));
19 | return "success";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/routing/RoutingProducer.java:
--------------------------------------------------------------------------------
1 | package com.kucw.routing;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.core.AmqpTemplate;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class RoutingProducer {
10 |
11 | @Autowired
12 | private AmqpTemplate rabbitmqTemplate;
13 |
14 | public void send(Student student) {
15 | String routingKey = student.getName();
16 | rabbitmqTemplate.convertAndSend("ROUTING_EXCHANGE", routingKey, student);
17 | System.out.println("send message " + student + " to ROUTING_EXCHANGE successfully");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/subscribe/SubscribeConsumer1.java:
--------------------------------------------------------------------------------
1 | package com.kucw.subscribe;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class SubscribeConsumer1 {
9 |
10 | @RabbitListener(queues = "SUBSCRIBE_QUEUE_1")
11 | public void listen(Student student) {
12 | System.out.println("receive message from SUBSCRIBE_QUEUE_1: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/subscribe/SubscribeConsumer2.java:
--------------------------------------------------------------------------------
1 | package com.kucw.subscribe;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class SubscribeConsumer2 {
9 |
10 | @RabbitListener(queues = "SUBSCRIBE_QUEUE_2")
11 | public void listen(Student student) {
12 | System.out.println("receive message from SUBSCRIBE_QUEUE_2: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/subscribe/SubscribeConsumer3.java:
--------------------------------------------------------------------------------
1 | package com.kucw.subscribe;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class SubscribeConsumer3 {
9 |
10 | @RabbitListener(queues = "SUBSCRIBE_QUEUE_3")
11 | public void listen(Student student) {
12 | System.out.println("receive message from SUBSCRIBE_QUEUE_3: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/subscribe/SubscribeController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.subscribe;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | public class SubscribeController {
10 |
11 | @Autowired
12 | private SubscribeProducer subscribeProducer;
13 |
14 | @RequestMapping("/subscribe/send")
15 | public String send() {
16 | subscribeProducer.send(new Student(1, "John"));
17 | return "success";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/subscribe/SubscribeProducer.java:
--------------------------------------------------------------------------------
1 | package com.kucw.subscribe;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.core.AmqpTemplate;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class SubscribeProducer {
10 |
11 | @Autowired
12 | private AmqpTemplate rabbitmqTemplate;
13 |
14 | public void send(Student student) {
15 | rabbitmqTemplate.convertAndSend("SUBSCRIBE_EXCHANGE", "", student);
16 | System.out.println("send message " + student + " to SUBSCRIBE_EXCHANGE successfully");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/worker/WorkerConsumer1.java:
--------------------------------------------------------------------------------
1 | package com.kucw.worker;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class WorkerConsumer1 {
9 |
10 | @RabbitListener(queues = "WORKER_QUEUE")
11 | public void listen(Student student) {
12 | System.out.println("WorkerConsumer1 receive message from WORKER_QUEUE: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/worker/WorkerConsumer2.java:
--------------------------------------------------------------------------------
1 | package com.kucw.worker;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class WorkerConsumer2 {
9 |
10 | @RabbitListener(queues = "WORKER_QUEUE")
11 | public void listen(Student student) {
12 | System.out.println("WorkerConsumer2 receive message from WORKER_QUEUE: " + student);
13 | }
14 | }
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/worker/WorkerController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.worker;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | public class WorkerController {
10 |
11 | @Autowired
12 | private WorkerProducer workerProducer;
13 |
14 | @RequestMapping("/worker/send")
15 | public String send() {
16 | workerProducer.send(new Student(1, "John"));
17 | workerProducer.send(new Student(2, "Amy"));
18 | workerProducer.send(new Student(3, "Bob"));
19 | workerProducer.send(new Student(4, "Mike"));
20 | workerProducer.send(new Student(5, "Sharon"));
21 | return "send 5 messages to queue successfully";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/java/com/kucw/worker/WorkerProducer.java:
--------------------------------------------------------------------------------
1 | package com.kucw.worker;
2 |
3 | import com.kucw.Student;
4 | import org.springframework.amqp.core.AmqpTemplate;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class WorkerProducer {
10 |
11 | @Autowired
12 | private AmqpTemplate rabbitmqTemplate;
13 |
14 | public void send(Student student) {
15 | rabbitmqTemplate.convertAndSend("WORKER_QUEUE", student);
16 | System.out.println("send message " + student + " to WORKER_QUEUE successfully");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-demo-rabbitmq/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.rabbitmq.host=localhost
2 | spring.rabbitmq.virtual-host=/
3 | spring.rabbitmq.username=root
4 | spring.rabbitmq.password=admin1234
--------------------------------------------------------------------------------
/spring-boot-demo-scheduler/.gitignore:
--------------------------------------------------------------------------------
1 | # kdiff3 ignore
2 | *.orig
3 |
4 | # maven ignore
5 | target/
6 |
7 | # eclipse ignore
8 | .settings/
9 | .project
10 | .classpath
11 | .springBeans
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | *.log
21 | *.cache
22 | *.diff
23 | *.patch
24 | *.tmp
25 |
26 | # system ignore
27 | .DS_Store
28 | Thumbs.db
29 |
30 | # package ignore (optional)
31 | # *.jar
32 | # *.war
33 | # *.zip
34 | # *.tar
35 | # *.tar.gz
36 | /bin/
37 |
--------------------------------------------------------------------------------
/spring-boot-demo-scheduler/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 2.2.8.RELEASE
11 |
12 |
13 |
14 | com.kucw
15 | spring-boot-demo-scheduler
16 | 0.0.1-SNAPSHOT
17 | spring-boot-demo-scheduler
18 |
19 |
20 | 1.8
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-maven-plugin
31 |
32 | true
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/spring-boot-demo-scheduler/src/main/java/com/kucw/DemoSchedulerApplication.java:
--------------------------------------------------------------------------------
1 | package com.kucw;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.scheduling.annotation.EnableScheduling;
6 |
7 | @EnableScheduling //要加上這個 annotation,@Scheduled 才會生效
8 | @SpringBootApplication
9 | public class DemoSchedulerApplication {
10 | public static void main(String[] args) {
11 | SpringApplication.run(DemoSchedulerApplication.class, args);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/spring-boot-demo-scheduler/src/main/java/com/kucw/scheduler/MyScheduler.java:
--------------------------------------------------------------------------------
1 | package com.kucw.scheduler;
2 |
3 | import org.springframework.scheduling.annotation.Scheduled;
4 | import org.springframework.stereotype.Component;
5 |
6 | @Component
7 | public class MyScheduler {
8 |
9 | @Scheduled(fixedRate = 5000) //每5秒執行一次test1()方法
10 | public void test1() {
11 | System.out.println("test1 : 每5秒執行一次");
12 | }
13 |
14 | @Scheduled(fixedDelay = 10000) //每次執行一次test2()方法後,間隔10秒再執行下一次
15 | public void test2() {
16 | System.out.println("test2 : 10秒後執行下一次");
17 | }
18 |
19 | @Scheduled(cron = "0 0 5 * * *") //每天早上5點執行一次test3()方法
20 | public void test3() {
21 | System.out.println("test3 : 每天早上5點執行一次");
22 | }
23 |
24 | @Scheduled(cron = "${my.daily}") //也可以把cron的設定值寫在application.properties裡面,再載入進來
25 | public void test4() {
26 | System.out.println("test4 : 每10分鐘執行一次");
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/spring-boot-demo-scheduler/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # 每10分鐘執行一次
2 | my.daily=0 */10 * * * *
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/.gitignore:
--------------------------------------------------------------------------------
1 | # kdiff3 ignore
2 | *.orig
3 |
4 | # maven ignore
5 | target/
6 |
7 | # eclipse ignore
8 | .settings/
9 | .project
10 | .classpath
11 | .springBeans
12 |
13 | # idea ignore
14 | .idea/
15 | *.ipr
16 | *.iml
17 | *.iws
18 |
19 | # temp ignore
20 | *.log
21 | *.cache
22 | *.diff
23 | *.patch
24 | *.tmp
25 |
26 | # system ignore
27 | .DS_Store
28 | Thumbs.db
29 |
30 | # package ignore (optional)
31 | # *.jar
32 | # *.war
33 | # *.zip
34 | # *.tar
35 | # *.tar.gz
36 | /bin/
37 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/Spring Boot with Unit Test.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kucw/spring-boot-demo/e6d8b0cfc2efc588bfbd120f46ec0de3e6a1d42b/spring-boot-demo-unit-test/Spring Boot with Unit Test.pdf
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 2.1.5.RELEASE
11 |
12 |
13 |
14 | com.kucw
15 | spring-boot-demo-unit-test
16 | 0.0.1-SNAPSHOT
17 | spring-boot-demo-unit-test
18 |
19 |
20 | 1.8
21 | UTF-8
22 | UTF-8
23 |
24 |
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-web
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-jdbc
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-test
37 | test
38 |
39 |
40 | com.h2database
41 | h2
42 | 1.4.200
43 |
44 |
45 | joda-time
46 | joda-time
47 | 2.10.5
48 |
49 |
50 | org.apache.commons
51 | commons-lang3
52 | 3.9
53 |
54 |
55 | org.apache.commons
56 | commons-collections4
57 | 4.4
58 |
59 |
60 | com.google.guava
61 | guava
62 | 28.1-jre
63 |
64 |
65 |
66 |
67 |
68 |
69 | org.springframework.boot
70 | spring-boot-maven-plugin
71 |
72 | true
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/java/com/kucw/DemoUnitTestApplication.java:
--------------------------------------------------------------------------------
1 | package com.kucw;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class DemoUnitTestApplication {
8 | public static void main(String[] args) {
9 | SpringApplication.run(DemoUnitTestApplication.class, args);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/java/com/kucw/config/DataSourceConfig.java:
--------------------------------------------------------------------------------
1 | package com.kucw.config;
2 |
3 | import org.springframework.beans.factory.annotation.Qualifier;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 | import org.springframework.boot.jdbc.DataSourceBuilder;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
9 |
10 | import javax.sql.DataSource;
11 |
12 | @Configuration
13 | public class DataSourceConfig {
14 | @Bean
15 | @ConfigurationProperties(prefix = "spring.datasource")
16 | public DataSource demoDataSource() {
17 | return DataSourceBuilder.create().build();
18 | }
19 |
20 | @Bean
21 | public NamedParameterJdbcTemplate demoJdbcTemplate(@Qualifier("demoDataSource") DataSource dataSource) {
22 | return new NamedParameterJdbcTemplate(dataSource);
23 | }
24 | }
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/java/com/kucw/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.kucw.controller;
2 |
3 | import com.kucw.dao.UserDao;
4 | import com.kucw.model.User;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.web.bind.annotation.*;
7 |
8 | import java.util.Date;
9 |
10 | @RestController
11 | public class UserController {
12 |
13 | @Autowired
14 | private UserDao userDao;
15 |
16 | @GetMapping("/user/get")
17 | public User get(@RequestParam Integer id) {
18 | return userDao.getUserById(id);
19 | }
20 |
21 | @PostMapping("/user/insert/{name}")
22 | public Integer insert(@PathVariable String name) {
23 | User user = new User();
24 | user.setName(name);
25 | user.setUpdateTime(new Date());
26 | return userDao.insertUser(user);
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/java/com/kucw/dao/UserDao.java:
--------------------------------------------------------------------------------
1 | package com.kucw.dao;
2 |
3 | import com.kucw.model.User;
4 | import org.apache.commons.collections4.CollectionUtils;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Qualifier;
7 | import org.springframework.jdbc.core.BeanPropertyRowMapper;
8 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.Collections;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | @Component
17 | public class UserDao {
18 | private static final BeanPropertyRowMapper USER_MAPPER = new BeanPropertyRowMapper<>(User.class);
19 |
20 | private static final String SQL_INSERT = "INSERT INTO user (name, update_time) VALUES (:name, :updateTime)";
21 | private static final String SQL_USER_BY_ID = "SELECT id, name, update_time FROM user WHERE id = :id";
22 |
23 | @Autowired
24 | @Qualifier("demoJdbcTemplate")
25 | private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
26 |
27 | public Integer insertUser(User user) {
28 | Map param = new HashMap<>();
29 | param.put("name", user.getName());
30 | param.put("updateTime", user.getUpdateTime());
31 | return namedParameterJdbcTemplate.update(SQL_INSERT, param);
32 | }
33 |
34 | public User getUserById(Integer id) {
35 | List userList = namedParameterJdbcTemplate.query(SQL_USER_BY_ID, Collections.singletonMap("id", id), USER_MAPPER);
36 | if (CollectionUtils.isNotEmpty(userList)) {
37 | return userList.get(0);
38 | }
39 | return null;
40 | }
41 |
42 | public void print() {
43 | System.out.println("This is user dao");
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/java/com/kucw/model/User.java:
--------------------------------------------------------------------------------
1 | package com.kucw.model;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | public class User implements Serializable {
7 | private Integer id;
8 | private String name;
9 | private Date updateTime;
10 |
11 | public User(){}
12 |
13 | public User(Integer id, String name, Date updateTime) {
14 | this.id = id;
15 | this.name = name;
16 | this.updateTime = updateTime;
17 | }
18 |
19 | public Integer getId() {
20 | return id;
21 | }
22 |
23 | public void setId(Integer id) {
24 | this.id = id;
25 | }
26 |
27 | public String getName() {
28 | return name;
29 | }
30 |
31 | public void setName(String name) {
32 | this.name = name;
33 | }
34 |
35 | public Date getUpdateTime() {
36 | return updateTime;
37 | }
38 |
39 | public void setUpdateTime(Date updateTime) {
40 | this.updateTime = updateTime;
41 | }
42 |
43 | @Override
44 | public String toString() {
45 | return "User{" +
46 | "id=" + id +
47 | ", name='" + name + '\'' +
48 | ", updateTime=" + updateTime +
49 | '}';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/java/com/kucw/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.kucw.service;
2 |
3 | import com.kucw.dao.UserDao;
4 | import com.kucw.model.User;
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Component;
8 |
9 | import java.util.Date;
10 |
11 | @Component
12 | public class UserService {
13 |
14 | @Autowired
15 | private UserDao userDao;
16 |
17 | public User getUserById(Integer id) {
18 | return userDao.getUserById(id);
19 | }
20 |
21 | public Integer insertUser(User user) {
22 | return userDao.insertUser(user);
23 | }
24 |
25 | public void print() {
26 | System.out.println("This is user service!");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | # db setting
2 | spring.datasource.driverClassName=org.h2.Driver
3 | spring.datasource.jdbcUrl=jdbc:h2:mem:testdb
4 | spring.datasource.username=sa
5 | spring.datasource.password=sa
6 |
7 | # h2 console setting
8 | spring.h2.console.enabled=true
9 | spring.h2.console.path=/h2-console
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/java/com/kucw/controller/UserControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.kucw.controller;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.http.MediaType;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 | import org.springframework.test.web.servlet.MockMvc;
11 | import org.springframework.test.web.servlet.RequestBuilder;
12 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
13 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
14 |
15 | @RunWith(SpringRunner.class)
16 | @SpringBootTest
17 | @AutoConfigureMockMvc
18 | public class UserControllerTest {
19 |
20 | @Autowired
21 | private MockMvc mockMvc;
22 |
23 | @Test
24 | public void testGetUser() throws Exception {
25 | RequestBuilder requestBuilder = MockMvcRequestBuilders
26 | .get("/user/get")
27 | .param("id", "1")
28 | .accept(MediaType.APPLICATION_JSON);
29 |
30 | mockMvc.perform(requestBuilder)
31 | .andExpect(MockMvcResultMatchers.status().isOk())
32 | .andExpect(MockMvcResultMatchers.jsonPath("id").value(1));
33 | }
34 |
35 | @Test
36 | public void testInsertUser() throws Exception {
37 | RequestBuilder requestBuilder = MockMvcRequestBuilders
38 | .post("/user/insert/{name}", "John");
39 |
40 | mockMvc.perform(requestBuilder)
41 | .andExpect(MockMvcResultMatchers.status().isOk());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/java/com/kucw/dao/UserDaoTest.java:
--------------------------------------------------------------------------------
1 | package com.kucw.dao;
2 |
3 | import com.kucw.model.User;
4 | import org.junit.Assert;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 | import org.springframework.transaction.annotation.Transactional;
11 |
12 | import java.util.Date;
13 |
14 | @RunWith(SpringRunner.class)
15 | @SpringBootTest
16 | @Transactional
17 | public class UserDaoTest {
18 |
19 | @Autowired
20 | private UserDao userDao;
21 |
22 | @Test
23 | public void insert() throws Exception {
24 | User user = new User();
25 | user.setName("test Boss");
26 | user.setUpdateTime(new Date());
27 | userDao.insertUser(user);
28 | }
29 |
30 | @Test
31 | public void getUserById() throws Exception {
32 | User user = userDao.getUserById(1);
33 | System.out.println(user.getName());
34 | Assert.assertNotNull(user);
35 | }
36 |
37 | @Test
38 | public void getUserById4() throws Exception {
39 | User user = userDao.getUserById(4);
40 | System.out.println(user.getName());
41 | Assert.assertNotNull(user);
42 | }
43 | }
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/java/com/kucw/service/UserServiceMockTest.java:
--------------------------------------------------------------------------------
1 | package com.kucw.service;
2 |
3 | import com.kucw.model.User;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.mockito.InOrder;
7 | import org.mockito.Mockito;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.boot.test.mock.mockito.MockBean;
10 | import org.springframework.test.context.junit4.SpringRunner;
11 |
12 | import java.util.Date;
13 |
14 | @RunWith(SpringRunner.class)
15 | @SpringBootTest
16 | public class UserServiceMockTest {
17 |
18 | @MockBean
19 | private UserService userService;
20 |
21 | @Test
22 | public void test() throws Exception {
23 | /**
24 | * Mockito 的語法通常是 when(object.methodName()).thenReturn(response) 表示當methodName這個方法被call時,就return response這個結果
25 | * 如果沒有先定義好 when().thenReturn(),就直接去調用該方法時,默認return null
26 | */
27 |
28 | //當使用任何int值call userService的getUserById方法時,就回傳一個名字為"I'm mockito name"的user
29 | Mockito.when(userService.getUserById(Mockito.anyInt())).thenReturn(new User(200, "I'm mockito name", new Date()));
30 |
31 | //限制只有當input的數字是3時,才會return名字為"I'm no.3"的User
32 | Mockito.when(userService.getUserById(3)).thenReturn(new User(3, "I'm no.3", new Date()));
33 |
34 | //當一個method有定義多次return值時,會從最後定義的那個when開始比對,如果參數符合的話,就返回那個when的return
35 | User user = userService.getUserById(3); //所以這裡會返回 "I'm no.3" User
36 | User user2 = userService.getUserById(5); //而這裡會返回 "I'm mockito name" User
37 |
38 | //當call userService的insertUser時,不管傳進來的User的值是什麼,都回傳100
39 | Mockito.when(userService.insertUser(Mockito.any(User.class))).thenReturn(100);
40 |
41 | Integer i = userService.insertUser(new User()); //會返回100
42 |
43 | /**
44 | * Mockito不僅能模擬方法調用返回值,也能記錄該mock對象的歷史調用記錄,可以使用verify()來檢查mock對象的某個方法是否曾被調用、或是他的方法調用順序
45 | */
46 |
47 | //檢查調用getUserById、且參數為3的次數是否為1次
48 | Mockito.verify(userService, Mockito.times(1)).getUserById(Mockito.eq(3)) ;
49 |
50 | //驗證調用順序,確保mock對象會先調用getUserById兩次with特定parameter,然後才調用insertUser
51 | InOrder inOrder = Mockito.inOrder(userService);
52 | inOrder.verify(userService).getUserById(3);
53 | inOrder.verify(userService).getUserById(5);
54 | inOrder.verify(userService).insertUser(Mockito.any(User.class));
55 |
56 | /**
57 | * 除了when().thenReturn()可以設置mock對象的方法返回值之外,也可以使用when().thenThrow()來拋出一個異常
58 | */
59 |
60 | //當調用getUserById時的參數是9時,拋出一個RuntimeException
61 | Mockito.when(userService.getUserById(9)).thenThrow(new RuntimeException("mock throw exception"));
62 |
63 | //如果方法沒有return值的話,要改用doThrow()拋出Exception
64 | //當call userService的print方法時,拋出一個Exception
65 | Mockito.doThrow(new UnsupportedOperationException("mock throw unsupported exception")).when(userService).print();
66 | }
67 | }
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/java/com/kucw/service/UserServiceSpyTest.java:
--------------------------------------------------------------------------------
1 | package com.kucw.service;
2 |
3 | import com.kucw.model.User;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.mockito.Mockito;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.boot.test.mock.mockito.SpyBean;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 |
11 | import java.util.Date;
12 |
13 | @RunWith(SpringRunner.class)
14 | @SpringBootTest
15 | public class UserServiceSpyTest {
16 |
17 | @SpyBean
18 | private UserService userService;
19 |
20 | @Test
21 | public void test() throws Exception {
22 | //雖然我們已經spy了userService,但是因為還沒有自己override任何方法的返回值,所以Spring會去用這個bean原本正常的方法
23 | //所以這裡返回的user才會是正常的John
24 | User user = userService.getUserById(1);
25 |
26 | /**
27 | * spy和mock一樣,也是使用 when(object.methodName()).thenReturn(response) 來設定方法返回值
28 | */
29 |
30 | //設定當input為3時,返回名字為"I'm no.3"的User
31 | Mockito.when(userService.getUserById(3)).thenReturn(new User(3, "I'm no.3", new Date()));
32 |
33 | //設定當input為任意數時,返回名字為"I'm any"的User
34 | Mockito.when(userService.getUserById(5)).thenReturn(new User(200, "I'm any", new Date()));
35 |
36 | //spy跟mock一樣,當一個method有定義多次return值時,也是從最後定義的那個when開始比對,如果參數符合的話,就返回那個when的doReturn,如果都沒有符合的,就call原本的bean的方法
37 | User user2 = userService.getUserById(3); //所以這裡返回的user會是 I'm any
38 | User user3 = userService.getUserById(5); //這裡返回的user也會是 I'm any
39 | User user4 = userService.getUserById(1); //這裡返回的user則是DB裡的 John
40 |
41 | //thenThrow和verifiy用法都跟mock一樣,就不展開了
42 | }
43 | }
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/resources/application.properties:
--------------------------------------------------------------------------------
1 | # db setting
2 | spring.datasource.driverClassName=org.h2.Driver
3 | spring.datasource.jdbcUrl=jdbc:h2:mem:testdb
4 | spring.datasource.username=sa
5 | spring.datasource.password=sa
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/resources/data.sql:
--------------------------------------------------------------------------------
1 | -- test record
2 | INSERT INTO user (name, update_time) VALUES ('John', current_date);
--------------------------------------------------------------------------------
/spring-boot-demo-unit-test/src/test/resources/schema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE user (
2 | id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
3 | name NVARCHAR(30) NOT NULL,
4 | update_time DATETIME NOT NULL
5 | );
--------------------------------------------------------------------------------