InstantiationAwareBeanPostProcessor接口是BeanPostProcessor的子接口,通过接口字面意思翻译该接口的作用是感知Bean实例话的处理器。实际上该接口的作用也是确实如此。


Spring之BeanPostProcessor(后置处理器)介绍


InstantiationAwareBeanPostProcessor接口介绍

1.接口介绍

  先来看下接口的源代码:

  1. package org.springframework.beans.factory.config;
  2. public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
  3. Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
  4. boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
  5. PropertyValues postProcessPropertyValues(
  6. PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
  7. }

  从源码中我们可以获知的信息是该接口除了具有父接口中的两个方法以外还自己额外定义了三个方法。所以该接口一共定义了5个方法,这5个方法的作用分别是

方法 描述
postProcessBeforeInitialization BeanPostProcessor接口中的方法,
在Bean的自定义初始化方法之前执行
postProcessAfterInitialization BeanPostProcessor接口中的方法
在Bean的自定义初始化方法执行完成之后执行
postProcessBeforeInstantiation 自身方法,是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走
postProcessAfterInstantiation 在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。因为它的返回值是决定要不要调用postProcessPropertyValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行;如果返回true,postProcessPropertyValues就会被执行
postProcessPropertyValues 对属性值进行修改,如果postProcessAfterInstantiation方法返回false,该方法可能不会被调用。可以在该方法内对属性值进行修改

注意两个单词

单词 含义
Instantiation 表示实例化,对象还未生成
Initialization 表示初始化,对象已经生成

2.举例说明

1).创建接口实现类

  1. /**
  2. * 自定义处理器
  3. * @author dengp
  4. *
  5. */
  6. public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor{
  7. /**
  8. * BeanPostProcessor接口中的方法
  9. * 在Bean的自定义初始化方法之前执行
  10. * Bean对象已经存在了
  11. */
  12. @Override
  13. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  14. // TODO Auto-generated method stub
  15. System.out.println(">>postProcessBeforeInitialization");
  16. return bean;
  17. }
  18. /**
  19. * BeanPostProcessor接口中的方法
  20. * 在Bean的自定义初始化方法执行完成之后执行
  21. * Bean对象已经存在了
  22. */
  23. @Override
  24. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  25. System.out.println("<<postProcessAfterInitialization");
  26. return bean;
  27. }
  28. /**
  29. * InstantiationAwareBeanPostProcessor中自定义的方法
  30. * 在方法实例化之前执行 Bean对象还没有
  31. */
  32. @Override
  33. public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  34. System.out.println("--->postProcessBeforeInstantiation");
  35. return null;
  36. }
  37. /**
  38. * InstantiationAwareBeanPostProcessor中自定义的方法
  39. * 在方法实例化之后执行 Bean对象已经创建出来了
  40. */
  41. @Override
  42. public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
  43. System.out.println("<---postProcessAfterInstantiation");
  44. return true;
  45. }
  46. /**
  47. * InstantiationAwareBeanPostProcessor中自定义的方法
  48. * 可以用来修改Bean中属性的内容
  49. */
  50. @Override
  51. public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,
  52. String beanName) throws BeansException {
  53. System.out.println("<---postProcessPropertyValues--->");
  54. return pvs;
  55. }
  56. }

2).创建目标类

  1. public class User {
  2. private int id;
  3. private String name;
  4. private String beanName;
  5. public User(){
  6. System.out.println("User 被实例化");
  7. }
  8. public int getId() {
  9. return id;
  10. }
  11. public void setId(int id) {
  12. this.id = id;
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. System.out.println("设置:"+name);
  19. this.name = name;
  20. }
  21. public String getBeanName() {
  22. return beanName;
  23. }
  24. public void setBeanName(String beanName) {
  25. this.beanName = beanName;
  26. }
  27. public void start(){
  28. System.out.println("自定义初始化的方法....");
  29. }
  30. @Override
  31. public String toString() {
  32. return "User [id=" + id + ", name=" + name + ", beanName=" + beanName + "]";
  33. }
  34. }

3).配置文件注册

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean class="com.dpb.pojo.User" id="user" init-method="start">
  7. <property name="name" value="波波烤鸭"></property>
  8. </bean>
  9. <!-- 注册InstantiationAwareBeanPostProcessor对象 -->
  10. <bean class="com.dpb.processor.MyInstantiationAwareBeanPostProcessor"></bean>
  11. </beans>

4).测试

  1. @Test
  2. public void test1() {
  3. ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
  4. User user = ac.getBean(User.class);
  5. System.out.println(user);
  6. // 关闭销毁
  7. ac.registerShutdownHook();
  8. }

输出结果

  1. --->postProcessBeforeInstantiation
  2. User 被实例化
  3. <---postProcessAfterInstantiation
  4. <---postProcessPropertyValues--->
  5. 设置:波波烤鸭
  6. >>postProcessBeforeInitialization
  7. 自定义初始化的方法....
  8. <<postProcessAfterInitialization
  9. User [id=0, name=波波烤鸭, beanName=null]

通过输出结果,我们可以看到几个方法的执行顺序,而且五个方法都执行了,那么每个方法的返回结果对其他方法有什么影响没有呢,接下来分别看下这几个方法。

3.分析相关方法

1).postProcessBeforeInstantiation

  该方法返回的结果如果为null,后面的方法都正常执行了,但是如果该方法返回了实例对象了呢?我们来看下

  1. /**
  2. * InstantiationAwareBeanPostProcessor中自定义的方法 在方法实例化之前执行 Bean对象还没有
  3. */
  4. @Override
  5. public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  6. System.out.println("--->postProcessBeforeInstantiation");
  7. // 利用cglib动态代理生成对象返回
  8. if (beanClass == User.class) {
  9. Enhancer e = new Enhancer();
  10. e.setSuperclass(beanClass);
  11. e.setCallback(new MethodInterceptor() {
  12. @Override
  13. public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  14. System.out.println("目标方法执行前:" + method + "\n");
  15. Object object = methodProxy.invokeSuper(obj, objects);
  16. System.out.println("目标方法执行后:" + method + "\n");
  17. return object;
  18. }
  19. });
  20. User user = (User) e.create();
  21. // 返回代理类
  22. return user;
  23. }
  24. return null;
  25. }

测试输出结果:

  1. 容器开始初始化....
  2. --->postProcessBeforeInstantiation
  3. User 被实例化
  4. <<postProcessAfterInitialization
  5. 容器初始化结束....

  通过数据结果我们发现,postProcessBeforeInstantiation方法返回实例对象后跳过了对象的初始化操作,直接执行了postProcessAfterInitialization(该方法在自定义初始化方法执行完成之后执行),跳过了postProcessAfterInstantiation,postProcessPropertyValues以及自定义的初始化方法(start方法),为什么会这样呢?我们要来查看下源代码。

在AbstractBeanFactory中的对InstantiationAwareBeanPostProcessor实现该接口的BeanPostProcessor 设置了标志

  1. @Override
  2. public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
  3. Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
  4. this.beanPostProcessors.remove(beanPostProcessor);
  5. this.beanPostProcessors.add(beanPostProcessor);
  6. if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
  7. this.hasInstantiationAwareBeanPostProcessors = true;
  8. }
  9. if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
  10. this.hasDestructionAwareBeanPostProcessors = true;
  11. }
  12. }

在AbstractAutowireCapableBeanFactory类中有个createBean方法,

  1. protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
  2. // ... 省略
  3. try {
  4. // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
  5. Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
  6. if (bean != null) {
  7. return bean;
  8. }
  9. // ... 省略
  10. Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  11. if (logger.isDebugEnabled()) {
  12. logger.debug("Finished creating instance of bean '" + beanName + "'");
  13. }
  14. return beanInstance;
  15. }

Object bean = resolveBeforeInstantiation(beanName, mbdToUse);这行代码之后之后根据bean判断如果不为空null就直接返回了,而不执行doCreateBean()方法了,而该方法是创建Bean对象的方法。

  1. protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
  2. Object bean = null;
  3. // //如果beforeInstantiationResolved还没有设置或者是false(说明还没有需要在实例化前执行的操作)
  4. if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
  5. // 判断是否有注册过InstantiationAwareBeanPostProcessor类型的bean
  6. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  7. Class<?> targetType = determineTargetType(beanName, mbd);
  8. if (targetType != null) {
  9. bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
  10. if (bean != null) {
  11. // 直接执行自定义初始化完成后的方法,跳过了其他几个方法
  12. bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
  13. }
  14. }
  15. }
  16. mbd.beforeInstantiationResolved = (bean != null);
  17. }
  18. return bean;
  19. }
  1. protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
  2. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  3. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  4. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  5. Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
  6. //只要有一个result不为null;后面的所有 后置处理器的方法就不执行了,直接返回(所以执行顺序很重要)
  7. if (result != null) {
  8. return result;
  9. }
  10. }
  11. }
  12. return null;
  13. }
  1. @Override
  2. public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
  3. throws BeansException {
  4. Object result = existingBean;
  5. for (BeanPostProcessor processor : getBeanPostProcessors()) {
  6. result = processor.postProcessAfterInitialization(result, beanName);
  7. //如果返回null;后面的所有 后置处理器的方法就不执行,直接返回(所以执行顺序很重要)
  8. if (result == null) {
  9. return result;
  10. }
  11. }
  12. return result;
  13. }

代码说明:

  1. 如果postProcessBeforeInstantiation方法返回了Object是null;那么就直接返回,调用doCreateBean方法();
  2. 如果postProcessBeforeInstantiation返回不为null;说明修改了bean对象;然后这个时候就立马执行postProcessAfterInitialization方法(注意这个是初始化之后的方法,也就是通过这个方法实例化了之后,直接执行初始化之后的方法;中间的实例化之后 和 初始化之前都不执行);
  3. 在调用postProcessAfterInitialization方法时候如果返回null;那么就直接返回,调用doCreateBean方法();(初始化之后的方法返回了null,那就需要调用doCreateBean生成对象了)
  4. 在调用postProcessAfterInitialization时返回不为null;那这个bean就直接返回给ioc容器了初始化之后的操作是这里面最后一个方法了;

2).postProcessAfterInstantiation

  同样在

  1. protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
  2. // 省略 。。
  3. boolean continueWithPropertyPopulation = true;
  4. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  5. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  6. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  7. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  8. // 此处执行 postProcessAfterInstantiation方法
  9. if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
  10. // postProcessAfterInstantiation 返回true与false决定
  11. // continueWithPropertyPopulation
  12. continueWithPropertyPopulation = false;
  13. break;
  14. }
  15. }
  16. }
  17. }
  18. // postProcessAfterInstantiation false
  19. // continueWithPropertyPopulation 就为false 然后该方法就结束了!!!
  20. if (!continueWithPropertyPopulation) {
  21. return;
  22. }
  23. // 省略 ...
  24. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  25. boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
  26. if (hasInstAwareBpps || needsDepCheck) {
  27. PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  28. if (hasInstAwareBpps) {
  29. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  30. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  31. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  32. // 调用 postProcessPropertyValues方法
  33. pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  34. if (pvs == null) {
  35. return;
  36. }
  37. }
  38. }
  39. }
  40. if (needsDepCheck) {
  41. checkDependencies(beanName, mbd, filteredPds, pvs);
  42. }
  43. }
  44. // 真正设置属性的方法。
  45. applyPropertyValues(beanName, mbd, bw, pvs);
  46. }

  这个postProcessAfterInstantiation返回值要注意,因为它的返回值是决定要不要调用postProcessPropertyValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行;如果返true,postProcessPropertyValues就会被执行

3).postProcessPropertyValues

  在populateBean方法中我们已经看到了postProcessPropertyValues执行的位置了。我们来看下postProcessPropertyValues的效果

  1. /**
  2. * InstantiationAwareBeanPostProcessor中自定义的方法 可以用来修改Bean中属性的内容
  3. */
  4. @Override
  5. public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,
  6. String beanName) throws BeansException {
  7. System.out.println("<---postProcessPropertyValues--->");
  8. if(bean instanceof User){
  9. PropertyValue value = pvs.getPropertyValue("name");
  10. System.out.println("修改前name的值是:"+value.getValue());
  11. value.setConvertedValue("bobo");
  12. }
  13. return pvs;
  14. }

配置文件中设值注入name属性

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean class="com.dpb.pojo.User" id="user" init-method="start">
  7. <property name="name" value="波波烤鸭"></property>
  8. </bean>
  9. <!-- 注册InstantiationAwareBeanPostProcessor对象 -->
  10. <bean class="com.dpb.processor.MyInstantiationAwareBeanPostProcessor"></bean>
  11. </beans>

测试看输出

  1. --->postProcessBeforeInstantiation
  2. User 被实例化
  3. <---postProcessAfterInstantiation
  4. <---postProcessPropertyValues--->
  5. 修改前name的值是:TypedStringValue: value [波波烤鸭], target type [null]
  6. 设置:bobo
  7. >>postProcessBeforeInitialization
  8. 自定义初始化的方法....
  9. <<postProcessAfterInitialization
  10. User [id=0, name=bobo, beanName=null]
  11. 容器初始化结束....

name的属性值被修改了。该方法执行的条件要注意postProcessAfterInstantiation返回true且postProcessBeforeInstantiation返回null。

最后总结

  1. InstantiationAwareBeanPostProcessor接口继承BeanPostProcessor接口,它内部提供了3个方法,再加上BeanPostProcessor接口内部的2个方法,所以实现这个接口需要实现5个方法。InstantiationAwareBeanPostProcessor接口的主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置
  2. postProcessBeforeInstantiation方法是最先执行的方法,它在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走
  3. postProcessAfterInstantiation方法在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。因为它的返回值是决定要不要调用postProcessPropertyValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行;如果返回true, postProcessPropertyValues就会被执行
  4. postProcessPropertyValues方法对属性值进行修改(这个时候属性值还未被设置,但是我们可以修改原本该设置进去的属性值)。如果postProcessAfterInstantiation方法返回false,该方法可能不会被调用。可以在该方法内对属性值进行修改
  5. 父接口BeanPostProcessor的2个方法postProcessBeforeInitialization和postProcessAfterInitialization都是在目标对象被实例化之后,并且属性也被设置之后调用的

Spring之InstantiationAwareBeanPostProcessor接口介绍的更多相关文章

  1. Spring的BeanPostProcesser接口介绍

    前言 废话不多说,直接进入主题. 同学们有想过这么一种情况吗:Spring容器提供给我们的一些接口实现类并不能满足我们的要求,但是我们又不想重新写一个类,只想在原来类上修改一些属性? 举个例子,Spr ...

  2. Spring事务管理接口定义

    Spring事务管理接口介绍 Spring事务管理接口: PlatformTransactionManager: (平台)事务管理器 TransactionDefinition: 事务定义信息(事务隔 ...

  3. Spring8:一些常用的Spring Bean扩展接口

    前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...

  4. Spring中Ordered接口简介

    目录 前言 Ordered接口介绍 Ordered接口在Spring中的使用 总结 前言 Spring中提供了一个Ordered接口.Ordered接口,顾名思义,就是用来排序的. Spring是一个 ...

  5. Spring Resource框架体系介绍

    Resource介绍 在使用spring作为容器进行项目开发中会有很多的配置文件,这些配置文件都是通过Spring的Resource接口来实现加载,但是,Resource对于所有低级资源的访问都不够充 ...

  6. Spring家族主流成员介绍

    摘 要:Spring 就像一个大家族,有众多衍生产品例如 Boot,Security,JPA等等.但他们的基础都是Spring 的 IOC 和 AOP,IOC提供了依赖注入的容器,而AOP解决了面向切 ...

  7. spring boot 连接Mysql介绍

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  8. spring boot rest 接口集成 spring security(2) - JWT配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  9. spring boot rest 接口集成 spring security(1) - 最简配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

随机推荐

  1. 关于H5在微信获取授权

    很尴尬,flag倒了很久,这才来更新. 1.作为一枚小前端,所做的就是把微信获取授权之后的链接和所需的参数给到后端,定好之后只要获取链接就好了.(⊙o⊙)…确实就是这么简单,基本上这种授权是需要后端来 ...

  2. 修改Macros的值

    修改模板的macro  修改对应主机的macro

  3. 【CF486E】LIS of Sequence题解

    [CF486E]LIS of Sequence题解 题目链接 题意: 给你一个长度为n的序列a1,a2,...,an,你需要把这n个元素分成三类:1,2,3: 1:所有的最长上升子序列都不包含这个元素 ...

  4. 28.TreeSet

    与HashSet是基于HashMap实现一样,TreeSet同样是基于TreeMap实现的.在前一篇中详细讲解了TreeMap实现机制,如果客官详细看了这篇博文或者对TreeMap有比较详细的了解,那 ...

  5. centos7的Kubernetes部署记录

    一.使用vm创建了三个centos系统,基本细节如下: 1.1 修改三台机器对应的主机名: [root@localhost ~] hostnamectl --static set-hostname k ...

  6. [编译] 2、minGW gcc在windows搭建编译win32程序环境

    1.普通下载一个MinGW程序.安装之后可以直接将MinGW目录拷贝到总工程的tool里面: demo_mesh_common tree -L 2 . ├── app ├── bin ├── buil ...

  7. 16bit CRC算法C语言实现

    #define CRC_16_POLYNOMIALS 0x8005 unsigned short CRC16_3(unsigned char* pchMsg, unsigned short wData ...

  8. 剑指offer面试题15:链表中倒数第K个节点

    题目:输入一个链表,输出该链表的倒数第K个节点.为了符合大多数人的习惯,本题从1开始计数,即链表尾节点是倒数第一个节点. 解题思路: 解法一:一般情况下,单向链表无法从后一个节点获取到它前面的节点,可 ...

  9. 【redux】详解react/redux的服务端渲染:页面性能与SEO

        亟待解决的疑问 为什么服务端渲染首屏渲染快?(对比客户端首屏渲染)   react客户端渲染的一大痛点就是首屏渲染速度慢问题,因为react是一个单页面应用,大多数的资源需要在首次渲染前就加载 ...

  10. 【sql注入教程】SQL注入是什么?我们如何去玩转它

    [sql注入教程]SQL注入是什么?我们如何去玩转它 本文转自:i春秋社区   SQL注入攻击是黑客攻击数据库最常见手段之一.简单讲,SQL注入攻击是黑客利用网站程序漏洞,通过提交精心构造的SQL语句 ...