public class AuthorDAO implements IAutorDAO { public AuthorDAO (){ System . out . println ( "实例化AuthorDAO" ); @PostConstruct public void init ( ){ System . out . println ( "执行AuthorDAO的初始化方法-init" ); @Override public String get ( ) { return "戴着假发的程序员" ;

当我们初始化容器时控制台输出:

 ApplicationContext ac =
         new AnnotationConfigApplicationContext("com. st.dk.demo4");
 实例化AuthorDAO
 执行AuthorDAO的初始化方法-init

2.22@PreDestroy

 @Documented
 @Retention (RUNTIME)
 @Target(METHOD)
 public @interface PreDestroy {

@PreDestroy和bean标签的destory-method有同样的作用,用于标注bean生命周期中的释放资源的方法.

  * @author 戴着假发的程序员   * @description  @Component  public class AuthorDAO implements IAutorDAO {      public AuthorDAO(){          System.out.println("实例化AuthorDAO");      @PostConstruct      public void init(){          System.out.println("执行AuthorDAO的初始化方法-init");      @PreDestroy      public void destory(){          System.out.println("执行AuthorDAO的释放资源的方法 -destory");      @Override      public String get() {          return "戴着假发的程序员";

当我们关闭容器时,控制台输出如下:

 AnnotationConfigApplicationContext ac =
         new AnnotationConfigApplicationContext("com. st.dk.demo4");
 System.out.println("容器初始化完成...");
 ac.close();
 System.out.println("容器关闭完成...");
 实例化AuthorDAO
 执行AuthorDAO的初始化方法-init
 容器初始化完成...
 执行AuthorDAO的释放资源的方法 -destory
 容器关闭完成...

2.23@ComponentScan和@ComponentScans注解

@ComponentScan往往是注解在@Configuration的类中,用于添加自动扫描的包。我们可以通过属性basePackages或者value指定一个或者多个要扫描的包。

简单案例如下:

  * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan("com. st.dk.demo4")  public class AppConfig {   * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan({"com. st.dk.demo4","com. st.dk.otherpk"})  public class AppConfig {

@ComponentScans是另外一个扫描包的注解,有一个数组形势的属性value,而数组的类型就是@ComponentScan,也就是一个@ComponentScans中可以配置多个@ComponentScan

 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 @Documented
 public @interface ComponentScans {
    ComponentScan[] value();

我们可以这样配置:

  * @author 戴着假发的程序员  @Configuration  @ComponentScans({          @ComponentScan("com.st.demo1"),          @ComponentScan("com.st")  public class AppConfig {

这时我们创建容器的方式应该是下面的方式:

 @Test
 public void testComponentScan(){
     ApplicationContext ac =
             new AnnotationConfigApplicationContext(AppConfig.class);

spring会扫描所有ComponentScan指定的包以及他们的子孙包中所有的类。 当然同样的我们其实可以在配置文件中使用context:component-scan标签配置扫描的包。

 <?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. st.dk.demo"/>
 </beans>

这里要注意,如果要使用context需要引入对应的namespace。

 通常情况下context:component-scan标签会隐式启用context:annotation-config的功能。

2.24@ComponentScan注解的basePackageClasses 属性

basePackageClasses 用来指定某个类所在包下的所有组件。springboot中的主应用类就是一个BasePackageClass。

我们准备如下结构的类:

其中每个类源码:

Person类:在beans包下

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

Car类:在otherbeans包下:

  * @author 戴着假发的程序员   * @description  @Component  public class Car {      public  Car(){          System.out.println("实例化Car类 ");

Truck类:在otherbeans包下:

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

Bus类:在otherbeans.pub包下

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

配置类,制定扫描beans包下的所有类,另外通过basePackageClasses指定Car.calss(这里注意 basePackageClasses属性是一个数组,可以同时配置多个class)

  * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan(basePackages = "com. st.dk.demo6.beans",basePackageClasses = Car.class)  public class AppConfig {
 @Test
 public void testBasePackageClasses(){
     ApplicationContext ac =
             new AnnotationConfigApplicationContext(AppConfig.class);
我们会发现,spring不光帮我们加载了beans包下的Person类,而且同时加载了Car所在包下的Car和Truck类,而且加载Car所在包的子孙包下的Bus类。

2.25@ComponentScan注解的useDefaultFilters属性

这里的useDefaultFilters和配置contentx:component-sacn的属性useDefaultFilters有一样的作用。默认是true。表示使用默认的过滤器。spring的默认Filter就会处理@Component、@Controller、@Service、@Repository这些注解的Bean。

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

修改上一章节的配置类:useDefaultFilters配置为false

  * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan(basePackages = "com. st.dk.demo6",useDefaultFilters = false)  public class AppConfig {

在创建容器,我们会发现,spring不会加载任何类。

2.26@ComponentScan注解的resourcePattern属性

这里的resourcePattern和配置contentx:component-sacn的属性resourcePattern有一样的作用。

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

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

我们可以修改配置类测试一下:我修改resourcePattern,只扫描us结尾的类:

  * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan(basePackages = "com. st.dk.demo6",resourcePattern = "**/*us.class")  public class AppConfig {

测试创建容器查看结果:

我们会发现spring只是加载Bus类,并没有加载其类。

2.27@ComponentScan注解的lazyInit属性

lazyInit用于通知spring对扫描的类是要延迟初始化。默认是false,就是不延迟。我们可以修改为true测试一下:

修改配置类,将lazyInit修改为true。

  * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan(basePackages = "com. st.dk.demo6",lazyInit = true)  public class AppConfig {

修改测试类:

 @Test
 public void testLazyInit(){
     ApplicationContext ac =
             new AnnotationConfigApplicationContext(AppConfig.class);
     System.out.println("容器创建完成");
     Bus bean = ac.getBean(Bus.class);
     System.out.println("获取bean:"+bean);
我们发现容器创建完成之后,spring并没有实例化任何类,当我们获取Bus类对象时,spring才会实例化Bus类对象。

2.28@ComponentScan注解的nameGenerator属性

nameGenerator和contentx:component-sacn的属性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;

修改配置类,配置nameGenerator为我们自定义的类:

  * @author 戴着假发的程序员   * @description  @Configuration  @ComponentScan(basePackages = "com. st.dk.demo6",nameGenerator = MyBaenNamegenerator.class)  public class AppConfig {

给Person类实现BeanNameAware接口:

  * @author 戴着假发的程序员   * @description  @Component  public class Person implements BeanNameAware {      public Person(){          System.out.println("实例化Person类");      @Override      public void setBeanName(String s) {          System.out.println("Person的beanName:"+s);

创建容器测试:

 @Test
 public void testNameGenerator(){
     ApplicationContext ac =
             new AnnotationConfigApplicationContext(AppConfig.class);
我们会发现Person类的beanName会按照我们的定义的规则进行制定。

2.29@ComponentScan注解的scopedProxy属性

这里的scopedProxy属性和context: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代理)

2.30@ComponentScan注解的scopeResolver属性

这里的scopeResolver也是和 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;

2.31@ComponentScan注解的includeFilters属性

这里的includeFilters和context:include-filter标签的作用完全一致。 我们已经知道ComponentScan的配置可以通知spring扫描拥有spring标准注解的类。这些标注大致是:@Component、@Controller、@Service、@Repository。但是我们也可以通过includeFilters属性配置通知spring扫描一些没有标准注解但是我们希望spring帮我们管理的类。

includeFilters的值是一个数组,可以配置多个,includeFilters中配置的是一个Filter类型的注解。Filter的源码如下:

 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
 @java.lang.annotation.Target({})
 static @interface Filter {
     org.springframework.context.annotation.FilterType type() default org.springframework.context.annotation.FilterType.ANNOTATION;
     @org.springframework.core.annotation.AliasFor("classes")
     java.lang.Class<?>[] value() default {};
     @org.springframework.core.annotation.AliasFor("value")
     java.lang.Class<?>[] classes() default {};
     java.lang.String[] pattern() default {};

Filter中有4个属性,分别是

type:用来配置Filter的类型,这个类型是一个枚举,这个类型一共有五种,下面再详细解释。

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

classes:当我们的type为ANNOTATION或者ASSIGNABLE_TYPE时,我们可以将对应的类配置在value属性中也可以配置在calsses属性中

pattern:当我们的type是REGEX时,我们可以将表达式配置的pattern中。

type属性的五个值:

ANNOTATION:-指定扫描使用某个注解的类

ASSIGNABLE_TYPE:指定扫描某个接口派生出来的类

ASPECTJ:指定扫描AspectJ表达式相匹配的类

REGEX:指定扫描符合正则表达式的类

CUSTOM:指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类

就这个五个类型,我准备五个案例看看

[1]ANNOTATION:-指定扫描使用某个注解的类,将其加载进spring的容器。

我们准备如下的类目录结构:

其中Person类实现:

Person有Spring的标准注解@Component.

  * @author 戴着假发的程序员   * @description  @Component//Person类交给spring管理  public class Person{      public Person(){          System.out.println("实例化Person类");

Dog类的实现:

Dog类并没有Spring的标准注解,添加添加了我们自定义的注解:

  * @author 戴着假发的程序员   * @description  @DkAnnotation  public class Dog {      public Dog(){          System.out.println("实例化Dog类");

我们自定的注解@DkAnnotation

  * @author 戴着假发的程序员   * @description  public @interface DkAnnotation {

添加一个配置类:

配置类中给ComponentScan添加属性excludeFilters,类型为ANNOTATION,value为DkAnnotation.class。当然 也可以使用classes代替value,注意value和classes都是数组,可以同时配置多个。

  * @author 戴着假发的程序员   * @description  @Configuration()  @ComponentScan(          basePackages = "com. st.dk.demo6.beans",          includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value= DkAnnotation.class)}  public class AppConfig {
 @Test
 public void testIncludeFilters(){
     ApplicationContext ac =
             new AnnotationConfigApplicationContext(AppConfig.class);
我们会发现spring会将有标注注解的Person类和我们有我们自定义注解的Dog都加载。

[2]ASSIGNABLE_TYPE:指定扫描某个接口派生出来的类

我们修改上面的案例,添加一个接口Info。

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

修改Dog类,删除Dog类上方的注解,但是让Dog类实现接口Info。

  * @author 戴着假发的程序员   * @description  public class Dog implements Info {      public Dog(){          System.out.println("实例化Dog类");

修改配置类,修改type属性为ASSIGNABLE_TYPE,value或者classes属性配置为Info .class。表示实现了Info接口的类要求spring加载。

  * @author 戴着假发的程序员   * @description  @Configuration()  @ComponentScan(          basePackages = "com. st.dk.demo6.beans",          includeFilters = {@ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE,value={Info.class})}  public class AppConfig {

再测试,我们会发现spring依然会加载Dog类。

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

我们添加一个Animal类:

  * @author 戴着假发的程序员   * @description  public class Animal {

修改Dog,继承Animal。

  * @author 戴着假发的程序员   * @description  public class Dog extends Animal {      public Dog(){          System.out.println("实例化Dog类");

修改配置类,type配置为ASPECTJ,这里注意去掉value或者calsses属性,添加pattern属性,值为:com. st.dk.demo6.beans.Animal+

  * @author 戴着假发的程序员   * @description  @Configuration()  @ComponentScan(          basePackages = "com. st.dk.demo6.beans",          includeFilters = {@ComponentScan.Filter(type= FilterType.ASPECTJ,pattern = "com. st.dk.demo6.beans.Animal+")}  public class AppConfig {

再测试,我们会发现spring依然会加载Dog类。

[4]REGEX:指定扫描符合正则表达式的类 我们可以通过REGEX配置一个正则表达式,spring会根据正则匹配加载对应的类。

案例:我们修改Dog类,不实现接口,不继承其他类,没有注解。

  * @author 戴着假发的程序员   * @description  public class Dog{      public Dog(){          System.out.println("实例化Dog类");

修改配置类,type修改为REGEX,pattern修改为:“.*.*og”,表示加载og结尾的类。

  * @author 戴着假发的程序员   * @description  @Configuration()  @ComponentScan(          basePackages = "com. st.dk.demo6.beans",          includeFilters = {@ComponentScan.Filter(type= FilterType.REGEX,pattern = ".*.*og")}  public class AppConfig {

再测试,我们会发现spring依然会加载Dog类。

[5]CUSTOM:指定扫描自定义的实现了org.springframework.core.type.filter.TypeFilter接口的类

CUSTOM允许我们自定义加载策略,我们可以自己实现一个TypeFilter,实现自己的加载策略。

案例:我们自己实现一个Filter,用来加载Dog类。添加一个自己的类实现接口TypeFilter

  * @author 戴着假发的程序员   * @description  public class DkFilter  implements TypeFilter {      //关于metadataReader和metadataReaderFactory可以关注spring源码解读部分      @Override      public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {          //获取当前正在扫描的类的类名          String className = metadataReader.getClassMetadata().getClassName();          //判断是不是Dog的class          if(className.equals(Dog.class.getName())){              //返回true表示让spring加载当前的类.              return true;          //返回false表示不让spring加载当前类          return false;

修改配置类,type修改为CUSTOM,value或者classes中配置DkFilter.class

  * @author 戴着假发的程序员   * @description  @Configuration()  @ComponentScan(          basePackages = "com. st.dk.demo6.beans",          includeFilters = {@ComponentScan.Filter(type= FilterType.CUSTOM,value = DkFilter.cl  public class AppConfig {

再测试,我们会发现spring依然会加载Dog类。

2.32@ComponentScan注解的excludeFilters属性

我们已经知道ComponentScan的配置可以通知spring扫描拥有spring标准注解的类。这些标注大致是:@Component、@Controller、@Service、@Repository。我们也可通过excludeFilters属性配置排除我们不希望spring容器加载的类。 比如我们再web工程中只希望加载@Controller,就可以排除其他的标准注解的类。

这个属性和includeFilters 都是ComponentScan的属性,作用和includeFilters 正好相反,用于排除类。 但是配置方式和includeFilters 完全一样,同样有5个类型:

有两个必须的属性:

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

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

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

具体的案例可以参考includeFilters 章节。

完结 后续继续更新springAOP相关细节。欢迎关注。。。。

分类:
后端
标签: