博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring源码(2) - Spring常用注解
阅读量:4165 次
发布时间:2019-05-26

本文共 18904 字,大约阅读时间需要 63 分钟。

Spring常用注解说明

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>com.levi.java</groupId>

  <artifactId>spring</artifactId>

  <version>0.0.1-SNAPSHOT</version>

 

  <!-- 版本变量控制 -->

    <properties>

        <!-- 源码编码 -->

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

 

        <!-- Maven编译版本 -->

        <maven.compiler.version>3.1</maven.compiler.version>

 

        <!-- 指定一下jdk的版本 ,这里我们使用jdk 1.8 ,默认是1.6 -->

        <java.version>1.8</java.version>

        

    </properties>

<dependencies>

         <dependency>

             <groupId>org.springframework</groupId>

             <artifactId>spring-context</artifactId>

             <version>5.0.6.RELEASE</version>

         </dependency>

         <dependency>

             <groupId>junit</groupId>

             <artifactId>junit</artifactId>

             <version>4.12</version>

             <scope>test</scope>

         </dependency>

         <dependency>

             <groupId>javax.inject</groupId>

             <artifactId>javax.inject</artifactId>

             <version>1</version>

         </dependency>

  </dependencies>

 

  <!-- 构建节点. -->

    <build>

         <plugins>

         <!-- 项目编译插件,所有子项目都必备的插件 -->

              <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>${maven.compiler.version}</version>

                <configuration>

                    <source>${java.version}</source>

                    <target>${java.version}</target>

                    <encoding>${project.build.sourceEncoding}</encoding>

                </configuration>

            </plugin>

    </plugins>

    </build>

</project>

 

基础测试类

public class Student {

   

    private String name;

 

    public Student() {

         System.out.println("构造...");

    }

 

    public Student(String name) {

         super();

         this.name = name;

    }

 

    public String getName() {

         return name;

    }

 

    public void setName(String name) {

         this.name = name;

    }

 

    public void init(){

         System.out.println("init...");

    }

   

    public void destroy(){

         System.out.println("destroy...");

    }

 

    @Override

    public String toString() {

         return "Student [name=" + name + "]";

    }

}

 

Xml配置方式启动项目

public class Student {

   

    private String name;

 

    public Student() {

         System.out.println("构造...");

    }

 

    public Student(String name) {

         super();

         this.name = name;

    }

 

    public String getName() {

         return name;

    }

 

    public void setName(String name) {

         this.name = name;

    }

 

    public void init(){

         System.out.println("init...");

    }

   

    public void destroy(){

         System.out.println("destroy...");

    }

 

    @Override

    public String toString() {

         return "Student [name=" + name + "]";

    }

}

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   

    <bean id="student" class="com.levi.spring.annotations.levi01beanxml.Student">

         <property name="name" value="abc"></property>

    </bean>

</beans>

/**

 * 以前是使用xml配置化时,就是使用ClassPathXmlApplicationContext

 */

public class L01MainTest {

    public static void main(String[] args) {

         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test.xml");

         Student student = (Student) applicationContext.getBean("student");

         System.out.println(student.getName());

    }

}

 

@Configuration

/**

 * @Configuration是用于定义配置类,可替换Xml配置文件,被注解的类的内部包含一个或多个被@Bean注释的方法,

 * 这些方法会被AnnotaionConfigApplicationContextAnnotationConfigWebApplicationContext类进行扫描,

 * 并用于构建bean定义,初始化Spring容器。

 */

@Configuration

public class L02MainConfig {

 

    @Bean

    public Student student() {

         return new Student();

    }

   

    @Bean

    public Student getStudent() {

         return new Student();

    }

   

    public static void main(String[] args) {

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(L02MainConfig.class);

         Student student = (Student) applicationContext.getBean("student");

//       Student student2 = (Student) applicationContext.getBean("getStudent");

         System.out.println(student);

//       System.out.println(student2);

    }

}

 

@ComponentScan

/**

 * @ComponentScan:根据定义的扫描路径,把符合扫描规则的类装配到Spring容器中。

 * 参数:

 * value:只扫描包

 * excludeFilters:指定扫描时,按照扫描规则排除哪些组件。

 * includeFilters:指定扫描时,只需要包含哪些组件

 * Filter.ANNOTATION:按照注解过滤

 * Filter.ASPECTJ:根据Aspetj的表达式

 * Filter.REGEX:按照正则表达式

 * Filter.CUSTOM:自定义规则

 */

@Configuration

@ComponentScan(value = {

"com.levi.spring.annotations.levi02scan"},

                  includeFilters = {

@Filter(type=FilterType.ANNOTATION)})

public class ComponentScanConfig {

   

    @Bean

    public Student student() {

         return new Student();

    }

   

}

/**

 *

 * @author suzhiwei

 */

public class MainConfig {

    public static void main(String[] args) {

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanConfig.class);

         String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

         for (String name : beanDefinitionNames) {

             System.out.println(name);

         }

    }

}

 

@Scope

@Configuration

public class MainConfig {

   

    /**

     * @Scope:指定位于Spring容器中的实例,默认是单例。

     * singleton:默认,单例。IOC容器启动时会调用方法创建对象并放到IOC容器中,以后每次获取的就是直接从容器中获取(Map)同一个

     * prototype:多实例,IOC容器启动的时候,不会去调用方法创建对象,而是每次获取的时候才会调用方法创建对象。另外,这也是为什么@Lazy对多例无效。

     * request:针对Web应用,一次请求创建一个对象。

     * session:同一个session创建一个实例。

     */

    @Scope(value = "prototype")

//  @Scope

    @Bean

    public Student student() {

         return new Student();

    }

   

    public static void main(String[] args) {

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         Student student = (Student) applicationContext.getBean("student");

         Student student2 = (Student) applicationContext.getBean("student");

         System.out.println(student);

         System.out.println(student2);

    }

}

 

@Lazy

@Configuration

public class MainConfig {

   

    /**

     * @Lazy:懒加载价值只对单例bean起作用,对于多例bean设置懒加载没有意义,因为多实例bean本来就是在使用时才创建。

     * 在没有用@Lazy注释标注时,不会启动懒加载,在容器创建的时候,就会初始化bean了。

     * 非懒加载的好处在于可以提前发现错误,但是消耗资源。

     */

    @Lazy

    @Bean

    public Student student() {

         return new Student();

    }

   

    public static void main(String[] args) {

         /*

          * @Lazy结果:

          * IOC

          * 构造对象...

          *

          * @Lazy结果:

          * 构造...

          * IOC

          */

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         System.out.println("IOC");

         Student student = (Student) applicationContext.getBean("student");

         System.out.println(student);

    }

}

 

@Conditional

public class LeviConditional implements Condition {

 

    @Override

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

         ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

         System.out.println("正在使用的类:" + beanFactory.getClass());

        

         String systemName = context.getEnvironment().getProperty("os.name");

         System.out.println("系统类型:" + systemName);

         System.out.println("不同系统类型做不同的事情。");

         if(systemName.contains("Windows")) {

             System.out.println("Windows系统允许创建");

             return true;

         }

         return false;

    }

}

@Configuration

public class MainConfig {

   

    /**

     * @Conditional:只有的满足某些条件的情况下才会注入到IOC容器中

     */

    @Conditional(value = LeviConditional.class)

    @Bean

    public Student student() {

         return new Student();

    }

   

    public static void main(String[] args) {

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         Student student = (Student) applicationContext.getBean("student");

         System.out.println(student);

    }

}

 

往容器注册组件的方式(注解)@Bean、@Import、ImportSelector、ImportBeanDefinitionRegistrar、FactoryBean

public class Boy {

 

}

public class Girl {

 

}

public class Person {

 

}

public class Teacher {

 

}

public class LeviFactoryBean implements FactoryBean<Boy> {

 

    @Override

    public Boy getObject() throws Exception {

         return new Boy();

    }

 

    @Override

    public Class<?> getObjectType() {

         return Boy.class;

    }

 

    @Override

    public boolean isSingleton() {

         return true;

    }

}

public class LeviImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

 

    @Override

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

         //判断这个是否在容器内

         boolean containsBeanDefinition = registry.containsBeanDefinition("com.levi.spring.annotations.levi06import.Teacher");

         System.out.println("========== LeviImportBeanDefinitionRegistrar" + containsBeanDefinition);

         if (containsBeanDefinition) {

             //如果在容器内,那么就把Girl类创建出来也加入到容器中

             RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Girl.class);

             registry.registerBeanDefinition("girl", rootBeanDefinition);

         }

    }

}

public class LeviImportSelector implements ImportSelector {

 

    @Override

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

         //返回全类名的bean

         return new String[] {

"com.levi.spring.annotations.levi06import.Person"};

    }

}

@Configuration

@Import(value = {Teacher.class,LeviImportSelector.class,LeviImportBeanDefinitionRegistrar.class,LeviFactoryBean.class})

public class MainConfig {

   

    /**

     * 容器注册组件的方式:

     * 1@Bean:导入类或包到IOC容器中

     * 2、包扫描+组件的标注注解,一般针对自己写的类,@ComponentScan@Controller@Service@Repository@Componet

     * 3@Import:导入类或包到IOC容器中,beanid为类名,主要是@Bean比较简单

     *   ImportSelector:是一个接口,返回需要导入到容器的组件的全类名数组

     *   ImportBeanDefinitionRegistrar:实现ImportBeanDefinitionRegistrar接口即可。

     * 4、使用Spring提供的FactoryBean(工厂bean)进行注册

     *   BeanFactory是直接从IOC中获取实例化后的实例

     *   FactoryBean是可以自定义Bean构建时这个过程加入自定义的内容

     */

    @Bean

    public Student student() {

         return new Student();

    }

 

    @Bean("studentT")

    public Student studentT() {

         return new Student();

    }

   

    public static void main(String[] args) {

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         Student student = (Student) applicationContext.getBean("student");

         System.out.println(student);

        

         Student student2 = (Student) applicationContext.getBean("studentT");

         System.out.println(student2);

        

         //打印所有定义的类

         for (String name : applicationContext.getBeanDefinitionNames()) {

             System.out.println(name);

         }

    }

}

 

Bean初始化与销毁方式(@Bean、InittializingBean & DisposableBean、JSR250注解@PreDestroy & @PostConstruct、BeanPostProcessorsr)

@Component

public class Teacher implements InitializingBean,DisposableBean {

   

    public Teacher() {

         System.out.println("Teacher(ID) - 构造器................");

    }

 

    public void afterPropertiesSet() throws Exception {

         System.out.println("Teacher(ID) - bean属性复制和初始化完成时调用");

    }

   

    public void destroy() throws Exception {

         System.out.println("Teacher(ID) - destroy........");

    }

}

@Component

public class Person {

   

    public Person() {

         System.out.println("Person JSR250 - 构造器................");

    }

 

    @PostConstruct

    public void afterPropertiesSet() {

         System.out.println("Person JSR250 - bean属性复制和初始化完成时调用");

    }

   

    @PreDestroy

    public void destroy() {

         System.out.println("Person JSR250 - destroy........");

    }

}

@Component

public class Girl implements BeanPostProcessor {

   

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

         //返回传进来的对象,在初始化之前调用进行处理工作

         System.out.println("Girl-Before" + bean + " - " + beanName);

         return bean;

    }

   

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

         System.out.println("Girl-After" + bean + " - " + beanName);

         return bean;

    }

}

/**

 * ApplicationContextAware:获取到Spring的上下文

 */

@Component

public class Boy implements ApplicationContextAware {

   

    private ApplicationContext applicationContext;

   

    public Boy() {

         System.out.println("Boy 构造器................");

    }

 

    @PostConstruct

    public void afterPropertiesSet() {

         System.out.println("Boy bean属性复制和初始化完成时调用");

    }

   

    @PreDestroy

    public void destroy() {

         System.out.println("Boy destroy........");

    }

 

    @Override

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

         this.applicationContext = applicationContext;

    }

}

@Configuration

@ComponentScan(value = "com.levi.spring.annotations.levi07initdestoryway")

public class MainConfig {

   

    /**

     * 定义和销毁的方式:

     * 1@Bean注解定义初始化和销毁的方法

     * 2、实现InitializingBean接口的afterPropertiesSet()方法,但BeanFactory创建好对象,而且把Bean所有属性

     * 都设置好了之后,会调用这个方法,相当于初始化方法。

     * 实现DisposableBeandestroy()方法,当Bean销毁时,会把单实例Bean进行销毁。

     * 3、根据JSR250规则定义的Java规范的两个注解来实现。

     * @PostConstruct:在Bean创建完成,而且属于复制完成后进行初始化,属于JDK规范的注解。

     * @PreDestroy:在Bean被移除之前进行通知,在容器销毁之前进行清理工作。

     * JSR250JDK提供的统一规范。

     * 4、使用BeanPostProcessor控制Bean的生命周期,这种接近源码

     */

    @Bean(initMethod = "init",destroyMethod = "destroy")

    public Student student() {

         return new Student();

    }

   

    public static void main(String[] args) {

         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         Student student = (Student) applicationContext.getBean("student");

         System.out.println(student);

        

         applicationContext.close();

    }

}

 

源码-Bean生命周期

AnnotationConfigApplicationContext

    public static void main(String[] args) {

         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         Student student = (Student) applicationContext.getBean("student");

         System.out.println(student);

        

         applicationContext.close();

    }

    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {

         this();

         register(annotatedClasses);

         refresh();

    }

 

516行:refresh()

550行:finishBeanFactoryInitialization(beanFactory);

869行:beanFactory.preInstantiateSingletons();

740行:if (isFactoryBean(beanName)) à FactoryBean的定制化作用

760行:getBean(beanName);

199行:doGetBean(name, null, null, false); à (debug)246行

317行:createBean(beanName, mbd, args);

501行:doCreateBean(beanName, mbdToUse, args);

541行:createBeanInstance(beanName, mbd, args); à 完成bean的创建

550行:synchronized (mbd.postProcessingLock) à 后置处理程序

553行:applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

578行:populateBean(beanName, mbd, instanceWrapper); à 属性赋值

579行:initializeBean(beanName, exposedObject, mbd); à Bean初始化,并包括前后置方法的处理 {

      1069行:wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); à

1702行:invokeInitMethods(beanName, wrappedBean, mbd); à 初始化方法

1710行:wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

 

ApplicationContextAware上下文获取

1、结合之前的Bean生命周期的源码,在debug中可以看到是多了几个方法。

2、会判断是否实现了ApplicationContextAware接口,是就会调用invokeAwareInterfaces注入值。

    @Override

    @Nullable

    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {

         AccessControlContext acc = null;

 

         if (System.getSecurityManager() != null &&

                  (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||

                          bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||

                          bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {

             acc = this.applicationContext.getBeanFactory().getAccessControlContext();

         }

 

         if (acc != null) {

             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {

                  invokeAwareInterfaces(bean);

                  return null;

             }, acc);

         }

         else {

             invokeAwareInterfaces(bean);

         }

 

         return bean;

    }

 

3、其他的前置、后置处理器都是一样的原理的,虽然是使用注解,但是背后原理是一样的。

 

总结:把Spring底层的组件可以注入到自定义的bean中,ApplicationContextAware是利用ApplicationContextAwareProcessor来处理的,其他的XXXAware也是类似的,都有相关的Processor来处理,其实就是后置处理器来处理。

 

@Value

public class Girl {

   

    /**

     * 直接复制

     */

    @Value("Aimi")

    private String name;

   

    /**

     * EL表达式

     */

    @Value("#{10 - 1}")

    private int age;

   

    /**

     * 读取配置文件,如果读取不到这个配置,默认为abc

     */

    @Value("${girl.desc:abc}")

    private String desc;

 

    @Override

    public String toString() {

         return "Girl [name=" + name + ", age=" + age + ", desc=" + desc + "]";

    }

}

@Configuration

public class MainConfig {

   

    @Bean

    public Girl girl() {

         return new Girl();

    }

   

    public static void main(String[] args) {

         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

         Girl girl = (Girl) applicationContext.getBean("girl");

         System.out.println(girl);

    }

}

 

自动装配(@Autowired、@Primary、@Resource、@Component、@Inject、@Quialifar、@Controller、@Service、@Repository)区别

@Repository

public class LeviDao {

   

    private String name = "A";

 

    public LeviDao() {

         super();

    }

 

    public LeviDao(String name) {

         super();

         this.name = name;

    }

 

    public String getName() {

         return name;

    }

 

    public void setName(String name) {

         this.name = name;

    }

}

@Service

public class LeviService {

   

    @Qualifier("leviDao") //寻找优先级最高的

    @Autowired

//  @Resource //不支持Primary、不支持Autowired=false

//  @Inject //在不使用Spring的情况下,可以使用,不支持Autowired=false

    private LeviDao leviDao;

   

    public void test() {

         System.out.println("LeviService" + leviDao.getName());

    }

}

@Controller

public class LeviController {

   

    @Autowired

    private LeviService leviService;

   

}

@Configuration

@ComponentScan(value = "com.levi.spring.annotations.levi09automatically")

public class MainConfig {

   

    /**

     * 自动装配:Spring利用依赖注入,完成对IOC容器中的各个组件的依赖关系赋值。

     * @Primary:标注注入IOC的类对象作为自动装配的首选,即优先级最高。如果在@Bean注入对象中,加入这个就说明了这个注入的优先级最高,

     *          那么@Autowired的优先级不在是优先限定类名了。

     * @Autowired:同一个类多个名称,默认优先限定类名,支持required=false,支持@Primary

     * @Resource:效果和Autowired一样可以装配bean1、不支持@Primary2、不支持required=false的功能,即IOC容器中找不到会报错

     * @Quialifar:指定ID,始终读取优先级最高的,相对于@Primary来说,@Primary减少了冗余,不用每次读取都指定

     * @Inject:可以完成bean装配,这个是需要额外引入第三方的包javax.inject,功能和@Autowired差不多,支持@Primary,只是没有required=false,在没有用Spring框架时,可以用这个。

     *

     * @Component:是一个通用的注解,可以将java类标记为bean,可以是任何Spring管理组件的通用构造型。

     *             @Repository@Service@Controller都是@Component的扩展,从源码中可以看得出来。

     * @Repository:源码依然是指向@Component,但是是标注在持久层的注解,具有将数据库操作抛出的原生异常翻译成Spring的持久层异常的功能。

     * @Service:源码依然是指向@Component,是业务逻辑层的注解,只是标注该类位于业务逻辑层,不会对@Component注解提供任何其他行为。

     * @Controller:源码依然是指向@Component,是SpringMVC的注解,具有将请求转发、重定向的功能。

     *

     * 如果IOC容器存在两个ID相同的Bean?会报错

     * bean组件优先级?默认优先限定名

     * 如何指定装配组件ID进行加载?@Qualifiar

     * 容器加载不存在的bean会出现什么问题?@Autowired可以用required=false

     * -@Primary注解bean首选如何使用?在注入的类的中位置加上这个@Primary注解

     */

   

    //优先使用

    @Primary

    @Bean

    public LeviDao leviDao2() {

         return new LeviDao("B");

    }

   

    public static void main(String[] args) {

        

         AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

        

         LeviService leviService = annotationConfigApplicationContext.getBean(LeviService.class);

         leviService.test();

        

        System.out.println(annotationConfigApplicationContext.getBean(LeviDao.class).getName());

         annotationConfigApplicationContext.close();

        

    }

}

 

@Autowired

      @Autowired的源码中可以看到用于构造器,用于方法。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Autowired {

 

    /**

     * Declares whether the annotated dependency is required.

     * <p>Defaults to {@code true}.

     */

    boolean required() default true;

 

}

@Component

public class Teacher {

   

    public Teacher() {

         System.out.println("。。。C。。。Teacher。。。");

    }

   

}

@Component

public class Person {

   

    private Teacher teacher;

   

    @Autowired

    public Person(Teacher teacher) {

         System.out.println("。。。C。。。Person。。。" + teacher);

    }

   

    @Autowired

    public void setTheacher(Teacher teacher) {

         this.teacher = teacher;

    }

   

    public Teacher getTheacher() {

         System.out.println("。。。M。。。Person。。。" + teacher);

         return this.teacher;

    }

   

}

@Configuration

@ComponentScan(value = "com.levi.spring.annotations.levi09automatically")

public class MainConfig {

   

    //优先使用

    @Primary

    @Bean

    public LeviDao leviDao2() {

         return new LeviDao("B");

    }

   

    public static void main(String[] args) {

        

         AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

        

         //测试@Autowired构造器注入,查看对象地址是否是同一个即可

         Teacher teacher = annotationConfigApplicationContext.getBean(Teacher.class);

         System.out.println(teacher);

 

         //测试@Autowired方法注入,查看对象地址是否是同一个即可

         Person person = annotationConfigApplicationContext.getBean(Person.class);

         System.out.println(person.getTheacher());

        

         annotationConfigApplicationContext.close();

    }

}

 

转载地址:http://wqmxi.baihongyu.com/

你可能感兴趣的文章
常用MYSQL命令
查看>>
谈数据库的性能优化
查看>>
数据库查询优化原则
查看>>
转一个关于优化sql的文章
查看>>
搜索引擎的联想功能如何实现
查看>>
教你怎样在MySQL中提高全文搜索效率
查看>>
数据库范式
查看>>
用PHP控制您的浏览器cache
查看>>
PHP中ob_start()函数的用法
查看>>
最优化原理与无后效性
查看>>
KMP算法详解
查看>>
Web技术四层结构
查看>>
简单叙述一下MYSQL的优化
查看>>
Clustered Index & Non Clustered Index
查看>>
为数据库建立索引
查看>>
对Session和Cookie的区分与理解
查看>>
HTTP协议中POST、GET、HEAD的区别是什么?分别在什么情况下使用?(
查看>>
表单中post与get的区别
查看>>
PHP文件上传
查看>>
半小时精通正则表达式
查看>>