简介spring
- Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来
- 轻量级:与EJB对比,依赖资源少,销毁的资源少。
spring由来
Expert One-to-One J2EE Design and Development
Expert One-to-One J2EE Development without EJB
spring核心
- 控制反转(IoC)(Inverse of Control)原本是自己控制对象变成由spring控制bean对象
- 面向切面(AOP)
spring优点
- 方便解耦,简化开发 (高内聚低耦合)
- Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理
- spring工厂是用于生成bean
- AOP编程的支持
- Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
- 声明式事务的支持
- 只需要通过配置就可以完成对事务的管理,而无需手动编程
- 方便程序的测试
- Spring对Junit4支持,可以通过注解方便的测试Spring程序
- 方便集成各种优秀框架
- Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
- 降低JavaEE API的使用难度
- Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
配置环境
- 引入jar
下载四个核心(beans、core、context、expression) + 1个依赖(commons-loggins.jar)
四个核心:spring-framework的lib目录下
依赖下载:commons-loggins.jar
IOC(控制反转)入门
- 之前开发中,直接new一个对象即可。
- 学习spring之后,将由Spring创建对象实例–> IoC 控制反转(Inverse of Control)之后需要实例对象时,从spring工厂(容器)中获得,需要将实现类的全限定名称配置到xml文件中
- 就是把创建对象的控制权反转给了spring,以前是通过new,现在是通过spring创建
创建Bean类
1
2
3
4
5public class TestClass {
public void add() {
System.out.println("a_add");
}
}创建配置文件
在任意地址创建一个任意名字的xml文件,但是默认是在src目录下创建一个applicationContext.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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">
<!-- 配置service
<bean> 配置需要创建的对象
id :用于之后从spring容器获得实例时使用的
class :需要创建实例的全限定类名
-->
<bean id="TestClassId" class = "cn.xwmdream.springMain.TestClass"></bean>
</beans>spring读取xml文件调用类
1
2
3
4
5
6
7
8//读取xml文件
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//通过xml中的id创建类
TestClass test = (TestClass)applicationContext.getBean("TestClassId");
//调用类方法
test.add();
DI(依赖注入)入门
- DI Dependency Injection ,依赖注入
概念
1 | class B{ |
- 依赖:一个对象需要使用另外一个对象
- 注入:通过setter(set&get的那个set)方法进行另一个对象实例设置
1
2
3
4
5
6
7//原来是
private B b = new B();
//spring之后是
private B b;
public void setB(B b){
this.b = b;
}
模拟spring执行过程
动作 | 实现 | 对应spring | 对应xml标签 |
---|---|---|---|
创建service | Service service = new Service() | IOC | <bean> |
创建dao | Dao dao = new Dao() | IOC | <bean> |
把dao设置给service | service.setDao(dao) | DI | <property> |
入门实例
Dao.java
1
2
3
4
5public class Dao {
public void add() {
System.out.println("a_add");
}
}Service.java
1
2
3
4
5
6
7
8
9public class Service {
private Dao dao;
public void setDao(Dao dao) {
this.dao = dao;
}
public void addDao() {
dao.add();
}
}配置文件
applicationContext.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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">
<!--
<property> 用于进行属性注入
name: bean中的属性名,通过setter方法获得
setBookDao ##> BookDao ##> bookDao
ref :另一个bean的id值的引用
-->
<!-- 创建service -->
<bean id="serviceId" class="cn.xwmdream.springMain.Service">
<property name="dao" ref="DaoId"></property>
</bean>
<!-- 创建dao实例 -->
<bean id="DaoId" class="cn.xwmdream.springMain.Dao"></bean>
</beans>
- 解释一下property标签,是因为Service类中有一个Dao的成员名字叫dao,所以name为dao,他是一个Dao类,引用Dao类的id,所以是DaoId
核心api
- BeanFactory :这是一个工厂,用于生成任意bean。采取延迟加载,第一次getBean时才会初始化Bean
- ApplicationContext:是BeanFactory的子接口,功能更强大。(国际化处理、事件传递、Bean自动装配、各种不同应用层的Context实现)。当配置文件被加载,就进行对象实例化。
- ClassPathXmlApplicationContext 用于加载classpath(类路径、src)下的xml。加载xml运行时位置 –> /WEB-INF/classes/…xml
- FileSystemXmlApplicationContext 用于加载指定盘符下的xml。加载xml运行时位置 –> /WEB-INF/…xml
- 通过java web ServletContext.getRealPath() 获得具体盘符
Bean装配基于xml
实例化方式
- 3种bean实例化方法:默认构造,静态工厂,实例工厂
默认构造
上面ioc用配置文件实例化对象的方法必须有默认构造方法1
<bean id="" class=""></bean>必须有默认构造方法
静态工厂
- 常用于spring整合其他框架(工具)
- 静态工厂:用于生产实例对象,所有的方法必须是static
1
<bean id="" class="工厂全限定类名" factory-method="静态方法"></bean>
举个栗子
创建工厂
DaoFactory.java1
2
3
4
5
6
7
8
9
10public class MyBeanFactory {
public static Dao createDao() {
return new Dao();
}
}
class Dao {
public void add() {
System.out.println("a_add");
}
}配置信息
applicationContext.xml1
2
3
4
5<!-- 将静态工厂创建的实例交予spring
class 确定静态工厂全限定类名
factory-method 确定静态方法名
-->
<bean id="daoFactoryId" class="cn.xwmdream.springMain.MyBeanFactory" factory-method="createDao"></bean>调用
1
2
3
4
5
6
7
8//自定义工厂
Dao dao = MyBeanFactory.createDao();
dao.add();
//spring工厂
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Dao dao1 = applicationContext.getBean("daoFactoryId",Dao.class);
dao1.add();
实例工厂
- 实例工厂:必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。
栗子
创建工厂
DaoFactory.java1
2
3
4
5
6
7
8
9
10public class MyBeanFactory {
public Dao createDao() {
return new Dao();
}
}
class Dao {
public void add() {
System.out.println("a_add");
}
}配置信息
applicationContext.xml1
2
3
4
5
6
7<!-- 创建工厂实例 -->
<bean id="myBeanFactoryId" class="cn.xwmdream.springMain.MyBeanFactory"></bean>
<!-- 获得userservice
* factory-bean 确定工厂实例
* factory-method 确定普通方法
-->
<bean id="myDaoId" factory-bean="myBeanFactoryId" factory-method="createDao"></bean>调用
1
2
3
4
5
6
7
8
9//自定义工厂
MyBeanFactory myBeanFactory = new MyBeanFactory();
Dao dao = myBeanFactory.createDao();
dao.add();
//spring工厂
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Dao dao1 = applicationContext.getBean("myDaoId",Dao.class);
dao1.add();
Bean种类
普通bean
之前操作的都是普通bean。
FactoryBean
- 是一个特殊的bean,具有工厂生成对象能力,只能生成特定的对象。
- bean必须使用 FactoryBean接口,此接口提供方法 getObject() 用于获得特定bean。
先创建FB实例,使用调用getObject()方法,并返回方法的返回值
相当于1
2FB fb = new FB();
return fb.getObject();
BeanFactory 和 FactoryBean 对比
- BeanFactory:工厂,用于生成任意bean。
- FactoryBean:特殊bean,用于生成另一个特定的bean。例如:ProxyFactoryBean ,此工厂bean用于生产代理。
获得代理对象实例。AOP使用
作用域
- 作用域:用于确定spring创建bean实例个数
- 取值:
- singleton 单例,默认值。
- prototype 多例,每执行一次getBean将获得一个实例。例如:struts整合spring,配置action多例。
- 配置信息
1
<bean id="" class="" scope="取值类型"></bean>
生命周期
一共有十一个生命周期Spring Bean的生命周期(非常详细)
初始化和销毁
- 目标方法执行前后执行后,将进行初始化或销毁。
1
2
3
4
5<!--
init-method 用于配置初始化方法,准备数据等
destroy-method 用于配置销毁方法,清理资源等
-->
<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称">
1 | //要求:1.容器必须close,销毁方法执行; 2.必须是单例的 |
- 容器必须close,销毁方法才能执行
- bean类不能是prototype(多例),否则不能调用销毁方法
BeanPostProcessor 后处理Bean
- 有两个方法,before和after
spring 提供一种机制,只要实现此接口BeanPostProcessor,并将实现类提供给spring容器,spring容器将自动执行,在初始化方法前执行before(),在初始化方法后执行after()。
文档中的描述:Factory hook(勾子) that allows for custom modification of new bean instances, e.g. checking for marker interfaces(接口) or wrapping(装饰者) them with proxies(代理).
spring提供工厂勾子,用于修改实例对象,可以生成代理对象,是AOP底层。
模拟
1
2
3
4
5
6A a =new A();
a = B.before(a)//before方法是在对象init前调用
a.init();//初始化
a = B.after(a);//after是在对象初始化后调用,可以在此返回代理对象,目的在目标方法前后执行(例如:开启事务、提交事务)
a.addUser();
a.destroy()B类实现了BeanPostProcessor接口,所以在A用spring实例化的前后会调用B.before(a)和B.after(a)
- 可以在before和after去写一些代码,比如可以在after中创建一个代理,代理可以在目标执行一些函数的前后执行一些操作,比如开启提交事务、计时等操作
- 如果是要创建代理,必须是接口创建类
- BeanPostProcessor只需要在配置文件中写入class参数即可
例子
配置文件
1
2
3
4
5<!-- 一个普通的类 -->
<bean id="daoFactoryId" class="cn.xwmdream.springMain.MyBeanFactory" init-method="init" destroy-method="destroy"></bean>
<!-- BeanPostProcessor在配置中声明 -->
<bean class="cn.xwmdream.springMain.MyBeanPostProcessor"></bean>创建一个BeanPostProcessor
MyBeanPostProcessor.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("前方法 : " + beanName);
return bean;
}
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
System.out.println("后方法 : " + beanName);
return Proxy.newProxyInstance(
MyBeanPostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------开启事务");
//执行目标方法
Object obj = method.invoke(bean, args);
System.out.println("------提交事务");
return obj;
}});
}
}
- 在before中是原封不动的返回
- 在after中使用了代理,返回去的对象调用方法时候会执行”开启事务”/“提交事务”那一段代码
- 注意:使用代理的对象必须是用接口调用方法
- 注意Proxy、Method、InvocationHandler都是引用自java.lang.reflect.包下的
创建接口
MyInterface.java1
2
3public interface MyInterface {
public void da();
}创建实现类
1
2
3
4
5
6
7
8
9
10
11public class MyBeanFactory implements MyInterface{
public void da() {
System.out.println("da");
}
public void destroy() {
System.out.println("销毁了");
}
public void init() {
System.out.println("创建了");
}
}执行
1
2
3
4
5
6//spring工厂
String xmlPath = "applicationContext.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
MyInterface myBeanFactory = (MyInterface) applicationContext.getBean("daoFactoryId");
myBeanFactory.da();
applicationContext.close();
执行结果:
前方法 : daoFactoryId
创建了
后方法 : daoFactoryId
——开启事务
da
——提交事务
销毁了
- 可以用此方法在after中给创建的对象创建代理,但是必须是用接口调用才行
属性依赖注入
- 依赖注入方式:手动装配 和 自动装配
- 手动装配:一般进行配置信息都采用手动
- 基于xml装配:构造方法、setter方法
- 基于注解装配:
- 自动装配:struts和spring 整合可以自动装配
- byType:按类型装配
- byName:按名称装配
- constructor构造装配,
- auto: 不确定装配。
构造方法
目标类
User.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class User {
private String username;
private Integer age;
public User(Integer age, String username) {
System.out.println("构造方法1");
this.age = age;
this.username = username;
}
public User(String username, Integer age) {
System.out.println("构造方法2");
this.username = username;
this.age = age;
}
public String toString() {
return "User [username=" + username + ", age=" + age + "]";
}
}spring配置
applicationContext.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<!-- 构造方法注入
* <constructor-arg> 用于配置构造方法一个参数argument
name :参数的名称
value:设置普通数据
ref:引用数据,一般是另一个bean id值
index :参数的索引号,从0开始 。如果只有索引,匹配到了多个构造方法时,默认使用第一个。
type :确定参数类型
例如:使用名称name
<constructor-arg name="username" value="jack"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
例如2:【类型type 和 索引 index】
<constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
-->
<bean id="userId" class="cn.xwmdream.springMain.User" >
<constructor-arg index="0" type="java.lang.String" value="我的名字"></constructor-arg>
<constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
</bean>
<bean id="userId1" class="cn.xwmdream.springMain.User" >
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="username" value="还是我的名字"></constructor-arg>
</bean>运行
1
2
3
4
5
6String xmlPath = "applicationContext.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
User user = (User) applicationContext.getBean("userId");
System.out.println(user);
user = (User) applicationContext.getBean("userId1");
System.out.println(user);
运行结果:
构造方法2
User [username=我的名字, age=2]
- index是第几个参数,从0开始,上面的配置文件说的是找第一个是String第二个是Integer类型的构造方法写入参数
- 第二个方式是通过参数名的形式创建构造方法
set方法
- 创建bean类
Person.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public class Person {
private String username;
private Integer age;
private Address homeAddress;
private Address workAddress;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getHomeAddress() {
return homeAddress;
}
public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}
public Address getWorkAddress() {
return workAddress;
}
public void setWorkAddress(Address workAddress) {
this.workAddress = workAddress;
}
public String toString() {
return "Pserson [username=" + username + ", age=" + age + ", homeAddress=" + homeAddress + ", workAddress="
+ workAddress + "]";
}
}
Address.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class Address {
private String address;
private String tel;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String toString() {
return "Address [address=" + address + ", tel=" + tel + "]";
}
}
- 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33<!-- setter方法注入
* 普通数据
<property name="" value="值">
等效
<property name="">
<value>值
* 引用数据
<property name="" ref="另一个bean">
等效
<property name="">
<ref bean="另一个bean"/>
-->
<bean id="personId" class="cn.xwmdream.springMain.Person">
<property name="username" value="名字"></property>
<property name="age">
<value>1234</value>
</property>
<property name="homeAddress" ref="homeAddrId"></property>
<property name="workAddress">
<ref bean="workAddrId"/>
</property>
</bean>
<bean id="homeAddrId" class="cn.xwmdream.springMain.Address">
<property name="address" value="阜南"></property>
<property name="tel" value="110"></property>
</bean>
<bean id="workAddrId" class="cn.xwmdream.springMain.Address">
<property name="address" value="北京八宝山"></property>
<property name="tel" value="120"></property>
</bean>
- 运行
1
2
3
4String xmlPath = "applicationContext.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Person pserson = (Person) applicationContext.getBean("personId");
System.out.println(pserson);
运行结果:
Pserson [username=名字, age=1234, homeAddress=Address [address=阜南, tel=110], workAddress=Address [address=北京八宝山, tel=120]]
p命名空间
- 对“setter方法注入”进行简化,替换
,而是在 p命名空间使用前提,必须添加命名空间
上述配置文件可以改成1
2
3
4
5
6
7
8
9
10
11
12
13<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="personId" class="cn.xwmdream.springMain.Person" p:username="我的名字" p:age="1234" p:homeAddress-ref="homeAddrId" p:workAddress-ref="workAddrId">
</bean>
<bean id="homeAddrId" class="cn.xwmdream.springMain.Address" p:address="安阳" p:tel="110">
</bean>
<bean id="workAddrId" class="cn.xwmdream.springMain.Address" p:address="北京" p:tel="120">
</bean>
</beans>注意加上第三行的命名空间,空间地址就是第一行的beans换成p
SpEL
- 对
进行统一编程,所有的内容都使用value
#{123}、#{‘jack’} : 数字、字符串
#{beanId} :另一个bean引用
#{beanId.propName} :操作数据
#{beanId.toString()} :执行方法
#{T(类).字段|方法} :静态方法或字段
1 | <!-- |
集合注入
1 | <!-- |
装配Bean 基于注解
- 用注解替代xml文件
@Component取代
@Component(“id”) 取代 web开发,提供3个@Component注解衍生注解(功能一样)取代
@Repository :dao层
@Service:service层
@Controller:web层
这三个注解和Component功能一样,只是用来区分不同的层,例如springmvc依赖注入 ,给私有字段设置,也可以给setter方法设置
普通值:@Value(“”)
引用值:
方式1:按照【类型】注入@Autowired
方式2:按照【名称】注入1
@Autowired @Qualifier("名称")
方式3:按照【名称】注入2
@Resource("名称") 方式3就是方式2两种注解合在一起,一样
4.生命周期
初始化:@PostConstruct
销毁:@PreDestroy
5.作用域
@Scope(“prototype”) 多例
配置xml为扫描所需要的包
1
2
3
4
5
6
7
8
9
10
11
<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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 组件扫描,扫描含有注解的类 -->
<context:component-scan base-package="cn.xwmdream.springMain"></context:component-scan>
</beans>开始表演
详情请参考
总结
编写流程
- 导入jar包:4+1 –> beans,core,context,expression,commons-logging
- 编写目标类:dao和service
spring配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23IoC:<bean id="" class="" >
DI:<bean> <property name="" value="" | ref="">
实例化方式:
默认构造
静态工厂:<bean id="" class="工厂类" factory-method="静态方法">
实例工厂:<bean id="工厂id" class="工厂类"> <bean id="" factory-bean="工厂id" factory-method="方法">
作用域:<bean id="" class="" scope="singleton | prototype">
生命周期:<bean id="" class="" init-method="" destroy-method="">
后处理bean BeanPostProcessor接口,<bean class="注册"> ,对容器中所有的bean都生效
属性注入
构造方法注入:<bean><constructor-arg index="" type="" >
setter方法注入:<bean><property>
p命名空间:简化<property> <bean p:属性名="普通值" p:属性名-ref="引用值"> 注意声明命名空间
SpEL:<property name="" value="#{表达式}">
#{123} #{'abc'}
#{beanId.propName?.methodName()}
#{T(类).静态方法|字段}
集合
数组<array>
List <list>
Set <set>
Map <map><entry key="" value="">
Properties <props><prop key="">....核心api
- BeanFactory,延迟实例化bean,第一次调用getBean
- ApplicationContext 一般常用,功能更强
ClassPathXmlApplicationContext 加载classpath xml文件
FileSystemXmlApplicationContext 加载指定盘符文件 , ServletContext.getRealPath()
后处理bean对单一对象生效
1 |
|
### 注解
1. 扫描含有注解的类1
<context:component-scan base-package="....">
2. 常见的注解
- @Component 组件,任意bean
WEB
@Controller web层
@Service service层
@Repository dao层 - 注入 –> 字段或setter方法
普通值:@Value
引用值:类型:@Autowired 名称1:@Autowired @Qualifier("名称") 名称2:@Resource("名称")
- 作用域:@Scope(“prototype”)
- 生命周期:
初始化:@PostConstruct
销毁方法:@PreDestroy
注解和xml混合使用
- 将所有的bean都配置xml中
- 将所有的依赖都使用注解
@Autowired
默认不生效。为了生效,需要在xml配置:context:annotation-config
总结:
- 注解1:<context:component-scan base-package=” “>
- 注解2:context:annotation-config
- 一般情况两个注解不一起使用。
- “注解1”扫描含有注解(@Component 等)类,注入注解自动生效。
“注解2”只在xml和注解(注入)混合使用时,使注入注解生效。