1 spring中的概念
IoC:Inverse of Control,控制反转。对象的创建权力由程序反转给Spring框架。
DI:Dependency Injection,依赖注入。在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!!
AOP:Aspect Oriented Programming,面向切面编程。在不修改目标对象的源代码情况下,增强IoC容器中Bean的功能。
Spring容器:指的就是IoC容器。
ioc和di是同一个概念的两个说法,aop是动态代理在spring中的名字
2 Ioc原理分析
IoC即将bean交由IoC容器管理
2.1 bean
Ioc容器管理的对象叫做bean,默认情况下,采用无参的构造函数创建
2.1.1 bean属性
id:bean在ioc容器中的唯一标识,用于获取bean
class:指定类的全限定名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围
- singleton :默认值,单例的(在整个容器中只有一个对象)
- prototype :多例的.
- request :将Spring 创建的 Bean 对象存入到 request 域中.
- session :将Spring 创建的 Bean 对象存入到 session 域中.
- global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
init-method:指定bean的初始化方法
destory-method:指定bean的销毁方法
2.1.2 scope
1 单例scope="singleton"
一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
- 创建:当应用加载,创建容器时,对象就被创建了。
- 销毁:当应用卸载,销毁容器时,对象就被销毁了
2 多例对象:scope="prototype"
每次访问对象时,都会重新创建对象实例。
生命周期:
- 创建:当使用对象时,创建新的对象实例。
- 销毁:当对象长时间不用时,被 java 的垃圾回收器回收了。
2.1.3 bean实例化
实例化bean的三种方式:无参构造方法、静态工厂、动态工厂
2.2 BeanFactory
BeanFactory接口提供了一种更先进的配置机制来管理任意类型的对象。
ApplicationContext 是 BeanFactory 的一个子接口。
2.3 BeanFactory子接口:只能提供单个bean
- AutowireCapableBeanFactory子接口:可以将第三方的bean进行自动装配
- ConfigurableBeanFactroy子接口:对beanFactory实现配置管理
- ListableBeanFactory子接口:提供beanFactory容器内bean实例全部获取并遍历的功能,需要预先加载所有bean定义的工厂需要实现这个接口
3 spring 基于xml使用
目录结构
在pom.xml中引入相关依赖
<properties>
<spring-version>5.1.2.RELEASE</spring-version>
</properties>
<dependencies>
<!-- 引入 spring core-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- junit 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4</version>
</dependency>
<!-- spring junit 整合测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- inject 注解 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
3.1 IoC基于XML的使用
ApplicationContext是能够保持bean定义以及相互依赖关系的高级工厂接口
3.1.1 applicationContext.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"
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">
<!-- bean definitions here -->
<!-- id:唯一标识 ,可以通过该标识获取java实例 -->
<!-- class:配置要实例化的java类全类名 -->
<bean id="userService" class="com.cc.service.UserServiceImpl" />
</beans>
3.1.2 UserDao 和UserSeviceImpl
@Repository
public class UserDao {
public void saveUser( User user){
System.out.println("userDao: saveUser:id为"+user.getId()+", name为:"+user.getName());
}
}
//@Service
public class UserServiceImpl implements UserService {
User user;
@Autowired
//@Resource
//@Inject
private UserDao userDao;
public UserServiceImpl() { }
// 为了使用构造函数注入+ Proerties配置 设计
public UserServiceImpl(User user) {
this.user = user;
}
// 为了使用构造函数注入+ Proerties配置 设计
@Override
public void saveUser() {
userDao.saveUser(user);
}
@Override
public void saveUser(User user) {
// System.out.println("保存用户:id为"+id+",name为"+name+" Service实现");
userDao.saveUser(user);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
3.1.2 测试
public class IoCTest {
@Test
public void testToC1(){
//创建
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取bean的实例
//方式1:根据类型容器中获取bean实例
UserService service1 = context.getBean(UserService.class);
service1.saveUser();
//方式2 根据bean的id从容器中获取bean实例
UserService service2= (UserService)context.getBean("userService");
service2.saveUser();
}
}
输出
保存用户:id为0,name为null Service实现
保存用户:id为0,name为null Service实现
3.2 DI基于xml的配置
3.2.1 使用构造函数进行注入
使用类中的构造函数,给成员变量赋值。
类中需要提供一个对应参数列表的构造函数。
涉及的标签:constructor-arg
- index:指定参数在构造函数参数列表的索引位置
- name:指定参数在构造函数中的名称
- value:它能赋的值是基本数据类型和 String 类型
- ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
<!-- Ioc 管理 user bean -->
<bean id="user" class="com.cc.pojo.User">
<!-- 构造函数注入 -->
<constructor-arg name="id" value="1111"/>
<constructor-arg name="name" value="userBean" />
</bean>
<!-- Ioc 配置 UserDao-->
<bean id="userDao" class="com.cc.dao.UserDao" />
<!-- Ioc 管理 UserSevice -->
<bean id="userServiceImpl" class="com.cc.service.UserServiceImpl">
<constructor-arg name="user" ref="user" />
<!-- set方法注入,在UserSevicesImpl中后setUserDao 方法 -->
<property name="userDao" ref="userDao"/>
</bean>
输出
userDao: saveUser:id为1111, name为:userBean
3.2.2 使用set方法进行注入
set方法注入又分为手动装配方式注入和自动装配方式注入。
手动装配方式(XML方式):
- 通过bean标签的子标签property来完成
- 需要在类中指定setter方法。
3.2.3 使用p名称空间注入
<bean id="userService" class="com.cc.service.UserServiceImpl" p:name="wangwu" p:userDao-ref="userDao" />
本质上还是使用的set方法进行的依赖注入,对应属性需要有set方法
4 spring基于注解和xml混合使用
4.1 IoC应用
applicationContext.xml
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.cc"/>
@Service和@Responsity注解
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;//不再需要set方法
}
@Repository
public class UserDao {
public void saveUser( User user){
System.out.println("userDao: saveUser:id为"+user.getId()+", name为:"+user.getName());
}
}
4.2 注解
@Component
- 作用:
把资源让 spring 来管理。相当于在 xml 中配置一个 bean。 - 属性:
value:指定 bean 的 id。
如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
@Controller、@Service、@Repository注解
- 他们三个注解都是针对@Component的衍生注解
- 他们的作用及属性都是一模一样的。他们只不过是提供了更加明确的语义化。
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。 - 细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。
4.3 DI注解
相当于:
4.3.1 @Authowired
- @Autowired是spring自带的注解
- 默认按类型装配(byType)
- 默认情况下要求依赖对象必须存在,如果需要允许null值,可以设置它的required属性为false,如:@Autowired(required=false)
- 如果我们想按名称装配(byName)可以结合@Qualifier注解进行使用
4.3.2 @Qualifier
- 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
- 它在给字段注入时不能独立使用,必须和@Autowire 一起使用;
- 但是给方法参数注入时,可以独立使用。
4.3.3 @Resource
- 属于J2EE JSR250规范的实现,用法和@Authowired 类似
- 默认按名称装配(byName),可以通过@Resource的name属性指定名称
- 如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,当找不到与名称匹配的bean时才按照类型进行装配。
- 但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
- 推荐使用@Resource注解,因为这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
4.3.4 @Inject
- @Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;
- @Inject是JSR330中的规范,需要导入javax.inject.Inject;实现注入。
- @Inject可以作用在变量、setter方法、构造函数上。
4.3.5 @Value
- 给基本类型和String类型注入值
- 可以使用占位符获取属性文件中的值。
user.properties
user.id=100
user.name=configUser
applicationContext.xml
<context:property-placeholder location="classpath*:*.properties"/>
UserServiceImp
@Value("${user.id}")//user.id是properties文件中的key
private int id;
@Value("${user.name}")
private String name;
4.3.6 @Scope
@Test
public void testToC1(){
//创建
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//从容器中获取bean的实例
//方式1:根据类型容器中获取bean实例
UserService service1 = context.getBean(UserService.class);
System.out.println(service1);
//service1.saveUser();
//方式2 根据bean的id从容器中获取bean实例
UserService service2= (UserService)context.getBean("userServiceImpl");
//System.out.println(service2);
service2.saveUser();
}
默认作用范围的输出
com.cc.service.UserServiceImpl@799d4f69
com.cc.service.UserServiceImpl@799d4f69
添加@Scope 标签
多例模式时输出
com.cc.service.UserServiceImpl@70beb599
com.cc.service.UserServiceImpl@4e41089d
4.3.7 @PostConstruct 和@PreDestroy
相当于
<bean id="" class="" init-method="" destroy-method="" />
5 比较xml和注解方式
- 注解
- 配置简单,维护方便(我们找到类,就相当于找到了对应的配置)
- 适用场景:bean由自己实现
- xml
- 修改时,不用改源码。不涉及重新编译和部署。
- 适用场景:bean来自第三方