spring应用手册-IOC(XML配置实现)第三部分

198 阅读8分钟

1.30 context:component-scan配置扫描包

context:component-scan用于通知spring自动扫描的class 的包。

我们可以通过context:component-scan标签的base-package配置一个或者多个包名,spring会根据我们的配置自动扫描这些包下的所有类以及他们的子孙包下的所有的类,会自动处理所有拥有spring标准注解的类。(关于spring的标准注解请参看IOC的注解实现)。

我们在使用context:component-scan时需要添加对应的namespace。

案例:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan base-package="com.boxuewa.dk.demo2,com.boxuewa.dk.demo4"/>
 ​
 </beans>

使用context:component-scan隐式启用context:annotation-config的功能。使用context:component-scan时通常不需要包含context:annotation-config元素。

1.31 context:component-scan的use-default-filters属性

context:component-scan是用来通知spring自动扫描指定的包中的类文件的。

use-default-filters属性是用来通知spring是否启用默认的Filter。这个配置默认是true,spring的默认Filter就会处理@Component、@Controller、@Service、@Repository这些注解的Bean。

如果use-default-filters配置为false,则spring就不会再扫描和处理上面这些注解的Bean。

案例:

我们准备一个类Student,交给sprin管理:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @Component
 public class Student{
 }

配置如下:

<?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: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/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan  use-default-filters="false" base-package="com.boxuewa.dk.demo5">
    </context:component-scan>
</beans>

测试:

 @Test
 public void testUseDefaultFilters(){
     ApplicationContext ac =
             new ClassPathXmlApplicationContext("applicationContext-demo9.xml");
     System.out.println(ac.getBean(Student.class));
 }

结果,抛出异常:

 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.boxuewa.dk.demo5.pk1.Student' available

1.32 context:component-scan的annotation-config属性

annotation-config默认是true,完成了context:annotation-config元素的工作,如果是true就开启了属性自动注入的功能,如果是false就是关闭属性自动注入的功能。

案例:

我们创建两个类Person和Student,并且都交个spring管理,在Person中自动注入Student。

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @Component
 public class Student{
 }
/**
 * @author 戴着假发的程序员
 *  
 * @description
 */
@Component
public class Person {
    @Autowired
    private Student student;
    public void shwoStu(){
        System.out.println(student);
    }
}

配置如下:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan annotation-config="false" base-package="com.boxuewa.dk.demo5">
     </context:component-scan>
 </beans>

测试:

 @Test
 public void testAnnotationConfig(){
     ApplicationContext ac =
             new ClassPathXmlApplicationContext("applicationContext-demo9.xml");
     Person bean = ac.getBean(Person.class);
     bean.shwoStu();
 }

结果:

1586067348325.png 我们发现Person中的Stduent属性不能注入了。

1.33context:component-scan的name-generator属性

这个属性指定你的构造型注解,注册为Bean的ID生成策略,这个生成器基于接口BeanNameGenerator实现generateBeanName方法,你可以自己写个类去自定义策略。这边,我们可不显示配置,它是默认使用org.springframework.context.annotation.AnnotationBeanNameGenerator生成器,也就是类名首字符小写的策略,如Account类,它注册的Bean的ID为account.并且可以自定义ID,.这边简单贴出这个默认生成器的实现。

 public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
         if (definition instanceof AnnotatedBeanDefinition) {
             String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
             if (StringUtils.hasText(beanName)) {
                 // Explicit bean name found.
                 return beanName;
             }
         }
         // Fallback: generate a unique default bean name.
         return buildDefaultBeanName(definition, registry);
 }

我们可以自定义一个BeanName的生成器:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public class MyBaenNamegenerator implements BeanNameGenerator {
     @Override
     public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
         String beanName = null;
         //从beanDefinition中取出类名
         String beanClassName = beanDefinition.getBeanClassName();
         if(beanClassName.indexOf(".")!=-1){
             beanClassName = beanClassName.substring(beanClassName.lastIndexOf(".")+1);
             //我们来添加一个前缀
             beanName = "dk_"+beanClassName;
         }
         return beanName;
     }
 }

修改配置: 其中的name-generator指定为我们自定义的生成器

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan name-generator="com.boxuewa.dk.demo5.MyBaenNamegenerator" base-package="com.boxuewa.dk.demo5">
     </context:component-scan>
 </beans>

修改Student类,添加一个BeanNameAware接口的实现(关于BeanNameAware接口请参看相关介绍 )。

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @Component
 public class Student implements BeanNameAware {
     @Override
     public void setBeanName(String s) {
         System.out.println("Student的BeanName:"+s);
     }
 }

测试:

 @Test
 public void testNameGen(){
     ApplicationContext ac =
             new ClassPathXmlApplicationContext("applicationContext-demo9.xml");
 }

结果:

1586068237127.png 我们发现Student的Beanname会按照我们的规则生成.

1.34context:component-scan的resource-pattern属性

resource-pattern是用来配置要扫描的资源的正则表达式的,一般这里都是一个粗略的配置。

默认的配置是”**.*class“ 表示扫描配置包下的所有class文件。

我们可以修改测试以下。

准备两个类:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @Component
 public class Person {
     public Person(){
         System.out.println("实例化:Person");
     }
 }
 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @Component
 public class Student{
     public Student(){
         System.out.println("实例化Student");
     }
 }

修改配置,只扫描dent结尾的类:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan resource-pattern="**/*dent.class" base-package="com.boxuewa.dk.demo5">
     </context:component-scan>
 </beans>

测试:

 @Test
 public void testResourcePattern(){
     ApplicationContext ac =
             new ClassPathXmlApplicationContext("applicationContext.xml");
 }

结果:

1586069148289.png 我们会发现Person类不会被加载和实例化.

1.35context:component-scan的scoped-proxy属性

scoped-proxy 往往是使用在web工程中,比如下面的场景:

我们有一个Bean的score是session,那么就是每一个回话都会创建一个Bean。spring容器在初始化时不会创建这个Bean对象。 但是如果我们将这个Bean注入到其他的单利的bean中,就会出现问题。这是spring的解决方案就是产生一个代理对象。

那么scoped-proxy 就是用来配置代理方式的,总共有三个值

no(默认值):如果有接口就使用JDK代理,如果没有接口就使用CGLib代理

interfaces: 接口代理(JDK代理)

targetClass:类代理(CGLib代理)

1.36 context:component-scan的scope-resolver属性

注意这个属性和scoped-proxy是互斥的。

这个属性跟name-generator有点类似,它是基于接口ScopeMetadataResolver的,实现resolveScopeMetadata方法,目的是为了将@Scope(value="",proxyMode=ScopedProxyMode.NO,scopeName="")的配置解析成为一个ScopeMetadata对象,Spring这里也提供了两个实现,我们一起看下。首先是org.springframework.context.annotation.AnnotationScopeMetadataResolver中,

 public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
         ScopeMetadata metadata = new ScopeMetadata();
         if (definition instanceof AnnotatedBeanDefinition) {
             AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
             AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
             if (attributes != null) {
                 metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource()));
                 ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
                 if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
                     proxyMode = this.defaultProxyMode;
                 }
                 metadata.setScopedProxyMode(proxyMode);
             }
         }
         return metadata;
     }

org.springframework.context.annotation.Jsr330ScopeMetadataResolver中的实现

 public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
         ScopeMetadata metadata = new ScopeMetadata();
         metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
         if (definition instanceof AnnotatedBeanDefinition) {
             AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
             Set<String> annTypes = annDef.getMetadata().getAnnotationTypes();
             String found = null;
             for (String annType : annTypes) {
                 Set<String> metaAnns = annDef.getMetadata().getMetaAnnotationTypes(annType);
                 if (metaAnns.contains("javax.inject.Scope")) {
                     if (found != null) {
                         throw new IllegalStateException("Found ambiguous scope annotations on bean class [" +
                                 definition.getBeanClassName() + "]: " + found + ", " + annType);
                     }
                     found = annType;
                     String scopeName = resolveScopeName(annType);
                     if (scopeName == null) {
                         throw new IllegalStateException(
                                 "Unsupported scope annotation - not mapped onto Spring scope name: " + annType);
                     }
                     metadata.setScopeName(scopeName);
                 }
             }
         }
         return metadata;
     }

1.37 context:include-filter标签

我们已经知道context:component-scan的配置可以通知spring扫描拥有spring标准注解的类。这些标注大致是:@Component、@Controller、@Service、@Repository。但是我们也可以通过context:include-filter配置通知spring扫描一些没有标准注解但是我们希望spring帮我们管理的类。

context:include-filter有两个必须的属性:

type: 配置filter的类型,这个类型一共有以下五个值:

assignable-指定扫描某个接口派生出来的类 annotation-指定扫描使用某个注解的类 aspectj-指定扫描AspectJ表达式相匹配的类 custom-指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类 regex-指定扫描符合正则表达式的类

expression: 根据type的不同,这个表达式的配置方式也不同。

我们直接看案例:

[1]assignable-指定扫描某个接口派生出来的类:

我们准备如下结构的类:

1586063075460.png 其中Info接口:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public interface Info {
 }

Person类: 注意,我们在Person类上方添加了标准注解@Component,所以Person类会被spring加载

/**
 * @author 戴着假发的程序员
 *  
 * @description
 */
@Component
public class Person {
}

Student类:Student类没有标注注解,理论上不会被Spring加载。但是Student类实现了接口Info

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public class Student implements Info {
 }

我们的spring配置:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan base-package="com.boxuewa.dk.demo5">
         <!-- 通知spring将实现接口Info的类也加载进来 -->
         <context:include-filter type="assignable" expression="com.boxuewa.dk.demo5.Info"/>
     </context:component-scan>
 </beans>

测试:

 @Test
 public void testIncoudFilter(){
     ApplicationContext ac =
             new ClassPathXmlApplicationContext("applicationContext-demo9.xml");
     System.out.println(ac.getBean(Person.class));
     System.out.println(ac.getBean(Student.class));
 }

结果:

1586063302374.png 我们发现spring已经帮我们加载了没有注解的Student类。

[2]annotation-指定扫描使用某个注解的类:

我们自己创建一个注解:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE})
 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
 public @interface DemoAno {
 }

修改上面的Student类,Studnet类不再实现接口Info,但是在Student类上添加我们自己的注解。

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 @DemoAno
 public class Student {
 }

修改配置文件如下:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan base-package="com.boxuewa.dk.demo5">
         <!-- 通知spring将有指定注解的类加载进来 -->
         <context:include-filter type="annotation" expression="com.boxuewa.dk.demo5.annotation.DemoAno"/>
 </beans>

在测试:

1586063601779.png 我们发现Student类依然可以被加载。

[3]aspectj-指定扫描AspectJ表达式相匹配的类:比如要求加载某个类的派生类

这里需要添加AspectJ的依赖:

 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aspects</artifactId>
     <version>5.1.3.RELEASE</version>
 </dependency>

我们添加一个新的User类:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public class User {
 }

继续修改我们的Stdeunt类,不实现接口,不需要注解,只要继承User类即可。

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public class Student extends User {
 }

修改配置:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan base-package="com.boxuewa.dk.demo5">
         <!-- 通知spring所有继承和扩展自指定类的类全部加载进来 -->
         <context:include-filter type="aspectj" expression="com.boxuewa.dk.demo5.User+"/>
     </context:component-scan>
 </beans>

再测试:

1586064387242.png 我们会发现Stduent类依然会被spring加载。

[4]custom-指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类

我们继续修改Student类,不实现接口,不添加注解,不继承任何类。

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public class Student{
 }

我们添加一个MyFilter实现TypeFilter接口:

 /**
  * @author 戴着假发的程序员
  *  
  * @description
  */
 public class MyFilter implements TypeFilter {
     @Override
     public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
         //这里判断读取的类型是否是Student,如果是就返回true,否则返回false;
         //返回true就会被spring加载,否则不加载
         if(metadataReader.getClassMetadata().getClassName().equals(Student.class.getName())){
             return true;
         }
         return false;
     }

修改配置:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan base-package="com.boxuewa.dk.demo5">
         <!-- 通知spring根据我们配置的MyFilter类进行加载  这里的 expression要配置我们自己的MyFilter -->
         <context:include-filter type="custom" expression="com.boxuewa.dk.demo5.MyFilter"/>
     </context:component-scan>
 </beans>

测试:

1586065166849.png 我们的Student类依然可以被加载。

[5]regex-指定扫描符合正则表达式的类

这个是比较简单的,就是通过我们给出的正则表达式进行匹配。

我们继续修改配置:

 <?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: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/context
         http://www.springframework.org/schema/context/spring-context.xsd">
 ​
     <context:component-scan base-package="com.boxuewa.dk.demo5">
         <!-- 通过spring根据我们给出的正则表达式进行匹配  -->
         <context:include-filter type="regex" expression=".*.*dent"/>
     </context:component-scan>
 </beans>

在测试:

1586065470596.png 依然可以成功的加载Student类。

1.38 context:exclude-filter标签

我们已经知道context:component-scan的配置可以通知spring扫描拥有spring标准注解的类。这些标注大致是:@Component、@Controller、@Service、@Repository。我们也可通过context:exclude-filter标签配置排除我们不希望spring容器加载的类。

比如我们再web工程中只希望加载@Controller,就可以排除其他的标准注解的类。

这个标签和context:include-filter 都是context:component-scan的自标签,作用和context:include-filter正好相反,用于排除类。 但是配置方式和context:include-filter完全一样,同样有5个类型:

有两个必须的属性:

type: 配置filter的类型,这个类型一共有以下五个值:

assignable-指定扫描某个接口派生出来的类 annotation-指定扫描使用某个注解的类 aspectj-指定扫描AspectJ表达式相匹配的类 custom-指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类 regex-指定扫描符合正则表达式的类

expression: 根据type的不同,这个表达式的配置方式也不同。

具体的案例可以参考context:include-filter章节。

结尾

本章结束,本章主要讲解springIOC容器的一些XL配置内容。

如果要查看相对的注解内容,请持续关注。。。。。。欢迎关注。。。。。。