前言:此前一直将精力放在快速输入的过程,每天学习大量的知识。虽然每天学习时间充裕,但是效率低下。究其原因,是只顾埋头输入,忘记输出。没有总结,没有自己的思考。所以,从现在开始,坚持每天输出。(Dream bigger, go higher.)
今天研究的对象是spring框架。关于spring,几乎已经统治了Javaweb开发。学习java,spring就必须掌握。
今天关注的主要的对象是其中之一 IOC:
1 IOC作用
通过spring提供的IOC容器,可以将对象间的依赖关系交由spring进行控制,避免代码所造成的过度程序耦合。可以让程序员更加专注于上层的应用,而不必考虑单例模式类,属性文件解析等底层的需求。
2 程序之间的耦合?
/**
* 客户的业务层实现类
*/
public class CustomerServiceImpl implements Customer{
private CustomerDao dao = new CustomerServiceImpl();
}
//业务层调用持久层,并且此时业务层依赖持久层的接口和实现类,若没有实现层实现类,编译将不能通过
再比如,使用数据库jdbc时,
public Class Demo{
//当需要更换数据库品牌时,就需要更换源码来修改数据库的驱动。
public static void main(String[] args){
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
}
}
3 解决耦合的思路
* 当在jdbc时,可以通过反射来注册实现类:
Class.forName("com.mysql.jdbc.Driver");
这样的好处就是,我们的类中不在依赖具体的驱动类。但此时,我们反射类对象的全限定类名字符串在java类中写死了,一旦改也需要修改源码。
解决这个问题,可以使用配置文件进行配置。
* 工厂模式解耦
实际开发中,可以将dao service action对象使用配置文件配置起来,当启动服务器应用时进行加载,通过读取配置文件,将这些对象创建并保存起来,以后使用时,直接可以使用。
IOC -- 控制反转( 工厂 + 反射 + 配置文件 )
上述的解耦合思路可以分为:
* 存在哪里?
分析:由于存在很多对象,必须需要集合来进行存储,Map List 可供选择。一般情况下,我们会选择map(因为需要查找)。所以,当应用加载时,将创建一个map,用于存放dao,service,dao对象。这个map即被称之为容器。
* 工厂?
工厂就是负责给我们从容器中获取指定对象的类,这时候我们获取对象的方式发生了改变。
以前我们都是采用new的方式,主动获取对象;当有工厂时,直接从工厂中获取。有工厂为我们查找或者创建对象,是被动的。这种被动接受的方式获取对象的思想就是控制反战,也即spring框架的核心之一。
<!--
1: 基本配置
1) bean标签: 指定要创建的实体类
id属性: 可以为任意值 但是在整个配置文件中唯一
class属性: 要实例化类的全限定名
-->
<!-- <bean id="user" class="com.spring.demo.User"></bean> -->
<!--
2) scope属性: 范围
singleton: 单实例 默认值
如果是单实例,配置文件文件只要一加载 就会创建对象 放在spring容器 (map<id,对象>)
当从spring容器取的时候(getBean),所用人用的都是同一个对象
prototype: 多实例
如果是多实例,配置文件加载,不创建对象
当getbean的时候,getbean一次创建一次 放在容器中
<bean id="user" class="com.spring.demo.User" scope="singleton" ></bean>
<bean id="user" class="com.spring.demo.User" scope="prototype" ></bean>
用途:
action: prototype
service/dao: singleton
singleton 与 prototype创建的的对象销毁时间
singleton的对象当spring容器关闭 对象销毁
prototype的对象 长时间不用自动被垃圾回收机制给回收了
3) init-method:指定初始化方法
destory-method:指定销毁方法
-->
<!-- <bean id="user" class="com.spring.demo.User" scope="prototype" init-method="init" destroy-method="destory"></bean> -->
<!--
2、 import:导入外部的配置文件
resource:外部配置文件的地址
web: 所有在web层创建的对象 applicationContext_web.xml
service:所有在service层创建的对象 applicationContext_service.xml
dao:所有在dao层创建的对象 applicationContext_dao.xml
<import resource="applicationContext_web.xml"/>
<import resource="applicationContext_service.xml"/>
<import resource="applicationContext_dao.xml"/>
<import resource="applicationContext_user.xml"/>
-->
<!-- bean的三种创建方式
无参构造方式
静态工厂方式
实例工厂方式
-->
<!-- 无参构造方式
<bean id="user" class="com.spring.demo.User"></bean>
-->
<!-- 静态工厂方式
条件:需要有一个工厂类 在这个工厂类中还需要有一个静态方法
<bean id="user" class="com.spring.demo.utils.BeanFactory" factory-method="createUser"/>
-->
<!-- 实例工厂
条件:需要有一个工厂类 在这个工厂类中还需要有一个普通方法
<bean id="beanFactory" class="com.spring.demo.utils.BeanFactory"></bean>
<bean id="user" factory-bean="beanFactory" factory-method="createUser"></bean>
-->
DI -- 依赖注入(IOC的具体实现方式)
<!--
DI的注入方式:
1 构造器的方式
条件:需要有参构造方法
-->
<!-- <bean id="car" class="com.spring.demo.serviceImpl.CarImpl">
构造器的方式
name:要赋值的属性名
value:要赋的值 (针对的是基本类型和String类型)
ref: 针对的是对象类型
<constructor-arg name="name" value="Tesla"></constructor-arg>
<constructor-arg name="price" value="500000"></constructor-arg>
</bean> -->
<!--
DI的注入方式:
2 set属性的方式
条件:属性必须要有set方法
name:要赋值的属性名
value:要赋的值 (针对的是基本类型和String类型)
ref: 针对的是对象类型 -- 指向的是spring中bean的id名
-->
<!-- <bean id="person" class="com.spring.demp.serviceImpl.PersonImpl">
set属性的方式
<property name="name" value="jorgen"></property>
<property name="car" ref="car"></property>
</bean> -->
<!--
DI的注入方式:
3 p名称空间的方式
条件: 在配置文件中有p的名称空间 (底层set方式 属性还是得有set方法)
语法:
<bean p:属性名="属性值" p:属性名-ref="bean的id对象值" >
-->
<!-- <bean id="person" class="com.spring.demo.serviceImpl.PersonImpl" p:name="polaris" p:car-ref="car"></bean> -->
<!-- DI:复杂属性注入 -->
<bean id="collBean" class="com.spring.demo.CollectionBean">
<property name="date">
<!-- 数组类型 -->
<list>
<value>year</value>
<value>month</value>
<value>day</value>
</list>
</property>
<property name="number">
<!-- list类型 -->
<list>
<value>111</value>
<value>222</value>
<ref bean="car"/>
</list>
</property>
<property name="mapBean">
<!-- map -->
<map>
<entry key="k1" value="a"></entry>
<entry key="k2" value="b"></entry>
<entry key="k3" value-ref="car"></entry>
</map>
</property>
<property name="properties">
<!-- properties类型 -->
<props>
<prop key="username">root</prop>
<prop key="password">1234</prop>
</props>
</property>
</bean>
总结完全部xml的方式配置后,一般企业开发都是采用半注解半xml配置方式
一般第三方的类采用xml方式,本地的类用注解方式。
* 注解方式
配置注解扫描器
<context:component-scan base-package="com.spring"></context:component-scan>
@Component("bean的id值") 定义在类上 注解扫描器就会创建该类的实例对象 放在spring容器中
注意:spring发布公告, @Component这个注解不维护了,要维护这个注解下面衍生出的3个注解
@Controller("bean的id值") 针对的就是web层
@Service("bean的id值") 针对的是service层
@Repository("bean的id值") 针对的是dao层
//@Component("userService")
//<bean id="userService" class="com.spring.demo.serviceimpl.UserServiceImpl">
@Service("userService")
public class UserServiceImpl implements UserService
{...}
@Value("属性值") 定义在属性字段上 针对的是基本类型和String类型
如果使用了这个注解 该属性的set方法可以省略不写
@Value("jorgen")
private String name;
@Autowired 定义在属性字段上的 针对的是对象类型
如果定义在了那个对象类型的属性身上 会自动去spring容器中找该类型的实例对象给赋值
@Qualifier("userDaoxxx")定义在属性字段上的 指定用该类型的哪个id名称的实例对象
注意: @Qualifier要想使用 必须结合 @Autowired 一起使用
@Resource(name="userDao")
@Autowired+@Qualifier("userDao")
//@Autowired // 自动去spring容器中找有没有该类型(UserDao)的实例对象 如果有直接赋值
//@Qualifier("userDao") // 指定用该类型的哪个名称的实例对象
@Resource
private UserDao userDao;
了解的注解:
@Scope("singleton"或则prototype) 定义在类上的 指定当前类是单实例还是多实例
@PostConstruct 定义在方法上 配置初始化方法
@PreDestroy 定义在方法上 配置销毁的方法