因爲本人的碼雲太多太亂了,因而決定一個一個的整合到一個springboot項目裏面。javascript
附上本身的項目地址https://github.com/247292980/spring-bootcss
功能html
1.spring-boot前端
2.FusionChartvue
3.thymeleafjava
4.vuemysql
5.ShardingJdbcjquery
6.mybatis-generatorios
7.微信分享受權git
8.drools
9.spring-security
10.spring-jpa
11.webjars
12.Aspect
13.drools-drt模板動態生成規則 https://www.cnblogs.com/ydymz/p/9590245.html
14.rabbitmq https://www.cnblogs.com/ydymz/p/9617905.html
15.zookeeper https://www.cnblogs.com/ydymz/p/9626653.html
16.mongodb https://www.cnblogs.com/ydymz/p/9814875.html
17.mysql的存儲過程 https://www.cnblogs.com/ydymz/p/9828707.html
18.前端懶加載 https://www.cnblogs.com/ydymz/p/9829150.html
19.netty https://www.cnblogs.com/ydymz/p/9849879.html
20.postgresql https://www.cnblogs.com/ydymz/p/9858795.html
21.樹的遍歷 https://www.cnblogs.com/ydymz/p/10076891.html
第一個就是springboot的helloworld了,具體不說什麼,就是快捷開發。
寫這個的速度限制是我電腦加載的速度!!
FusionCharts.js 是一個很老的圖表插件。老到在咱們要使用的時候,不只要導入js代碼,還要導入你要的對應swf模板文件,導完了還要你按他們的規矩寫相應的數據格式,簡直是反程序員啊。
這是用xml導入數據的格式
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width"> <title>3D雙柱圖xml</title> <script type="text/javascript" src="/Charts/jquery-1.7.2.js"></script> <script type="text/javascript" src="/Charts/FusionCharts.js"></script> <script type="text/javascript"> $(function () { FusionCharts.debugMode.enabled(true); FusionCharts.debugMode.outputTo(function () { console.log(arguments); }); var myChart = new FusionCharts("../Charts/MSColumn3D.swf", "54356345", "100%", "520", "0"); myChart.setXMLUrl("doubleColumn3D.xml"); // myChart.setXMLUrl("/data/doubleColumn3D.xml"); myChart.render("chart"); console.log(myChart); }); </script> </head> <body> <div id="chart"></div> </body> </html>
這是用json導入數據
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width"> <title>3D雙柱圖json</title> <script type="text/javascript" src="/Charts/jquery-1.7.2.js"></script> <script type="text/javascript" src="/Charts/FusionCharts.js"></script> </head> <body> <script type="text/javascript"> $(function () { var column3D = new FusionCharts("/Charts/MSColumn3D.swf", "myChartId", "100%", "520", "0"); var jsonData = { "chart": { "caption": "Sales in Last Two Years", "subcaption": "Quarter-wise comparison", "xaxisname": "Quarter", "yaxisname": "Sales (In USD)", "palette": "2", "numberprefix": "$", "yaxismaxvalue": "32000", "numdivlines": "3", }, "categories": [ { "category": [ { "label": "Q1" }, { "label": "Q2" }, { "label": "Q3" }, { "label": "Q4" } ] } ], "dataset": [ { "seriesname": "Previous Year", "data": [ { "value": "10000" }, { "value": "11500" }, { "value": "12500" }, { "value": "15000" } ] }, { "seriesname": "Current Year", "data": [ { "value": "25400" }, { "value": "29800" }, { "value": "21800" }, { "value": "26800" } ] } ] }; column3D.setJSONData(jsonData); column3D.render("doubleColumn3DChart"); console.log(column3D); }); </script> <div id="doubleColumn3DChart"></div> </body> </html>
swf,js父目錄必定是Charts。
xml父目錄必定是data。
瀏覽器必定要裝flash player。
就算裝了flash player瀏覽器,如今都很良心的默認禁止,必需要網頁申請權限 或者 瀏覽器本身打開。
當年寫的時候,還沒出現後兩個,卻是如今重現了這技術的時候,才發現這bug,感受FusionCharts應該過期了。
畢竟大數據這麼久了,相應的數據顯示已經很智能,像FusionCharts這種簡直能放棄就放棄吧。
thymeleaf其實在以前的幾節也有用上thymeleaf了。
可是,我我的是堅決的先後分離的擁護者,惋惜工做基本都是往全棧工程師培養的。
公司的測試用的jenkins等測試工具是後端搭的,測試文檔仍是咱們寫的,也就是基本測不出什麼bug的...
前端基本上只負責css的編寫和html,數據填充和ajax都要咱們本身填...
安卓和ios的同事也是大爺,9點鐘反映問題,下午纔回復的,甩鍋一個比一個塊,態度一個比一個端正...
至於th標籤,用是能夠用的。可是,不建議掌握也不打算講。
個人建議是後端開發與其熟悉一鍾限制性特高的thymeleaf的標籤,還不如老老實實的不斷使用h5原生代碼,jq,vue這三個知識點,而後根據這三個猜想其餘前端框架怎麼用...(這裏吐槽一下,公司前端不會vue...)
pom.xml
<dependencies>
<!--boot啓動,其實後面有-starter的包,一般spring都寫好了一些默認配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--web啓動,默認用tomcat端口8080,若不導入這個包,將是普通的boot啓動,死活啓動不了-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--thymeleaf包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--web包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
</dependencies>
application.properties
######################################################## ###THYMELEAF (ThymeleafAutoConfiguration) ######################################################## #spring.thymeleaf.prefix=classpath:/templates/ #spring.thymeleaf.suffix=.html #spring.thymeleaf.mode=HTML5 #spring.thymeleaf.encoding=UTF-8 ## ;charset=<encoding> is added #spring.thymeleaf.content-type=text/html #set to false for hot refresh #理論上已經不須要以上的配置了,只要設置thymeleaf的緩存不保存便可 spring.thymeleaf.cache=false
額,這玩意在我看起來,搭建速度上限也是電腦響應速度的....
可是,初學者仍是有一些搭建失敗的狀況。
我就總結一下
導入thymeleaf卻啓動不了或者啓動的不是tomcat,沒導入spring-boot-starter-xxx的相關包。
不瞭解thymeleaf的默認配置,能夠看下application.properties,spring.thymeleaf.cache=false這個配置在開發要false,正式要true,js的變動應用版本號控制。
根據配置明顯html文件應該放在resourse文件夾下的templates文件夾裏面(idea的狀況,eclipse的話不清楚classpath是什麼文件夾,不過同樣是classpath下的templates文件夾)
springMVC跳轉的時候不用寫後綴,這裏和跳到jsp有很大不一樣,剛剛從jsp來thymeleaf的十有八九犯這個錯誤,至於爲何配置文件裏面註釋掉的部分有寫。
當我第一次碰到vue,理解了mvvc以後,曾經以爲這是一個很好的東西,方便前端方便後端。
可是隨着工做時間加長,我忽然醒悟我一個後端被忽悠去學了一個前端的玩意就不說了,我所在的公司就沒一個前端使用過vue。(現公司1w+員工,不知道算不算大公司)
因此我對這玩意實際上是持有很大的偏見的。
吹得高大上,可是前端不用。
說是前端框架,頗有多是忽悠人去當全棧。
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/js/vue.min.js"></script> </head> <style> .class1 { background: #444; color: #eee; } </style> <body> <div id="app"> <p>{{ message }}</p> <input v-model="message"/> <br><br> <div v-html="htmlMsg"></div> <br><br> <label for="ck1">修改顏色</label> <input id="ck1" type="checkbox" v-model="isClass1"> <div v-bind:class="{class1: isClass1}"> directiva v-bind:class </div> <!--雙向綁定--> <!--vue的data綁定的數據,被其全局代理,即r1值變爲true時,isClass1也等於true--> <br><br> <!--<button v-on:click="counter += 1">點一下</button>--> <button v-on:click="count">點一下</button> <p>這個按鈕被點擊了 {{ counter }} 次。</p> <br><br> </div> <!-- vue的JavaScript 代碼須要放在尾部(指定的HTML元素以後) --> <script src="/js/mvvc.js"></script> </body> </html>
new Vue({ el: '#app', data: { message: 'mvvc hi!', htmlMsg: '<h1>hi!</h1>', isClass1: false, counter: 0 }, methods: { count: function (event) { // `this` 在方法裏指當前 Vue 實例 alert('count 1!'); this.counter += 1; // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName); } } } })
v-if 某元素是否顯示
v-model 修改元素的值
v-bind 修改元素的屬性,style
v-html 修改html元素
v-on 綁定方法
基本上這五個是很是的經常使用了,v-bind或許是裏面最小用到的,寫上他的主要緣由就是知道這五個就能完成大多數騷操做了。
另外,他還有各類縮寫之類的,雖然看起來很方便。
可是縮寫的格式不統一,不像標準的v-xx那樣讓人一看就知道是什麼包出來的,混合使用時強迫症簡直受不了,就像一地人蔘裏面有幾顆土豆。
因此我是不建議使用縮寫,可讀性太差。
使用vue.js的時候,new Vue必須放到html頁面的下面,必須在全部html元素渲染以後,才能生效。
由此引入了mvvc的概念的話,業務代碼必須在全部元素渲染以後才生效。那麼,靜態資源放在body前,業務的js代碼放在最後面。
固然,強迫症如我就是css放在body上面,js放在下面,js中的業務代碼在最下面。
還有一些v-for的,可是我實際用的時候仍是用js的for循環生成好html文件,而後jq插進去,簡單粗暴。。。
固然,要是不僅是展現數據,還有操做數據的話,那麼v-for裏面用上v-model是很是好的。(也就各類後臺系統有這種需求了)
分庫分表這東西之前實現,一直是操做數據前,根據id來判斷的。直到遇到ShardingJdbc這個第三方包,真的是代碼簡單易懂的表明啊!
ModuloTableShardingAlgorithm.java
public class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Integer> { /** * select * from t_order from t_order where order_id = 11 * └── SELECT * FROM t_order_1 WHERE order_id = 11 * select * from t_order from t_order where order_id = 44 * └── SELECT * FROM t_order_0 WHERE order_id = 44 */ @Override public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) { for (String each : availableTargetNames) { if (each.endsWith(shardingValue.getValue() % 2 + "")) { return each; } } throw new IllegalArgumentException(); } /** * select * from t_order from t_order where order_id in (11,44) * ├── SELECT * FROM t_order_0 WHERE order_id IN (11,44) * └── SELECT * FROM t_order_1 WHERE order_id IN (11,44) * select * from t_order from t_order where order_id in (11,13,15) * └── SELECT * FROM t_order_1 WHERE order_id IN (11,13,15) * select * from t_order from t_order where order_id in (22,24,26) * └──SELECT * FROM t_order_0 WHERE order_id IN (22,24,26) */ @Override public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) { Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size()); for (Integer value : shardingValue.getValues()) { for (String tableName : availableTargetNames) { if (tableName.endsWith(value % 2 + "")) { result.add(tableName); } } } return result; } /** * select * from t_order from t_order where order_id between 10 and 20 * ├── SELECT * FROM t_order_0 WHERE order_id BETWEEN 10 AND 20 * └── SELECT * FROM t_order_1 WHERE order_id BETWEEN 10 AND 20 */ @Override public Collection<String> doBetweenSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) { Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size()); Range<Integer> range = (Range<Integer>) shardingValue.getValueRange(); for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) { for (String tableName : availableTargetNames) { if (tableName.endsWith(i % 2 + "")) { result.add(tableName); } } } return result; } }
ModuloDataBaseShardingAlgorithm.java
public class ModuloDataBaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Integer> { @Override public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) { for (String name : availableTargetNames) { /*分紅兩個庫*/ if (name.endsWith(shardingValue.getValue() % 2 + "")) { return name; } } throw new IllegalArgumentException(); } @Override public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) { Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size()); for (Integer value : shardingValue.getValues()) { for (String name : availableTargetNames) { if (name.endsWith(value % 2 + "")) { result.add(name); } } } return result; } @Override public Collection<String> doBetweenSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) { Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size()); Range<Integer> range = (Range<Integer>) shardingValue.getValueRange(); for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) { for (String name : availableTargetNames) { if (name.endsWith(i % 2 + "")) { result.add(name); } } } return result; } }
ShardingJdbc.java
public class ShardingJdbc { /** * main方法 */ public static void main(String[] args) { Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>(2); dataSourceMap.put("sharding_0", createDataSource("sharding_0")); dataSourceMap.put("sharding_1", createDataSource("sharding_1")); DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap); //分表分庫的表,第一個參數是邏輯表名,第二個是實際表名,第三個是實際庫 TableRule orderTableRule = new TableRule("t_order", Arrays.asList("t_order_0", "t_order_1"), dataSourceRule); TableRule orderItemTableRule = new TableRule("t_order_item", Arrays.asList("t_order_item_0", "t_order_item_1"), dataSourceRule); /** * DatabaseShardingStrategy 分庫策略 * 參數一:根據哪一個字段分庫 * 參數二:分庫路由函數 * * TableShardingStrategy 分表策略 * 參數一:根據哪一個字段分表 * 參數二:分表路由函數 * * user_id選擇哪一個庫 * order_id選擇那個表 * * ModuloDataBaseShardingAlgorithm * ModuloTableShardingAlgorithm * 被2整除是0,反之是1 * */ ShardingRule shardingRule = new ShardingRule(dataSourceRule, Arrays.asList(orderTableRule, orderItemTableRule) , Arrays.asList(new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule))) , new DatabaseShardingStrategy("user_id", new ModuloDataBaseShardingAlgorithm()) , new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())); DataSource dataSource = new ShardingDataSource(shardingRule); String sql = "SELECT i.* FROM t_order o JOIN t_order_item i " + "ON o.order_id=i.order_id " + "WHERE o.user_id= ? AND o.order_id = ?"; try { Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql); // preparedStatement.setInt(1, 10); // preparedStatement.setInt(2, 1001); // 先根據分庫規則去了sharding_1 // o.user_id=11 preparedStatement.setInt(1, 11); // 再根據分表規則去了t_order_0,t_order_item_0 // o.order_id=1000 preparedStatement.setInt(2, 1000); ResultSet result = preparedStatement.executeQuery(); while (result.next()) { System.out.println("1--------" + result.getInt(1)); System.out.println("2--------" + result.getInt(2)); System.out.println("3--------" + result.getInt(3)); } } catch (SQLException e) { e.printStackTrace(); } } /** * @param dataSourceName * @return dataSource * @DESCRIPTION 建立數據源 */ private static DataSource createDataSource(String dataSourceName) { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName)); dataSource.setUsername("root"); dataSource.setPassword("123456789"); return dataSource; } }
sql語句
#實驗數據 CREATE TABLE IF NOT EXISTS t_order_0 ( order_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (order_id) ); CREATE TABLE IF NOT EXISTS t_order_item_0 ( item_id INT NOT NULL, order_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (item_id) ); CREATE TABLE IF NOT EXISTS t_order_1 ( order_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (order_id) ); CREATE TABLE IF NOT EXISTS t_order_item_1 ( item_id INT NOT NULL, order_id INT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (item_id) ); INSERT INTO t_order_1 VALUES ('1001', '10'); INSERT INTO t_order_item_0 VALUES ('0', '1001', '10'); INSERT INTO t_order_item_0 VALUES ('1', '1000', '11'); INSERT INTO t_order_item_0 VALUES ('2', '1001', '10'); INSERT INTO t_order_0 VALUES ('1000', '11'); INSERT INTO t_order_item_1 VALUES ('0', '1000', '11'); INSERT INTO t_order_item_1 VALUES ('1', '1000', '11');
沒什麼好說的代碼註釋裏面已經說得很清楚了。
沒什麼好說的,根據數據庫的數據結構反向生成mybatis的代碼。具體注意點,代碼註釋也說的很清楚了。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--mysql 鏈接數據庫jar 這裏選擇本身本地位置-->
<!--理論上只要不改idea默認maven的設置,基本不會變,但我改了-->
<!--<classPathEntry location="D:/GitProjects/mybatis-generator/src/main/resources/mysql-connector-java-5.1.44.jar" />-->
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自動生成的註釋 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--數據庫鏈接的信息:驅動類、鏈接地址、用戶名、密碼 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/webmanager?characterEncoding=UTF-8"
userId="root" password="123456789">
</jdbcConnection>
<!-- 默認false,把JDBC DECIMAL 和 NUMERIC 類型解析爲 Integer,爲 true時把JDBC DECIMAL 和
NUMERIC 類型解析爲java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- targetProject:生成PO類的位置 targetProject的文件夾必須存在 targetPackage就不必定-->
<javaModelGenerator targetPackage="com.lgp.domain" targetProject="src/main/java">
<!-- enableSubPackages:是否讓schema做爲包的後綴 -->
<property name="enableSubPackages" value="false"/>
<!-- 從數據庫返回的值被清理先後的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置
若是maven工程只是單獨的一個工程,targetProject="src/main/java"
若果maven工程是分模塊的工程,targetProject="所屬模塊的名稱",例如:
targetProject="ecps-manager-mapper",下同-->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources/static">
<!-- enableSubPackages:是否讓schema做爲包的後綴 -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.lgp.mapper" targetProject="src/main/java">
<!-- enableSubPackages:是否讓schema做爲包的後綴 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- 指定數據庫表 -->
<!--<table schema="" tableName="one"></table>-->
<!--可是仍是通殺好!-->
<table schema="" tableName="%"></table>
</context>
</generatorConfiguration>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lgp</groupId> <artifactId>mybatis-generator</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>mybatis-generator</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.0</version> </dependency> </dependencies> <build> <!--<!–把xml放入編譯環境內 eclipse配置–>--> <!--<resources>--> <!--<resource>--> <!--<directory>src/main/java</directory>--> <!--<includes>--> <!--<include>**/*.xml</include>--> <!--</includes>--> <!--<filtering>true</filtering>--> <!--</resource>--> <!--</resources>--> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <!--配置要用的驅動--> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.44</version> </dependency> </dependencies> <configuration> <!--配置文件的路徑--> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build> </project>
關於微信分享和受權,我是以爲代碼是不重要的,主要是思路。(主要是沒有企業級公衆號,並且也不能暴露公司的出來)
爲何呢?
由於微信的坑爹邏輯能看代碼看出來的話,只能說是天生的程序員。
微信通用的坑就是必定是https!
可是你用http仍是在本身的機子上能搞出本身想要的結果,可是一對外就呵呵了。
因此,爲了排除這種狀況,你必須使用微信的開發者工具!
能夠像我代碼那樣什麼都不寫,這樣的話,仍是能在電腦微信那裏發出正確的分享連接出來的,23333
因此記得用微信開發者工具!
還要注意的是,微信公衆號要開分享的功能,並且微信開發者工具開發必須拉近公衆號開發人員裏面。
邏輯來講,只要對着微信那個api填基本不會出錯。
受權就要細說了。
去微信的受權網站-跳到公司的受權服務器-獲取到code
正常人是這樣理解的吧,可是是錯的!
由於獲取的code是拼接在url裏面,公司的受權服務器還要加個重定向頁面的參數,你才能在該頁面得到code。
這個方法應該是通用的,不該該每一次分享的地方落地頁都同樣。
得到code
微信的受權網站-跳到公司的受權服務器-跳到重定向參數指向的網址-獲取到code
獲取了code以後,要根據code獲取access_token
這個就簡單了,直接走微信的api就好,還附帶各類信息
注意的是
這個東西有個時限,7200s,兩小時。
能夠的話直接用剛剛得到的信息裏面的refresh_token,刷新時限至30天。
方法也是直接走微信api就能夠了。
建議看一下,或者直接去碼雲拷貝源碼,或者直接不看...
package com.lgp.wechatshare.handle; import com.fasterxml.jackson.databind.ObjectMapper; import com.lgp.wechatshare.constant.WeiXinInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; /** * @AUTHOR lgp * @DATE 2018/4/16 21:27 * @DESCRIPTION **/ @Component public class WeiXinHandler { private static Logger logger = LoggerFactory.getLogger(WeiXinHandler.class); @Autowired private RestTemplate restTemplate; @Autowired private WeiXinInfo weiXinInfo; /** * 生成用於獲取access_token的Code的Url * * @param redirectUrl * @return */ public String getRequestCodeUrl(String redirectUrl, String scope) { return String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect", weiXinInfo.getAppID(), redirectUrl, scope, "code"); } /** * 獲取請求用戶信息的access_token * * @param code * @return { " * access_token":"ACCESS_TOKEN", * "expires_in":7200, * "refresh_token":"REFRESH_TOKEN", * "openid":"OPENID", * "scope":"SCOPE" * } */ public Map<String, String> getWeiXinAccessInfo(String code) throws Exception { String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", weiXinInfo.getAppID(), weiXinInfo.getAppSecret(), code); logger.info("WeiXinClient.getWeiXinAccessInfo.url: {}", url); try { String result = restTemplate.getForObject(url, String.class); logger.info("WeiXinClient.getWeiXinAccessInfo.result: {}", result); ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(result, Map.class); } catch (NullPointerException e) { throw new NullPointerException("code 已被使用" + code); } catch (Exception e) { throw new Exception(e.getMessage()); } } public String getWeiXinOpenId(String code) throws Exception { Map<String, String> data = getWeiXinAccessInfo(code); return data.get("openid"); } public String getAccessToken(String code) throws Exception { Map<String, String> data = getWeiXinAccessInfo(code); return data.get("access_token"); } /** * 獲取用戶信息 * grantType 默認爲refresh_token * { * "access_token":"ACCESS_TOKEN", * "expires_in":7200, * "refresh_token":"REFRESH_TOKEN", * "openid":"OPENID", * "scope":"SCOPE" * } * 返回的新token於舊token不一樣 */ public Map<String, String> refreshAcessToken(String accessToken, String openId, String grantType) throws Exception { String url = "ttps://api.weixin.qq.com/sns/oauth2/refresh_token?appid==" + openId + "&grant_type=" + grantType + "&refresh_token=" + accessToken; logger.info("WeiXinClient.refreshAcessToken.url: {}", url); try { String result = restTemplate.getForObject(url, String.class); logger.info("WeiXinClient.refreshAcessToken.result: {}", result); ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(result, Map.class); } catch (NullPointerException e) { throw new NullPointerException("accessToken和openId已通過期"); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * 獲取用戶信息 * { * "openid":" OPENID", * " nickname": NICKNAME, * "sex":"1", * "province":"PROVINCE" * "city":"CITY", * "country":"COUNTRY", * "headimgurl": "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46", * "privilege":[ "PRIVILEGE1" "PRIVILEGE2" ], * "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" * } */ public Map<String, String> getUserInfo(String accessToken, String openId) throws Exception { String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN"; logger.info("WeiXinClient.getUserInfo.url: {}", url); try { String result = restTemplate.getForObject(url, String.class); logger.info("WeiXinClient.getUserInfo.result: {}", result); ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(result, Map.class); } catch (NullPointerException e) { throw new NullPointerException("accessToken和openId已通過期"); } catch (Exception e) { throw new Exception(e.getMessage()); } } /** * 檢驗受權憑證 */ public Boolean auth(String accessToken, String openId) throws Exception { Map<String, String> data = new HashMap(); String url = "https://api.weixin.qq.com/sns/auth?access_token=" + accessToken + "&openid=" + openId; logger.info("WeiXinClient.auth.url: {}", url); try { String result = restTemplate.getForObject(url, String.class); logger.info("WeiXinClient.auth.result: {}", result); ObjectMapper mapper = new ObjectMapper(); data = mapper.readValue(result, Map.class); if ("ok".equals(data.get("errmsg"))) { return true; } return false; } catch (NullPointerException e) { throw new NullPointerException("accessToken和openId已通過期"); } catch (Exception e) { throw new Exception(e.getMessage()); } } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>分享</title> </head> <body> <p>share to your friends</p> </body> <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script> wx.config({ debug: false, //調式模式,設置爲ture後會直接在網頁上彈出調試信息,用於排查問題 appId: '', timestamp:"", nonceStr: '', signature: '', jsApiList: [ //須要使用的網頁服務接口 // 'checkJsApi', //判斷當前客戶端版本是否支持指定JS接口 // 'onMenuShareTimeline', //分享給朋友圈 'onMenuShareAppMessage', //分享到好友 // 'onMenuShareQQ', //分享到QQ // 'onMenuShareWeibo',//分享到微博 ' ] }); wx.ready(function () { //ready函數用於調用API,若是你的網頁在加載後就須要自定義分享和回調功能,須要在此調用分享函數。 // 若是是微信遊戲結束後,須要點擊按鈕觸發獲得分值後分享,這裏就不須要調用API了,能夠在按鈕上綁定事件直接調用。 // 所以,微信遊戲因爲大多須要用戶先觸發獲取分值,此處請不要填寫以下所示的分享API wx.onMenuShareAppMessage({ //例如分享到朋友圈的API title: '分享標題', // 分享標題 desc: '分享描述', // 分享描述 link: 'www.baidu.com', // 分享連接 imgUrl: '', // 分享圖標 success: function () { // 用戶確認分享後執行的回調函數 console.log("share sucess"); }, cancel: function () { // 用戶取消分享後執行的回調函數 } }); }); wx.error(function (res) { alert(res.errMsg); //打印錯誤消息。及把 debug:false,設置爲debug:ture就能夠直接在網頁上看到彈出的錯誤提示 }); </script> <br/> </html>
drools是一個規則引擎,是用來作人工智能的。看起來是否是功能很強大?是否是很想學?
在這裏我就不指條歪路給你了,不要試圖從百度學習drools,直接從drools的項目裏面學。
由於drools第一批使用的人沒有什麼開源精神,他的教程什麼的都是要付錢的,付錢的就算了,他們的教程還作不到真正的與時俱進。
由於他們公司作不到與時俱進的使用drools,越新的功能,他的代碼充滿了不切實際的味道。
充滿了落後的設計,落後的知識點,甚至會說新人就先從低版本學起,他好收兩次錢。
可是,當程序員沒多久的仍是交一下這個智商稅吧!
由於我推薦的方法不適合大多數人。
直接看官方例子,痛並快樂的啃吧!
https://github.com/kiegroup/drools/tree/master/drools-examples
這個我建議各位直接去我碼雲那裏看,由於知識點太散了。
https://gitee.com/a247292980/springBoot/tree/master/drools
並且個人demo裏面也沒有業務上drools實際應用的代碼,我只是搭了個基本環境和幾種數據導入到drl文件的方法。
spring-security依然是一個很好學習的框架(只要你會看源碼,或者官方的例子 https://springcloud.cc/spring-security-zhcn.html 進入這個頁面 ctrl+f 樣品和指南)
可是,公司單點登陸的框架仍是選擇了本身寫網關的控制,2333(我也以爲沒作錯畢竟涉及到用戶信息,可是我的對spring-security仍是頗有好感的)
瞭解spring-security最大的收穫是讓你作單點登陸等權限控制功能的思路獲得很大的擴展。
此次我沒有使用mybatis而是用了spring-data-jpa,而後就能不用發sql語句了!開頭是這麼想的,可是後面發現,仍是要給些測試數據的insert。
例子中主要實現了網址攔截和權限控制。基本的講解都在代碼註釋裏。
後端網址攔截主要代碼
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 1.首先當咱們要自定義Spring Security的時候咱們須要繼承自WebSecurityConfigurerAdapter來完成,相關配置重寫對應 方法便可。 * 2.咱們在這裏註冊CustomUserService的Bean,而後經過重寫configure方法添加咱們自定義的認證方式。 */ @Bean UserDetailsService customUserService() { return new CustomUserService(); } @Override protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(customUserService()); } /** * 3.在configure(HttpSecurity http)方法中,咱們設置了登陸頁面,並且登陸頁面任何人均可以訪問,而後設置了登陸失敗地址,也設置了註銷請求,註銷請求也是任何人均可以訪問的。 * 4.permitAll表示該請求任何人均可以訪問,.anyRequest().authenticated(),表示其餘的請求都必需要有權限認證。 * 5.這裏咱們能夠經過匹配器來匹配路徑,好比antMatchers方法,假設我要管理員才能夠訪問admin文件夾下的內容,我能夠這樣來寫:. * antMatchers("/admin/**").hasRole("ROLE_ADMIN"),也 * 能夠設置admin文件夾下的文件能夠有多個角色來訪問,寫法以下: * antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER") * 6.能夠經過hasIpAddress來指定某一個ip能夠訪問該資源,假設只容許訪問ip爲210.210.210.210的請求獲取admin下的資源,寫法以下. * antMatchers("/admin/**").hasIpAddress("210.210.210.210") */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.authorizeRequests() // 留意代碼順序 // 首頁(不須要登錄就能訪問的頁面 .antMatchers("/index").permitAll() .anyRequest().authenticated() // 須要登錄才能訪問的頁面 .and().formLogin().loginPage("/login").defaultSuccessUrl("/index").failureUrl("/login?error").permitAll() .and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll() .and().rememberMe().tokenValiditySeconds(60 * 60 * 24 * 7).key("kkkkkkkk"); } }
前端權限控制主要代碼
<div class="starter-template"> <h1 th:text="${msg.title}"></h1> <p class="bg-primary" th:text="${msg.content}"></p> <div sec:authorize="hasRole('ROLE_ADMIN')"> <p class="bg-info" th:text="${msg.extraInfo}"></p> </div> <div sec:authorize="hasRole('ROLE_USER')"> <p class="bg-info">無更多顯示信息</p> </div> <form th:action="@{/logout}" method="post"> <input type="submit" class="btn btn-primary" value="註銷"/> </form> </div>
spring-jpa其實和mybatis差很少,可是spring-jpa比它更規範,因此一些自定義的複雜的sql語句,mybatis執行的更好。
可是,其實spring-jpa也支持這種想法,歸根到底,只能說mybatis已經佔領了市場了。
抽出最小的配置代碼出來,方便理解。
其實我挺喜歡這樣的,先搞個hello world框架,剩下的本身折騰。
在我這個代碼裏面,有我理解多對一,一對多的測試,雖然都已經註釋了,有興趣的本身試試,記得要刪庫,看新的表的數據結構。
@Entity public class One { @Id @GeneratedValue private Long id; private Long name; // @OneToOne(cascade = CascadeType.ALL) // private Two two; // @OneToMany // private Set<Two> two; // @ManyToMany // private Set<Two> two; }
@Entity public class Two { @Id @GeneratedValue private Long id; private Long name; @ManyToOne private One one; }
一個管理靜態資源文件的包,額,離先後端分離愈來愈遠了啊。。。。。
寫了基本配置和正常配製,正常配置就是忽略版本號的那種。
其實他還有個能力,能夠給一些index.js這些業務js加版本號index.js?v=1這種,可是這樣搞的話就真的離先後端分離愈來愈遠了啊。。。
老實說,老大有點心動。可是,這功能雞肋啊,由於咱們不會吧業務代碼deploy到maven倉庫裏面,因此這功能真的233333
/** * Created by IntelliJ IDEA. * User: a247292980 * Date: 2017/08/14 * <p> * 處理WebJars,無視版本號,使用帶版本號的方法可註釋此類 **/ @Controller public class WebJarController { private final WebJarAssetLocator assetLocator = new WebJarAssetLocator(); /** * RequestMapping的地址是固定的 */ @ResponseBody @RequestMapping("/webjarslocator/{webjar}/**") public ResponseEntity locateWebjarAsset(@PathVariable String webjar, HttpServletRequest request) { try { String mvcPrefix = "/webjarslocator/" + webjar + "/"; String mvcPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); String fullPath = assetLocator.getFullPath(webjar, mvcPath.substring(mvcPrefix.length())); return new ResponseEntity(new ClassPathResource(fullPath), HttpStatus.OK); } catch (Exception e) { return new ResponseEntity(HttpStatus.NOT_FOUND); } } }
<head> <!--<script src="/webjars/jquery/3.1.1/jquery.min.js"></script>--> <script src="/webjars/jquery/jquery.min.js"></script> <!--<script src="/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js"></script>--> <script src="/webjars/bootstrap/js/bootstrap.min.js"></script> <title>demo.html Demo</title> <!--<link rel="stylesheet" href="/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css" />--> <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css" /> </head>
很強的一個功能面向切面編程,看起來能開發成各類各樣的工具,不過我喜歡直接開發成日誌攔截器,那就少寫了不少日誌啦。
主要有三個,一個是方法切面,一個是註釋切面,還有一個是我用的根據註釋切面寫的日誌攔截,基本講解我卸載代碼裏面了,強烈建議動手跑一下我寫的日誌攔截。
package com.lgp.aop.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * @AUTHOR lgp * @DATE 2018/8/3 17:12 * @DESCRIPTION **/ @Aspect @Component public class AnnotationAspect { protected org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass()); /** * @Aspect 做用是把當前類標識爲一個切面供容器讀取 * @Before 標識一個前置加強方法,至關於BeforeAdvice的功能 * @AfterReturning 後置加強,至關於AfterReturningAdvice,方法退出時執行 * @AfterThrowing 異常拋出加強,至關於ThrowsAdvice * @After final加強,無論是拋出異常或者正常退出都會執行 * @Around 環繞加強,至關於MethodInterceptor * * 除了@Around外,每一個方法裏均可以加或者不加參數JoinPoint, * 若是有用JoinPoint的地方就加,不加也能夠,JoinPoint裏包含了類名、被切面的方法名,參數等屬性,可供讀取使用。 * @Around參數必須爲ProceedingJoinPoint pjp.proceed相應於執行被切面的方法。 * @AfterReturning方法裏, 能夠加returning = 「XXX」,XXX即爲在controller裏方法的返回值。 * @AfterThrowing方法裏, 能夠加throwing = "XXX",供讀取異常信息,throwing = "ex" */ @Pointcut(value = "@annotation(com.lgp.aop.aop.Log)") public void log() { } @Before("log()") public void deBefore(JoinPoint joinPoint) throws Throwable { System.out.println("annotation before"); } @Around("@annotation(log)") public Object around(ProceedingJoinPoint pjp, Log log) { //獲取註解裏的值 System.out.println("annotation around:" + log.description()); try { return pjp.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); return null; } } }
package com.lgp.aop.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; /** * Created by IntelliJ IDEA. * User: a247292980 * Date: 2017/08/14 * <p> * 方法的aop */ @Aspect @Component public class FunctionAspect { protected org.slf4j.Logger logger = LoggerFactory.getLogger(FunctionAspect.class); /** * execution函數 * 用於匹配方法執行的鏈接點,語法爲:execution(方法修飾符(可選) 返回類型 方法名 參數 異常模式(可選)) * 參數部分容許使用通配符: * * 匹配任意字符,但只能匹配一個元素 * .. 匹配任意字符,能夠匹配任意多個元素,表示類時,必須和*聯合使用 * + 必須跟在類名後面,如Horseman+,表示類自己和繼承或擴展指定類的全部類 * 除了execution(),Spring中還支持其餘多個函數,這裏列出名稱和簡單介紹,以方便根據須要進行更詳細的查詢 * @annotation() 表示標註了指定註解的目標類方法 * 例如 @annotation(org.springframework.transaction.annotation.Transactional) 表示標註了@Transactional的方法 * args()經過目標類方法的參數類型指定切點 例如 args(String) 表示有且僅有一個String型參數的方法 * @args() 經過目標類參數的對象類型是否標註了指定註解指定切點 如 @args(org.springframework.stereotype.Service) 表示有且僅有一個標註了@Service的類參數的方法 * within()經過類名指定切點 如 with(examples.chap03.Horseman) 表示Horseman的全部方法 * @within() 匹配標註了指定註解的類及其全部子類 如 @within(org.springframework.stereotype.Service) 給Horseman加上@Service標註,則Horseman和Elephantman 的全部方法都匹配 * target()經過類名指定,同時包含全部子類 如 target(examples.chap03.Horseman) 且Elephantman extends Horseman,則兩個類的全部方法都匹配 * @target() 全部標註了指定註解的類 如 @target(org.springframework.stereotype.Service) 表示全部標註了@Service的類的全部方法 * this() 大部分時候和target()相同,區別是this是在運行時生成代理類後,才判斷代理類與指定的對象類型是否匹配 * */ /** * 定義有一個切入點,範圍爲service包下的類 */ @Pointcut("execution(public * com.lgp.aop.service.*.*(..))") public void service() { } @Around("service()") public void doAround(JoinPoint joinPoint) throws Throwable { // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 記錄下請求內容 System.out.println("URL : " + request.getRequestURL().toString()); System.out.println("HTTP_METHOD : " + request.getMethod()); System.out.println("IP : " + request.getRemoteAddr()); System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs())); } }
基本無坑,可是我仍是傻了一下,由於這玩意用了幾回,在抽取出來的時候,居然忘了導spring-boot配aop的啓動配置,pom.xml少了一個依賴,而折騰了半個多小時。
在這裏,感謝spring-cloud的羣友幫我確認個人代碼是正確的....