Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration
Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration
Spring Framework是基于两个核心概念开发的 -依赖注入和面向方面编程(Spring AOP)。
目录[ 隐藏 ]
- 1春天AOP
- 1.1 Spring AOP概述
- 1.2面向方面的编程核心概念
- 1.3 AOP建议类型
- 2 Spring AOP示例
- 2.1 Spring AOP AspectJ依赖关系
- 2.2模型类
- 2.3服务等级
- 2.4使用AOP进行Spring Bean配置
- 2.5 Aspect AOP之前的Aspect AOP
- 2.6 Spring AOP切入点方法和重用
- 2.7 Spring AOP JoinPoint和建议参数
- 2.8建议示例后的Spring AOP
- 2.9 Spring AOP围绕Aspect示例
- 2.10具有自定义注释切入点的Spring建议
- 2.11 Spring AOP XML配置
- 2.12 Spring AOP示例
春天AOP
我们已经看到了Spring Dependency Injection的工作原理,今天我们将研究面向方面编程的核心概念以及如何使用Spring Framework实现它。
Spring AOP概述
大多数企业应用程序都有一些常见的横切关注点,适用于不同类型的对象和模块。一些常见的横切关注点是日志记录,事务管理,数据验证等。在面向对象编程中,应用程序的模块化由类实现,而在面向方面编程应用程序中,模块化由Aspects实现,并且它们被配置为跨越不同的类。
Spring AOP从通过普通面向对象编程模型无法实现的类中获取横切任务的直接依赖性。例如,我们可以有一个单独的日志记录类,但功能类必须再次调用这些方法来实现跨应用程序的日志记录。
面向方面的编程核心概念
在我们深入实现Spring AOP实现之前,我们应该了解AOP的核心概念。
- 方面:方面是一个实现跨越多个类的企业应用程序问题的类,例如事务管理。方面可以是通过Spring XML配置配置的普通类,也可以使用Spring AspectJ集成将类定义为使用
@Aspect
注释的Aspect 。 - 连接点:连接点是应用程序中的特定点,例如方法执行,异常处理,更改对象变量值等。在Spring AOP中,连接点始终是方法的执行。
- 建议:建议是针对特定连接点采取的操作。在编程方面,它们是在应用程序中达到具有匹配切入点的特定连接点时执行的方法。您可以将Advices视为Struts2拦截器或Servlet过滤器。
- 切入点:切入点是与连接点匹配的表达式,用于确定是否需要执行建议。Pointcut使用与连接点匹配的不同类型的表达式,Spring框架使用AspectJ切入点表达式语言。
- 目标对象:它们是应用建议的对象。Spring AOP是使用运行时代理实现的,因此该对象始终是代理对象。意味着在运行时创建子类,其中覆盖目标方法并根据其配置包含建议。
- AOP代理:Spring AOP实现使用JDK动态代理来创建具有目标类和通知调用的Proxy类,这些类称为AOP代理类。我们也可以通过将它添加为Spring AOP项目中的依赖项来使用CGLIB代理。
- 编织:将方面与其他对象链接以创建建议的代理对象的过程。这可以在编译时,加载时或在运行时完成。Spring AOP在运行时执行编织。
AOP建议类型
基于建议的执行策略,它们具有以下类型。
- 建议之前:这些建议在执行连接点方法之前运行。我们可以使用
@Before
注释将建议类型标记为Before advice。 - 之后(最后)建议:在连接点方法完成执行后执行的建议,无论是正常还是抛出异常。我们可以使用
@After
注释创建建议。 - 返回建议后:有时我们只要在连接点方法正常执行时才需要执行建议方法。我们可以使用
@AfterReturning
注释在返回建议后将方法标记为。 - 抛出建议后:只有当连接点方法抛出异常时才会执行此建议,我们可以使用它以声明方式回滚事务。我们
@AfterThrowing
对这种建议使用注释。 - 围绕建议:这是最重要和最有力的建议。此建议围绕连接点方法,我们还可以选择是否执行连接点方法。我们可以编写在执行连接点方法之前和之后执行的建议代码。周围的建议是调用连接点方法并在方法返回某些内容时返回值。我们使用
@Around
注释来创建建议方法。
上面提到的几点可能听起来令人困惑,但是当我们看一下Spring AOP的实现时,情况会更清楚。让我们开始用AOP实现创建一个简单的Spring项目。Spring支持使用AspectJ注释创建方面,为简单起见,我们将使用它。所有上述AOP注释都在org.aspectj.lang.annotation
包中定义。
Spring Tool Suite提供了有关方面的有用信息,因此我建议您使用它。如果您不熟悉STS,我建议您查看Spring MVC Tutorial,我已经解释了如何使用它。
Spring AOP示例
创建一个新的Simple Spring Maven项目,以便所有Spring Core库都包含在pom.xml文件中,我们不需要显式包含它们。我们的最终项目将如下图所示,我们将详细介绍Spring核心组件和Aspect实现。
Spring AOP AspectJ依赖项
Spring框架默认提供AOP支持,但由于我们使用AspectJ注释来配置方面和建议,我们需要将它们包含在pom.xml文件中。
<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>org.springframework.samples</groupId><artifactId>SpringAOPExample</artifactId><version>0.0.1-SNAPSHOT</version><properties><!-- Generic properties --><java.version>1.6</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!-- Spring --><spring-framework.version>4.0.2.RELEASE</spring-framework.version><!-- Logging --><logback.version>1.0.13</logback.version><slf4j.version>1.7.5</slf4j.version><!-- Test --><junit.version>4.11</junit.version><!-- AspectJ --><aspectj.version>1.7.4</aspectj.version></properties><dependencies><!-- Spring and Transactions --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring-framework.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring-framework.version}</version></dependency><!-- Logging with SLF4J & LogBack --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version><scope>compile</scope></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version><scope>runtime</scope></dependency><!-- AspectJ dependencies --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>${aspectj.version}</version><scope>runtime</scope></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId><version>${aspectj.version}</version></dependency></dependencies>
</project>
我已经添加的通知aspectjrt
,并aspectjtools
在项目依赖(版本1.7.4)。此外,我已将Spring框架版本更新为截至日期的最新版本,即4.0.2.RELEASE。
模型类
让我们创建一个简单的java bean,我们将通过一些其他方法将它们用于我们的示例。
Employee.java代码:
package com.journaldev.spring.model;import com.journaldev.spring.aspect.Loggable;public class Employee {private String name;public String getName() {return name;}@Loggablepublic void setName(String nm) {this.name=nm;}public void throwException(){throw new RuntimeException("Dummy Exception");}
}
您是否注意到setName()方法使用注释进行Loggable
注释。它是我们在项目中定义的自定义java注释。我们稍后会研究它的用法。
服务类
让我们创建一个服务类来使用Employee bean。
EmployeeService.java代码:
package com.journaldev.spring.service;import com.journaldev.spring.model.Employee;public class EmployeeService {private Employee employee;public Employee getEmployee(){return this.employee;}public void setEmployee(Employee e){this.employee=e;}
}
我本可以使用Spring注释将其配置为Spring组件,但我们将在此项目中使用基于XML的配置。EmployeeService类非常标准,只为我们提供了Employee bean的访问点。
使用AOP进行Spring Bean配置
如果您正在使用STS,则可以选择创建“Spring Bean配置文件”并选择AOP架构命名空间,但如果您使用的是其他IDE,则只需将其添加到spring bean配置文件中即可。
我的项目bean配置文件如下所示。
spring.xml:
<?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-4.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"><!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy /><!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="com.journaldev.spring.model.Employee"><property name="name" value="Dummy Name"></property>
</bean><!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService"><property name="employee" ref="employee"></property>
</bean><!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.journaldev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.journaldev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.journaldev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.journaldev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.journaldev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.journaldev.spring.aspect.EmployeeAnnotationAspect" /></beans>
要在Spring bean中使用Spring AOP,我们需要执行以下操作:
- 声明AOP名称空间,如xmlns:aop =“http://www.springframework.org/schema/aop”
- 添加aop:aspectj-autoproxy元素以在运行时启用带有自动代理的Spring AspectJ支持
- 将Aspect类配置为其他Spring bean
您可以看到我在spring bean配置文件中定义了很多方面,是时候逐个查看它们了。
在Aspect示例之前的Spring AOP
EmployeeAspect.java代码:
package com.journaldev.spring.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class EmployeeAspect {@Before("execution(public String getName())")public void getNameAdvice(){System.out.println("Executing Advice on getName()");}@Before("execution(* com.journaldev.spring.service.*.get*())")public void getAllAdvice(){System.out.println("Service method getter called");}
}
上述方面类的重点是:
- Aspect类需要具有
@Aspect
注释。 - @Before注释用于创建Before建议
@Before
注释中传递的字符串参数是Pointcut表达式- 对于任何带签名的Spring Bean方法,都会执行getNameAdvice()建议
public String getName()
。这是一个非常重要的要点,如果我们使用new运算符创建Employee bean,则不会应用建议。只有当我们使用ApplicationContext来获取bean时,才会应用建议。 - 我们可以在Pointcut表达式中使用星号(*)作为通配符,getAllAdvice()将应用于
com.journaldev.spring.service
名称get
以及不带任何参数的包中的所有类。
在我们查看了所有不同类型的建议后,我们将在测试类中查看这些建议。
Spring AOP切入点方法和重用
有时我们必须在多个地方使用相同的Pointcut表达式,我们可以使用@Pointcut
注释创建一个空方法,然后将其用作建议中的表达式。
EmployeeAspectPointcut.java代码:
package com.journaldev.spring.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;@Aspect
public class EmployeeAspectPointcut {@Before("getNamePointcut()")public void loggingAdvice(){System.out.println("Executing loggingAdvice on getName()");}@Before("getNamePointcut()")public void secondAdvice(){System.out.println("Executing secondAdvice on getName()");}@Pointcut("execution(public String getName())")public void getNamePointcut(){}@Before("allMethodsPointcut()")public void allServiceMethodsAdvice(){System.out.println("Before executing service method");}//Pointcut to execute on all the methods of classes in a package@Pointcut("within(com.journaldev.spring.service.*)")public void allMethodsPointcut(){}}
上面的例子非常清楚,而不是表达式,我们在advice注释参数中使用方法名称。
Spring AOP JoinPoint和建议参数
我们可以使用JoinPoint作为通知方法中的参数,并使用它获取方法签名或目标对象。
我们可以args()
在切入点中使用表达式来应用于与参数模式匹配的任何方法。如果我们使用它,那么我们需要在确定参数类型的advice方法中使用相同的名称。我们也可以在advice参数中使用Generic对象。
EmployeeAspectJoinPoint.java代码:
package com.journaldev.spring.aspect;import java.util.Arrays;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class EmployeeAspectJoinPoint {@Before("execution(public void com.journaldev.spring.model..set*(*))")public void loggingAdvice(JoinPoint joinPoint){System.out.println("Before running loggingAdvice on method="+joinPoint.toString());System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));}//Advice arguments, will be applied to bean methods with single String argument@Before("args(name)")public void logStringArguments(String name){System.out.println("String argument passed="+name);}
}
建议示例后的Spring AOP
让我们看一个简单的方面类,例如After,After Throwing和After Returning。
EmployeeAfterAspect.java代码:
package com.journaldev.spring.aspect;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;@Aspect
public class EmployeeAfterAspect {@After("args(name)")public void logStringArguments(String name){System.out.println("Running After Advice. String argument passed="+name);}@AfterThrowing("within(com.journaldev.spring.model.Employee)")public void logExceptions(JoinPoint joinPoint){System.out.println("Exception thrown in Employee Method="+joinPoint.toString());}@AfterReturning(pointcut="execution(* getName())", returning="returnString")public void getNameReturningAdvice(String returnString){System.out.println("getNameReturningAdvice executed. Returned String="+returnString);}}
我们可以within
在切入点表达式中使用向类中的所有方法应用建议。我们可以使用@AfterReturning建议来获取建议方法返回的对象。
我们在Employee bean中有throwException()方法来展示After Throwing建议的使用。
Spring AOP围绕Aspect示例
如前所述,我们可以使用Around方面来减少前后方法的执行。我们可以用它来控制建议的方法是否会执行。我们还可以检查返回的值并进行更改。这是最强大的建议,需要正确应用。
EmployeeAroundAspect.java代码:
package com.journaldev.spring.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;@Aspect
public class EmployeeAroundAspect {@Around("execution(* com.journaldev.spring.model.Employee.getName())")public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){System.out.println("Before invoking getName() method");Object value = null;try {value = proceedingJoinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("After invoking getName() method. Return value="+value);return value;}
}
要始终使用ProceedingJoinPoint作为参数,我们应该使用它的proceed()方法来调用目标对象建议方法。如果建议方法返回某些内容,建议将其返回给调用者程序。对于void方法,advice方法可以返回null。由于围绕建议方法的建议,我们可以控制方法的输入和输出以及它的执行行为。
具有自定义注释切入点的Spring建议
如果您查看上述所有建议切入点表达式,则有可能将它们应用于其他非预期的bean。例如,某人可以使用getName()方法定义一个新的spring bean,并且即使它不是预期的,也会开始应用该建议。这就是我们应该尽可能缩小切入点表达范围的原因。
另一种方法是创建自定义注释并注释我们希望应用建议的方法。这是使用@Loggable注释注释Employee setName()方法的目的。
Spring Framework @Transactional注释是Spring事务管理这种方法的一个很好的例子。
Loggable.java代码:
package com.journaldev.spring.aspect;public @interface Loggable {}
EmployeeAnnotationAspect.java代码:
package com.journaldev.spring.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class EmployeeAnnotationAspect {@Before("@annotation(com.journaldev.spring.aspect.Loggable)")public void myAdvice(){System.out.println("Executing myAdvice!!");}
}
myAdvice()方法只会建议setName()方法。这是一种非常安全的方法,每当我们想要对任何方法应用建议时,我们所需要的只是使用Loggable注释来注释它。
Spring AOP XML配置
我总是喜欢注释,但我们也可以选择在spring配置文件中配置方面。例如,假设我们有一个类如下。
EmployeeXMLConfigAspect.java代码:
package com.journaldev.spring.aspect;import org.aspectj.lang.ProceedingJoinPoint;public class EmployeeXMLConfigAspect {public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");Object value = null;try {value = proceedingJoinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value="+value);return value;}
}
我们可以通过在Spring Bean配置文件中包含以下配置来配置它。
<bean name="employeeXMLConfigAspect" class="com.journaldev.spring.aspect.EmployeeXMLConfigAspect" /><!-- Spring AOP XML Configuration -->
<aop:config><aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1"><aop:pointcut expression="execution(* com.journaldev.spring.model.Employee.getName())" id="getNamePointcut"/><aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/></aop:aspect>
</aop:config>
AOP xml配置元素的目的很明显,因此我不会详细介绍它。
Spring AOP示例
让我们有一个简单的Spring程序,看看所有这些方面如何切入bean方法。
SpringMain.java代码:
package com.journaldev.spring.main;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.journaldev.spring.service.EmployeeService;public class SpringMain {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);System.out.println(employeeService.getEmployee().getName());employeeService.getEmployee().setName("Pankaj");employeeService.getEmployee().throwException();ctx.close();}
}
现在当我们执行上面的程序时,我们得到以下输出。
Mar 20, 2014 8:50:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Thu Mar 20 20:50:09 PDT 2014]; root of context hierarchy
Mar 20, 2014 8:50:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Service method getter called
Before executing service method
EmployeeXMLConfigAspect:: Before invoking getName() method
Executing Advice on getName()
Executing loggingAdvice on getName()
Executing secondAdvice on getName()
Before invoking getName() method
After invoking getName() method. Return value=Dummy Name
getNameReturningAdvice executed. Returned String=Dummy Name
EmployeeXMLConfigAspect:: After invoking getName() method. Return value=Dummy Name
Dummy Name
Service method getter called
Before executing service method
String argument passed=Pankaj
Before running loggingAdvice on method=execution(void com.journaldev.spring.model.Employee.setName(String))
Agruments Passed=[Pankaj]
Executing myAdvice!!
Running After Advice. String argument passed=Pankaj
Service method getter called
Before executing service method
Exception thrown in Employee Method=execution(void com.journaldev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exceptionat com.journaldev.spring.model.Employee.throwException(Employee.java:19)at com.journaldev.spring.model.Employee$$FastClassBySpringCGLIB$$da2dc051.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)at com.journaldev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)at com.journaldev.spring.main.SpringMain.main(SpringMain.java:17)
您可以看到建议根据其切入点配置逐个执行。您应该逐个配置它们以避免混淆。
这就是Spring AOP示例教程的全部内容,我希望您通过Spring学习AOP的基础知识,并从示例中学到更多知识。从下面的链接下载示例项目并使用它。
下载Spring AOP项目
Spring AOP示例教程 - Aspect,Advice,Pointcut,JoinPoint,Annotations,XML Configuration相关推荐
- aop中joinpoint_Spring AOP示例教程–方面,建议,切入点,JoinPoint,注释,XML配置...
aop中joinpoint Spring Framework is developed on two core concepts – Dependency Injection and Aspect O ...
- spring aop示例_具有AOP事务管理的Spring ORM示例
spring aop示例 This is a very simple Spring ORM example that shows you how to configure an application ...
- Spring AOP:搞清楚advice的执行顺序
文章目录 目录 AOP的核心概念 模拟aspect advice的执行过程 同一aspect,不同advice的执行顺序 不同aspect,advice的执行顺序 同一aspect,相同advice的 ...
- Spring IoC,Spring Bean示例教程
Spring IoC,Spring Bean示例教程 欢迎来到Spring IoC示例教程.Spring Framework基于Inversion of Control原理.依赖注入是在应用程序中实现 ...
- spring aop示例_Spring查找方法示例
spring aop示例 当一个bean依赖于另一个bean时,我们使用setter属性或通过构造函数注入bean. getter方法将向我们返回已设置的引用,但是假设您每次调用getter方法时都想 ...
- spring aop示例_Spring JpaRepository示例(内存中)
spring aop示例 这篇文章描述了一个使用内存中HSQL数据库的简单Spring JpaRepository示例. 该代码示例可从GitHub的Spring-JpaRepository目录中获得 ...
- spring aop示例_Spring Profile模式示例
spring aop示例 最近,我们介绍了Spring Profiles的概念. 此概念是针对不同部署环境的轻松配置区分符. 直接的用例(已提出)是对相关的类进行注释,以便Spring根据活动的配置文 ...
- Spring安全示例教程
Spring安全示例教程 Spring Security提供了在Web应用程序中执行身份验证和授权的方法.我们可以在任何基于servlet的Web应用程序中使用spring security. 目录[ ...
- Spring Batch示例教程
Spring Batch示例教程 欢迎使用Spring Batch示例.Spring Batch是一个用于执行批处理作业的弹簧框架模块.我们可以使用spring批处理来处理一系列作业. 目录[ 隐藏 ...
最新文章
- 31万奖金算法赛事来了!
- 记一次信息泄露(被美团泄露出去的)和被诈骗经历
- OpenCV提取图像颜色直方图
- docker Harbor 问题
- android layout(l, t, r, b);,服务器里的a,t,l,r,b是什么意思? Android编程中关于layout(l,t,r,b)函数的问题...
- php2个数字拼接,PHP合并2个数字键数组值示例详解
- format函数_畅游人工智能之海 | Keras教程之后端函数(一)
- Spoon新建repository的时候
- day20/FileDemo1.java
- 程序员如何在工作之外获得收入
- 五一成都峨眉山三日游 20140501~20140503
- python打开txt文件以gbk,python中txt文件的编码转换问题utf-8转gbk
- 雀巢咖啡旗下感CAFÉ推出高端鎏光咖啡;武田剥离中国大陆非核心业务至海森 | 美通企业日报...
- 22湖南大学866数据结构真题(回忆版)
- 2018-2019-1 20165309 20165312 20165330 实验一 开发环境的熟悉
- omap3isp上层应用解析
- redis基础知识:数据结构SDS
- 墨珩科技超衡联盟链通过中国赛宝实验室测试
- 黑苹果OC引导AX201网卡教程,小新Pro13不换网卡也能上网
- 大二了 突破自己,努力,加油
热门文章
- 教你七招提高.NET网站性能
- Linux: 系统文件权限总结
- Ubuntu 16.04 安装第三方Apps Can’t Install Third-Party Apps on Ubuntu 16.04? You’re No
- Swift中文教程(二十) 扩展
- 对PHP输入输出流学习和认识
- 道德沦丧的电商推广app开发②
- Python——集合字典解析的对象无序和无副本特性
- 【AI视野·今日NLP 自然语言处理论文速览 第十期】Fri, 18 Jun 2021
- 【Linux】生产者消费者编程实现-线程池+信号量
- command对象的三个主要方法 1120