Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
IoC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护。反转即获得依赖对象的过程被反转了,控制被反转后,获得依赖对象的过程由自身管理变为了由IoC容器主动注入。
DI:依赖注入,是IoC的一种实现方式。即是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。
目的:创建对象并且组装对象之间的关系。
Spring注入是指在启动Spring容器加载bean配置的时候,就完成对变量的赋值行为
1.设值注入
2.构造注入
设值注入:property,类中要有set方法
若property中的name属性为injectionDao,其在执行的时候就是调用setInjectionDao()这个方法
构造注入:constructor-arg,类中要有构造方法,且要传入对应的参数
若constructor-arg中的name属性为injectDao,则类中的构造方法的参数名必须为injectDao
//设值注入
<bean id="injectionService" class="com.lmr.spring.ioc.injection.service.InjectionServiceImpl">
<property name="injectionDao" ref="injectionDao"></property>
</bean>
//构造注入
<bean id="injectionService" class="com.lmr.spring.ioc.injection.service.InjectionServiceImpl">
<constructor-arg name="injectionDao" ref="injectionDao"></constructor-arg>
</bean>
public class InjectionServiceImpl implements InjectionService{
private InjectionDao injectionDao;
//设值注入
// public void setInjectionDao(InjectionDao injectionDao) {
// this.injectionDao = injectionDao;
// }
//构造器注入
public InjectionServiceImpl(InjectionDao injectionDao) {
// TODO Auto-generated constructor stub
this.injectionDao=injectionDao;
}
@Override
public void save(String arg) {
// TODO Auto-generated method stub
//模拟业务操作
System.out.println("Service层接收数据: "+arg);
injectionDao.save(arg);
}
}
Bean的作用域 Scope
参数:
singleton 单例,指一个Bean容器中只存在一份(若对一个Bean实例化两个对象,则前者的内容会被后者覆盖,即两个对象的值都相同,引用的是同一个内存块)
prototype 每次请求(每次使用)创建新的实例,destroy方式不生效
request 每次http请求创建一个实例且仅在当前request内有效
session 同上,每次http请求创建,当前session内有效
global session 基于portlet的web中有效(portlet定义了global session),如果是在web中,同session
测试singleton和prototype作用域
public class BeanScope {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void print(){
System.out.println(this.hashCode()+" - - "+this.id);
}
public void say(){
System.out.println("BeanScope say : "+this.hashCode());//通过hashCode()方法来判断通过容器生成的对象是否为同一个对象
}
}
@Test
public void TestSay(){
BeanScope beanscope1=super.getBean("beanScope");
beanscope1.say();
beanscope1.setId(2);
beanscope1.print();
BeanScope beanscope2=super.getBean("beanScope");
beanscope2.say();
beanscope2.setId(3);
beanscope2.print();
beanscope1.say();
beanscope1.print();
}
<bean id="beanScope" class="com.lmr.spring.bean.BeanScope" scope="singleton"></bean>
结果:两个对象的hashCode值和id值都一样,引用的是同一内存块
BeanScope say : 858242339
858242339 - - 2
BeanScope say : 858242339
858242339 - - 3
BeanScope say : 858242339
858242339 - - 3
<bean id="beanScope" class="com.lmr.spring.bean.BeanScope" scope="prototype"></bean>
结果:两个对象的hashCode值和id值都不一样,后者没有影响到前者
BeanScope say : 1007653873
1007653873 - - 2
BeanScope say : 836514715
836514715 - - 3
BeanScope say : 1007653873
1007653873 - - 2
Bean的生命周期
定义
graph LR
定义-->初始化
初始化-->使用
使用-->销毁
初始化
1.实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法
2.配置init-method
销毁
1.实现org.springframework.beans.factory.DisposableBean接口,覆盖destroy方法
2.配置destroy-method
<bean id="beanLifeCycle" class="com.lmr.spring.bean.BeanLifeCycle" init-method="Start" destroy-method="Stop"></bean>
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BeanLifeCycle implements InitializingBean,DisposableBean{
//配置全局默认初始化、销毁方法
public void defaultInit(){
System.out.println("Bean Default Init");
}
public void defaultDestory(){
System.out.println("Bean Default Destory");
}
//实现InitializingBean、DisposableBean接口
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("Bean Init");
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("Bean Destroy");
}
//配置init-method、destroy-method
public void Start(){
System.out.println("Bean Start");
}
public void Stop(){
System.out.println("Bean Stop");
}
}
配置全局默认初始化、销毁方法(去检测Bean中是否有对应的方法,若有则执行,没有也不会报错)
<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-3.0.xsd"
default-init-method="defaultInit" default-destroy-method="defaultDestory">
</beans>
若Bean实现了InitializingBean、DisposableBean接口或者配置了init-method、destroy-method,则全局默认初始化、销毁方法将不起作用,不会执行。若Bean同时实现了InitializingBean、DisposableBean接口又配置了init-method、destroy-method,则按先后顺序执行,先执行接口的方法,再执行配置的方法
Aware
使实现了Aware接口的bean在被初始化之后,可以获取相应资源
ApplicationContextAware
BeanNameAware
public class BeanAware implements BeanNameAware,ApplicationContextAware{
private String beanname;
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
// TODO Auto-generated method stub
this.context=arg0;
System.out.println("BeanAware setApplicationContext : "+context.getBean(beanname).hashCode());
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
this.beanname=name;
System.out.println("BeanAware name : "+beanname);
}
}
Bean的自动装配(Autowiring)
参数:
No 不做任何操作
byName 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配;如果没有找到,则什么都不做
byType 如果容器中存在一个与指定属性类型相同的bean,那么将于该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生
Constructor 与byType方式类似,不同之处在于它应用与构造器参数。如果容器中没有找到与构造器参数类型一致的bean,那么抛出异常
<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-3.0.xsd"
default-autowire="byName">
<bean id="beanAutowireService" class="com.lmr.spring.bean.BeanAutowireService"></bean>
<bean id="beanAutowireDao" class="com.lmr.spring.bean.BeanAutowireDao"></bean>
</beans>
public class BeanAutowireService {
private BeanAutowireDao beanAutowireDao;
public void setBeanAutowireDao(BeanAutowireDao beanAutowireDao) {
System.out.println("setBeanAutowireDao");
this.beanAutowireDao = beanAutowireDao;
}
// public BeanAutowireService(BeanAutowireDao beanAutowireDao) {
// System.out.println("Constructor");
// this.beanAutowireDao = beanAutowireDao;
// }
public void say(String word){
System.out.println("BeanAutowireService : "+word);
beanAutowireDao.say(word);
}
}
@Test
public void testAutowire(){
BeanAutowireService beanAutowireService=super.getBean("beanAutowireService");
beanAutowireService.say("hello,Autowire");
}
结果:
setBeanAutowireDao
BeanAutowireService : hello,Autowire
BeanAutowireDao : hello,Autowire
Bean的资源文件的统一接口 Resources
ResourceLoader:资源加载器,所有的ApplicationContext都实现了该接口,可以直接获取资源
classpath:xx/xx/xx.xx 从classpath中加载
file:/xx/xx/xx.xx
http://xx/xx/xx.xx
(none):/xx/xx/xx.xx 依赖于ApplicationContext
public class BeanResource implements ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext=arg0;
}
public void resource() throws IOException{
Resource resource=applicationContext.getResource("classpath:config.xml");
// Resource resource=applicationContext.getResource("file:D:\\coding\\codeworksace\\Spring\\src\\main\\resources\\config.xml");
// Resource resource=applicationContext.getResource("url:http://docs.spring.io/spring-boot/docs/2.0.0.BUILD-SNAPSHOT/reference/htmlsingle/");
// Resource resource=applicationContext.getResource("config.xml");
System.out.println(resource.getFilename());
System.out.println(resource.contentLength());
FileInputStream input=new FileInputStream(resource.getFile());
byte[] bytes=new byte[1024];
while(input.read(bytes)!=-1){
System.out.println(new String(bytes));
}
input.close();
}
}
结果:
config.xml
306
<?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-3.0.xsd">
</beans>
注解
@Component是一个通用注解,可用于任何bean
@Repository,@Service,@Controller是更有针对性的注解
@Repository通常用于注解DAO类,即持久层
@Service通常用于注解Service类,即服务层
@Controller通常用于注解Controller类,即控制层(MVC)
引入以上注解时,默认的Bean的id为类名首字母变小写,BeanAnnotation->beanannotation;也可加入参数,参数的值即为Bean的id,@Component(“bean”)
@Scope作用域
默认为singleton
@Scope(“prototype”)
扫描器
过滤器
通过此扫描器,spring会扫描包下面的注解,通过各个注解的规则,来进行bean的自动装配
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lmr.spring.beanannotation"></context:component-scan>
</beans>
Bean的自动装配
@Required注解使用于bean属性的setter方法
该注解仅仅表示受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值
@Autowired注解可用于“传统”的setter方法,也可用于构造器或成员变量
用于成员变量上,就不需要setter方法了
默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过设置required=false来避免。每个类中只能有一个构造器被标记为required=true
@Autowired的自动注入策略是byType,按类型自动装配
@Qualifier注解
当按类型自动装配可能多个bean实例的情况,使用@Qualifier可以缩小范围,或者指定唯一,也可以用于指定单独的构造器参数或方法参数
其value值为所选bean的id,也可在xml中配置bean的qualifier值
<bean class="com.lmr.spring.beanannotation.injection.dao.InjectionDaoImplOne">
<qualifier value="daoOne"></qualifier>
</bean>
@Autowired()
@Qualifier("injectionDaoImplOne")
private InjectionDao injectionDao;
//设值注入
@Autowired()
public void setInjectionDao(@Qualifier("injectionDaoImplOne")InjectionDao injectionDao) {
this.injectionDao = injectionDao;
}
@Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName
@Autowired适用于fields,constructors,multi-argument methods这些允许在参数级别使用@Qualifier注解缩小范围的情况
@Resource使用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式时使用qualifiers
容器注解
@Bean 标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件中的
属性: name、init-method、destroy-method
@Configuration 配置
通常与@bean公用的注解是@configuration而不是@component。
在方法头加上@bean注解,然后方法返回一个bean实例,完成向springIOC容器中注册一个bean实例。
@Scope 默认是单例的
属性:value 作用域(singleton,prototype,session,request,global session)
proxyMode 代理方式,代理可以采取延迟解析策略(ScopedProxyMode.NO,ScopedProxyMode.INTERFACES,ScopedProxyMode.TARGET_CLASS)
@Configuration
public class AppConfig { //这里使用@Configuration注解,标识这个类相当于一个注册文件
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
加载外部资源文件
一种是Xml获取,一种是Java注解方式获取
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<context:property-placeholder location="classpath:/jdbc.properties" />
<bean class="org.springframework.jdbc.datasource.DiverManagerDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
@ImportResource
参数:value为声明properties的xml文件的路径
使用@Value(“${}”)给类中属性赋值
@Configuration
@ImportResource(value = { "classpath:/resourceconfig.xml" })
public class StoreConfig {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Bean(name="diverManager")
public DiverManager getDiverManager(){
return new DiverManager(username, password, url);
}
}
//resourceconfig.xml
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<context:property-placeholder location="classpath:/jdbc.properties" />
</beans>
JSR注解
使用JSR-250的注解
@Resource
默认的名称是从属性名或者setter方法得出
可通过name属性来将其解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
@PostConstruct 初始化方法
@PreDestroy 销毁方法
需要先注册CommonAnnotationBeanPostProcessor类;效果与xml配置的init-method ,destroy-method一致。
使用JSR-330标准注解(依赖注入注解)
扫描方式与Spring注解一致,需要依赖javax.inject包
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Inject 等效于@Autowired, 可以使用于类,属性,方法,构造器
@Named 如果想使用特定名称进行依赖注入,使用@Named
@Named与@Component是等效的,通常与@Inject一起使用,跟@Qualifier类似
@Service
public class JsrService {
// @Resource
// @Autowired
@Inject
private JsrDao jsrDao;
// @Resource
public void setJsrDao(@Named("jsrDao")JsrDao jsrDao) {
this.jsrDao = jsrDao;
}
public void save(String word){
System.out.println("JsrService : "+word);
jsrDao.save(word);
}
@PostConstruct
public void init(){
System.out.println("JsrService init !");
}
@PreDestroy
public void destory(){
System.out.println("JsrService destory !");
}
}