spring aop 样例
基于注解的AOP 方式
1.加入jar包
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.3.jar
spring-aop-4.1.0.RELEASE.jar
spring-aspects-4.1.0.RELEASE.jar
spring-beans-4.1.0.RELEASE.jar
spring-context-4.1.0.RELEASE.jar
spring-core-4.1.0.RELEASE.jar
spring-expression-4.1.0.RELEASE.jar
2.在配置文件中加入AOP的命名空间
3.基于注解的方式
①在配置文件中加入如下配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
②把横切关注点的代码抽象到切面的类中
切面首先是一个IOC中的bean,即加入@Component注解
切面还需要加入@Aspect注解
③在类中声明各种通知
@Before 前置通知,在方法执行之前执行
@After 后置通知,在方法执行之后执行
@AfterRunning 返回通知,在方法返回结果之后执行
@AfterThrowing 异常通知,在方法抛出异常之后执行
@Around 环绕通知,围绕着方法执行
③在方法中声明一个类型为JoinPoint的参数就可以访问链接细节

ArithmeticCalculator接口
|
1
2
3
4
5
6
7
8
9
10
11
|
package com.spring.aop.impl;public interface ArithmeticCalculator { public int add(int i, int j); public int sub(int i, int j); public int mul(int i, int j); public int div(int i, int j);} |
接口实现类 ArithmeticCalculatorImpl.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.spring.aop.impl;import org.springframework.stereotype.Component;@Componentpublic class ArithmeticCalculatorImpl implements ArithmeticCalculator{ @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; }} |
切面类 LoggingAspect.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
package com.spring.aop.impl;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;//指定切面的优先级,当有多个切面时,数值越小优先级越高@Order(1)//把这个类声明为一个切面:需要把该类放入到IOC容器中。再声明为一个切面.@Aspect@Componentpublic class LoggingAspect { /** * 声明切入点表达式,一般在该方法中不再添加其他代码。 * 使用@Pointcut来声明切入点表达式。 * 后面的通知直接使用方法名来引用当前的切入点表达式。 */ @Pointcut("execution(public int com.spring.aop.impl.ArithmeticCalculator.*(..))") public void declareJoinPointExpression() {} /** *前置通知,在目标方法开始之前执行。 *@Before("execution(public int com.spring.aop.impl.ArithmeticCalculator.add(int, int))")这样写可以指定特定的方法。 * @param joinpoint */ @Before("declareJoinPointExpression()") //这里使用切入点表达式即可。后面的可以都改成切入点表达式。如果这个切入点表达式在别的包中,在前面加上包名和类名即可。 public void beforeMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object>args = Arrays.asList(joinpoint.getArgs()); System.out.println("前置通知:The method "+ methodName +" begins with " + args); } /** *后置通知,在目标方法执行之后开始执行,无论目标方法是否抛出异常。 *在后置通知中不能访问目标方法执行的结果。 * @param joinpoint */ @After("execution(public int com.spring.aop.impl.ArithmeticCalculator.*(int, int))") public void afterMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); //List<Object>args = Arrays.asList(joinpoint.getArgs()); 后置通知方法中可以获取到参数 System.out.println("后置通知:The method "+ methodName +" ends "); } /** *返回通知,在方法正常结束之后执行。 *可以访问到方法的返回值。 * @param joinpoint * @param result 目标方法的返回值 */ @AfterReturning(value="execution(public int com.spring.aop.impl.ArithmeticCalculator.*(..))", returning="result") public void afterReturnning(JoinPoint joinpoint, Object result) { String methodName = joinpoint.getSignature().getName(); System.out.println("返回通知:The method "+ methodName +" ends with " + result); } /** *异常通知。目标方法出现异常的时候执行,可以访问到异常对象,可以指定在出现特定异常时才执行。 *假如把参数写成NullPointerException则只在出现空指针异常的时候执行。 * @param joinpoint * @param e */ @AfterThrowing(value="execution(public int com.spring.aop.impl.ArithmeticCalculator.*(..))", throwing="e") public void afterThrowing(JoinPoint joinpoint, Exception e) { String methodName = joinpoint.getSignature().getName(); System.out.println("异常通知:The method "+ methodName +" occurs exception " + e); } /** * 环绕通知类似于动态代理的全过程,ProceedingJoinPoint类型的参数可以决定是否执行目标方法。 * @param point 环绕通知需要携带ProceedingJoinPoint类型的参数。 * @return 目标方法的返回值。必须有返回值。 */ /*不常用 @Around("execution(public int com.spring.aop.impl.ArithmeticCalculator.*(..))") public Object aroundMethod(ProceedingJoinPoint point) { Object result = null; String methodName = point.getSignature().getName(); try { //前置通知 System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs())); //执行目标方法 result = point.proceed(); //翻译通知 System.out.println("The method "+ methodName +" ends with " + result); } catch (Throwable e) { //异常通知 System.out.println("The method "+ methodName +" occurs exception " + e); throw new RuntimeException(e); } //后置通知 System.out.println("The method "+ methodName +" ends"); return result; } */} |
applicationContext.xml
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <!-- 配置自动扫描包 --> <context:component-scan base-package="com.spring.aop.impl"></context:component-scan> <!-- 使AspectJ注解起作用:自动为匹配的类生产代理对象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans> |
Main.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.spring.aop.impl;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) { //创建spring IOC容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //从IOC容器中获取bean实例 ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class); int result = arithmeticCalculator.add(4, 6); System.out.println(result); result = arithmeticCalculator.sub(4, 6); System.out.println(result); System.out.println(result); result = arithmeticCalculator.mul(4, 6); System.out.println(result); System.out.println(result); result = arithmeticCalculator.div(4, 0); System.out.println(result);}} |
基于配置文件的AOP 方式
引入的jar包与基于注解的方式引入的jar包相同

ArithmeticCalculator接口
|
1
2
3
4
5
6
7
8
9
10
11
|
package com.spring.aop.impl.xml;public interface ArithmeticCalculator { public int add(int i, int j); public int sub(int i, int j); public int mul(int i, int j); public int div(int i, int j);} |
接口实现类 ArithmeticCalculatorImpl.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.spring.aop.impl.xml;public class ArithmeticCalculatorImpl implements ArithmeticCalculator{ @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; }} |
切面类 LoggingAspect.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package com.spring.aop.impl.xml;import java.util.Arrays;import java.util.List;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;public class LoggingAspect { public void beforeMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); List<Object>args = Arrays.asList(joinpoint.getArgs()); System.out.println("前置通知:The method "+ methodName +" begins with " + args); } public void afterMethod(JoinPoint joinpoint) { String methodName = joinpoint.getSignature().getName(); //List<Object>args = Arrays.asList(joinpoint.getArgs()); 后置通知方法中可以获取到参数 System.out.println("后置通知:The method "+ methodName +" ends "); } public void afterReturnning(JoinPoint joinpoint, Object result) { String methodName = joinpoint.getSignature().getName(); System.out.println("返回通知:The method "+ methodName +" ends with " + result); } public void afterThrowing(JoinPoint joinpoint, Exception e) { String methodName = joinpoint.getSignature().getName(); System.out.println("异常通知:The method "+ methodName +" occurs exception " + e); } public Object aroundMethod(ProceedingJoinPoint point) { Object result = null; String methodName = point.getSignature().getName(); try { //前置通知 System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs())); //执行目标方法 result = point.proceed(); //翻译通知 System.out.println("The method "+ methodName +" ends with " + result); } catch (Throwable e) { //异常通知 System.out.println("The method "+ methodName +" occurs exception " + e); throw new RuntimeException(e); } //后置通知 System.out.println("The method "+ methodName +" ends"); return result; }} |
切面类 ValidationAspect.java
|
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.spring.aop.impl.xml;import java.util.Arrays;import org.aspectj.lang.JoinPoint;public class ValidationAspect { public void validateArgs(JoinPoint joinPoint) { System.out.println("validate:" + Arrays.asList(joinPoint.getArgs())); }} |
applicationContext-xml.xml
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <!-- 配置bean --> <bean id="arithmeticCalculator" class="com.spring.aop.impl.xml.ArithmeticCalculatorImpl"></bean> <!-- 配置切面的bean --> <bean id="loggingAspect" class="com.spring.aop.impl.xml.LoggingAspect"></bean> <bean id="validationAspect" class="com.spring.aop.impl.xml.ValidationAspect"></bean> <!-- 配置AOP --> <aop:config> <!-- 配置切点表达式 --> <aop:pointcut expression="execution(* com.spring.aop.impl.xml.ArithmeticCalculator.*(..))" id="pointcut"/> <!-- 配置切面及通知,使用order指定优先级 --> <aop:aspect ref="loggingAspect" order="1"> <!-- 环绕通知 --> <!-- <aop:around method="aroundMethod" pointcut-ref="pointcut"/> --> <!-- 前置通知 --> <aop:before method="beforeMethod" pointcut-ref="pointcut"/> <!-- 后置通知 --> <aop:after method="afterMethod" pointcut-ref="pointcut"/> <!-- 异常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/> <!-- 返回通知 --> <aop:after-returning method="afterReturnning" pointcut-ref="pointcut" returning="result"/> </aop:aspect> <aop:aspect ref="validationAspect" order="2"> <!-- 前置通知 --> <aop:before method="validateArgs" pointcut-ref="pointcut"/> </aop:aspect> </aop:config></beans> |
Main.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.spring.aop.impl.xml;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) { //创建spring IOC容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-xml.xml"); //从IOC容器中获取bean实例 ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class); int result = arithmeticCalculator.add(4, 6); System.out.println(result); result = arithmeticCalculator.sub(4, 6); System.out.println(result); System.out.println(result); result = arithmeticCalculator.mul(4, 6); System.out.println(result); System.out.println(result); result = arithmeticCalculator.div(4, 0); System.out.println(result);}}
|
AOP 与动态代理混合使用已添加log逻辑
AOP前传之动态代理
假设有如下需求:
写一个计算器类,里面包含加减乘除四个方法。在每个方法开始前打印出该方法开始的消息,在每个方法结束前打印出该方法结束的消息和计算的结果。
普通方法,先写一个接口,然后在接口里实现四个方法。在每个方法里加上要打印的语句。实现代码如下。
ArithmeticCalculator接口
|
1
2
3
4
5
6
7
8
9
10
11
|
package com.spring.aop.helloworld;public interface ArithmeticCalculator { int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j);} |
ArithmeticCalculatorLoggingImpl.java 实现上面的接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.spring.aop.helloworld;public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator{ @Override public int add(int i, int j) { System.out.println("The method add begins with [" + i + ", " + j + "]"); int result = i + j; System.out.println("The method add end with " + result); return result; } @Override public int sub(int i, int j) { System.out.println("The method sub begins with [" + i + ", " + j + "]"); int result = i - j; System.out.println("The method sub end with " + result); return result; } @Override public int mul(int i, int j) { System.out.println("The method mul begins with [" + i + ", " + j + "]"); int result = i * j; System.out.println("The method mul end with " + result); return result; } @Override public int div(int i, int j) { System.out.println("The method div begins with [" + i + ", " + j + "]"); int result = i / j; System.out.println("The method div end with " + result); return result; }} |
Main.java
|
1
2
3
4
5
6
7
8
9
|
ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorLoggingImpl();arithmeticCalculator.add(1, 5);System.out.println("----------");arithmeticCalculator.sub(5, 3);System.out.println("----------");arithmeticCalculator.mul(3, 7);System.out.println("----------");arithmeticCalculator.div(9, 3); |
程序运行结果:
The method add begins with [1, 5]
The method add end with 6
----------
The method sub begins with [5, 3]
The method sub end with 2
----------
The method mul begins with [3, 7]
The method mul end with 21
----------
The method div begins with [9, 3]
The method div end with 3
可见,上面的代码中间存在这大量相似的代码。而面向对象编程又不能很好地解决这个问题,下面采用动态代理的方法来解决上面的问题。
接口不变。写一个实现类ArithmeticCalculatorImpl.java 这个实现类只关注业务,没有需要打印的内容
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.spring.aop.helloworld;public class ArithmeticCalculatorImpl implements ArithmeticCalculator{ @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; }} |
ArithmeticCaculatorLogginProxy.java
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package com.spring.aop.helloworld;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;public class ArithmeticCaculatorLogginProxy { //要代理的对象 private ArithmeticCalculator target; public ArithmeticCaculatorLogginProxy(ArithmeticCalculator target){ this.target = target; } public ArithmeticCalculator getLoggingProxy() { ArithmeticCalculator proxy = null; //代理对象由哪一个类加载器负责加载 ClassLoader loader = target.getClass().getClassLoader(); //代理对象的类型,即其中有哪些方法 Class[] interfaces = new Class[]{ArithmeticCalculator.class}; //当调用代理对象其中的方法时,该执行的代码 InvocationHandler h = new InvocationHandler() { /** * proxy:正在返回的代理对象,一般情况下,在invoke方法中都不适用该对象 * method:正在被调用的方法 * args:调用方法时,传入的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //下面这句执行的时候又会调用invoke方法,所以会出现死循环导致内存溢出 //System.out.println(proxy.toString()); String methodName = method.getName(); //日志 System.out.println("The method " + methodName + " begins with" + Arrays.asList(args)); System.out.println("Invoke..."); //执行方法 Object result = method.invoke(target, args); //日志 System.out.println("The method" + methodName + " ends with " + result); return result; } }; proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy; }} |
main方法
|
1
2
3
4
5
6
7
8
9
|
ArithmeticCalculator target = new ArithmeticCalculatorImpl();ArithmeticCalculator proxy = new ArithmeticCaculatorLogginProxy(target).getLoggingProxy();proxy.add(1, 5);System.out.println("----------");proxy.sub(5, 3);System.out.println("----------");proxy.mul(3, 7);System.out.println("----------");proxy.div(9, 3); |
程序运行结果 :
The method add begins with[1, 5]
Invoke...
The methodadd ends with 6
----------
The method sub begins with[5, 3]
Invoke...
The methodsub ends with 2
----------
The method mul begins with[3, 7]
Invoke...
The methodmul ends with 21
----------
The method div begins with[9, 3]
Invoke...
The methoddiv ends with 3
spring aop 样例的更多相关文章
- Spring JDBC样例
这里介绍一下通过Spring JDBC的方式进行数据库的增删改查的操作.在进行程序的编写之前我们需要在本地MySQL数据库中创建一张User表,如下所示: create database user_d ...
- Spring MVC使用样例
Spring MVC使用样例 步骤1:添加Spring的相关依赖 1 <dependency> 2 3 <groupId>com.alibaba.external</gr ...
- spring事务详解(二)简单样例
系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...
- 【spring教程之中的一个】创建一个最简单的spring样例
1.首先spring的主要思想,就是依赖注入.简单来说.就是不须要手动new对象,而这些对象由spring容器统一进行管理. 2.样例结构 如上图所看到的,採用的是mavenproject. 2.po ...
- Spring Cloud使用样例
Spring Cloud Demo 项目地址:https://github.com/hackyoMa/spring-cloud-demo 组件 基于Spring Boot 2.0.4.Spring C ...
- Spring Ajax一个简单样例
配置不说了.要在前面helloworld的样例基础上弄. 相同在hello下新建ajax.jsp <%@ page language="java" contentType=& ...
- spring+springmvc+hibernate架构、maven分模块开发样例小项目案例
maven分模块开发样例小项目案例 spring+springmvc+hibernate架构 以用户管理做測试,分dao,sevices,web层,分模块开发測试!因时间关系.仅仅測查询成功.其它的准 ...
- Spring Boot入门样例-001-Java和Maven安装配置
Spring Boot入门样例-001-Java和Maven安装配置 本文说明Java和Maven在windows下的安装和配置 前言 本Spring Boot入门样例准备工作参考: Spring B ...
- spring aop配置及用例说明(2)
欢迎交流转载:http://www.cnblogs.com/shizhongtao/p/3473362.html 这里先介绍下几个annotation的含义, @Before:表示在切入点之前执行. ...
随机推荐
- 利用AbstractRoutingDataSource实现动态数据源切换
需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...
- 一站式学习Wireshark(一):Wireshark基本用法
按照国际惯例,从最基本的说起. 抓取报文: 下载和安装好Wireshark之后,启动Wireshark并且在接口列表中选择接口名,然后开始在此接口上抓包.例如,如果想要在无线网络上抓取流量,点击无线接 ...
- 从 QA 到 EP
两三年以前,和友人谈到 QA(软件质量保证) 这个行业,还有 QA 这个团队的未来,就有了一丝忧虑.而现在,终于有机会实践一下自己之前的想法,在这里分享给大家. 从我有限的从业经验到现在,经历了很多次 ...
- javascript删除字符串最后一个字符
搜到很多无用的答案,这对新手来讲是很耗费时间的事情,我把正确的方法贴出来,以便有的新手不再踩这个坑. JavaScript slice() 方法 定义和用法 slice() 方法可提取字符串的某个部分 ...
- linux -- ubuntu 何为软件源
新手学Ubuntu的时候,一般不知道什么是源,但源又是Ubuntu下常用到的东西.因此,本文就详细介绍一下Ubuntu 源. 什么是软件源? 源,在Ubuntu下,它相当于软件库,需要什么软件,只要记 ...
- jquery -- onchange
触发onchange 首先页面有一个input标签,并且已绑定onchange事件,如: 1 <input type="text" onchange="consol ...
- ubuntu下android源码的下载(最新)
在ubuntu下下载android源码我断断续续搞了好几个月,希望大家不要向我学习啊!一次性搞定! 这里给大家一些建议啊,如果是看书的话看下书的出版日期,超过一年的基本上失效,网上的也是,特别是在国内 ...
- 双卡手机怎么指定SIM卡打电话
双卡手机如何指定SIM卡打电话 package com.example.dualsimtest; import android.app.Activity; import android.content ...
- erlang的Socket的积压的消息的数量
转自:http://blog.csdn.net/pkutao/article/details/8572216 {ok, Listen} = gen_tcp:listen(?defPort, [bina ...
- Windows下安装Scrapy
安装python 根据你的需求下载python安装包,安装python(本文基于python27)https://www.python.org/downloads/ 在 环境变量---"Pa ...