怎让做淘宝网站,微商城开发用华网天下首选,德阳网站建设,新的seo网站优化排名 排名#x1f3e1;Java码农探花#xff1a; #x1f525; 推荐专栏#xff1a;springboot学习
#x1f6f8;学无止境#xff0c;不骄不躁#xff0c;知行合一 文章目录 前言一、IoC容器的简介BeanFactory接口源码二、Bean装配扫描装配探索启动类条件装配自定义Bean总…
Java码农探花 推荐专栏springboot学习
学无止境不骄不躁知行合一 文章目录 前言一、IoC容器的简介BeanFactory接口源码二、Bean装配扫描装配探索启动类条件装配自定义Bean总结 前言
IoCInversion of Control,控制反转容器是 Spring 的核心可以说 Spring 是一种基于 IoC容器编程的框架。因为Spring Boot 是基于注解的开发 Spring IoC 所以我们就从全注解的方式来讲诉Bean装配。 一、IoC容器的简介 Spring IoC 容器是一个管理 Bean 的容器在 Spring 的定义中它要求所有的 IoC 容器都需要实现接口 BeanFactory它是一个顶级容器接口。 我们从源码讲诉。 BeanFactory接口源码
package org.springframework.beans.factory;import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;public interface BeanFactory {// 前缀String FACTORY_BEAN_PREFIX ;// 多个getBean方法Object getBean(String name) throws BeansException;T T getBean(String name, ClassT requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;T T getBean(ClassT requiredType) throws BeansException;T T getBean(ClassT requiredType, Object... args) throws BeansException;T ObjectProviderT getBeanProvider(ClassT requiredType);T ObjectProviderT getBeanProvider(ResolvableType requiredType);// 是否包含Beanboolean containsBean(String name);//是否单例boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否原型boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否类型匹配boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class? typeToMatch) throws NoSuchBeanDefinitionException;// 获取Bean的类型NullableClass? getType(String name) throws NoSuchBeanDefinitionException;// 获取Bean的别名NullableClass? getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;String[] getAliases(String name);
}分析
上诉源码中加入了中文注释通过它们就可以理解这些方法的含义。这里值得注意的是接口中的几个方法 首先我们看到了多个getBean 方法这也是IoC 容器最重要的方法之一 它的意义是从IoC 容器中获取Bean而从多个getBean方法中可以看到有按类型(bytype)获取Bean 的也有按名称(by name)获取 Bean 的这就意味着在 Spring IoC 容器中允许我们按类型或者名称获取 Bean。这对理解后面将讲到的Spring 的依赖注入Dependency Injection, DI 是十分重要的。isSingleton 方法则判断 Bean 是否在 Spring IoC 中为单例。这里需要记住的是在 Spring IoC 容器中默认的情况下 Bean 都是以单例存在的也就是使用 getBean 方法返回的都是同一个对象。与isSingleton 方法相反的是 isPrototype 方法如果它返回的是 true那么当我们使用 getBean 方法获取Bean 的时候 Spring IoC 容器就会创建一个新的 Bean 返回给调用者。 由于BeanFactory 的功能还不够强大因此 Spring 在 BeanFactory 的基础上 还设计了一个更为高级的接口 ApplicationContext。 它是 BeanFactory 的子接口之一 在 Spring 的体系中 BeanFactory 和ApplicationContext 是最为重要的接口设计在现实中我们使用的大部分 Spring IoC 容器是ApplicationContext 接口的实现类。 在图中可以看到 ApplicationContext 接口通过继承上级接口进而继承 BeanFactory 接口 但是在BeanFactory 的基础上扩展了消息国际化接口MessageSource、环境可配置接口 EnvironmentCapable、应用事件发布接口ApplicationEventPublish巳r 和资源模式解析接口ResourcePatternResolver所以它的功能会更为强大。在Spring Boot 当中我们主要是通过注解来装配Bean到 Spring IoC 容器中为了贴近 SpringBoot 的需要 这里不再介绍与 XML 相关的 IoC 容器而主要介绍一个基于注解的 IoC 容器它就是AnnotationConfigApplicationContext从名称就可以看出它是一个基于注解的 IoC 容器。 之所以研究它 是因为Spring Boot 装配和获取 Bean 的方法与它如出一辙。
例创建一个User类然后使用AnnotationConfigApplicationContext构建IoC容器
public class User {private Long id; private String userName;
/**setter and getter **/
}import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Con f工guration ;
import com.springboot.chapter3.po] o.User;
Configuration
public class AppConfig { Bean(name ”user” } public User ini tUser () { User user new User (); user. set Id (1L) ; user.setUserName (”aa”); return user;}
}Configuration 代表这是一个 Java 配置文件 Spring 的容器会根据它来生成IoC 容器去装配BeanBean 代表将 initUser 方法返回的 POJO 装配到 IoC 容器中而其属性name 定义这个 Bean 的名称如果没有配置它则将方法名称“initUser”作为 Bean 的名称保存到Spring IoC 容器中。
import org.apache. log4j .Logger;
import org. springframework.context.ApplicationContext;
import org. springframework.context annotation.AnnotationConfigApplicat工onContext;
import com.springboot.chapter3.po] o .User;
public class IoCTest { private static Logger log Logger.getLogger(IoCTest.class); publ工c static 飞roid main (String [] args) { ApplicationContext ctx new AnnotationConfigAppl丰cationContext(AppConfig. class);User user ctx.getBean(User.class); }
}代码中将Java 配置文件 AppConfig 传递给 AnnotationConfigApplicationContext 的构造方法这样它就能够读取配置了。然后将配置里面的Bean装配到IoC容器中于是可以使用 getBean方法获取对应的POJO。
二、Bean装配
扫描装配 上诉讲诉的User对象装配就是使用Bean装配。但是如果一个个的 Bean 使用注解Bean 注入 Spring loC 容器中那将是一件很麻烦的事情。好在Spring 还允许我们进行扫描装配 Bean 到 loC 容器中对于扫描装配而言使用的注解是Component和ComponentScan。Component 是标明l哪个类被扫描进入 Spring IoC 容器而ComponentScan则是标明采用何种策略去扫描装配Bean。 Component(”user)
public class User { Value(1) private Long id; Value(aa} private String userName;
/**setter and getter **/
}这里的注解Component表明这个类将被SpringIoC 容器扫描装配其中配置的“user则是作为Bean 的名称当然你也可以不配置这个字符串那么IoC容器就会把类名第一个字母作为小写其他不变作为Bean 名称放入到IoC 容器中注解Value则是指定具体的值使得Spring IoC给予对应的属性注入对应的值。为了让SpringIoC 容器装配这个类 需要改造类AppConfig
import org.springframework.context.annotat工on.ComponentScan;
import org.springframework.context.annotation Configuration;
Configuration
ComponentScan
public class AppConfig {
}这里加入了ComponentScan意味着它会进行扫描但是它只会扫描类AppConfig所在的当前包和其子包。也就是ComponentScan默认扫描当前类所在包及其子包。 所以User类的位置要注意。
测试
Applicat工onContext ctx new AnnotationConfigApplicationContext{AppConfig.class) ;
User user ctx.getBean(User.class);
log. info(user.getid());为了更加合理ComponentScan还允许我们自定义扫描的包我们看一下源码
package org.springframework.context.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;Retention(RetentionPolicy.RUNTIME)
Target({ElementType.TYPE})
Documented
//在一个类中可重复定义
Repeatable(ComponentScans.class)
public interface ComponentScan {// 定义扫描的包AliasFor(basePackages)String[] value() default {};//定义扫描的包AliasFor(value)String[] basePackages() default {};//定义扫描的类Class?[] basePackageClasses() default {};//Bean name生成器Class? extends BeanNameGenerator nameGenerator() default BeanNameGenerator.class;//作用域解析器Class? extends ScopeMetadataResolver scopeResolver() default AnnotationScopeMetadataResolver.class;//作用域代理模式ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;//资源匹配模式String resourcePattern() default **/*.class;//是否启用默认的过滤器boolean useDefaultFilters() default true;//当满足过滤器的条件时扫描Filter[] includeFilters() default {};//当不满足过滤器的条件时扫描Filter[] excludeFilters() default {};//是否延迟初始化boolean lazyInit() default false;//定义过滤器Retention(RetentionPolicy.RUNTIME)Target({})public interface Filter {//过滤器类型可以按注解类型或者正则式等过滤FilterType type() default FilterType.ANNOTATION;//定义过滤的类AliasFor(classes)Class?[] value() default {};AliasFor(value)Class?[] classes() default {};//匹配方式String[] pattern() default {};}
}分析
首先可以通过配置项basePackages定义扫描的包名在没有定义的情况下它只会扫描当前包和其子包下的路径还可以通过basePackageClasses 定义扫描的类其中还有 includeFilters 和 excludeFilters, includeFilters 是定义满足过滤器Filter条件的 Bean 才去扫描 excludeFilters 则是排除过滤器条件的 Bean它们都需要通过一个注解Filter 去定义它有一个type 类型这里可以定义为注解或者正则式等类型。 classes定义注解类 pattern 定义正则式类
所以得出三个扫描路径表示
ComponentScan (com.springboot.example.* )
ComponentScan(basePackages {com.springboot.example.pojo})
ComponentScan(basePackageClasses {User.class} ) 以及排除扫描包或类让其不被装配
//扫描example下所有包除了Service装配的类
//这样由于加入了 excludeFilters 的配置使标注了Service 的类将不被 IoC 容器扫描注入这样就可以把它类排除到 Spring IoC容器中了。
ComponentScan(basePackages {com.dragon.restart},excludeFilters {ComponentScan.Filter(classes Service.class)})探索启动类
事实上之前在 Spring Boot 的注解SpringBootApplication 也注入了ComponentScan这里不妨探索其源码
Target({ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
Inherited
SpringBootConfiguration
EnableAutoConfiguration
//自定义排除的扫描类
ComponentScan(excludeFilters {Filter(type FilterType.CUSTOM,classes {TypeExcludeFilter.class}
), Filter(type FilterType.CUSTOM,classes {AutoConfigurationExcludeFilter.class}
)}
)
public interface SpringBootApplication {//通过类型排除自动配置AliasFor(annotation EnableAutoConfiguration.class)Class?[] exclude() default {};//通过名称排除自动配置类AliasFor(annotation EnableAutoConfiguration.class)String[] excludeName() default {};//定义扫描包AliasFor(annotation ComponentScan.class,attribute basePackages)String[] scanBasePackages() default {};//定义被扫描的类AliasFor(annotation ComponentScan.class,attribute basePackageClasses)Class?[] scanBasePackageClasses() default {};显然通过它就能够定义扫描哪些包。但是这里需要特别注意的是它提供的exclude和excludeName两个方法是对于其内部的自动配置类才会生效的。为了能够排除其他类还可以再加入ComponentScan以达到我们的目的。
条件装配 例如在数据库连接池的配置中漏掉一些配置会造成数据源不能连接上。 在这样的情况下 IoC容器如果还进行数据源的装配 则系统将会抛出异常导致应用无法继续。这时倒是希望IoC容器不去装配数据源。为了处理这样的场景 Spring 提供了Conditional注解帮助我们而它需要配合另外一个接口Condition(org.springframework.context.annotation.Condition 来完成对应的功能。 装配的Bean
Bean(name dataSource, destroyMethod close )
Conditional(DatabaseConditional.class)
public DataSource getDataSource (
Value(${database.driverName}) String driver,
Value(${database.url}) String url,
Value(${database.username}) String username,
Value({database.password}) String password
){ Properties props new Properties(); props.setProperty(driver, driver); props setProperty(url, url); props.setProperty(username, username); props setProperty(password, password); DataSource dataSource null; try { dataSource BasicDataSourceFactory.createDataSource(props) ; ) catch (Exception e) { e.printStackTrace();}return dataSource;
}自定义DatabaseConditional类
public class DatabaseConditional implements Condition {
/**
* 数据库装配条件
*
* param context 条件上下文
* param metadata 注释类型的元数据
* return true 装配 Bean否则不装配
*/
Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //取出环境配置Environment env context.getEnvironment(); //判断属性文件是否存在对应的数据库配置return env.containsProperty(”database.driverName” ) env.containsProperty(”database.url”) env.containsProperty(” database.username”) env.containsProperty (”database.password);matches 方法首先读取其上下文环境 然后判定是否已经配置了对应的数据库信息。这样当这些都己经配置好后则返回true。这个时候Spring会装配数据库连接池的Bean否则是不装配的。
自定义Bean 现实的Java 的应用往往需要引入许多来自第三方的包 并且很有可能希望把第三方包的类对象也放入到Spring IoC 容器中这时Bean注解就可以发挥作用了。例如要引入一个DBCP数据源我们先在pom.xml上加入项目所需要DBCP包和数据库MySQL驱动程序的依赖。 dependency groupidorg.apache.commons/groupid artifactidcommons-dbcp2/artifactid
/dependency
dependency groupidmysql/groupid artifactidmysql-connector-ava/artifactid
/dependency这样 DBCP 和数据库驱动就被加入到了项目中接着将使用它提供的机制来生成数据源
Bean(name dataSource)
Conditional(DatabaseConditional.class)
public DataSource getDataSource (){ Properties props new Properties(); props.setProperty(driver, driver); props setProperty(url, url); props.setProperty(username, username); props setProperty(password, password); DataSource dataSource null; try { dataSource BasicDataSourceFactory.createDataSource(props) ; ) catch (Exception e) { e.printStackTrace();}return dataSource;
}这里通过Bean 定义了其配置项 name 为“dataSource“那么 Spring 就会把它返回的对象用名称“dataSource” 保存在 loC 容器中。当然 你也可以不填写这个名称那么它就会用你的方法名称作为Bean 名称保存到 IoC 容器中。通过这样就可以将第三方包的类装配到SpringIoC容器中了。 总结
以上就是SpringBoot的Bean装配的详细讲解。