您的位置:首页 > 编程语言 > Java开发

Spring详解之一:ICP(控制反转)

2016-05-29 00:00 330 查看
摘要: Spring是一个开源的控制反转(Inversion of Control-IOC)和面向切面(AOP)的容器框架,其主要作用是简化企业开发

#Spring
##Spring中的Bean的生命周期过程

通过Bean对应的Java类的构造器或者工厂方法创建Bean实例

为Bean的属性设置值和对其他Bean的引用(如Spring的xml文件中该Bean配置有properties子节点)

将Bean实例传递给Bean后置处理器(实现BeanPostProcessor接口的类,并在Spring的xml文件中配置如下Bean标签)的postProcessBeforeInitialization()方法操作

<!-- com.abinge.spring.beans.MyBeanPostProcessor为自定义的实BeanPostProcessor接口的类 -->
<!-- 该bean不用配置id,因为其对Spring容器中的全部Bean对象都会起作用进行操作处理 -->
<bean class="com.abinge.spring.beans.MyBeanPostProcessor"/>


调用Bean的初始化方法(init-method)

将Bean实例传递给Bean后置处理器(实现BeanPostProcessor接口的类)的postProcessAfterInitialization()方法操作

使用Bean实例

当Spring容器关闭时,调用Bean的销毁方法(destroy-method)
###定义

Spring是一个开源的控制反转(Inversion of Control-IOC)和面向切面(AOP)的容器框架

其主要作用是简化企业开发
###控制反转IOC

定义:控制反转IOC指应用本身(PersonServiceBean)不负责依赖对象(PersonDao)的创建及维护,依赖的对象的创建及维护由外部容器负责,这样对依赖对象的控制权就由应用本身转移到了外部容器,这种控制权的转移就是所谓的反转。

public class PersonServiceBean{

private PersonDao personDao = new PersonDao();

public void save(Person person){
personDao.save(person);
}
}


在上面的例子中,PersonDao是在应用内部创建及维护的,故不是控制反转。若使用控制反转的话,则上面的例子需要进行修改。
###依赖注入(Dependency Injection-DI)

定义:在运行期,由外部容器动态的将依赖对象注入到组件中

对上述的代码进行控制反转下的修改,改为由外部容器负责创建:

通过setter方法进行依赖注入

//定义一个PersonServiceBean类
public class PersonServiceBean{
private PersonDao personDao;

public void setPersonDaoBean(PersonDao personDao){
this.personDao = personDao;
}

public void save(Person person){
personDao.save(person);
}
}
//在Spring配置文件中定义bean:
//1、
<bean id="person" class="com.abinge.springtest.Person"/>
<bean id="personDao" class="com.abinge.springtest.PersonDao"/>
<bean id="personServiceBean" class="com.abinge.springtest.PersonServiceBean">
<property name="personDao" ref="personDao"/>
</bean>
//2、使用内部bean,此时PersonDao不能被其他bean使用
<bean id="person" class="com.abinge.springtest.Person"/>
<bean id="personServiceBean" class="com.abinge.springtest.PersonServiceBean">
<property name="personDao"/>
<bean class="com.abinge.springtest.PersonDao"/>
</property>
</bean>

//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
PersonServiceBean personServiceBean = (Test)context.getBean("personServiceBean");
Person person = (Test)context.getBean("person");
personServiceBean.save(person);


通过构造器进行依赖注入

//定义一个PersonServiceBean类
public class PersonServiceBean{
private PersonDao personDao;

public PersonServiceBean(PersonDao personDao){
this.personDao = personDao;
}

public void save(Person person){
personDao.save(person);
}
}
//在Spring配置文件中定义bean:
<bean id="person" class="com.abinge.springtest.Person"/>
<bean id="personDao" class="com.abinge.springtest.PersonDao"/>
<bean id="personServiceBean" class="com.abinge.springtest.PersonServiceBean">
<constructor-arg index="0" ref="personDao" type="personDao"/>
</bean>

//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
PersonServiceBean personServiceBean = (Test)context.getBean("personServiceBean");
Person person = (Test)context.getBean("person");
personServiceBean.save(person);


说明:

1、constructor-arg代表指定的构造器函数中的一个参数

2、可以利用index(参数的位置索引,从0开始),type(参数类型),ref(参数为依赖bean对象时,赋值为对应的bean标签的id),value(参数为基本数据类型时,可直接赋值)来指定唯一的构造器

3、如果一个bean的配置中没有constructor-arg属性,则必须利用默认的构造函数创建对象

4、所以在写一个javabean的时候,应该提供属性的setter方法,默认的构造器,带参数的构造器

使用Field注入(用于注解方式)

###Spring注入依赖对象的装配方式

手工装配(推荐使用)—— 存在两种编程方式

在xml配置文件中,通过在bean节点下配置,如上面展示的前2中注入方式

在Java代码中使用@Autowired或者@Resource注解的方式进行装配,但是我们需要在Spring的xml配置文件中添加如下配置:

<beans
……>
<context:annotation-config/>
</beans>


#####自动装配(容易出现不可预知的错误)

实现方式:在需要注入依赖对象的应用,在Spring的xml文件中对应bean标签中定义autowire属性

//对PersonServiceBean中的PersonServiceDao属性会按照类型自动装配注入
<bean id="personServiceBea" class="com.abinge.PersonServiceBean" autowire="byType"/>


autowire属性的值:

byType——按类型装配,在容器中寻找该类型匹配的bean,如果找到多个则抛出异常,如果没有找到即属性值为null

byName——按名称装配,在容器中寻找该名称匹配的bean,如果没有找到即属性值为null

constructor——与byType类似,不同之处在于它应用于构造器参数,如果构造器参数中没有找到与构造器参数类型一致的bean则抛出异常

autodetect——通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自行装配。如果发现默认的构造器,那就使用byType方式进行自行装配
#####手动装配:@Autowired和@Resource注解比较

@Autowired默认按照类型装配

可用于类的属性字段上,也可用于属性字段的setter方法上

@Autowired(required=false) @Qualifier("personDao");
private PersonDao personDao;//注解在字段上
@Autowired
public void setPersonDao(PersonDao personDao){//注解在属性字段的setter方法上
this.personDao = personDao;
}


默认情况下要求依赖对象必须存在,如果要允许依赖对象可以为null值时,可设置required属性为false

如果想让@Autowired按名称来装配的话,可以结合@Qualifier注解一起使用,如:

@Autowired(required=false) @Qualifier("personDao");
private PersonDao personDao;


@Resource默认按名称装配,当找不到与名称匹配的bean时,会按类型装配

也可用于类的属性字段上,也可用于属性字段的setter方法上

默认按名称装配,同时名称可以通过其本身的属性name指定,如果没有指定的话:

1、当@Resource注解注在属性字段上时,默认取字段的名称作为匹配bean名称寻找依赖对象
2、当@Resource注解注在属性的setter方法上时,默认取属性的名称作为匹配bean名称寻找依赖对象


@Resource默认按名称装配,当找不到与名称匹配的bean时,会按类型装配,但是如果指定了name属性,就只能按名称装配了。

###Spring的意义

通过控制反转降低组件之间的耦合度,实现软件各层之间的解耦;

Controller ——> Service ——>Dao


可以使用容器提供的众多服务,如:事务管理服务、消息服务等等;

容器提供单利模式支持,开发人员不再需要自己编写实例代码;

容器提供AOP技术,利用它可以很容易实现如权限拦截、运行期监控等功能;

容器提供的众多辅助类,使用这些类能够加快应用的开发,如:JdbcTemplate、HibernateTemple;

对主流的应用框架提供了集成支持,如:集成了Hibernate、JPA、Sturts等,便于应用的开发。
###Spring实例化对象的方法

通过默认构造器的方法实例化:

//Test类的定义:
public class Test{
public Test(){
System.out.println("create Test");
}
}

//在Spring配置文件中定义bean:
<bean id="test" class="com.abinge.springtest.Test"/>

//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
Test test = (Test)context.getBean("test");


在控制台将会打印”create Test”
由此可知:spring内部默认是调用了Test个类的默认的构造函数创建对象

通过静态工厂实例化

//Test类的定义:
public class Test{
public Test(){
System.out.println("create Test");
}
}
//Test工厂的定义,需要在该工厂中定义一个静态的创建Test实例的方法:
public class TestFactory{
public static Test getInstance(){
return new Test();
}
}

//在Spring配置文件中定义TestFactory的bean,而不是Test类的bean:
<!--<bean id="test" class="com.abinge.springtest.Test"/>-->
<bean id="testFactory" class="com.abinge.springtest.TestFactory" factory-method="getInstance"/>
//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
Test test = (Test)context.getBean("testFactory");


通过实例工厂实例化

//Test类的定义:
public class Test{
public Test(){
System.out.println("create Test");
}
}
//Test工厂的定义,需要在改工厂中定义非静态的创建Test实例的方法:
public class TestFactory{
public Test getInstance(){
return new Test();
}
}

//在Spring配置文件中定义TestFactory的bean,用于让Spring创建该工厂对象,同时定义一个创建Test类的bean:
<!--<bean id="test" class="com.abinge.springtest.Test"/>-->
<bean id="testFactory" class="com.abinge.springtest.TestFactory" />
<bean id="test" factory-bean="testFactory" factory-method="getInstance"/>
//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
Test test = (Test)context.getBean("test");


###Spring中Bean的实例化模式(或者叫:实例作用域Scope)

singleton(Spring默认)

在每个Spring IOC容器中一个bean定义只有一个实例对象,也就是单例模式

prototype

每次从IOC容器中获取的bean都是新的对象。
###Spring中的Bean的实例化时机(实例对象的创建时机)

默认:

在Spring容器启动的时候创建即创建了各个Bean的实例对象,也就是在执行如下代码时创建了各个Bean的实例对象:

ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");


调用context.getBean()方法时创建:

可以通过设置bean标签中的lazy-init属性设置为true,即表示该bean在context.getBean()方法被调用时才会被创建实例对象

<bean id="test" class="com.abinge.springtest.Test" lazy-init="true"/>


如果想对所有的bean都应用延迟初始化,可以在Spring配置文件的根节点beans设置dafault-lazy-init="true"属性:

<beans default-lazy-init="true" ……>


lazy-init属性有3个属性值:

Default——相当于false,在spring容器启动的时候创建对象

True——在context.getBean时创建对象

False——在spring容器启动的时候创建对象
###Spring中Bean的实例化模式(实例作用域Scope)与Spring中的Bean的实例化时机(实例创建时机)的结合说明:当scope为prototype时,始终在context.getBean()方法调用时时创建各bean实例对象

###Spring中Bean的初始化

可以在Test类中定义一个方法,用于Test实例对象创建后,对该实例对象进行数据的初始化操作

//Test类的定义:
public class Test{
public Test(){
System.out.println("create Test");
}
public init(){
//……相应的数据初始化代码
System.out.println("init data");
}
}

//在Spring配置文件中定义bean的同时,通过init-mathod属性指定数据初始化方法以完成数据初始化:
<bean id="test" class="com.abinge.springtest.Test" init-mathod="init"/>

//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
Test test = (Test)context.getBean("test");


执行步骤如下:

Spring容器创建Test实例对象;

由Spring容器内部调用init初始化方法,也就是说先实例化,然后才会初始化;

###Spring中Bean的销毁

Spring中Bean的销毁时机

只有在Spring容器被关闭的时候,该容器中的各个Bean对象才会被销毁

也就是在之心context.close()方法时,该容器中的各个Bean对象才会被销毁

可以在Test类中定义一个方法,用于Test实例对象销毁前,对该实例对象中使用的部分资源进行关闭等操作

//Test类的定义:
public class Test{
public Test(){
System.out.println("create Test");
}
public void init(){
//……相应的数据初始化代码
System.out.println("init data");
}
public void destory(){
//……相应的数据资源的关闭等操作代码
System.out.println("destory data");
}
}

//在Spring配置文件中定义bean的同时,通过destory-mathod属性指定数据资源关闭方法以完成资源的关闭:
<bean id="test" class="com.abinge.springtest.Test" init-mathod="init" destory-mathod="destory"/>

//在客户端
ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
Test test = (Test)context.getBean("test");


注意:

destory方法是在Spring容器关闭后,Test实例对象销毁前执行;

如果一个bean的配置的实例化模式为”prototype”,则Spring容器不负责销毁。

###让Spring自动扫描和管理Bean

在Spring的xml配置文件中配置如下:

<beans
……>
<!--<context:annotation-config/>-->
<!--1、base-package="com.abinge":这样配置,Spring会自动扫描com.abinge包下及其子包下的java类,将这些Java类按照注解全部加载到其自己的容器中作为Bean对象进行管理-->
<context:component-san base-package="com.abinge"/>

<!-- 2、resource-pattern="service/*.class":具体指定Spring扫描包的路径,表示扫描com.abinge包下的子包service下的所有class文件
也就是com.abinge.service包下的class文件 -->
<context:component-san base-package="com.abinge" resource-pattern="service/*.class"/>

<!-- 3.通过exclude-filter和include-filter指定具体扫描路径 -->
<context:component-san base-package="com.abinge" use-default-filters="false">
<!-- context:exclude-filter:通过expression属性指定排除指定包下com.abinge.service中的不扫描-->
<context:exclude-filter type="annotation" expression="com.abinge.service"/>

<!-- context:include-filter:通过expression属性指定只扫描指定包下com.abinge.service中的class对象
但是需要在context:component-san标签中设置属性use-default-filters="false"-->
<context:include-filter type="annotation" expression="com.abinge.service"/>
</context:component-san>
</beans>


要想Spring对扫描的各java类进行管理,需要在各java类中注解标识

@Service——业务层组件

@Controller——控制层组件

@Repository——数据访问组件,也就是Dao组件

@Component——泛指组件,当组件不好归类时,使用这个注解标识

@Scope("prototype")——设置bean的实例化模式为非单例,一般与上述各注释一并使用

@PostConstruct——注解数据初始化的方法(原本为EJT3的注解,Spring进行了支持)

@PreDestory——注解Bean销毁前的方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: