Spring IoC容器
1、什么是IoC
- 把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IoC目的:为了降低耦合度
2、IoC底层
xml解析、工厂模式、反射
3、Spring提供的IoC容器实现的两种方式(两个接口)
BeanFactory
接口:IoC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
ApplicationContext
接口:BeanFactory
接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!
4、ApplicationContext接口的实现类
5、管理Bean的方式有两种:
基于xml方式
1、IoC操作Bean管理
Bean管理就是两个操作:
2、配置文件创建对象
1 2
| <bean id="user" class="com.zhao.spring5.User"></bean>
|
获取类对象
1 2 3 4 5 6 7 8 9
| @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user = context.getBean("user", User.class); user.show(); }
|
3、注入属性(DI:依赖注入(注入属性))
set方式注入
1 2 3 4 5 6 7 8 9 10
| public class Book { private String bname;
public void setBname(String bname) { this.bname = bname; } }
|
1 2 3 4 5 6 7 8 9
| <bean id="book" class="com.zhao.spring5.Book">
<property name="bname" value="Hello"></property> <property name="bauthor" value="World"></property> </bean>
|
有参构造函数注入
1 2 3 4 5 6 7 8 9 10 11
| public class Orders { private String oname; private String address; public Orders(String oname,String address) { this.oname = oname; this.address = address; } }
|
1 2 3 4 5
| <bean id="orders" class="com.zhao.spring5.Orders"> <constructor-arg name="oname" value="Hello"></constructor-arg> <constructor-arg name="address" value="China!"></constructor-arg> </bean>
|
p名称空间注入
1 2 3 4 5 6 7 8 9
| <?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" <!--在这里添加一行p-->
<bean id="book" class="com.zhao.spring5.Book" p:bname="very" p:bauthor="good"> </bean>
|
4、注入空值和特殊符号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <bean id="book" class="com.zhao.spring5.Book"> <property name="address"> <null/> </property>
<property name="address"> <value><![CDATA[<<南京>>]]></value> </property> </bean>
|
5、注入属性-外部bean
创建两个类service和dao类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class UserService {
private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; }
public void add() { System.out.println("service add..............."); userDao.update(); } }
public class UserDaoImpl implements UserDao {
@Override public void update() { System.out.println("dao update..........."); } }
|
在Spring配置文件中进行配置
1 2 3 4 5 6 7 8 9
| <bean id="userService" class="com.zhao.spring5.service.UserService">
<property name="userDao" ref="userDaoImpl"></property> </bean> <bean id="userDaoImpl" class="com.zhao.spring5.dao.UserDaoImpl"></bean>
|
注入属性-Bean自动装配
在不使用自动装配时需要手动注入每个bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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="dog" class="com.zhao.pojo.Dog"/> <bean id="cat" class="com.zhao.pojo.Cat"/> <bean id="user" class="com.zhao.pojo.User" > <property name="cat" ref="cat"/> <property name="dog" ref="dog"/> <property name="name" value="zhangsan"/> </bean> </beans>
|
在xml中使用autowire
设置自动装配:
byName
:按照bean名称自动装配
byType
:按照bean类型自动装配
1 2 3 4 5 6 7 8 9
| <bean id="user" class="com.zhao.pojo.User" autowire="byName"> <property name="name" value="zhangsan"/> </bean>
<bean id="dog" class="com.zhao.pojo.Dog"/> <bean id="cat" class="com.zhao.pojo.Cat"/> <bean id="user" class="com.zhao.pojo.User" autowire="byType"> <property name="name" value="zhangsan"/> </bean>
|
6、注入内部bean和级联赋值
注入属性-内部bean
- 一对多关系:部门和员工一个部门有多个员工,一个员工属于一个部门(部门是一,员工是多)
- 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
- 在spring配置文件中配置
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
| public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } }
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; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| <bean id="emp" class="com.zhao.spring5.bean.Emp"> <property name="ename" value="Andy"></property> <property name="gender" value="女"></property> <property name="dept"> <bean id="dept" class="com.zhao.spring5.bean.Dept"> <property name="dname" value="宣传部门"></property> </bean> </property> </bean>
|
注入属性-级联赋值
1 2 3 4 5 6 7 8 9 10 11
| <bean id="emp" class="com.zhao.spring5.bean.Emp"> <property name="ename" value="Andy"></property> <property name="gender" value="女"></property> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.zhao.spring5.bean.Dept"> <property name="dname" value="公关部门"></property> </bean>
|
1 2 3 4
| public Dept getDept() { return dept; }
|
1 2 3 4 5 6 7 8 9 10 11
| <bean id="emp" class="com.zhao.spring5.bean.Emp"> <property name="ename" value="jams"></property> <property name="gender" value="男"></property> <property name="dept" ref="dept"></property> <property name="dept.dname" value="技术部门"></property> </bean> <bean id="dept" class="com.zhao.spring5.bean.Dept"> </bean>
|
7、IoC 操作 Bean 管理——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
| public class Stu { private String[] courses; private List<String> list; private Map<String,String> maps; private Set<String> sets;
public void setSets(Set<String> sets) { this.sets = sets; } public void setCourses(String[] courses) { this.courses = courses; } public void setList(List<String> list) { this.list = list; } public void setMaps(Map<String, String> maps) { this.maps = maps; } }
|
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
| <bean id="stu" class="com.zhao.spring5.collectiontype.Stu"> <property name="courses"> <array> <value>java课程</value> <value>数据库课程</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>Redis</value> </set> </property> </bean>
|
8、在集合里面设置对象类型值
1 2 3 4 5
| private List<Course> courseList; public void setCourseList(List<Course> courseList) { this.courseList = courseList; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <bean id="course1" class="com.zhao.spring5.collectiontype.Course"> <property name="cname" value="Spring5框架"></property> </bean> <bean id="course2" class="com.zhao.spring5.collectiontype.Course"> <property name="cname" value="MyBatis框架"></property> </bean>
<property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?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:util="http://www.springframework.org/schema/util" <!--添加util名称空间--> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:list id="bookList"> <value>易筋经</value> <value>九阴真经</value> <value>九阳神功</value> </util:list>
<bean id="book" class="com.zhao.spring5.collectiontype.Book" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
|
9. Bean 作用域
在 Spring 里面,默认情况下,bean 是单实例对象,下面进行作用域设置:
- 在 Spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
singleton
:表示是单实例对;prototype
:表示是多实例对象
1 2 3
| <bean id="book" class="com.zhao.spring5.collectiontype.Book" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
|
设置 scope 值是 singleton
时候,加载 Spring 配置文件时候就会创建实例对象 ;设置 scope 值是 prototype
时候,不是在加载 Spring 配置文件时候创建对象,而是在调用 getBean
方法时候才创建实例对象
10、Bean 生命周期
生命周期 :从对象创建到对象销毁的过程。bean 的生命周期有七步 (正常生命周期为五步,而配置后置处理器后为七步)更多细节见【Spring】Spring5注解驱动开发。
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 把 bean 实例传递 bean 后置处理器的方法
postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要在配置文件中配置初始化的方法
init-method
)
- 把 bean 实例传递 bean 后置处理器的方法
postProcessAfterInitialization
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法
destroy-method
)
1 2 3 4 5 6 7 8 9 10 11 12
| public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法"); return bean; } }
|
10. FactoryBean
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
- 普通bean:在配置文件中定义 bean 类型就是返回类型
- 工厂bean:在配置文件定义 bean 类型可以和返回类型不一样。
- 第一步:创建类,让这个类作为工厂bean,实现接口
FactoryBean
;
- 第二步:实现接口里面的方法,在实现的方法中定义返回的 bean 类型
1 2 3 4 5 6 7 8 9 10
| public class MyBean implements FactoryBean<Course> {
@Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("abc"); return course; } }
|
1 2
| <bean id="myBean" class="com.zhao.spring5.factorybean.MyBean"> </bean>
|
1 2 3 4 5 6 7
| @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); Course course = context.getBean("myBean", Course.class); System.out.println(course); }
|
11. 引入外部属性文件(如Druid)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <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="com.zhao"></context:component-scan> <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>
|
基于注解方式
基于注解方式的Spring开发详细内容见【Spring】Spring5 注解驱动开发。
1、什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
- 使用注解,注解作用在类上面,方法上面,属性上面
- 使用注解目的:简化 xml 配置
2、Spring 针对 Bean 管理中创建对象提供注解
下面四个注解功能是一样的,都可以用来创建 bean 实例
3、基于注解方式实现对象创建
第一步 引入依赖 (引入spring-aop jar包)
第二步 开启组件扫描
1 2 3 4 5
|
<context:component-scan base-package="com.zhao"></context:component-scan>
|
第三步 创建类,在类上面添加创建对象注解
1 2 3 4 5 6 7 8 9
|
@Component(value = "userService") public class UserService { public void add() { System.out.println("service add......."); } }
|
4、开启组件扫描细节配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
<context:component-scan base-package="com.zhao" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
<context:component-scan base-package="com.zhao"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
|
5、基于注解方式实现属性注入
(1)@Autowired
:根据属性类型进行自动装配:
- 第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
- 第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Service public class UserService { @Autowired private UserDao userDao; public void add() { System.out.println("service add......."); userDao.add(); } }
@Repository
public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("dao add....."); } }
|
(2)@Qualifier
:根据对象名称进行注入,这个@Qualifier
注解的使用,和上面@Autowired
一起使用(目的在于区别同一接口下有多个实现类)
1 2 3 4 5 6 7
|
@Autowired
@Qualifier(value = "userDaoImpl1") private UserDao userDao;
|
(3)@Resource
:可以根据类型注入,也可以根据名称注入(它属于javax包下的注解,不推荐使用!)
1 2 3
| @Resource(name = "userDaoImpl1") private UserDao userDao;
|
(4)@Value
:注入普通类型属性
1 2
| @Value(value = "abc") private String name
|
总结:若某类型的对象只有一个,则可以使用@Autowired
注解,此时只会找到唯一的一个对象;但若某类型的对象不止一个,则要使用@Qualifier
注解,其会根据对象名称去寻找指定的对象。若注入基本类型对象使用@Value
。
6、作用域
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
1 2 3 4 5 6
| @Controller("user") @Scope("prototype") public class User { @Value("秦疆") public String name; }
|
7、完全注解开发
(1)创建配置类,替代 xml 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Configuration @ComponentScan(basePackages = {"com.zhao"}) @Import(SpringConfig2.class) public class SpringConfig { @Bean public User getUser() { return new User(); } }
|
(2)编写测试类
1 2 3 4 5 6 7 8 9 10
| @Test public void testService2() { ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
|
Spring 循环依赖
先去单例池(一级缓存),没有,再去半成品池取(二级缓存),最后去工厂池(三级缓存)里找,从工厂池里获取一个工厂,该工厂调用getObject() ,获得提前引用,获取到的是代理对象,将该对象放到二级缓存半成品里,在从工厂里移除这个beanName。
为什么有半成品池:
- 开启AOP时,需要将三级缓存创建的代理对象放到二级缓存里,之后a直接从二级缓存里取这个代理对象就可以了
- 提前引用可能有多次,比如a依赖b,c,两个都依赖a,b获得了a的提前引用后将该对象放到二级缓存里,并从工厂池移除该a的代理对象,c之后就不用再创建a了(若不放到二级缓存就会重复提前引用),直接去二级缓存取A。保证动态代理对象A只会被创建一次。
工厂池里存了很多工厂ObjectFactory,这些工厂调用getObject()取得a的动态代理对象singletonObject(提前引用),并将其放到二级缓存里,并从三级里移除该对象以免重复创建
Spring 对象生命周期