2019独角兽企业重金招聘Python工程师标准>>>

前言

到目前为止,已经简单学习了Spring的Core模块、....于是我们就开启了Spring的AOP模块了...在讲解AOP模块之前,首先我们来讲解一下cglib代理、以及怎么手动实现AOP编程

cglib代理

在讲解cglib之前,首先我们来回顾一下静态代理和动态代理....我之前就写过了静态代理、动态代理的博文:blog.csdn.net/hon_3y/arti…

由于静态代理需要实现目标对象的相同接口,那么可能会导致代理类会非常非常多....不好维护---->因此出现了动态代理

动态代理也有个约束:目标对象一定是要有接口的,没有接口就不能实现动态代理.....----->因此出现了cglib代理

cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能!

  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。

编写cglib代理

接下来我们就讲讲怎么写cglib代理:

  • 需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。
  • 引入功能包后,就可以在内存中动态构建子类
  • 代理的类不能为final,否则报错【在内存中构建子类来做扩展,当然不能为final,有final就不能继承了】
  • 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。
//需要实现MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{// 维护目标对象private Object target;public ProxyFactory(Object target){this.target = target;}// 给目标对象创建代理对象public Object getProxyInstance(){//1. 工具类Enhancer en = new Enhancer();//2. 设置父类en.setSuperclass(target.getClass());//3. 设置回调函数en.setCallback(this);//4. 创建子类(代理对象)return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {System.out.println("开始事务.....");// 执行目标对象的方法Object returnValue = method.invoke(target, args);System.out.println("提交事务.....");return returnValue;}}
  • 测试:

public class App {public static void main(String[] args) {UserDao userDao = new UserDao();UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();factory.save();}
}

使用cglib就是为了弥补动态代理的不足【动态代理的目标对象一定要实现接口】

手动实现AOP编程

AOP 面向切面的编程:

  • AOP可以实现“业务代码”与“关注点代码”分离

下面我们来看一段代码:


// 保存一个用户
public void add(User user) { Session session = null; Transaction trans = null; try { session = HibernateSessionFactoryUtils.getSession();   // 【关注点代码】trans = session.beginTransaction();    // 【关注点代码】session.save(user);     // 核心业务代码trans.commit();     //…【关注点代码】} catch (Exception e) {     e.printStackTrace(); if(trans != null){ trans.rollback();   //..【关注点代码】} } finally{ HibernateSessionFactoryUtils.closeSession(session);   ..【关注点代码】} }
  • 关注点代码,就是指重复执行的代码。
  • 业务代码与关注点代码分离,好处?
    • 关注点代码写一次即可
    • 开发者只需要关注核心业务
    • 运行时期,执行核心业务代码时候动态植入关注点代码; 【代理】

案例分析:

  • IUser接口

public interface IUser {void save();
}

我们一步一步来分析,首先我们的UserDao有一个save()方法,每次都要开启事务和关闭事务


//@Component  -->任何地方都能用这个
@Repository  //-->这个在Dao层中使用public class UserDao {public void save() {System.out.println("开始事务");System.out.println("DB:保存用户");System.out.println("关闭事务");}}
  • 在刚学习java基础的时候,我们知道:如果某些功能经常需要用到就封装成方法:

//@Component  -->任何地方都能用这个
@Repository  //-->这个在Dao层中使用public class UserDao {public void save() {begin();System.out.println("DB:保存用户");close();}public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
  • 现在呢,我们可能有多个Dao,都需要有开启事务和关闭事务的功能,现在只有UserDao中有这两个方法,重用性还是不够高。因此我们抽取出一个类出来

public class AOP {public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
  • 在UserDao维护这个变量,要用的时候,调用方法就行了

@Repository  //-->这个在Dao层中使用
public class UserDao {AOP aop;public void save() {aop.begin();System.out.println("DB:保存用户");aop.close();}}
  • 现在的开启事务、关闭事务还是需要我在userDao中手动调用。还是不够优雅。。我想要的效果:当我在调用userDao的save()方法时,动态地开启事务、关闭事务。因此,我们就用到了代理。当然了,真正执行方法的都是userDao、要干事的是AOP,因此在代理中需要维护他们的引用
public class ProxyFactory {//维护目标对象private static Object target;//维护关键点代码的类private static AOP aop;public static Object getProxyInstance(Object target_, AOP aop_) {//目标对象和关键点代码的类都是通过外界传递进来target = target_;aop = aop_;return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {aop.begin();Object returnValue = method.invoke(target, args);aop.close();return returnValue;}});}
}

工厂静态方法:

  • 把AOP加入IOC容器中
//把该对象加入到容器中
@Component
public class AOP {public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}
  • 把UserDao放入容器中

@Component
public class UserDao {public void save() {System.out.println("DB:保存用户");}}
  • 在配置文件中开启注解扫描,使用工厂静态方法创建代理对象

<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><bean id="proxy" class="aa.ProxyFactory" factory-method="getProxyInstance"><constructor-arg index="0" ref="userDao"/><constructor-arg index="1" ref="AOP"/></bean><context:component-scan base-package="aa"/></beans>
  • 测试,得到UserDao对象,调用方法


public class App {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");IUser iUser = (IUser) ac.getBean("proxy");iUser.save();}
}

工厂非静态方法

上面使用的是工厂静态方法来创建代理类对象。我们也使用一下非静态的工厂方法创建对象


package aa;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** Created by ozc on 2017/5/11.*/public class ProxyFactory {public Object getProxyInstance(final Object target_, final AOP aop_) {//目标对象和关键点代码的类都是通过外界传递进来return Proxy.newProxyInstance(target_.getClass().getClassLoader(),target_.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {aop_.begin();Object returnValue = method.invoke(target_, args);aop_.close();return returnValue;}});}
}

配置文件:先创建工厂,再创建代理类对象

<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--创建工厂--><bean id="factory" class="aa.ProxyFactory"/><!--通过工厂创建代理--><bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance"><constructor-arg index="0" ref="userDao"/><constructor-arg index="1" ref="AOP"/></bean><context:component-scan base-package="aa"/></beans>

AOP的概述

Aop: aspect object programming 面向切面编程

  • 功能: 让关注点代码与业务代码分离!
  • 面向切面编程就是指: 对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。

关注点:

  • 重复代码就叫做关注点。

// 保存一个用户
public void add(User user) { Session session = null; Transaction trans = null; try { session = HibernateSessionFactoryUtils.getSession();   // 【关注点代码】trans = session.beginTransaction();    // 【关注点代码】session.save(user);     // 核心业务代码trans.commit();     //…【关注点代码】} catch (Exception e) {     e.printStackTrace(); if(trans != null){ trans.rollback();   //..【关注点代码】} } finally{ HibernateSessionFactoryUtils.closeSession(session);   ..【关注点代码】} }

切面:

  • 关注点形成的类,就叫切面(类)!

public class AOP {public void begin() {System.out.println("开始事务");}public void close() {System.out.println("关闭事务");}
}

切入点:

  • 执行目标对象方法,动态植入切面代码。
  • 可以通过切入点表达式指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码

切入点表达式:

  • 指定哪些类的哪些方法被拦截

使用Spring AOP开发步骤

1) 先引入aop相关jar文件 (aspectj aop优秀组件)

  • spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】
  • aopalliance.jar 【spring2.5源码/lib/aopalliance】
  • aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
  • aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题

  • 需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。

2) bean.xml中引入aop名称空间

  • xmlns:context="http://www.springframework.org/schema/context"
  • http://www.springframework.org/schema/context
  • http://www.springframework.org/schema/context/spring-context.xsd

引入jar包

引入4个jar包:

引入名称空间


<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"></beans>

注解方式实现AOP编程

我们之前手动的实现AOP编程是需要自己来编写代理工厂的**,现在有了Spring,就不需要我们自己写代理工厂了。Spring内部会帮我们创建代理工厂**。

  • 也就是说,不用我们自己写代理对象了。

因此,我们只要关心切面类、切入点、编写切入表达式指定拦截什么方法就可以了!

还是以上一个例子为案例,使用Spring的注解方式来实现AOP编程

在配置文件中开启AOP注解方式


<?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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="aa"/><!-- 开启aop注解方式 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

代码:

  • 切面类

@Component
@Aspect//指定为切面类
public class AOP {//里面的值为切入点表达式@Before("execution(* aa.*.*(..))")public void begin() {System.out.println("开始事务");}@After("execution(* aa.*.*(..))")public void close() {System.out.println("关闭事务");}
}
  • UserDao实现了IUser接口

@Component
public class UserDao implements IUser {@Overridepublic void save() {System.out.println("DB:保存用户");}}
  • IUser接口

public interface IUser {void save();
}
  • 测试代码:

public class App {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");//这里得到的是代理对象....IUser iUser = (IUser) ac.getBean("userDao");System.out.println(iUser.getClass());iUser.save();}
}

目标对象没有接口

上面我们测试的是UserDao有IUser接口,内部使用的是动态代理...那么我们这次测试的是目标对象没有接口

  • OrderDao没有实现接口

@Component
public class OrderDao {public void save() {System.out.println("我已经进货了!!!");}
}
  • 测试代码:
public class App {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");OrderDao orderDao = (OrderDao) ac.getBean("orderDao");System.out.println(orderDao.getClass());orderDao.save();}
}

优化和AOP注解API

API:

  • @Aspect 指定一个类为切面类

  • @Pointcut("execution( cn.itcast.e_aop_anno..(..))") 指定切入点表达式*

  • @Before("pointCut_()") 前置通知: 目标方法之前执行

  • @After("pointCut_()") 后置通知:目标方法之后执行(始终执行)

  • @AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)

  • @AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行

  • @Around("pointCut_()") 环绕通知: 环绕目标方法执行

  • 测试:

  // 前置通知 : 在执行目标方法之前执行@Before("pointCut_()")public void begin(){System.out.println("开始事务/异常");}// 后置/最终通知:在执行目标方法之后执行  【无论是否出现异常最终都会执行】@After("pointCut_()")public void after(){System.out.println("提交事务/关闭");}// 返回后通知: 在调用目标方法结束后执行 【出现异常不执行】@AfterReturning("pointCut_()")public void afterReturning() {System.out.println("afterReturning()");}// 异常通知: 当目标方法执行异常时候执行此关注点代码@AfterThrowing("pointCut_()")public void afterThrowing(){System.out.println("afterThrowing()");}// 环绕通知:环绕目标方式执行@Around("pointCut_()")public void around(ProceedingJoinPoint pjp) throws Throwable{System.out.println("环绕前....");pjp.proceed();  // 执行目标方法System.out.println("环绕后....");}

优化

我们的代码是这样的:每次写Before、After等,都要重写一次切入点表达式,这样就不优雅了。

@Before("execution(* aa.*.*(..))")public void begin() {System.out.println("开始事务");}@After("execution(* aa.*.*(..))")public void close() {System.out.println("关闭事务");}

于是乎,我们要使用@Pointcut这个注解,来指定切入点表达式,在用到的地方中,直接引用就行了!

  • 那么我们的代码就可以改造成这样了:

@Component
@Aspect//指定为切面类
public class AOP {// 指定切入点表达式,拦截哪个类的哪些方法@Pointcut("execution(* aa.*.*(..))")public void pt() {}@Before("pt()")public void begin() {System.out.println("开始事务");}@After("pt()")public void close() {System.out.println("关闭事务");}
}

XML方式实现AOP编程

首先,我们把所有的注解都去掉...

  • 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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--对象实例--><bean id="userDao" class="aa.UserDao"/><bean id="orderDao" class="aa.OrderDao"/><!--切面类--><bean id="aop" class="aa.AOP"/><!--AOP配置--><aop:config ><!--定义切入表达式,拦截哪些方法--><aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/><!--指定切面类是哪个--><aop:aspect ref="aop"><!--指定来拦截的时候执行切面类的哪些方法--><aop:before method="begin" pointcut-ref="pointCut"/><aop:after method="close" pointcut-ref="pointCut"/></aop:aspect></aop:config></beans>
  • 测试:

public class App {@Testpublic  void test1() {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");OrderDao orderDao = (OrderDao) ac.getBean("orderDao");System.out.println(orderDao.getClass());orderDao.save();}@Testpublic  void test2() {ApplicationContext ac =new ClassPathXmlApplicationContext("aa/applicationContext.xml");IUser userDao = (IUser) ac.getBean("userDao");System.out.println(userDao.getClass());userDao.save();}
}

测试OrderDao

测试UserDao

切入点表达式

切入点表达式主要就是来配置拦截哪些类的哪些方法

查官方文档

..我们去文档中找找它的语法...

在文档中搜索:execution(

语法解析

那么它的语法是这样子的:


execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

符号讲解:

  • ?号代表0或1,可以不写
  • “*”号代表任意类型,0或多
  • 方法参数为..表示为可变参数

参数讲解:

  • modifiers-pattern?【修饰的类型,可以不写】
  • ret-type-pattern【方法返回值类型,必写】
  • declaring-type-pattern?【方法声明的类型,可以不写】
  • name-pattern(param-pattern)【要匹配的名称,括号里面是方法的参数】
  • throws-pattern?【方法抛出的异常类型,可以不写】

官方也有给出一些例子给我们理解:

测试代码

<!-- 【拦截所有public方法】 --><!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>--><!-- 【拦截所有save开头的方法 】 --><!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>--><!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 --><!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>--><!-- 【拦截指定类的所有方法】 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>--><!-- 【拦截指定包,以及其自包下所有类的所有方法】 --><!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>--><!-- 【多个表达式】 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!-- 下面2个且关系的,没有意义 --><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) &amp;&amp; execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--><!-- 【取非值】 --><!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y

转载于:https://my.oschina.net/u/3777556/blog/1634746

Spring【AOP模块】就是这么简单相关推荐

  1. 武林秘籍之Spring AOP 切面编程的简单应用

    年轻人,我观你骨骼精奇,定是万里无一的练武奇才,老夫这里有一本失传已久的武林秘籍,现赠于你,望你勤加苦练,早日修成正果... AOP(面向切面编程):Aspect Oriented Programmi ...

  2. spring aop实例讲解_Spring核心技术详解(一)

    一.Sring简介 Spring是一个分层的Java SE/EE应用一站式的轻量级开源框架.Spring核心是IOC和AOP. Spring主要优点包括: 方便解耦,简化开发,通过Spring提供的I ...

  3. 追根溯源之最好的Spring AOP解析

    作者:极乐君 链接:https://zhuanlan.zhihu.com/p/25522841 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 本篇是年后第一篇博文,由于博主用了 ...

  4. 关于 Spring AOP (AspectJ) 你该知晓的一切

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/54629058 出自[zejian ...

  5. 【Spring】Spring AOP源码分析-导读(一)

    文章目录 1.简介 2.AOP 原理 3.AOP 术语及相应的实现 3.1 连接点 - Joinpoint 3.2 切点 - Pointcut 3.3 通知 - Advice 3.4 切面 - Asp ...

  6. 《Spring揭秘》读书笔记 2:Spring AOP

    7 一起来看AOP 2009年8月,<一起来看流星雨>开播. 2009年9月,<Spring揭秘>出版. 7.1 AOP核心概念 AOP AOP全称为Aspect-Orient ...

  7. 深入聊一聊 Spring AOP 实现机制

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 本文转载于公众号:吉姆餐厅ak 概述 AOP(Aspect-Oriented Programmin ...

  8. Spring AOP注解方式实现

    简介 上文已经提到了Spring AOP的概念以及简单的静态代理.动态代理简单示例,链接地址:https://www.cnblogs.com/chenzhaoren/p/9959596.html 本文 ...

  9. spring aop 注释_使用Spring AOP,自定义注释和反射为您的应用程序审核基础结构

    spring aop 注释 下一篇文章将演示如何使用Spring AOP和注释编写简单的审计. 审核机制将是干净,高效且易于维护的(和Kewwl!). 我将在用户管理系统上演示我的示例(假设您具有反射 ...

  10. 使用Spring AOP,自定义注释和反射为您的应用审核基础架构

    下一篇文章将演示如何使用Spring AOP和注释编写简单的审计. 审核机制将是干净,高效且易于维护的(和Kewwl!). 我将在用户管理系统上演示我的示例(假设您对反射和AOP具有一般知识). 我们 ...

最新文章

  1. python 字符串list转为数字list
  2. 介绍一个好用的日期倒计时工具
  3. 数据库水平切分的实现原理解析——分库,分表,主从,集群,负载均衡器(转)...
  4. php电商网站开发流程图,php网上购物平台设计+ER图+流程图.doc
  5. 【英语学习】【科学】【Glencoe Science】【A】Life's Structure and Function目录及术语表
  6. Ettercap-中间人欺骗
  7. VB 字符串续行符最多25行…………
  8. Ubuntu 10.10用LibreOffice替换OpenOffice
  9. JavaWeb——jsp原理
  10. 云计算服务与基本术语、概念
  11. java虚拟机学习笔记(五)---运行时的数据区域
  12. 【代码规范】Google C++开源风格指南
  13. linux vi只写入1个字节,关于linux命令的说明(这是一个命令集)
  14. golang中文字符编码转换
  15. LeetCode(数据库)- 2142. The Number of Passengers in Each Bus I
  16. 基于 Windows系统的 KingbaseES 数据库软件安装指南(3. 安装前准备工作)
  17. 面试时被问“你的缺点是什么?”,这么答就对了
  18. Ubuntu-20.04永久修改DNS服务器
  19. D3DXCreateTextureFromFileEx创建纹理的悲催
  20. 2021-02-17

热门文章

  1. html5场景编辑工具,3款容易上手的HTML5编辑工具推荐~
  2. 分类模型的评估方法-精确率(Precision)
  3. 2022年跨境品牌出海新玩法策略:Tiktok+速卖通平台运营必不可少
  4. 中越跨国结婚需要什么条件和手续
  5. VC文件扩展名解读大全
  6. vc6.0快捷键大全- -
  7. 0x0800 IP数据包格式分析
  8. HTML实现包含公共部分:通过ECMA6的模块化,纯前端实现类似jsp:include的功能
  9. 可覆写的函数与创建节点
  10. oracle 清空表数据的2种方式及速度比较