IoC,Spring的核心理念之一,的确这是一个陈词滥调的货色。然而明天呢!又从新复习之后,想再说说本人对IOC的一些想法。
IoC——Inversion of Control,管制反转。要想了解IoC还是要从其自身登程,首先就管制而言,管制是对谁的管制——是对象的管制。其次,反转是什么的反转或者说为什么要称做反转——是对象控制权反转。
对象管制,传统的形式就是程序员通过new
关键字的形式来生成一个对象,而后由程序员依据程序逻辑人为地管制对象的应用。从这里登程,就能够很好地了解什么是管制反转了。
所谓管制反转,就是将本来在程序员手中的对象创立和治理的权限交给了Spring IoC容器。也就是说,管制反转就是要转移程序员对对象的控制权,而在Spring当中的实现就是Spring IoC容器通过Xml或注解的形容生成或者获取对象,再由IoC容器对这些Bean进行治理。
所以,了解IoC(管制反转),就只须要记住,控制权由谁反转给了谁。
IoC容器
顶级IoC容器接口—BeanFactory
对于BeanFactory
,它的重要性源自于所有IoC容器都是间接或者间接派生自它。尽管,它的性能不是很弱小,然而从其源码当中却能够看出很多端倪。
public interface BeanFactory {
/**
工厂Bean的前缀,
用于判断获取的是FactoryBean还是FactoryBean所产生的实例 上面会有具体解释
**/
String FACTORY_BEAN_PREFIX = "&";
/**通过name 获取Bean**/
Object getBean(String name) throws BeansException;
/**通过name和Class类型 获取Bean**/
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
/**通过name和结构参数,也就是能够指定调用某个构造方法 获取Bean**/
Object getBean(String name, Object... args) throws BeansException;
/**通过Class类型 获取Bean**/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**通过Class类型和结构参数,同样能够指定调用某个构造方法 获取Bean**/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**返回一个被ObjectProvider包装的Bean**/
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
/**通过name判断是否在容器中有这个Bean**/
boolean 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, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**依据name找到Bean的Class类型**/
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**获取此Bean之外的别名**/
String[] getAliases(String name);
}
-
FACTORY_BEAN_PREFIX 在Spring当中,有一个叫做
FactoryBean
的接口,这个类有一个T getObject() throws Exception;
这样的办法,这个办法会返回一个对象实例。对于这个接口的实现类而言,通过BeanFactory
的getBean()
返回的Bean
是实现类自身的实例,还是getObject()
的返回实例就在于有没有前缀。有,返回FactoryBean
;没有,返回getObject()
的返回实例。
/**举个简略的例子
实现这样一个FactoryBean
用这样一个FactoryBean来创立一个咱们须要的User
**/
@Component("user")
public class UserFactoryBean implements FactoryBean<User> {
@Autowired
private User user;
@Override
public User getObject() throws Exception {
return user;
}
@Override
public Class<?> getObjectType() {
return user.getClass();
}
}
//测试方法
public static void test1(){
ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class);
//没有前缀
//失去的是User getObject() throws Exception的返回值
User user = (User) ctx.getBean("user");
System.out.println(user);
//有前缀
//失去的是UserFactoryBean的实例
UserFactoryBean userFactoryBean =
(UserFactoryBean) ctx.getBean("&user");
System.out.println(userFactoryBean);
}
这里只是简略的例子,用来阐明FACTORY_Bean_PREFIX的作用,FactoryBean更具体的用法,能够参考工厂模式当中工厂的作用。
- ObjectProvider 这是在spring4.3之后才呈现的一个接口,它次要作用是解决注入时Bean不存在或者Bean存在多个时呈现的异常情况。
//getIfAvailable()能够解决容器中没有userDao时的异样
public class UserService{
private UserDao userDao;
public UserService(ObjectProvider<UserDao> dao){
userDao = dao.getIfAvailable();
}
}
//5.1之后能够通过流式解决来解决容器中存在多个userDao状况
public class UserService{
private UserDao userDao;
public UserService(ObjectProvider<UserDao> dao){
userDao = dao.orderedStream()
.findFirst()
.orElse(null)
}
}
外围容器—ApplicationContext
学习过Spring的人,对ApplicationContext都不会生疏。它是BeanFactory的子(精确的说应该是孙子)接口之一,而咱们所应用到的大部分Spring IoC容器都是ApplicationContext的实现类。
Spring的源码很宏大,也很简单,所以倡议学习的时候,从某几个重点类开始,剖析其继承、扩大关系,以此横向开展对Spring的意识。
这里也就不再对ApplicationContext
的各个继承接口一一解释了,API文档外面都有: ApplicationContext。对于ApplicationContext
这个容器更多的是侧重于对它的利用介绍,就是如何通过这个容器来获取Bean。
通过一个简略的例子来理解一下:
//一般的JavaBean
public class User {
private Long id;
private String name;
private int age;
/**getter,setter,toString**/
}
//配置类,采纳注解的模式来配置Bean
@Configuration
public class UserConfig {
@Bean(name="user")
public User getBeanUser(){
User user = new User();
user.setId(1L);
user.setName("klasdq1");
user.setAge(18);
return user;
}
}
//测试类
public class IocTest {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class);
User user = ctx.getBean("user");//
System.out.println(user);
}
}
-
@Configuration
@Configuration
这个注解的作用就在于它标示的类领有一个或多个@Bean
润饰的办法,这些办法会被Spring容器解决,而后用于生成Bean或者服务申请。
//@Configuration的源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
从注解的源码当中能够看出,它有两个;一是value
,用于为配置类申明一个具体的Bean name。二是proxyBeanMethods
,用于指定@Bean
润饰的办法是否被代理。
- @Bean 这个注解只用在办法下面,用于Spring容器治理生成Bean。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
/** @deprecated */
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
@Bean
的参数中重要的就是name(value),其含意在于为Bean申明具体的名称,一个Bean能够有多个名称,这也是为什么BeanFactory
中有一个getAliases()
办法。其余参数,看名字就晓得什么用意,就不再多解释了。
-
AnnotationConfigApplicationContext 这是
ApplicationContext
类的具体实现类之一,用于注解模式的Bean的生成。与之绝对应的还有ClassPathXmlApplicationContext
从XML文件中获取Bean。
Bean的拆卸
在Spring当中对于Bean的拆卸容许咱们通过XML或者配置文件拆卸Bean,但在Spring Boot中罕用注解的模式,为了不便Spring Boot开发的须要,就不再应用XML的模式了。
间接看例子:
//配置JavaBean
@Component("klasdq2")
public class User {
@Value("2")
private Long id;
@Value("klasdq2")
private String name;
@Value("19")
private int age;
/**getter,setter,toString**/
}
//配置类扫描拆卸Bean
@Configuration
@ComponentScan
public class UserConfig {
}
//测试类
public class IocTest {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(UserConfig.class);
User user = (User) ctx.getBean("klasdq2");
System.out.println(user);
}
-
@Component
@Component
的源码很简略:@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface Component { String value() default ""; }
参数当中只有一个
value
,用于申明Bean的名字(标识)。这里又呈现一个新的注解@Indexed
,顾名思义这个注解就是减少一个索引,这是因为Spring Boot当中大量采纳扫描的模式来拆卸Bean之后,扫描的Bean越多,解析工夫就越长,为了进步性能,在5.0版本的时候就引入了这样一个注解。 - @Value
@Target({ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
String value();
}
这个注解能够用在字段、办法、办法参数、注解上,通过一个表达式或者具体字符串为其传入相应的值。@Value
是一个性能十分弱小的注解,倡议对其多做理解。
其性能次要包含以下几种:
- 注入一般字符串
- 书写SpEL表达式,如:@Value(“#{person.name}”),能够从配置文件、Bean属性、调用办法等等失去数据。
- 注入Resource,如:
@Value("classpath:com/demo/config.txt")
应用Resource类型接管 - 注入URL资源,如:
@Value("http://www.baidu.com")
应用Resource类型接管
- @ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
/**
* 这个参数是ComponetScan注解最罕用的,其作用就是申明扫描哪些包,
* 通过扫描,将含有@Componet注解的Bean装入Spring容器中。
* value和basePackages成果一样,其默认值为配置类所在包及其子包。
**/
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
/**扫描哪些类**/
Class<?>[] basePackageClasses() default {};
/**Bean Name生成器:自定义bean的命名生成规定**/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**作用域解析器**/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
/**作用域代理**/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
/**资源的匹配模式,默认就是.Class**/
String resourcePattern() default "**/*.class";
/**是否启用默认过滤器(源码上面自定义的过滤器)**/
boolean useDefaultFilters() default true;
/**合乎过滤器条件的组件 才会扫描**/
ComponentScan.Filter[] includeFilters() default {};
/**合乎过滤器条件的组件 不会扫描**/
ComponentScan.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 {};
}
}
例如:
@ComponetScan(basePackages="com.klasdq.sb.service.*"
,excludeFilters=(@Filter(classes="UtilService.Class")))
这样的一个例子中,basePcakages
指定了扫描service
包下所有具体@Component
注解的Service Bean(@Service
蕴含了@Component
)。而excludeFilters
定义应用@Filter
过滤掉UtilService.Class
。其余的参数应用,能够参数API文档中的介绍,大同小异。
-
@ComponetScans 这个注解也能够用于扫描组件,能够定义
@ComponetScan
,如:
@ComponentScans(value = { @ComponentScan(value = "com.klasdq.sb.service.*"),
@ComponentScan(value = "com.klasdq.sb.dao.*", excludeFilters=(@Filter(classes="UtilDao.Class")) })
通过这样一种形式来定义多个扫描组件,使得扫描更加准确。因为@ComponentScan(value="com.klasdq.sb.*")
全包扫描的形式尽管写起来简略,然而消耗的工夫代价却是极大的。
最初,最近很多小伙伴找我要Linux学习路线图,于是我依据本人的教训,利用业余时间熬夜肝了一个月,整顿了一份电子书。无论你是面试还是自我晋升,置信都会对你有帮忙!
收费送给大家,只求大家金指给我点个赞!
电子书 | Linux开发学习路线图
也心愿有小伙伴能退出我,把这份电子书做得更完满!
有播种?心愿老铁们来个三连击,给更多的人看到这篇文章
举荐浏览:
- 干货 | 程序员进阶架构师必备资源免费送
- 神器 | 反对搜寻的资源网站
发表回复