SSM-回炉重造-Spring IOC
Spring 主要两部分:
- IOC (Inversion Of Control): 控制反转
- AOP (Aspect Oriented Programming) : 面向切面编程
IOC
控制: 资源的获取方式
主动式:手动创建资源
BookController{ BookService bs = new BookService(); public void test(){ bs.check(); } }
被动式: 交给容器来创建资源
BookController{ BookService bs; //由容器来帮助我们创建此对象,并且赋值 public void test(){ bs.check(); } }
获取资源的方式由主动new
变成被动接收
DI(Dependency Injection): 依赖注入
容器通过反射的形式,将需要的对象注入到相应的变量中,只要容器管理的组件,即可使用容器提供的功能
HelloWorld 示例
- 创建空的Java工程
导入必需的Spring Jar包依赖包
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
创建JavaBean对象
package com.oylong.bean; /** * @ProjectName: springioc * @Description: * @Author: OyLong * @Date: 2020/9/10 0:35 */ public class Person { private String name; private Integer age; private String email; ...... }
创建Spring 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="person" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="[email protected]"></property> </bean> </beans>
通过ApplicationContext对象以及对象的
id
来获取容器中的对象public class IOCTest { @Test public void test(){ ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Person person = ioc.getBean("person", Person.class); //person为bean对象的id System.out.println(person); } }
打印了由IOC容器管理的对象
Person{name='oyLong', age=15, email='[email protected]'}
ClassPathXmlApplicationContext
: 从当前的类路径下去寻找IOC配置文件
注意事项
- 容器中的对象在容器创建完成的时候,也创建好了
- ioc容器中的对象是单例的
- 容器中若没有相应的组件,则获取组件时会报异常
- ioc容器创建对象时会利用相应的
setter
方法进行赋值(property标签) - javaBean的属性名,由getter/setter方法决定(尽量自动生成setter/getter方法)
通过类型直接获取Bean实例
当ioc容器
中只有一个同类型的bean对象时,如下:
ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
Person person = ioc.getBean(Person.class);
System.out.println(person);
我们将可以直接得到对应的bean对象
Person{name='oyLong', age=15, email='[email protected]'}
但是当容器中有多个相同类型的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="person" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
</bean>
<bean id="person1" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
</bean>
</beans>
报错:
No qualifying bean of type [com.oylong.bean.Person] is defined: expected single matching bean but found 2: person,person1
报错提示的也很明显,没有匹配的bean对象,因为找到了2个,不知道你需要的是哪一个,因此此时不适合使用类型来获取bean
对象
通过调用有参构造器创建bean对象
前提是这个bean对象本身有对应的构造器,如下:
public class Person {
private String name;
private Integer age;
private String email;
public Person() {
}
public Person(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
...
}
然后通过xml配置:
<bean id="person2" class="com.oylong.bean.Person">
<constructor-arg name="age" value="16"></constructor-arg>
<constructor-arg name="email" value="[email protected]"></constructor-arg>
<constructor-arg name="name" value="oyLong"></constructor-arg>
</bean>
在获取bean对象即可
ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
Person person = ioc.getBean("person2", Person.class);
System.out.println(person);
}
property标签 复杂属性赋值
复杂类型不能直接通过value
属性来赋值
1.空值(null)
如果不设置,则对象属性默认为null,其他基本类型属性则根据类的创建规则来自动赋值,也可手动设空值
如下:
<bean id="person1" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email">
<null/>
</property>
</bean>
2.对象赋值
car对象:
public class Car {
private String name;
private String color;
private BigDecimal price;
......
}
person对象:
public class Person {
private String name;
private Integer age;
private String email;
private Car car;
......
通过引用为Person的Car赋值,如下:
<bean id="car" class="com.oylong.bean.Car">
<property name="name" value="大卡车"></property>
<property name="color" value="绿色"></property>
<property name="price" value="2800"></property>
</bean>
<bean id="person2" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
<property name="car" ref="car"></property>
</bean>
public void test1(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");
Person person = ioc.getBean("person2", Person.class);
System.out.println(person);
System.out.println(person.getCar() == ioc.getBean("car", Car.class));
}
输出如下:
Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大卡车', color='绿色', price=2800}}
true
同时可以看到,引用赋值成功了,同时从ioc容器获取的bean对象与赋值的对象是同一个
如果要设置一个不同的car,则需要内部创建一个 如下:
<bean id="person2" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
<property name="car">
<bean class="com.oylong.bean.Car">
<property name="name" value="测试车"></property>
</bean>
</property>
</bean>
输出如下:
Person{name='oyLong', age=15, email='[email protected]', car=Car{name='测试车', color='null', price=null}}
false
注:这个内部创建的bean对象不能通过ioc容器获取到,只能在内部使用
### 3.集合赋值
更改Person对象:
public class Person {
private String name;
private Integer age;
private String email;
private Car car;
private List list;
private Map map;
private Properties properties;
......
}
bean配置如下:
<bean id="person3" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
<property name="car" ref="car"></property>
<!-- list赋值-->
<property name="list">
<list>
<value>中文</value>
<value>英文</value>
<ref bean="car"></ref>
</list>
</property>
<!-- map赋值 -->
<property name="map">
<map>
<entry key="m1" value="m1"></entry>
<entry>
<key>
<value>car</value>
</key>
<ref bean="car"></ref>
</entry>
</map>
</property>
<!-- properties赋值 -->
<property name="properties">
<props>
<prop key="aa">aa</prop>
<prop key="bb">bb</prop>
</props>
</property>
</bean>
运行结果
Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大卡车', color='绿色', price=2800}, list=[中文, 英文, Car{name='大卡车', color='绿色', price=2800}], map={m1=m1, car=Car{name='大卡车', color='绿色', price=2800}}, properties={aa=aa, bb=bb}}
4. 通过xml为JavaBean属性的属性赋值
如下xml
<bean id="person4" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
<property name="car" ref="car"></property>
<property name="car.name" value="大车"></property>
</bean>
其中car.name
就是将引用的car的名字更改为相应的值,需要注意的就是,假如更改了,那么本身在ioc容器里面的这个car
对象的属性也会随之更改
如下:
Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null}
car的名字变成了大车
5. xml中bean标签的继承
通过parent
属性就可以直接将对应的属性的值直接拷贝一份,然后将需要更改的再使用property
标签更改即可,如下:
<bean id="person4" class="com.oylong.bean.Person">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
<property name="car" ref="car"></property>
<property name="car.name" value="大车"></property>
</bean>
<bean id="person5" class="com.oylong.bean.Person" parent="person4">
<property name="name" value="parent=p4"></property>
</bean>
输出如下:
Person{name='parent=p4', age=15, email='[email protected]', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null}
创建单实例和多实例的bean对象
singleton: 单实例(默认)
- 容器启动时就创建好对象,保存在容器中
- 任何时候获取都是之前创建好的对象
prototype:多实例
- 容器启动时默认不创建对象
- 每次获取时再创建对象
- 每次获取都会创建一个新的对象
通过设置bean
标签的scope
属性即可设置不同的创建方式
<bean id="person4" class="com.oylong.bean.Person" scope="singleton">
<property name="age" value="15"></property>
<property name="name" value="oyLong"></property>
<property name="email" value="[email protected]"></property>
<property name="car" ref="car"></property>
<property name="car.name" value="大车"></property>
</bean>
自动装配
默认情况下,xml中设置的autowired
属性的值为default/no
,即不自动装配,不为指定的属性赋值
byName
以属性名作为id,去容器中找到这个组件,为其赋值,找不到则为null
<bean id="car" class="com.oylong.bean.Car"> <property name="name" value="大卡车"></property> <property name="color" value="绿色"></property> <property name="price" value="2800"></property> </bean> <bean id="person6" class="com.oylong.bean.Person" autowire="byName"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="[email protected]"></property> </bean>
如上xml中,由于
Person
类中的Car
属性的属性名为car
,所以将在ioc容器中去寻找一个id为car的组件,找到了之后为其赋值输出:
Person{name='oyLong', age=15, email='[email protected]', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null}
byType
以属性的类型为依据去ioc容器寻找组件,如果容器中有多个同类型的组件,那么就会报异常,没找到则赋值为
null
constructor
按照构造器进行赋值
- 按照有参构造器的类型进行装配(在ioc中寻找指定类型然后通过构造器赋值)
- 如果按照类型找到了多个,参数名作为id,继续装配,找不到就为
null
- 如果有List集合,且在容器中有多个匹配的类型,那么它们会被封装进List
- 基本类型不会自动装配
- 不会报错
SPEL
通过注解将组件加入ioc容器
Controller
控制器:推荐给控制器层的组件加这个注解
Service
业务逻辑:推荐给业务逻辑层的组件加这个注解
Repository
持久层(dao层):给数据库层(持久化,dao)的组件添加这个注解
Component
给除以上外的组件添加这个注解
component-scan 标签
在xml中配置标签
<context:component-scan base-package="com.oylong"/>
只有配置了component-scan后,才能扫描指定的目录下的注解,才能将相应的组件添加至ioc容器
使用注解之前,需要先导入Spring的aop包
默认情况下添加的组件的id为类名首字母小写
更改id则在相应的注解后面添加即可,如:
@Service("myGoodService")
@Scope(value = "prototype")
public class GoodService{
}
同样,如果需要更改组件的作用域,添加Scope注解在设置其value
值即可
@AutoWired注解
Spring通过@AutoWired
这个注解自动为属性赋值(从ioc容器中去查找相应的值),如下代码:
@Controller
public class CarServlet {
@Autowired
private CarService carService;
}
将自动从ioc容器中找到carService并为其赋值
@AutoWired注入流程
首先按照类型去ioc容器中寻找对应的组件,类似:
bookService = ioc.getBean(BookService.class);
- 如果没找到,会抛异常
- 如果找到多个,按照变量名作为id继续进行匹配(BookService[bookService])
- 如果没有匹配的变量名,就抛异常
可以使用
@Qualifier
注解进行指定id匹配@Controller public class CarServlet { @Autowired @Qualifier("carService") private CarService carServiceTmp; }
上面的代码就是通过
@Qualifier
这个注解,让carServiceTmp
这个变量去指定装配id为carService
的组件- 如果使用
@Qualifier
注解指定id还是没找到,那还是会抛异常
@Resource注解
这个注解的功能与@Autowired
注解功能类似,也是用于自动注入,如下:
@Controller
public class CarServlet {
@Resource
private CarService carService;
}
@AutoWired
是最强大的,由Spring规定。@Resource
是j2ee规定的Java标准,与之类似的还有一个@Inject
注解,由EJB规定。
- @Resource: 因为是java标准,所以拓展性更强,切换不同的容器框架,都可使用,而
@AutoWired
则不行,但是目前市面上的容器基本上也就只有Spring一家。默认按照id进行匹配,如果没有匹配的id,则会进行类型匹配,也可以单独指定其name
属性的值去按指定名称进行注入