什么是IOC
Inversion of Control的缩写,中文译为控制反转,简单来说就是把对象创建和对象之间的调用过程,交给 Spring 进行管理。
创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是你在xml文件控制,侧重于原理
底层原理
- XML解析
使用dom4j解析
- 工厂模式
- 反射创建类对象
1 2 3
| Class clazz=Class.forName(classValue); UserService service=clazz.newInstance(); return service;
|
XML注入实现IOC
注入常规类型属性
set方法注入
- 第一步 在类中定义属性和对应的 set 方法
- 第二步 在XML文件中添加property标签
1 2 3 4 5
| <bean id="user" class="com.spring5.User"> <property name="name" value="小明"></property> </bean>
|
有参构造注入
- 第一步 在类中定义有参构造方法
- 第二步 在XML文件中添加constructor-arg标签
1 2 3 4 5
| <bean id="user" class="com.spring5.User"> <constructor-arg name="name" value="张三"></constructor-arg> </bean>
|
P空间注入(了解)
- 第一步 在类中定义属性和对应的 set 方法
- 第二步 修改XML文件
在beans中添加xmlns:p属性;在bean中添加p:name属性
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="UTF-8"?> <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="user" class="com.spring5.User" p:name="李四"></bean> </beans>
|
注入特殊类型属性
空值
1 2 3
| <property name="name"> <null></null> </property>
|
特殊符号
-
方法一 把<>进行转义
1 2
| <property name="name" value="<<王五>>"> </property>
|
-
把带特殊符号内容写到 CDATA
1 2 3
| <property name="name"> <value><![CDATA[<<王五>>]]></value> </property>
|
外部Bean
现有类UserService、接口UserDao、类UserDaoImpl,其中UserDaoImpl实现了接口UserDao。代码结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| src │ bean.xml │ └─com └─spring5 │ Test.java │ ├─dao │ UserDao.java │ UserDaoImpl.java │ └─service UserService.java
|
如果类UserService要调用类UserDaoImpl的方法,传统做法是创建UserDao对象然后调用其方法,这种做法耦合太高。用spring注入的步骤如下:
- 在UserService中创建属性UserDao,并写好set方法
- 在XML中配置UserService和UserDao对象
- 通过property标签注入UserDao对象
示例代码如下:
接口UserDao:
1 2 3
| public interface UserDao { public void update(); }
|
类UserDaoImpl:
1 2 3 4 5 6
| public class UserDaoImpl implements UserDao { @Override public void update() { System.out.println("I am dao update..."); } }
|
类UserService:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class UserService { private UserDao userDao;
public void setUserDao(UserDao userDao) { this.userDao = userDao; }
@Test public void add(){ System.out.println("I am service add..."); userDao.update(); } }
|
bean.xml:
1 2 3 4 5 6 7
| <bean id="userDaoImpl" class="com.spring5.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.spring5.service.UserService"> <property name="userDao" ref="userDaoImpl"> </property> </bean>
|
内部Bean和级联配置
简单来说就是bean标签里面嵌套bean标签。在员工和部门,一个部门有多个员工,一个员工属于一个部门。示例代码如下:
Dept类:
1 2 3 4 5 6
| public class Dept { private String dname; public void setDname(String dname) { this. dname = dname; } }
|
Emp类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Emp { private String ename; private String gender; private Dept dept; public void setDept(Dept dept) { this. dept = dept; } public void setEname(String ename) { this. ename = ename; } public void setGender(String gender) { this. gender = gender; } }
|
内部Bean配置
1 2 3 4 5 6 7 8 9 10 11 12
| <bean id= "emp" class= "com.spring5.bean.Emp"> <property name= "ename" value= "lucy"></property> <property name= "gender" value="女"></property> <property name= "dept"> <bean id= "dept" class= "com.spring5.bean.Dept"> <property name= "dname" value="技术部"></property> </bean> </property> </bean>
|
级联配置
Emp类需要生成getDep的方法
1 2 3
| public Dept getDept(){ return dept; }
|
XML文件中dept.dname获取属性
1 2 3 4 5 6 7 8
| <bean id= "emp" class= "com.spring5.bean.Emp"> <property name= "ename" value= "lucy"></property> <property name= "gender" value="女"></property> <property name= "dept.dname" value="商务部"></property> </bean>
|
注入集合属性
普通注入
新建类User,包含集合属性和相关set方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class User {
private String[] arrs; private List<String> list; private Map<String,String> maps; private Set<String> sets;
public void setArrs(String[] arrs) { this.arrs = arrs; }
public void setList(List<String> list) { this.list = list; }
public void setMaps(Map<String, String> maps) { this.maps = maps; }
public void setSets(Set<String> sets) { this.sets = sets; } }
|
XML配置
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
| <bean id= "user" class= "com.spring5.User"> <property name= "arrs"> <array> <value>JAVA课程</value> <value>mySQL课程</value> </array> </property> <property name= "list"> <list> <value>张三</value> <value>李四</value> </list> </property> <property name= "maps"> <map> <entry key= "JAVA" value= "java"></entry> <entry key= "PHP" value= "php"></entry> </map> </property> <property name= "sets"> <set> <value>MySQL</value> <value>MySQL</value> <value>Oracle</value> <value>Oracle</value> </set> </property> </bean>
|
注入对象集合
Dept类
1 2 3 4 5 6 7 8
| public class Dept { private List<Emp> empList;
public void setEmpList(List<Emp> empList) { this.empList = empList; } }
|
Emp类
1 2 3 4 5 6 7
| public class Emp { private String eName;
public void seteName(String eName) { this.eName = eName; } }
|
XML配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <bean id= "dept" class= "com.spring5.Dept"> <property name="empList"> <list> <ref bean="emp1"></ref> <ref bean="emp2"></ref> <ref bean="emp3"></ref> </list> </property> </bean> <bean id="emp1" class="com.spring5.Emp"> <property name="eName" value="张三"></property> </bean> <bean id="emp2" class="com.spring5.Emp"> <property name="eName" value="李四"></property> </bean> <bean id="emp3" class="com.spring5.Emp"> <property name="eName" value="王五"></property> </bean>
|
自动装配
Spring可以根据属性名称或者类型进行自动装配,使用byType时相同类型的Bean不能配置多个。
1 2 3 4
| <bean id= "emp" class= "com.spring5.autowire.Emp" autowire="byType"> </bean> <bean id= "dept" class= "com.spring5.autowire.Dept"></bean>
|
外部属性文件
一、创建外部属性文件jdbc.properties,填写数据库信息,key随便写
1 2 3 4
| prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql: prop.userName=root prop.password=root
|
二、把外部 properties 属性文件引入到 spring 配置文件中
-
引入 context 名称空间
xmlns:context=“http://www.springframework.org/schema/context”
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring- - context.xsd"
-
在 spring 配置文件使用标签引入外部属性文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean> </beans>
|
工厂Bean
Spring 有两种类型:普通 bean和工厂bean(FactoryBean)。普通 bean在配置文件中定义的bean类型和返回类型一样,而工厂bean在配置文件定义 bean类型可以和返回类型不一样。实现工厂Bean的步骤如下:
- 第一步 创建类,让这个类作为工厂 bean,实现FactoryBean接口
- 第二步 重写接口里面的方法,在方法中定义返回的bean类型
示例代码如下:
MyBean和User类
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 34
| public class MyBean implements FactoryBean<User> {
@Override public User getObject() throws Exception { User user = new User(); user.setName("张三"); return user; }
@Override public Class<User> getObjectType() { return null; }
@Override public boolean isSingleton() { return false; } }
public class User {
private String name;
public void setName(String name) { this.name = name; }
public void test(){ System.out.println("I am test..."); }
}
|
bean.xml
1
| <bean id="myBean" class="com.spring5.MyBean"></bean>
|
Test类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Test {
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User user = context.getBean("myBean", User.class); System.out.println(user); user.test(); } }
|
运行结果:
com.spring5.User@42d8062c
I am test…
Bean的作用域
作用域 |
说明 |
单例(singleton) |
默认)每一个Spring IoC容器都拥有唯一的一个实例对象 |
原型(prototype) |
一个Bean定义,任意多个对象 |
请求(request) |
一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例。只在基于web的Spring ApplicationContext中可用 |
会话(session) |
限定一个Bean的作用域为HTTPsession的生命周期。同样,只有基于web的Spring ApplicationContext才能使用 |
全局会话(global session) |
限定一个Bean的作用域为全局HTTPSession的生命周期。通常用于门户网站场景,同样,只有基于web的Spring ApplicationContext可用 |
Bean的生命周期
-
第一步,执行无参构造创建Bean实例
-
第二步,调用类的set方法设置属性值
-
第三步,调用预初始化方法postProcessBeforeInitialzation()
需要有Bean实现了BeanPostProcessor接口
-
第四步,调用bean的初始化的方法
需要在XML的bean属性中进行配置
-
第五步,调用后初始化方法postProcessAfterInitialization()
需要有Bean实现了BeanPostProcessor接口
-
第六步,对象获取到了,bean可以使用了
-
第七步,当容器关闭时调用bean的销毁的方法
需要在XML的bean属性中进行配置
1
| destroy-method="销毁调用方法名"
|
实例代码如下:
User类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class User {
private String name;
public User() { System.out.println("第一步 执行无参构造创建Bean实例"); }
public void setName(String name) { this.name = name; System.out.println("第二步 调用set方法设置属性值"); } public void initMethod(){ System.out.println("第四步 调用bean的初始化的方法"); } public void destroyMethod(){ System.out.println("第七步 当容器关闭时调用bean的销毁的方法"); } public void test(){ System.out.println("I am test..."); } }
|
MyBeanPost类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("第三步,调用预初始化方法postProcessBeforeInitialzation()"); return bean; }
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("第五步,调用后初始化方法postProcessAfterInitialization()"); return bean; } }
|
Test类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Test {
@org.junit.Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User user = context.getBean("user", User.class); System.out.println(user); System.out.println("第六步 对象获取到了,bean可以使用了"); user.test(); context.close(); } }
|
bean.xml
1 2 3 4 5 6
| <!--配置User对象--> <bean id="user" class="com.spring5.User" init-method="initMethod" destroy-method="destroyMethod"> <!--name的值对应于javaBean中的属性值--> <property name="name" value="小明"></property> </bean> <bean id="myBeanPost" class="com.spring5.MyBeanPost"></bean>
|
注解方式实现IOC
注解概述
- 注解是代码特殊标记
语法:@注解名称(属性名称=属性值, 属性名称=属性值…)
- 怎么用注解
注解可以用在类、方法和属性上面
- 使用注解目的
简化 xml 配置
Spring针对 Bean管理中创建对象提供的注解
- @Component
- @Service
- @Controller
- @Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例,在服务层用@Service、在控制层用@Controller只是方便管理。
实现对象创建
- 第一步 引入依赖
把spring-aop-5.2.6.RELEASE.jar包引入到IDEA中
- 第二步 开启组件扫描
需要引入context名称空间:xmlns:context=“http://www.springframework.org/schema/context”、http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
1 2 3 4 5 6
|
<context:component-scan base-package="com.spring5"></context:component-scan>
|
- 第三步 创建类,在类上面添加注解
注解中的value值可以省略,默认值是类名首字母小写
1 2 3 4 5 6
| @Component(value = "userService") public class UserService { public void add(){ System.out.println("I am UserService add..."); } }
|
扫描细节配置
- 不加use-default-filters="false"表示会扫描包下面的所有注解
- context:include-filter 配置扫描哪些内容
- context:exclude-filter配置不扫描哪些内容
示例一
1 2 3 4 5
| <context:component-scan base-package="com.spring5" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
|
示例二
1 2 3 4 5
| <context:component-scan base-package="com.spring5"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
|