1、spring框架

spring框架

一、技术说明(技术介绍,技术优势以及发展史等)

1.1、什么是spring

  • Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

​ 目的:解决企业应用开发的复杂性

​ 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

​ 范围:任何Java应用

  • Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。

  • Spring是分层的JavaSE、EE、full-stack 轻量级开源框架,轻量级:使用时占用资源少,依赖程序少。

​ 分层:三层体系结构,为每一个层都提供解决方案

​ -----web层:struts2、spring-mvc

​ -----service层:spring

​ -----dao层:hibernate、mybatis、jdbcTemplate(spring)……

1.2、spring优点

  • 方便解耦,简化开发 (易扩展,易维护)

    Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理

    扫描二维码关注公众号,回复: 9854356 查看本文章
  • AOP编程的支持

    Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能

  • 声明式事务的支持

    只需要通过配置就可以完成对事务的管理,而无需手动编程

  • 方便程序的测试

    Spring对Junit4支持,可以通过注解方便的测试Spring程序

  • 方便集成各种优秀框架

    Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持

  • 降低JavaEE API的使用难度

    Spring 对开发中非常难用的一些(、、远程调用等),都提供了封装,使这些应用难度大大降低

1.3、 spring体系结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdjmDXVx-1574504390518)(spring框架.assets/1.png)]

spring核心功能:beans、core、context、expression

二、环境搭建(技术开发环境)

1、官网下载

  • 从官网下载spring 最新的相关jar包,官网download地址 http://www.springsource.org/springcommunity-download下载完成后会发现三个目录,命名很明确。

  • Docs 目录相关文档。包括一份和一份各种的使用说明,提供了版本,非常详细。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rH5TnUM3-1574504390520)(spring框架.assets/image-20191114134100081.png)]

2、spring包的核心包

搭建第一个用到 spring 依赖注册的程序,直接用 IDEA建立一个 WEB项目,然后添加 spring 的 jar 包引入

​ spring-core-3.2.0.M1.jar 核心依赖 jar 包

​ spring-context-3.2.0.M1.jar Spring 容器包

​ spring-beans-3.2.0.M1.jar Spring beans 的管理包

​ spring-asm-3.2.0.M1.jar Spring

​ spring-expression-3.2.0.M1.jar

​ commons-logging-1.1.1.jar 千万不要忘记日志包,否则会报错

3. 配置 XML

  1. ​ Spring 的最大的作用就是提供 bean 的管理功能,在 spring 中 bean 的管理是通过 XML 实现的,要用此功能,需要把 bean 配置到 spring 的 xml.
  2. 新建立一个 xml.名字任意,如 applicationContext.xml,或者 text.xml 都可以
  3. 添加 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
   
</beans>

4、 依赖注入

​ spring注入的简单案例(入门级)

Demo类:

public class Demo {
    public void say() {
        System.out.println ("hellow spring");
    }
}

application.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="demo" class="com.zhangyong.Demo"></bean>
</beans>

测试类:DemoTest

public class DemoTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
        Demo demo = (Demo) context.getBean ("demo");
        demo.say ();
    }
}

5、注入方式

​ spring框架为我们提供了三种注入方式,分别是set注入,构造方法注入,接口注入。接口注入不作要求,下面介绍前两种方式。

1、set注入

采用属性的set方法进行初始化,就成为set注入。给普通字符类型赋值。

User类:

public class User {

    private String name;

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    public void say() {
        System.out.println ("hellow spring" + name);
    }
}

​ application.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
    <bean id="user" class="com.zhangyong.User">
        <property name="name" value="admin"></property>
    </bean>
</beans>

​ 测试类:

public class DemoTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
        
        User user = (User) context.getBean ("user");
        user.say ();
    }
}

​ 运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGfjC1xC-1574504390523)(spring框架.assets/image-20191114143728121.png)]

给对象赋值

​ User类

public class User {

    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

​ UserService 接口

public interface UserService  {
    void say();
}

​ UserServiceImpl实现类

public class UserServiceImpl implements UserService {
    @Override
    public void say() {
        System.out.println ("注入进来");
    }
}

​ DemoTest 测试:

public class DemoTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
        UserService userService = (UserService) context.getBean ("userService");
        userService.say ();
    }
}

总结:这样配置,框架就会将UserService对象注入到User类中。

给list集合赋值
同样提供set方法

​ User类

public class User {
    private List<String> name;

    public List<String> getName() {
        return name;
    }

    public void setName(List<String> name) {
        this.name = name;
    }
    //为了测试
    public void say() {
        System.out.println (name);
    }
}

​ application.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.zhangyong.User">
        <property name="name">
            <list>
                <value>zhangsan</value>
                <value>lisi</value>
                <value>wanger</value>
            </list>
        </property>
    </bean>
</beans>

​ 测试

public class DemoTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
        User user = (User) context.getBean ("user");
        user.say ();
    }
}
给属性文件中的字段赋值

​ User类

public class User {
    private Properties props;

    public Properties getProps() {
        return props;
    }

    public void setProps(Properties props) {
        this.props = props;
    }
}

​ application.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.zhangyong.User">
        <property name="props">
            <props>
                <prop key="url">jdbc:oracle:thin:@localhost:orl</prop>
                <prop key="driverName">oracle.jdbc.driver.OracleDriver</prop>
                <prop key="username">scott</prop>
                <prop key="password">tiger</prop>
            </props>
        </property>
    </bean>
</beans>

​ 注:prop标签中的key值是.properties属性文件中的名称

2、构造方法注入
构造方法一个参数

​ User类

public class User {
    private String usercode;

    public User(String usercode) {
        this.usercode = usercode;
    }
    public void say() {
        System.out.println (usercode);
    }
}

​ application.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.zhangyong.User">
       <constructor-arg value="admin"></constructor-arg>
    </bean>
    
</beans>

​ DemoTest 测试:

public class DemoTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
        User user = (User) context.getBean ("user");
        user.say ();
    }
}
构造函数有两个参数时

当参数为非字符串类型时,在配置文件中需要制定类型,如果不指定类型一律按照字符串类型赋值。

当参数类型不一致时,框架是按照字符串的类型进行查找的,因此需要在配置文件中制定是参数的位置

<constructor-arg value="admin" type="String" index="0"></constructor-arg>                  
<constructor-arg value="23" type="int" index="1"></constructor-arg>

5、Spring的 junit 单元测试

单元测试以set注入的第一个实例为测试对象。进行单元测试。

​ User 类

public class User {
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    //添加方法
    public String login(){
        if ("admin".equals (username)){
            System.out.println ("success");
            return "success";
        }else {
            System.out.println ("error");
            return "error";
        }
    }
}

​ application.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.zhangyong.User">
       <property name="username" value="admin"></property>
    </bean>

</beans>

​ 测试类:

public class DemoTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
        User user = (User) context.getBean ("user");
        user.login ();
    }
}

6、Spring对注解(Annotation)处理源码分析1——扫描和读取Bean定义

1、Spring IOC容器对于类级别的注解和类内部的注解分以下两种处理策略:

​ 1.类级别的注解:

​ 如@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解,Spring容器根据注解的过滤规则扫描读取注解Bean定义类,并将其注册到Spring IoC容器中。

​ 2.类内部的注解:

​ 如@Autowire、@Value、@Resource以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方法上的类内部注解,SpringIoC容器通过Bean后置注解处理器解析Bean内部的注解。

​ 下面将根据这两种处理策略,分别分析Spring处理注解相关的源码。

2、Annotation Config Application Context对注解Bean初始化:

​ Spring中,管理注解Bean定义的容器有两个:AnnotationConfigApplicationContext和 AnnotationConfigWebApplicationContext。这两个类是专门处理Spring注解方式配置的容器,直接依赖于注解作为容器配置信息来源的IOC容器。 AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,两者的用法以及对注解的处理方式几乎没有什么差别,因此本文将以AnnotationConfigApplicationContext为例进行讲解。

AnnotationConfigApplicationContext的源码如下:

public class AnnotationConfigApplicationContext extends GenericApplicationContext {
        //创建一个读取注解的Bean定义读取器,并将其设置到容器中  
        private final AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader (this);
        //创建一个扫描指定类路径中注解Bean定义的扫描器,并将其设置到容器中  
        private final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner (this);

        //默认构造函数,初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其register() //方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程  
        public AnnotationConfigApplicationContext() {
        }

        //最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean  
        //自动注册到容器中  
        public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
            register (annotatedClasses);
            refresh ();
        }
    
        //该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其  
        //注册到容器中  
        public AnnotationConfigApplicationContext(String... basePackages) {
            scan (basePackages);
            refresh ();
        }

        //为容器的注解Bean读取器和注解Bean扫描器设置Bean名称产生器  
        public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
            this.reader.setBeanNameGenerator (beanNameGenerator);
            this.scanner.setBeanNameGenerator (beanNameGenerator);
        }

        //为容器的注解Bean读取器和注解Bean扫描器设置作用范围元信息解析器  
        public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
            this.reader.setScopeMetadataResolver (scopeMetadataResolver);
            this.scanner.setScopeMetadataResolver (scopeMetadataResolver);
        }

        //为容器注册一个要被处理的注解Bean,新注册的Bean,必须手动调用容器的  
        //refresh()方法刷新容器,触发容器对新注册的Bean的处理  
        public void register(Class<?>... annotatedClasses) {
            this.reader.register (annotatedClasses);
        }

        //扫描指定包路径及其子包下的注解类,为了使新添加的类被处理,必须手动调用  
        //refresh()方法刷新容器  
        public void scan(String... basePackages) {
            this.scanner.scan (basePackages);
        }
}

​ 通过对 AnnotationConfigApplicationContext 的源码分析,我们了解到Spring对注解的处理分为两种方式:

a、直接将注解Bean注册到容器中:

​ 可以在初始化容器时注册;也可以在容器创建之后手动调用注册方法向容器注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理。

b、通过扫描指定的包及其子包下的所有类:

​ 在初始化注解容器时指定要自动扫描的路径,如果容器创建以后向给定路径动态添加了注解Bean,则需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的Bean进行处理。

3、AnnotationConfigApplicationContext 注册注解Bean:

​ 当创建注解处理容器时,如果传入的初始参数是具体的注解Bean定义类时,注解容器读取并注册。

(1)、AnnotationConfigApplicationContext 通过调用注解Bean定义读取器 AnnotatedBeanDefinitionReader 的register方法向容器注册指定的注解Bean

​ 注解Bean定义读取器向容器注册注解Bean的源码如下:

//注册多个注解Bean定义类  
    public void register(Class<?>... annotatedClasses) {
        for (Class<?> annotatedClass : annotatedClasses) {
            registerBean (annotatedClass);
        }
    }

    //注册一个注解Bean定义类  
    public void registerBean(Class<?> annotatedClass) {
        registerBean (annotatedClass, null, (Class<? extends Annotation>[]) null);
    }

    //Bean定义读取器注册注解Bean定义的入口方法  
    public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {
        registerBean (annotatedClass, null, qualifiers);
    }

    //Bean定义读取器向容器注册注解Bean定义类  
    public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
        //根据指定的注解Bean定义类,创建Spring容器中对注解Bean的封装的数据结构  
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition (annotatedClass);
        //解析注解Bean定义的作用域,若@Scope("prototype"),则Bean为原型类型;  
        //若@Scope("singleton"),则Bean为单态类型  
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata (abd);
        //为注解Bean定义设置作用域  
        abd.setScope (scopeMetadata.getScopeName ());
        //为注解Bean定义生成Bean名称  
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName (abd, this.registry));
        //处理注解Bean定义中的通用注解  
        AnnotationConfigUtils.processCommonDefinitionAnnotations (abd);
        //如果在向容器注册注解Bean定义时,使用了额外的限定符注解,则解析限定符注解。  
        //主要是配置的关于autowiring自动依赖注入装配的限定条件,即@Qualifier  
        //注解,Spring自动依赖注入装配默认是按类型装配,如果使用@Qualifier则按名称  
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                //如果配置了@Primary注解,设置该Bean为autowiring自动依赖注入装//配时的首选  
                if (Primary.class.equals (qualifier)) {
                    abd.setPrimary (true);
                }
                //如果配置了@Lazy注解,则设置该Bean为非延迟初始化,如果没有配置,  
                //则该Bean为预实例化  
                else if (Lazy.class.equals (qualifier)) {
                    abd.setLazyInit (true);
                }
                //如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一  
                //个autowiring自动依赖注入装配限定符,该Bean在进autowiring  
                //自动依赖注入装配时,根据名称装配限定符指定的Bean  
                else {
                    abd.addQualifier (new AutowireCandidateQualifier (qualifier));
                }
            }
        }
        //创建一个指定Bean名称的Bean定义对象,封装注解Bean定义类数据  
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder (abd, beanName);
        //根据注解Bean定义类中配置的作用域,创建相应的代理对象  
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode (scopeMetadata, definitionHolder, this.registry);
        //向IoC容器注册注解Bean类定义对象   			 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);  
}

(2)从上面的源码我们可以看出,注册注解Bean定义类的基本步骤:

​ a、需要使用注解元数据解析器解析注解Bean中关于作用域的配置。

​ b、使用 AnnotationConfigUtils的processCommonDefinitionAnnotations方法处理注解Bean定义类中通用的注解。

​ c、使用 AnnotationConfigUtils的applyScopedProxyMode 方法创建对于作用域的代理对象。

​ d、通过 BeanDefinitionReaderUtils 向容器注册Bean。

​ 下面我们继续分析这3步的具体实现过程

(3)AnnotationScopeMetadataResolver解析作用域元数据:

​ AnnotationScopeMetadataResolver通过processCommonDefinitionAnnotations方法解析注解Bean定义类的作用域元信息,即判断注册的Bean是原生类型(prototype)还是单态(singleton)类型

其源码如下:

//解析注解Bean定义类中的作用域元信息  
    public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        ScopeMetadata metadata = new ScopeMetadata ();
        if (definition instanceof AnnotatedBeanDefinition) {
            AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
            //从注解Bean定义类的属性中查找属性为”Scope”的值,即@Scope注解的值  
            // annDef.getMetadata().getAnnotationAttributes方法将Bean  
            //中所有的注解和注解的值存放在一个map集合中  
            Map<String, Object> attributes =
                    annDef.getMetadata ().getAnnotationAttributes (this.scopeAnnotationType.getName ());
            //将获取到的@Scope注解的值设置到要返回的对象中  
            if (attributes != null) {
                metadata.setScopeName ((String) attributes.get ("value"));
                //获取@Scope注解中的proxyMode属性值,在创建代理对象时会用到  
                ScopedProxyMode proxyMode = (ScopedProxyMode) attributes.get ("proxyMode");
                //如果@Scope的proxyMode属性值为null、DEFAULT或者NO  
                if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
                    //设置proxyMode为NO  
                    proxyMode = this.defaultProxyMode;
                }
                //为返回的元数据设置proxyMode  
                metadata.setScopedProxyMode (proxyMode);
            }
        }
        //返回解析的作用域元信息对象  
        return metadata;
}

​ 上述代码中的 annDef.getMetadata().getAnnotationAttributes方法就是获取对象中指定类型的注解的值。

(4)AnnotationConfigUtils处理注解Bean定义类中的通用注解:

​ AnnotationConfigUtils类的processCommonDefinitionAnnotations在向容器注册Bean之前,首先对注解Bean定义类中的通用Spring注解进行处理

源码如下:

	//处理Bean定义中通用注解  
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
        //如果Bean定义中有@Primary注解,则为该Bean设置为autowiring自动依赖注入//装配的首选对象  
        if (abd.getMetadata ().isAnnotated (Primary.class.getName ())) {
            abd.setPrimary (true);
        }
        //如果Bean定义中有@Lazy注解,则将该Bean预实例化属性设置为@lazy注解的值  
        if (abd.getMetadata ().isAnnotated (Lazy.class.getName ())) {
            Boolean value = (Boolean) abd.getMetadata ().getAnnotationAttributes (Lazy.class.getName ()).get ("value");
            abd.setLazyInit (value);
        }
        //如果Bean定义中有@ DependsOn注解,则为该Bean设置所依赖的Bean名称,  
        //容器将确保在实例化该Bean之前首先实例化所依赖的Bean  
        if (abd.getMetadata ().isAnnotated (DependsOn.class.getName ())) {
            String[] value = (String[]) abd.getMetadata ().getAnnotationAttributes (DependsOn.class.getName ()).get ("value");
            abd.setDependsOn (value);
        }
}

(5)AnnotationConfigUtils根据注解Bean定义类中配置的作用域为其应用相应的代理策略:

AnnotationConfigUtils类的applyScopedProxyMode方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用

源码如下:

	//根据作用域为Bean应用引用的代码模式  
    static BeanDefinitionHolder applyScopedProxyMode(
            ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
        //获取注解Bean定义类中@Scope注解的proxyMode属性值  
        ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode ();
        //如果配置的@Scope注解的proxyMode属性值为NO,则不应用代理模式  
        if (scopedProxyMode.equals (ScopedProxyMode.NO)) {
            return definition;
        }
        //获取配置的@Scope注解的proxyMode属性值,如果为TARGET_CLASS,则返  
        //回true,如果为INTERFACES,则返回false  
        boolean proxyTargetClass = scopedProxyMode.equals (ScopedProxyMode.TARGET_CLASS);
        //为注册的Bean创建相应模式的代理对象  
        return ScopedProxyCreator.createScopedProxy (definition, registry, proxyTargetClass);
    }

​ 这段为Bean引用创建相应模式的代理,如果在Spring面向切面编程(AOP)中涉及到再详细分析,这里不做深入的分析。

(6)BeanDefinitionReaderUtils向容器注册Bean:

4、AnnotationConfigApplicationContext扫描指定包及其子包下的注解Bean:

​ 当创建注解处理容器时,如果传入的初始参数是注解Bean定义类所在的包时,注解容器将扫描给定的包及其子包,将扫描到的注解Bean定义载入并注册。

(1)Spring中常用的注解:

​ a、Component注解:(把普通pojo实例化到spring容器中,相当于配置文件中的)
​ 泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
    String value() default "";
}

​ b、Service注解:@service 服务(注入dao)

​ 用于标注服务层,主要用来进行业务的逻辑处理

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Service {  
	String value() default "";  
}

​ c、Controller注解: @controller 控制器(注入服务)
​ 用于标注控制层,相当于struts中的action层

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Controller {  
    String value() default "";  
}

​ d、Repository注解: 用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件。

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Repository {  
    String value() default "";  
}

通过分析Spring这4个常用的注解源码,我们看到:@Service、@Controller和@Repository注解都添加了一个@Component注解,因此他们都属于@Component注解。

(2)ClassPathBeanDefinitionScanner扫描给定的包及其子包:

AnnotationConfigApplicationContext通过调用类路径Bean定义扫描器ClassPathBeanDefinitionScanner扫描给定包及其子包下的所有类,主要源码如下:

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
        //创建一个类路径Bean定义扫描器  
        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
            this (registry, true);
        }
        //为容器创建一个类路径Bean定义扫描器,并指定是否使用默认的扫描过滤规则。  
        //即Spring默认扫描配置:@Component、@Repository、@Service、@Controller  
        //注解的Bean,同时也支持JavaEE6的@ManagedBean和JSR-330的@Named注解  
        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
            //调用父类ClassPathScanningCandidateComponentProvider构造方法设置过滤规则  
            super (useDefaultFilters);
            Assert.notNull (registry, "BeanDefinitionRegistry must not be null");
            //为容器设置加载Bean定义的注册器  
            this.registry = registry;
            //如果注册器是资源加载器,则为容器设置资源加载器  
            if (this.registry instanceof ResourceLoader) {
                setResourceLoader ((ResourceLoader) this.registry);
            }
        }
        //调用类路径Bean定义扫描器入口方法  
        public int scan(String... basePackages) {
            //获取容器中已经注册的Bean个数  
            int beanCountAtScanStart = this.registry.getBeanDefinitionCount ();
            //启动扫描器扫描给定包  
            doScan (basePackages);
            //注册注解配置(Annotation config)处理器  
            if (this.includeAnnotationConfig) {
                AnnotationConfigUtils.registerAnnotationConfigProcessors (this.registry);
            }
            //返回注册的Bean个数  
            return this.registry.getBeanDefinitionCount () - beanCountAtScanStart;
        }
        //类路径Bean定义扫描器扫描给定包及其子包  
        protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
            //创建一个集合,存放扫描到Bean定义的封装类  
            Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder> ();
            //遍历扫描所有给定的包  
            for (String basePackage : basePackages) {
                //调用父类ClassPathScanningCandidateComponentProvider的方法  
                //扫描给定类路径,获取符合条件的Bean定义  
                Set<BeanDefinition> candidates = findCandidateComponents (basePackage);
                //遍历扫描到的Bean  
                for (BeanDefinition candidate : candidates) {
                    //获取Bean定义类中@Scope注解的值,即获取Bean的作用域  
                    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata (candidate);
                    //为Bean设置注解配置的作用域  
                    candidate.setScope (scopeMetadata.getScopeName ());
                    //为Bean生成名称  
                    String beanName = this.beanNameGenerator.generateBeanName (candidate, this.registry);
                    //如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,  
                    //设置Bean的自动依赖注入装配属性等  
                    if (candidate instanceof AbstractBeanDefinition) {
                        postProcessBeanDefinition ((AbstractBeanDefinition) candidate, beanName);
                    }
                    //如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解  
                    if (candidate instanceof AnnotatedBeanDefinition) {
                        //处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过  AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);  
                    }
                    //根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突  
                    if (checkCandidate (beanName, candidate)) {
                        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder (candidate, beanName);
                        //根据注解中配置的作用域,为Bean应用相应的代理模式  

                        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode (scopeMetadata, definitionHolder, this.registry);
                        beanDefinitions.add (definitionHolder);
                        //向容器注册扫描到的Bean  
                        registerBeanDefinition (definitionHolder, this.registry);
                    }
                }
            }
            return beanDefinitions;
        }  
   ……
    }

义类读取器时已经分析过 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
if (checkCandidate (beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder (candidate, beanName);
//根据注解中配置的作用域,为Bean应用相应的代理模式

                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode (scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add (definitionHolder);
                    //向容器注册扫描到的Bean  
                    registerBeanDefinition (definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }  

……
}


类路径Bean定义扫描器ClassPathBeanDefinitionScanner主要通过findCandidateComponents方法调用其父类ClassPathScanningCandidateComponentProvider类来扫描获取给定包及其子包下的类。
发布了37 篇原创文章 · 获赞 7 · 访问量 1198

猜你喜欢

转载自blog.csdn.net/zy13765287861/article/details/103217060