底层原理
1、什么是IOC
(1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理。
(2)使用IOC目的:为了耦合度降低。
(3)做入门案例就是IOC实现。
2、IOC底层原理。
(1) xml解析、工厂模式、反射。
目的:耦合度降低最低限度 eg.实例一个类调用另一个类的方法
进一步解耦
IOC
3. IOC(接口)
1、IOC 思想基于IOC容器完成,IOC 容器底层就是对象工厂
2、Spring 提供IOC容器实现两种方式: (两个接口) 。
(1) BeanFactory: IOC 容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。
*加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。
(2) ApplicationContext: BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
*加载配置文件时候就会把在配置文件对象进行创建。
3、ApplicationContext 接口有实现类
Bean管理XML方式
1、什么是Bean管理
(0) Bean管理指的是两个操作。
(1) Spring 创建对象。
(2) Spirng 注入属性。
Bean 管理操作有两种方式
(1)基于xml配置文件方式实现。
①基于XML方式创建对象
(1)在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。
(2)在bean标签有很多属性,介绍常用的属性。
id属性:唯一标识。 class属性:类全路径(包类路径)。
(3) 创建对象时候,默认也是执行无参数构造方法完成对象创建。
②基于XML方式注入属性(DI)
1.set构造注入属性
创建属性实例 name和age
public class User {
public String name;
public Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void add(){
System.out.println("Hello World!");
}
}
使用property进行属性注入
<?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">
<!-- 配置User对象创建 -->
<bean id="user" class="com.atguigu.spring5.User">
<!-- set方法注入属性 -->
<!-- 使用property完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="age" value="13"></property>
<property name="name" value="王兴华"></property>
</bean>
</beans>
测试
//1.加载Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml");
//2.获取配置创建的对象
User user = context.getBean("user",User.class);
System.out.println(user.age + user.name);
user.add();
2.有参构造注入属性
创造有参实例name和age
package com.atguigu.spring5;
public class User {
public String name;
public Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public void add(){
System.out.println("Hello World!");
}
}
使用constructor-arg完成有参构造注入
<?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">
<!-- 配置User对象创建 -->
<bean id="user" class="com.atguigu.spring5.User">
<!-- 使用constructor-arg完成有参构造注入 -->
<constructor-arg name="age" value="13"></constructor-arg> <!-- 通过属性名称 -->
<constructor-arg name="name" value="wxh"></constructor-arg>
<constructor-arg index="0" value="wxh"></constructor-arg> <!-- 也可以通过构造方法索引 -->
<constructor-arg index="1" value="16"></constructor-arg>
</bean>
</beans>
3.p名称空间注入(了解)
4.xml注入其他类型属性
1.字面量
注入null值
<!-- 向name属性注入null -->
<property name="name">
<null/>
</property>
注入特殊符号
<!-- 第一种方式使用转义字符 < >
第二种方式使用<![CDATA[内容]]> -->
<property name="name">
<value><![CDATA[<<王兴华>>]]></value>
</property>
5.注入属性 -外部bean
创建两个类service类和dao类
package com.atguigu.spring5.service;
import com.atguigu.spring5.dao.UserDao;
import com.atguigu.spring5.dao.UserDaoImpl;
public class UserService {
//创建UserDao类型属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void add(){
System.out.println("Service add..................");
// //原始方式
// UserDao userDao = new UserDaoImpl();
userDao.update();
}
}
package com.atguigu.spring5.dao;
public interface UserDao {
public void update();
}
package com.atguigu.spring5.dao;
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update..............");
}
}
在service调用dao里面的方法
这里要用ref属性
<?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">
<!-- 1 Service和dao对象创建 -->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!-- 注入userDao对象
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
</beans>
测试
public class SpringBean {
@Test
public void test01(){
//1.加载Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean02.xml");
//2.获取配置创建的对象
UserService userService = context.getBean("UserService", UserService.class);
userService.add();
}
}
6.注入属性- 内部bean和级联赋值
(1) 一对多关系:部门和员工。
一个部门有多个员工,一个员工属于一个部门。
部门是一,员工是多。
(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示。
//部门类
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;
}
}
创建内部bean
<?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="emp" class="com.atguigu.spring5.bean.Emp">
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!-- 设置对象类型属性 -->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="保安部"></property>
</bean>
</property>
</bean>
</beans>
XML同时还可以写成级联赋值
<?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="emp" class="com.atguigu.spring5.bean.Emp">
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!-- 级联赋值 -->
<property name="dept" ref="dept "></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
</beans>
第二种级联赋值 !! !!但注意这种方法必须生成内部bean的get方法
<?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="emp" class="com.atguigu.spring5.bean.Emp">
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!-- 级联赋值 -->
<property name="dept" ref="dept "></property>
<property name="dept.dname" value="技术部"></property>
</bean>
</beans>
7.XML注入集合类型
package com.atguigu.spring5.collectiontype;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu {
//1.数组类型属性
private String[] courses;
//2.list集合类型属性
private List<String> list;
//3.map集合类型属性
private Map<String,String> maps;
//4.set集合类型属性
private Set<String> 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;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
}
XML配置
<?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="Stu" class="com.atguigu.spring5.collectiontype.Stu">
<!-- 数组注入 -->
<property name="courses">
<array>
<value>java</value>
<value>python</value>
<value>Ruby</value>
<value>C++</value>
</array>
</property>
<!-- list注入 -->
<property name="list">
<list>
<value>java</value>
<value>python</value>
<value>Ruby</value>
<value>C++</value>
</list>
</property>
<!-- map类型注入 -->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PYTHON" value="PYTHON"></entry>
<entry key="RUBY" value="ruby"></entry>
</map>
</property>
<!-- set类型注入 -->
<property name="sets">
<set>
<value>java</value>
<value>python</value>
<value>Ruby</value>
<value>C++</value>
</set>
</property>
</bean>
</beans>
注入集合类型,但是集合为类集合(常用)
需要在外面创建类对象,之后在类内部使用ref标签导入
<property name="courseList">
<list>
<ref bean="course01"></ref>
<ref bean="course02"></ref>
</list>
</property>
</bean>
<!-- 创建多个course对象 -->
<bean id="course01" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Spring框架"></property>
</bean>
<bean id="course02" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Mybatis框架"></property>
</bean>
8.XML自动装配
根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。
创建Emp和Dept类
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
}
//==============================================
public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}
(1)根据属性名称注入
<?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">
<!-- 使用自动装配注入Dept对象 -->
<!--bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入,注入值bean的id值和类属性名称一样
byType根据属性类型注入
-->
<bean id="emp" class="com.atguigu.spring5.auto.Emp" autowire="byName">
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.atguigu.spring5.auto.Dept"></bean>
</beans>
(2)根据属性类型注入
<?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">
<!-- 使用自动装配注入Dept对象 -->
<!--bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入,注入值bean的id值和类属性名称一样
byType根据属性类型注入
-->
<bean id="emp" class="com.atguigu.spring5.auto.Emp" autowire="byType">
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.atguigu.spring5.auto.Dept"></bean>
</beans>
9.外部属性文件
Bean管理
IOC操作Bean管理
1、Spring有两种类型bean,一种普通bean,另外一种工厂bean (FactoryBean)
2、普通bean:在配置文件中定义bean类型就是返回类型。
3、工厂bean:在配置文件定义bean类型可以和返回类型不一样。
第一步创建类,让这个类作为工厂bean, 实现接口FactoryBean兵实现接口里面的方法,在实现的方法中定义返回的bean类型
package com.atguigu.spring5.bean;
import org.springframework.beans.factory.FactoryBean;
public class FactoryBeanTest implements FactoryBean<Dept> {
@Override
public Dept getObject() throws Exception {
Dept dept = new Dept();
dept.setDname("lcl");
return dept;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
第二步实现配置文件。
<?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="mybean" class="com.atguigu.spring5.bean.FactoryBeanTest"></bean>
</beans>
第三部测试。
package com.atguigu.spring5.spirngTest;
import com.atguigu.spring5.bean.Dept;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FactoryTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean05.xml");
//这里注意类为实现工厂类泛型的类
Dept mybean = applicationContext.getBean("mybean", Dept.class);
System.out.println(mybean.getDname());
}
}
Bean的作用域
!!!在Spring里面,默认情况下,bean 是单实例对象
可以看到属于同一个对象
如何设置单实例还是多实例?
(1)在spring配置文件bean标签里面有属性(scope) 用于设置单实例还是多实例
(2) scope 属性值
第一个值默认值,singleton, 表示是单实例对象。
第二个值 prototype,表示是多实例对象。
<bean id="mybean" class="com.atguigu.spring5.bean.FactoryBeanTest" scope="prototype"></bean>
(3) singleton 和prototype区别
第一singleton 单实例,prototype 多实例。
第二设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象。设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象。
Bean生命周期
1、生命周期
(1)从对象创建到对象销毁的过程。
2、bean生命周期。
(1)通过构造器创建bean实例(无参数构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法) 把bean实例传递bean后置处理器的方法postProcessBeforeInitializations
(3)调用bean的初始化的方法(需要进行配置初始化的方法) 把bean实例传递bean后置处理器的方法postProcessAfterInitializations
(4) bean可以使用了(对象获取到了)
(5)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)。
Bean管理注解方式
1、Spring 针对Bean管理中创建对象提供注解。
(1) @Component 普通组件
(2) @Service 控制层组件
(3) @Controller 业务层组件
(4) @Repository 持久层组件
上面四个注解功能是一样的,都可以用来创建bean实例,但通常用在不同层
基于注解实现对象创建
1.引入依赖
2.开启组件扫描
<?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"
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">
<!-- 开启组件扫描
1如果扫描多个包,多个包使用逗号隔开
2扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
</beans>
3.创建类,在类上面添加创建对象注解
package com.atguigu.spring5.service;
import org.springframework.stereotype.Component;
//在注解里面value属性值可以省略不写
//默认值是类名称,首字母小写
@Component(value = "empService") //<bean id="empService" class=" "/>
public class EmpService {
public void hello(){
System.out.println("Hello World!");
}
}
4.测试
public class SpringTest03 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean07.xml");
EmpService empService = context.getBean("empService", EmpService.class);
empService.hello();
}
}
5.遇到问题
这里遇到bug nested exception is org.springframework.core.NestedIOException: ASM ClassReader failed to parse cla
解决办法:java1.8版本只支持spring4.0以上,需把pom里的sping版本换4.1.5就ok了,也可以把jdk调低点。
组件扫描配置细节
<!-- 第一种 -->
<!-- use-default-filters="false" 表示不使用默认的扫描方式
include-filter 表示哪些内容被扫描
type="annotation" 为注解扫描
expression="org.springframework.stereotype.Controller" 表示只扫描Controller的注解
-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 第二种 -->
<!-- exclude-filter 表示哪些内容不扫描 -->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
基于注解方式注入属性
(1) @AutoWired : 根据属性类型注入
第一步把service和dao对象创建,在service和dao类添加创建对象注解。
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update..............");
}
}
@Service
public class EmpService {
public void hello(){
System.out.println("Hello World!");
}
}
第二步在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解
@Service
public class EmpService {
//不需要添加set方法
@Autowired
private UserDaoImpl userDao;
public void hello(){
System.out.println("Hello World!");
}
}
(2) @Qualifier : 根据属性名称注入
这个 @Qualifier 注解的使用,和上面@AutoWired 一起使用
package com.atguigu.spring5.service;
import com.atguigu.spring5.dao.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//在注解里面value属性值可以省略不写
//默认值是类名称,首字母小写
//@Component(value = "empService") //<bean id="empService" class=" "/>
@Service
public class EmpService {
//不需要添加set方法
//如果有多个实现类就会出现找不到的情况就要使用@Qualifier和@Autowired配合使用
@Autowired
@Qualifier("userdao01")
private UserDaoImpl userDao;
public void hello(){
System.out.println("Hello World!");
}
}
package com.atguigu.spring5.dao;
import org.springframework.stereotype.Repository;
@Repository(value = "userdao01")
public class UserDaoImpl implements UserDao{
@Override
public void update() {
System.out.println("dao update..............");
}
}
(3) @Resource : 可以根据类型注入,可以根据名称注入
注意通常不使用这个,因为这个不是spring包里面的,而是javax包里的
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
(4) @Value : 注入普通类型属性
@Value(value = "abc")
private String name;
完全注解开发
创建类
@Service //表示创建该bean对象
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("add............");
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add(){
System.out.println("Serivce add........");
userDao.add();
}
}
全局配置类(通过一个全局配置类替换xml配置)
package com.atguigu.spring5.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration //作为配置类,代替xml文件
@ComponentScan(basePackages = {"com.atguigu"}) //扫描
public class SpringConfig {
}
测试类 注意有所不同
public class Test01 {
@Test
public void test01(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add();
}
}