什么是IOC容器
IOC全称Inversion of Control,直译为控制反转,它是具有依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。应用程序无需直接在代码中new相关的对象,应用程序由IOC容器进行组装。在Spring中BeanFactory是IOC容器的实际代表者。使用IOC的目的是为了降低耦合度。
IOC容器中的Bean
Bean就是由Spring容器初始化、装配及管理的对象。那IOC怎样确定如何实例化Bean、管理Bean之间的依赖关系以及管理Bean呢?先让我们来做个简单的例子。
小试牛刀
搭建Spring5环境
打开Inteillj然后new一个Java项目:
导入相关的jar包:
写一个HelloWorld
写一个简单的Person类:
package com.jackma.spring5;
public class Person {
public void sayHello(){
System.out.println("Hello World");
}
}
创建一个xml文件,用来告诉Spring的IOC容器应该如何创建并组装Bean,其中id是一个Bean的唯一标识,class表示类全路径:
<?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 name="person" class="com.jackma.spring5.Person"></bean>
</beans>
然后写一个单元测试方法:
public class MySpringTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Person person = context.getBean("person", Person.class);
person.sayHello();
}
}
测试:
IOC底层原理
先来看个实例:假设有两个类,UserService和UserDao,UserDao中有一个add()方法,我们要在UserService调用它,那么使用原始的方式是这样实现的:
public class UserDao {
public void add(){
System.out.println("add.......");
}
}
public class UserService {
public void execute(){
UserDao userDao = new UserDao();
userDao.add();
}
}
但原始的方法会存在一个问题,即耦合度太高了,UserService和UserDao关系过于紧密,解决方法是提供一个工厂类,把new userDao的工作交给工厂类,实现解耦:
public class UserFactory {
public static UserDao getuserDao(){
return new userDao();
}
}
public class UserService {
public void execute(){
UserDao userDao = UserFactory.getuserDao();
userDao.add();
}
}
Spring的IOC的底层原理使用到了xml解析、工厂模式和反射:
第一步先配置xml文件:
<bean id="userdao" class="com.jackma.spring5.dao.UserDao"></bean>
第二步是Spring会根据User类和userDao类提供一个UserFactory类,且类中根据反射来实现创建userDao对象:
public class UserFactory {
public static UserDao getuserDao(){
String classValue = class属性值; // 该值通过xml解析得到
Class clazz = Class.forName(classValue);
return (UserDao)clazz.newInstance();
}
}
IOC的Bean管理
Bean管理指的是Spring创建对象和Spring注入属性两个操作,实现方式有两种:
①:基于 xml 配置文件方式实现
②:基于注解方式实现
基于 xml 方式创建对象
像上面的HelloWorld例子使用的就是 xml 方式创建对象,在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建,且创建对象时候,默认也是执行无参数构造方法完成对象创建。
基于 xml 方式的属性注入
基于 xml 方式的属性注入有两种方式:set方法注入和构造器注入,还是拿User类来举例 。
set方法注入
先在User类中添加两个属性,然后记得要提供对应的set方法:
public class User {
private int id;
private String name;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public void add(){
System.out.println("add......");
}
}
然后在配置文件中添加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">
<!-- set 方法注入-->
<bean id="user" class="com.jackma.spring5.User" >
<property name="id" value="1"></property>
<property name="name" value="Jackma"></property>
</bean>
</beans>
然后测试:
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
}
构造器注入
要在User类中添加构造器方法:
package com.jackma.spring5;
public class User {
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
// public void setId(int id) {
// this.id = id;
// }
//
// public void setName(String name) {
// this.name = name;
// }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public void add(){
System.out.println("add......");
}
}
<?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="user" class="com.jackma.spring5.User">
<constructor-arg name="id" value="2"></constructor-arg>
<constructor-arg name="name" value="马保国"></constructor-arg>
</bean>
</beans>
测试代码同上,结果:
基于XML方式注入其他类型的属性
注入null
使用set方法注入为上面的name属性注入null,那么就要修改xml文件(注意User类要提供set方法):
<bean id="user" class="com.jackma.spring5.User">
<property name="id" value="3"></property>
<property name="name">
<null></null> // 就是这个
</property>
</bean>
测试代码同上,结果:
注入特殊符号
使用set方法注入为上面的name属性注入含有特殊符号<>的数据,那么就要修改xml文件,使用<![CDATA[...内容...]]>,这样它就会将内容之间输出:
<!-- 属性值包含特殊符号-->
<bean id="user" class="com.jackma.spring5.User">
<property name="id" value="3"></property>
<property name="name">
<value><![CDATA[<马云>]]></value> // 就是这个
</property>
</bean>
测试代码同上,结果:
注入引用数据类型
看一个案例:有一个公司Company,有一个部门类Dept和一个员工类Emp,Emp中有一个Dept类型的属性,用来表示员工的部门:
Dept:
package com.jackma.spring5.company;
// 部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept{" +
"dname='" + dname + '\'' +
'}';
}
}
Emp:
package com.jackma.spring5.company;
// 员工类
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;
}
public Dept getDept() {
return dept;
}
@Override
public String toString() {
return "Emp{" +
"ename='" + ename + '\'' +
", gender='" + gender + '\'' +
", dept=" + dept +
'}';
}
}
使用外部bean
xml中添加外部bean:
<!-- 外部bean-->
<bean id="emp" class="com.jackma.spring5.company.Emp">
<property name="ename" value="马云"></property>
<property name="gender" value="男"></property>
<property name="dept" ref="dept" ></property>
</bean>
// 外部bean
<bean name="dept" class="com.jackma.spring5.company.Dept">
<property name="dname" value="退休了"></property>
</bean>
测试:
@Test
public void test3(){
// 测试内部bean
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
使用内部bean
xml中添加内部bean:
<!-- 内部bean-->
<bean id="emp" class="com.jackma.spring5.company.Emp">
<property name="ename" value="马云"></property>
<property name="gender" value="男"></property>
<property name="dept" >
// 内部bean
<bean name="dept" class="com.jackma.spring5.company.Dept">
<property name="dname" value="总裁"></property>
</bean>
</property>
</bean>
测试代码同上,结果:
级联赋值
先关联(外部bean或者内部bean),后赋值:先使用外部bean关联,然后赋值为首席执行官
<bean id="emp" class="com.jackma.spring5.company.Emp">
<property name="ename" value="马云"></property>
<property name="gender" value="男"></property>
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="首席执行官"></property>
</bean>
<bean name="dept" class="com.jackma.spring5.company.Dept">
<property name="dname" value="总裁"></property>
</bean>
测试代码同上,结果:
注入集合类型
注入集合类型包括:数组、List、Set、Map。使用Stu类来演示:
第一步是添加属性,并提供对应set方法:
package com.jackma.spring5.collectiontype;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Stu{
private String[] course;
private List<String> list;
private Set<String> set;
private Map<String, String> maps;
public void setCourse(String[] course) {
this.course = course;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void test(){
// 测试类
System.out.println(Arrays.toString(course));
System.out.println(list);
System.out.println(set);
System.out.println(maps);
}
}
第二步是在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="stu" class="com.jackma.spring5.collectiontype.Stu">
<!-- 数组类型属性注入-->
<property name="course">
<array>
<value>JavaSE</value>
<value>JavaWeb</value>
<value>JavaEE</value>
</array>
</property>
<!-- 数组类型属性注入-->
<property name="list">
<list>
<value>语文</value>
<value>数学</value>
<value>英语</value>
</list>
</property>
<!-- Set类型属性注入-->
<property name="set">
<set>
<value>早上</value>
<value>中午</value>
<value>晚上</value>
</set>
</property>
<!-- Map类型属性注入-->
<property name="maps">
<map>
<entry key="蒙牛" value="酸酸乳"></entry>
<entry key="瓜子" value="二手车"></entry>
<entry key="O泡" value="时间到"></entry>
</map>
</property>
</bean>
</beans>
测试:
设置集合属性对象类型的值
先创建一个类Course表示一个学生学的课程
package com.jackma.spring5.collectiontype;
public class Course {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
'}';
}
}
然后Stu类中添加一个存放Course类型数据的属性List,然后提供set方法:
// 学生所学的多门课程
private List<Course> list2;
public void setList2(List<Course> list2) {
this.list2 = list2;
// 用来测试
public void test1(){
System.out.println(list2);
}
}
现在我们要对List中Course类型的对象进行赋值,xml文件配置:
<!-- 设置集合属性对象类型的值-->
<bean id="stu" class="com.jackma.spring5.collectiontype.Stu">
<property name="list2">
<list>
<ref bean="course1" ></ref>
<ref bean="course2" ></ref>
</list>
</property>
</bean>
<bean id="course1" class="com.jackma.spring5.collectiontype.Course">
<property name="name" value="物理"></property>
</bean>
<bean id="course2" class="com.jackma.spring5.collectiontype.Course">
<property name="name" value="化学"></property>
</bean>
测试:
@Test
public void test5(){
// 测试设置集合属性对象类型的值
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
Stu stu = context.getBean("stu", Stu.class);
stu.test1();
}
提取集合类型属性的注入部分
先写一个book类:
package com.jackma.spring5.collectiontype;
import java.util.List;
import java.util.Set;
public class Book {
private List<String> book;
private List<String> book2;
public void setBook(List<String> book) {
this.book = book;
}
public void setBook2(List<String> book2) {
this.book2 = book2;
}
public void test(){
System.out.println("bean1" + book);
}
public void test2(){
System.out.println("bean2:" + book2);
}
}
步骤:
- 在Spring配置文件中引入名称空间util:
<?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"
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">
</beans>
- 使用util标签完成提取list集合类型属性的提取(相当于是一个公共的,可以注入到多个List类型的bean中),下面把提取出来的booklist注入到list和list2两个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"
xmlns:util="http://www.springframework.org/schema/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="list" class="com.jackma.spring5.collectiontype.Book">
<property name="book" ref="booklist"></property>
</bean>
<bean id="list2" class="com.jackma.spring5.collectiontype.Book">
<property name="book2" ref="booklist"></property>
</bean>
</beans>
测试:
@Test
public void test5(){
// 测试设置集合属性对象类型的值
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Book book = context.getBean("list:", Book.class);
book.test();
Book book2 = context.getBean("list2", Book.class);
book2.test2();
}
普通bean和工厂bean
什么是普通 bean?前面我们在配置文件中定义的bean就是普通bean,其创建的类型就是返回类型:
而工厂 bean就是在配置文件中定义bean的类型可以和返回类型不一样,下面来举个例子:
- 先新建一个包factorybean,然后在包里新建一个类Mybean:
package com.jackma.spring5.factorybean;
public class Mybean {
}
改之前先看一下使用普通bean的结果会是什么样子,创建xml文件(注意这里写的类型是Mybean):
<?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.jackma.spring5.factorybean.Mybean"></bean>
</beans>
测试和结果:
@Test
public void test6(){
// 测试工厂bean
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Mybean mybean = context.getBean("myBean", Mybean.class);
System.out.println(mybean);
}
可以看到这里是一个Mybean类型的,接下来把它改成Coursr类型的。
- 让这个类作为工厂bean,实现接口FactoryBean,并实现 接口里面的方法,在实现的方法中定义返回的bean类型:
package com.jackma.spring5.factorybean;
import com.jackma.spring5.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;
public class Mybean implements FactoryBean<Course> {
// 定义返回bean的类型
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setName("newbean");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
修改测试代码:
@Test
public void test6(){
// 测试工厂bean
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
bean的作用域
- 在Spring里,我们可以设置bean是单实例还是多实例。
- 默认为单实例。
拿前面那个Book类来测试:
// 测试单实例多实例
ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
Book book1 = context.getBean("list", Book.class);
Book book2 = context.getBean("list", Book.class);
System.out.print("book1 == book2 ? ");
System.out.println(book1 == book2);
System.out.println(book1);
System.out.println(book2);
可以看到两个book的地址相同,就说明是单实例,那么如何改成多实例?bean 标签里面有属性scope来实现:
可以看到有两个值,其中singleton表示是单实例对象,是默认值;而prototype表示是多实例对象。那么修改后结果就变成:
注意事项:设置scope值是 singleton时候,加载spring配置文件时候就会创建单实例对象。 设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,而是在调用getBean方法时候创建多实例对象。
bean的生命周期
什么是生命周期?就是从对象创建到对象销毁的过程,bean的生命周期如下:
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其他bean引用(调用 set 方法)
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- 获取到了创建的bean实例对象(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
创建一个Orders类来演示bean的生命周期:
package com.jackma.spring5.bean;
public class Orders {
private String oname;
public Orders() {
System.out.println("第一步,执行了无参构造函数,创建实例");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步,set注入属性");
}
public void initMethod(){
System.out.println("第三步,调用bean的初始化的方法");
}
public void destroyMethod(){
System.out.println("第五步,销毁bean");
}
}
需要在配置文件中配置bean的初始化的方法和bean的销毁的方法init-method和destroy-method:
<?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="orders" class="com.jackma.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
</beans>
bean的销毁的方法还需要自行调用:
@Test
public void test7(){
// 测试生命周期
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步,获取到了创建的bean实例对象:");
System.out.println(orders);
// 手动让bean实例销毁
context.close();
}
以上是bean基本的生命周期,总共有5步,但如果添加了bean的后置处理器之后,还会有两个步骤,即生命周期变成7步:
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其他bean引用(调用 set 方法)
- 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- bean实例传递bean后置处理器的方法postProcessAfterInitialization
- 获取到了创建的bean实例对象(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
怎么添加bean的后置处理器?首先要创建一个类来实现BeanPostProcessor接口:
package com.jackma.spring5.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
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;
}
}
然后在配置文件中配置后置处理器:
<?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="orders" class="com.jackma.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
<!-- 配置后置处理器-->
<bean id="mybeanpost" class="com.jackma.spring5.bean.MyBeanPost"></bean>
</beans>
测试代码同上,结果:
XML的自动装配
什么是自动装配?根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。举例:
- 创建一个包autowire,创建一个Emp类和一个Dept类:
Emp:
package com.jackma.spring5.autowire;
public class Emp{
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
public void test(){
System.out.println(this);
}
}
Dept:
package com.jackma.spring5.autowire;
// 部门类
public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}
- 没设置自动装载前的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.jackma.spring5.autowire.Emp">
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.jackma.spring5.autowire.Dept"></bean>
</beans>
测试和结果:
@Test
public void test8(){
// 测试自动装载
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
Emp emp = context.getBean("emp", Emp.class);
emp.test();
}
下面来配置自动装载,配置文件中的bean标签有一个属性autowire,可以用来配置自动装配:
autowire 属性常用两个值:byName根据属性名称注入 ,注入值bean的id值和类属性名称一样;byType根据属性类型注入(如果使用byType,那么相同类型的bean不能定义多个,不然就不知道是哪一个了):
<!-- 配置自动装配-->
<bean id="emp" class="com.jackma.spring5.autowire.Emp" autowire="byName">
</bean>
<bean id="dept" class="com.jackma.spring5.autowire.Dept"></bean>
<!-- 配置自动装配-->
<bean id="emp" class="com.jackma.spring5.autowire.Emp" autowire="byType">
</bean>
<bean id="dept" class="com.jackma.spring5.autowire.Dept"></bean>
测试和结果:
外部属性文件
应用场景:假设要创建一个数据库的连接池,按照以往的方法创建,假如我们的用户名或者密码之类的信息要修改,那么同时就要对xml文件中的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="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 直接配置-->
<property name="driverClassName" value="com,mysql.jdbc.Driver"></property>-->
<property name="url" value="jdbc.mysql://localhost:3306/userDb"></property>-->
<property name="username" value="root"></property>-->
<property name="password" value="123456"></property>-->
</bean>
</beans>
改进方法是创建一个properties外部属性文件,然后把信息注入到xml文件中,这样在修改信息时只需要修改properties文件即可:
首先提供properties文件:
然后需要在xml文件中引入context名称空间:
<?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: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">
<!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
<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针对Bean管理中创建对象提供的注解:
- @Component
- @Service
- @Controller
- @Repository
注解方式创建对象的步骤:
- 引入AOP依赖。
- 开启组件扫描:扫描类中的注解,需要用到context,如果扫描多个包,多个包使用逗号隔开,或者直接写扫描包的上层目录。
<?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: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.jackma.spring5.service"></context:component-scan>
</beans>
- 新建一个类,在类上面添加创建对象注解
package com.jackma.spring5.service;
import org.springframework.stereotype.Component;
// 注解里的value相当于bean里的id值,也可以不写,若不写则默认为首字母小写的类名
@Component(value = "userService") // <bean id="userService">
public class UserService {
public void add(){
System.out.println("Service add......");
}
}
测试和结果:
@Test
public void test9(){
// 测试自动装载
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
UserService userservice = context.getBean("userService", UserService.class);
userservice.add();
}
开启组件扫描时默认扫描包中所有的类,但也可以进行配置,使得它扫描指定的类:
<context:component-scan base-package="com.jackma.spring5.service" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
context标签中有属性use-default-filters,true表示使用默认的扫描,而false表示不使用默认的扫描,然后在下面自己设置扫描的方法:
context:include-filter:设置扫描哪些内容;context:exclude-filter:设置哪些内容不进行扫描,type表示根据什么来扫描,这里我设置成按照注解来扫描,然后expression选择注解的类型,我在这是扫描注解类型是Component的,不扫描Controller类型的。
此外,type的类型还有:
基于注解方式注入属性
和上面的创建对象一样,Spring针对Bean管理中注入属性提供的注解有:
- @Autowired:根据属性类型进行自动装配
- @Qualifier:根据名称进行注入
- @Resource:可以根据类型注入,可以根据名称注入
- @Value:注入普通类型属性
实例:有一个ManService类,一个ManDaoImpl类实现ManDAO接口和它里面的upDate()方法,现在要在ManService类中调用这个方法:
步骤:
- 在Man类和ManDaoImpl类添加创建对象的注解
- 在service中注入dao对象,在service类添加dao类型属性,在属性上面使用注解,不用提供set方法
@Autowired
ManDao 接口:
package com.jackma.spring.dao;
public interface ManDao {
public void upDate();
}
接口实现类ManDaoImpl :
package com.jackma.spring.dao;
import org.springframework.stereotype.Repository;
@Repository(value = "manDaoImpl")
public class ManDaoImpl implements ManDao {
@Override
public void upDate() {
System.out.println("dao upDate......");
}
}
ManService :
package com.jackma.spring;
import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository(value = "manService")
public class ManService {
// 定义DAO类型属性,添加注入属性注解
// 不用提供set方法
@Autowired // 根据类型注入
ManDao manDao = new ManDaoImpl();
public void add(){
System.out.println("service add......");
manDao.upDate();
}
}
测试:
@Test
public void test10(){
// 测试注解注入属性
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
ManService manService = context.getBean("manService", ManService.class);
manService.add();
}
@Qualifier
这个@Qualifier 注解的使用,和上面@Autowired 一起使用。因为万一ManDao有多个实现类,而仅仅使用@Autowired的话它不知道是哪个实现类,因此可以使用@Qualifier来指定特定名字的实现类。
package com.jackma.spring;
import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
@Repository(value = "manService")
public class ManService {
// 定义DAO类型属性,添加注入属性注解
// 不用提供set方法
@Autowired // 根据类型注入
@Qualifier(value = "manDaoImpl")
ManDao manDao = new ManDaoImpl();
public void add(){
System.out.println("service add......");
manDao.upDate();
}
}
@Resource
结合了上两个注解的效果:
package com.jackma.spring;
import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository(value = "manService")
public class ManService {
// 定义DAO类型属性,添加注入属性注解
// 不用提供set方法
// @Autowired // 根据类型注入
// @Qualifier(value = "userDaoImpl")
// @Resource // 根据类型注入
@Resource(name = "manDaoImpl") // 根据名称注入
ManDao manDao = new ManDaoImpl();
public void add(){
System.out.println("service add......");
manDao.upDate();
}
}
@Value
注入普通类型属性:
package com.jackma.spring;
import com.jackma.spring.dao.ManDao;
import com.jackma.spring.dao.ManDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository(value = "manService")
public class ManService {
// 定义DAO类型属性,添加注入属性注解
// 不用提供set方法
// @Autowired // 根据类型注入
// @Qualifier(value = "userDaoImpl")
// @Resource // 根据类型注入
@Resource(name = "manDaoImpl") // 根据名称注入
ManDao manDao = new ManDaoImpl();
@Value("马保国")
private String name;
public void add(){
System.out.println("service add......");
manDao.upDate();
System.out.println("name:" + name);
}
}
测试代码相同:
完全注解开发
完全注解开发,即使用一个配置类来替代 xml 配置文件。
创建配置类,替代 xml 配置文件:
package com.jackma.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.jackma.spring")
public class SpringConfig {
}
测试:
@Test
public void test11(){
// 测试纯注解开发
// 加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
ManService man = context.getBean(ManService.class);
man.add();
}