spring下载与开发
spring3.x
版本主要是整合hibernate3
,如果想要整合hibernate5.x
可以使用spring4.x
ioc
什么是ioc
简单理解ioc(inverse of control)
就是对象的创建方式由new
转而交给spring
,这里反转的意思就是对象的创建方式由创建到->直接拿。
平时我们创建一个对象的时候使用的new
,但是使用spring
之后,我们获取一个对象就不是使用new
,而是从容器中拿,所以说创建对象的方式被反转了。
ioc的底层实现原理
原始创建对象的方式
java接口代码
package top.twolovelypig.dao;
public interface IUserDAO {
public void save();
}
实现类代码
package top.twolovelypig.dao;
public class JdbcUserDAO implements IUserDAO{
@Override
public void save() {
System.out.println("保存学生的代码执行了");
}
}
测试代码
package top.twolovelypig.test;
import org.junit.Test;
import top.twolovelypig.service.IUserDAO;
import top.twolovelypig.service.UserDAO;
public class TestUser {
@Test
public void save() {
//原始创建对象的方式
IUserDAO obDao = new JdbcUserDAO();
obDao.save();
}
}
上面dao
中有一个save
方法是通过jdbc
方式来实现的,现在想要修改为通过hibernate
方式来实现,就是再增加一个hibernateUserDAO
接口,但是在调用的时候需要修改很多的代码,原来创建的JdbcUserDAO
现在都需要修改为hibernateUserDAO
。显然这种代码的扩展方式不够好。
通过工厂模式优化
上面的方式之所以想要修改一个功能就需要修改很多的代码就是因为实现类与接口直接耦合性太强,此时有一个解决办法就是不让接口与实现类直接接触,可以在二者中间插入一个工厂,将所有的创建对象的代码放入到该工厂当中。虽然这种方式对于上面的代码来说是有了改进,但是接口和工厂之间依然是有耦合的,此时可以使用工厂+反射+配置文件来实现程序解耦合。
进一步优化-ioc实现原理
上面可以看到ioc的实现原理其实就是使用工厂+反射+配置文件三者结合来实现的。
对于spring的ioc注意功能就是将实现类以及接口(主要是实现类)交给spring来管理。
使用spring的配置文件
<?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.xsd">
<bean id="userDao" class="top.twolovelypig.dao.UserDAO"></bean>
</beans>
测试代码
public class TestUser {
@Test
public void testSave() {
//原始创建对象的方式
/*IUserDAO obDao = new UserDAO();
obDao.save();*/
//使用ioc的方式
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserDAO objUserDao = (UserDAO)applicationContext.getBean("userDao");
objUserDao.save();
}
}
Spring工厂
spring的工厂主要分为两类:
BeanFactory
:旧版本的工厂类,在调用getBean()
方法的时候才会生成类的实例。ApplicationContext
:新版本的工厂类,加载配置文件的时候就会将Spring管理的类都实例化,主要有两个实现类。ClassPathApplicationContext
:加载类路径下的配置文件FileSystemXmlApplicationContext
:加载文件系统下的配置文件
bean的相关配置
bean的生命周期
<bean id="userDao" class="top.twolovelypig.dao.UserDAO" init-method="init" destroy-method="destroy"></bean>
在bean标签中主要是有init-method
和destroy-method
两个属性来配置初始化方法和销毁的方法,比如这里配置了init-method=“init”
,也就是再UserDAO
中需要有一个init
方法,当该类被初始化的时候这个方法就会执行,同样的如果配置了销毁的方法,那么当applicationContext
对象执行close()
方法时销毁方法就会被执行。
bean的作用范围的配置
Scope
: Bean
的作用范围
singleton
:默认值,spring会采用单例模式创建这个对象prototype
:多例模式(Struts2
和spring
整合的时候会使用到)request
:应用在web
项目中,spring
创建该类后,将这个类存入到request
范围中session
:应用在web
项目中,spring
创建该类后,将该类存入到session
范围中globalsession
:应用在web项目中,必须在porlet
(比如登录百度账号,然后在登录百度网盘时就不需要输入账户和密码就可以直接登录,像这种存在子域的关系就是porlet
环境),但是如果没有这种环境,就相当于是session
。
DI(依赖注入)
依赖注入的前提是必须有IOC
的环境,Spring
管理这个类的时候将类的依赖的属性注入(设置)进来。有两种方式。
构造方法注入
普通类型
Bean
类
package top.twolovelypig.demo1;
public class Car {
private String name;
private double price;
public Car(String name, Double price) {
this.name = name;
this.price = price;
}
//重写tostring
}
Bean
的配置
<bean id="car" class="top.twolovelypig.demo1.Car">
<constructor-arg name="name" value="法拉利"></constructor-arg>
<constructor-arg name="price" value="10000001"></constructor-arg>
</bean>
对象类型
Bean
类
package top.twolovelypig.demo1;
public class Person {
private String name;
private Car car;
public Person(String name, Car car) {
super();
this.name = name;
this.car = car;
}
//重写tostring
}
Bean
的配置
<bean id="car" class="top.twolovelypig.demo1.Car">
<constructor-arg name="name" value="法拉利"></constructor-arg>
<constructor-arg name="price" value="10000001"></constructor-arg>
</bean>
<bean id="person" class="top.twolovelypig.demo1.Person">
<constructor-arg name="name" value="张三"></constructor-arg>
<!--使用构造方法含有对象的时候就不是使用value而是使用ref,后面接的是配置那个对象的id值-->
<constructor-arg name="car" ref="car"></constructor-arg>
</bean>
set方法注入
普通类型
使用set
方法注入就不需要构造器了,但是需要添加set
方法。
package top.twolovelypig.demo1;
public class Car {
private String name;
private double price;
public void setName(String name) {
this.name = name;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car [name=" + name + ", price=" + price + "]";
}
}
<bean id="car" class="top.twolovelypig.demo1.Car">
<property name="name" value="奔驰"></property>
<property name="price" value="10000"></property>
</bean>
对象类型
package top.twolovelypig.demo1;
public class Person {
private String name;
private Car car;
public void setName(String name) {
this.name = name;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [name=" + name + ", car=" + car + "]";
}
}
<bean id="person" class="top.twolovelypig.demo1.Person">
<property name="name" value="张三"></property>
<property name="car" ref="car"></property>
</bean>
注:无论是set方法注入还是构造器注入,对于普通属性都是使用name与value即可,对于对象类型后面的value就需要更换为ref,ref的值为该对象在配置文件里面配置的bean的id。
P命名空间方式注入
可以通过引入p名称空间完成属性的注入:
写法
- 普通属性: p:属性名=“值”>
- 对象属性: p:属性名-ref=“值”
使用步骤
-
添加p命名空间
xmlns:p="http://www.springframework.org/schema/p"
添加也是很简单的,一开始肯定是有
bean
命名空间的,我们赋值一份,只需要将开始的xmlns修改为xmlns:p
,然后将最后的beans
修改为p
即可. -
按照上述的写法写配置文件
<bean id="car" class="top.twolovelypig.demo1.Car"> <property name="name" value="奔驰"></property> <property name="price" value="10000"></property> </bean> <!-- <bean id="person" class="top.twolovelypig.demo1.Person"> <property name="name" value="张三"></property> <property name="car" ref="car"></property> </bean> --> <bean id="person" class="top.twolovelypig.demo1.Person" p:car-ref="car" p:name="张三"></bean>
SpEL(Spring expression language)注入
语法格式
#{SpEL}
基本属性
<bean id="car" class="top.twolovelypig.demo1.Car"> <property name="name" value="奔驰"></property> <property name="price" value="10000"></property> </bean> <!-- <bean id="person" class="top.twolovelypig.demo1.Person"> <property name="name" value="张三"></property> <property name="car" ref="car"></property> </bean> --> <bean id="person" class="top.twolovelypig.demo1.Person"> <property name="name" value="#{'张三'}"></property> <property name="car" value="#{car}"></property> </bean>
注意点:
- 无论是基本属性还是对象属性都是使用
value
而不是使用ref
- 如果是字符串需要使用单引号括起来,如果不是就不用单引号
通过计算得到值
上面只是基本的属性和对象,其余使用
SpEl
的一个好处是很灵活,value
里面的值可以是表达式也可以是某一个类的属性或者方法,比如现在又如下类:public class getCarInfo{ public String name; public void setName(String name){ this.name = name; } public String getName(){ return this.name; } public double getPrice(){ return Math.random() * 3000; } }
现在继续修改上面的配置文件:
<bean id="carInfo" class="top.twolovelypig.demo1.CarInfo"> <property name="name" value="奔驰"></property> </bean> <bean id="person" class="top.twolovelypig.demo1.Person"> <property name="name" value="#{carInfo.name}"></property> <property name="car" value="#{carInfo.getPrice()}"></property> </bean>
可以看到其
value
值是可以通过其他类的属性或者方法来赋值的。 - 无论是基本属性还是对象属性都是使用