基本原理
spring的基础是IOC和DI,其实IOC和DI是对同一件事从不同的方面进行描述的,两者在spring中是同一件事务。
IOC:控制反转,在这里就是指创建bean的主动权发生了转移,原来是由类主动创建bean,现在类不再创建bean,而是由框架去创建类,所以类的创建权限发生了转移。
DI:依赖注入,在这里是指每个类所需的外部实例,都是先向框架发出需求请求,然后由框架去将创建好的bean注入到类中,所以类是依赖于框架的。
AOP:面向切面,就是将一些公共功能分离出来的一种技术。
IOC/DI/AOP在spring中实现的技术基础是java动态代理和反射技术。
spring模块框架图如下所示:
spring版本变化
spring2.0版本
spring2.5版本
spring3.0 3.1 3.2
spring4.0 4.1 4.2 4.3
spring 5.0 最新版本
其中spring3.x 需要jdk5+ / spring4.x需要jdk6+
spring3.x相比于spring2.5.x的区别可分为以下几个方面:模块管理更加细致,分出的包更多;增加了一些新特性,对原来的一些功能进行了增强;针对java5的核心API升级。
spring装配
spring提供了三种装配机制:在xml中进行显示配置;在java中进行显示配置;隐式的bean发现机制和自动装配机制。(注解是java提供的一种机制,允许你对类进行标记,然后在运行时动态操作标记的类,注解机制包括三部分,注解声明,被注解的类,操作注解的代码(通过反射获取类上的注解))
工作中三种方式的梳理:
1.在xml中进行bean的声明和配置;
2.使用javaconfig在java类中进行配置,此处需要使用的@Configuration/@ComponentScan/@Import/@ImportResource注解,其中@Configuration注解表明该类是spring配置类,@ComponentScan表示启动组件扫描,@Import表示导入其他的配置类,@ImortResource表示导入其他的配置文件。
3.混合使用xml和javaconfig,就是使用@Component/@Bean/@Name/@Autowired/@Inject注解配置bean类,同时使用xml配置公共属性,并启动组件扫描。@Component表示该类是一个组件,spring将自动创建该组件实例,@Autowired表示注入组件实例,@Name和@Component功能类似,@Inject和@Autowired功能类似,但@Name和@Inject是Java规范中提供的注解。
java依赖注入和spring注解的不同点和相同点可以参考:
http://www.cnblogs.com/liangxiaofeng/p/6390868.html
http://bhdweb.iteye.com/blog/1663907
http://blog.csdn.net/DL88250/article/details/4838803
当spring引入第三方库中的组件时,此时就不能使用@Component和@Autowired注解了,若使用JavaConfig引入第三方框架时,@Bean注解将会使用到,它一般用在一个方法上,表示方法返回的实例将作为spring中的实例使用,这样就可以在其他类中,像使用spring其他实例那样直接@Autowired即可。
@Autowired和@Resoure的异同点:http://bhdweb.iteye.com/blog/1663907
spring中的profile
在程序开发中,往往有几个好几环境比如:开发环境、测试环境、生产环境,每个环境的数据库配置等信息都不相同,如果每次都通过修改程序和配置文件来切换开发环境,非常容易出现错误,spring的profile就是为了解决不同环境之间切换问题的。
profile在spring3.1中就出现了,不过spring3.1中@Profile只能应用在类上,而在spring3.2中@Profile已经可以在方法上使用。使用如下所示:
@Configuration public class DataSourceConfig{ @Bean @Profile("dev") public DataSource embeddedDataSource(){}
@Bean @Profile("prod") public DataSource jndiDataSource(){}
}
上面的类中定义了两个profile,但是只有当规定的profile被激活时,相应的bean才会生成。spring在确定激活profile时,需要依赖两个独立的属性,spring.profiles.active和spring.profiles.default。spring的处理机制是,先查找active确定激活的profile,若没有设置active属性,再去查找default。有多种方式设置两个属性:
作为DispatcherServerlet的初始化属性; 作为web应用的上下文参数; 作为JNDI条目 作为环境变量(需要用到ServletContextListener,重写contextInitialized方法) 作为jvm的系统属性 在测试类上,使用@ActiveProfiles注解设置。
激活示例和xml配置示例可以参考:http://www.cnblogs.com/yw0219/p/5990056.html http://www.jfox.info/springprofile%E6%BF%80%E6%B4%BB%E5%A4%84%E7%90%86.html
Spring中Bean的特殊用法
条件化的bean
条件化bean指,只有当满足一定条件时才会创建bean,但是在spring4之前,很难实现这一点,但是spring4引入了一个新的@Conditional注解,它可以用到带有@Bean的注解方法上,如果给定的条件计算结果为true,就会创建bean,否则的话,这个bean会被忽略。spring4重构了@Profile,@Profile使用@Conditional注解实现,代码如下所示:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) @Documented @Conditional(ProfileCondition.class) public @interface Profile{ String[] value(); }
class ProfileCondition implements Condition{ public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata){ if(context.getEnvironment()!=null){ MultiValueMapattrs=metadata.getAllAnnotationAttributes(Profile.class.getName()); if(attrs!=null){ for(Object value:attrs.get("value")){ if(context.getEnvironment().acceptsProfiles((String[])value)){ return true; } } return false;}}return true;}}
自动装配的歧义性处理
当发生歧义性时,spring提供了多种可供选择的方案来解决该问题。
1.标示首选的bean:当spring遇到歧义性时,spring将会使用首选的bean,而不是其他可选的bean.示例如下所示:
@Component @Primary public class IceCream implements Dessert{...}
2.限定自动装配的bean:spring提供了@Qualifier限定符用于缩小可选的bean。@Qualifier可以和@Autowired与@Inject协同使用。如下所示:
@Autowired @Qualifier("iceCream") public class setDessert( Dessert dessert){...}
@Qualifier注解所设置的参数就是想要注入的bean的ID,所有使用@Component注解声明的类都会创建为bean,并且bean的ID为首字目小写的类。
也可以创建自定义的限定符如下所示:
@Component @Qualifier("cold") public class IceCream implements Dessert{...}
此时IceCreate的bean ID的为:cold.
3.自定义限定符注解
java不允许在同一个条目上重复出现相同类型的多个注解,但是java8注解允许出现重复的注解,只要这个注解本身的定义的时候带有@Repeatable注解就可以,不过@Qualifier注解并没有在定义时添加@Repeatable。@Qualifier可以允许我们定义自己的限定符注解,举例如下所示:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) @Qualifier public @interface Cold{ }
下面我们就可以使用@Cold注解代替@Qualifierz("cold")
bean的作用域
spring定义了多种作用域,可以基于这些作用域创建bean.
单例:在整个应用中,只创建bean的一个实例。 原型:每次注入或者通过spring应用上下文获取的时候,都会创建一个新的bean的实例。 会话:在web应用中为每个会话创建一个bean实例。 请求:在web应用中,为每个请求创建一个bean实例。
单例是默认的作用域,如果想修改bean的作用域可是使用@Scope注解,示例如下所示:
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Cold{.... }
@Scope还有一个proxyMode属性,该属性是解决了将一个会话或请求作用域的bean注入到单例bean中。举例如下所示:
@Component @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES) public class ShoppingCart implements Cart{.... }
@Component public class StoreService{ @Autowired Cart cart;
}
因为StoreService是单例模式,而ShoppingCart是会话作用域,在spring创建StoreService时,ShoppingCart并不存在,因此通过设置proxyMode属性,spring不会将实际的ShoppingCart注入StoreService中,spring会注入一个代理,只有当StoreService调用cart时,代理会对其进行懒解析并将调用委托给真正的ShoppingCart