索引
先来看看常用的几种DI方式
- 构造器注入:利用构造方法的参数进行注入
- Setter注入:使用Setter方法注入依赖
- 字段注入:使用@Autowired或者@Resource
我们先来设定一个情景:在一个UserServiceImpl中利用构造方法注入一个UserDaoImpl对象,以供控制层调用使用,这是一个非常常规的控制层获取持久层数据的方案。
先来看构造器注入:
构造器注入又分为xml方式和注解方式:
xml方式注入:
首先需要一个UserService接口和UserDao接口,以及他们两个的实现类UserServiceImpl和UserDaoImpl实现类。
/**
UserService接口
**/
public interface UserService {
void save();
}
/**
UserDao接口
**/
public interface UserDao {
void save();
}
/**
UserServiceImpl实现类
**/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl() {
}
@Override
public void save() {
System.out.println("我是userServiceImpl:" + userDao);
userDao.save();
}
}
/**
UserDaoImpl实现类
**/
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl现在被创建啦");
}
public void init() {
System.out.println("UserDaoImpl初始化成功");
}
public void destroy() {
System.out.println("UserDaoImpl正在被销毁");
}
@Override
public void save() {
System.out.println("我是userServiceImpl:" + userDao + " 我将要执行userDao中的sava方法了!!!");
userDao.save();
}
}
然后再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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton" init-method="init"/>
<!-- 构造方法注入-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" scope="singleton">
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
测试代码:
@Test
//利用了构造方法 依赖注入
public void test05() throws Exception {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
执行结果:
UserDaoImpl现在被创建啦
UserDaoImpl初始化成功
我是userServiceImpl:com.itheima.dao.impl.UserDaoImpl@302552ec 我将要执行userDao中的sava方法了!!!
sava is running......
注解方式:
上面我们是通过配置xml方式来实现构造器注入的,还是比较繁琐的,现在很少使用xml配置来进行注入了,直接使用注解更方便,如下:
我们既然要再Spring中使用注解,可能想的很简单,在UserDaoImpl
上加一个@Repository
注解,再UserServiceImpl
加一个@Service
注解,表示这两个类的对象创建交给Spring容器管理
@Repository
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl现在被创建啦");
}
/**
这个@PostConstruct注解就相当于上面xml中的 init-method="init"
**/
@PostConstruct
public void init() {
System.out.println("UserDaoImpl初始化成功");
}
public void destroy() {
System.out.println("UserDaoImpl正在被销毁");
}
@Override
public void save() {
System.out.println("sava is running......");
}
}
@Service
public class UserServiceImpl02 implements UserService {
private UserDao userDao;
public UserServiceImpl02(UserDao userDao) {
this.userDao = userDao;
}
/**
这个无参构造不能删
**/
public UserServiceImpl02() {
}
@Override
public void save() {
System.out.println("我是userServiceImpl" + userDao);
userDao.save();
}
}
但这样还不够,想想,我们是要在UserServiceImpl
中使用userDao
的,也就是UserDaoImpl
,怎么用呢, 之前xml中,我们是写了一个构造器,然后xml配置文件进行配置,声明这个类里面使用了UserDaoImpl
,现在用注解注入了,就用注解的方式来做,具体如下:
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
//这里就不能使用@Resource注解了
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl02() {
}
@Override
public void save() {
System.out.println("我是userServiceImpl" + userDao + " 我将要执行userDao中的sava方法了!!!");
userDao.save();
}
}
我们既然使用注解了,那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: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
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 构造方法注入-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" scope="singleton">
<!-- <constructor-arg name="userDao" ref="userDao"/>-->
</bean>
<!--注解的组件扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
</beans>
之前的< bean id=“userDao”>现在用不着了,因为我们使用了注解,但是要注意的是,要想使用@Resource
注解,得写一个依赖
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
然后,还得再applicationContext.xml
中写一个注解扫描
<!--注解的组件扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
这样就可以在Spring中使用注解了。测试如下:
@Test
//利用了构造方法 依赖注入
public void test05() throws Exception {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
UserDaoImpl现在被创建啦
UserDaoImpl初始化成功
我是userServiceImpl:com.itheima.dao.impl.UserDaoImpl@65c7a252 我将要执行userDao中的sava方法了!!!
sava is running......
再来看Setter注入:
Setter注入又分为xml方式和注解方式:
xml方式注入:
首先需要一个UserService接口和UserDao接口,以及他们两个的实现类UserServiceImpl和UserDaoImpl实现类,这里的UserServiceImpl实现类使用的是setter注入方式,注意。
/**
UserService接口
**/
public interface UserService {
void save();
}
/**
UserDao接口
**/
public interface UserDao {
void save();
}
/**
UserServiceImpl实现类
**/
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("我是userServiceImpl" + userDao + " 我正在被创建");
userDao.save();
}
}
/**
UserDaoImpl实现类
**/
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl现在被创建啦");
}
public void init() {
System.out.println("UserDaoImpl初始化成功");
}
public void destroy() {
System.out.println("UserDaoImpl正在被销毁");
}
@Override
public void save() {
System.out.println("我是userServiceImpl:" + userDao + " 我将要执行userDao中的sava方法了!!!");
userDao.save();
}
}
然后再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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton" init-method="init"/>
<!-- set注入方法 使用依赖注入但是没有使用p标签简化-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
测试代码:
@Test
//利用了setter方法 依赖注入
public void test05() throws Exception {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
执行结果:
UserDaoImpl现在被创建啦
UserDaoImpl初始化成功
我是userServiceImplcom.itheima.dao.impl.UserDaoImpl@408d971b 我正在被创建
sava is running......
注解方式:
大概写法和构造器中的注解方式差不多,就直接上代码了
@Repository
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl现在被创建啦");
}
/**
这个@PostConstruct注解就相当于上面xml中的 init-method="init"
**/
@PostConstruct
public void init() {
System.out.println("UserDaoImpl初始化成功");
}
public void destroy() {
System.out.println("UserDaoImpl正在被销毁");
}
@Override
public void save() {
System.out.println("sava is running......");
}
}
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
//@Resource 注解也可以,区别下面会说
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("我是userServiceImpl" + userDao + " 我正在被创建");
userDao.save();
}
}
最后看看字段注入:
就直接放UserServiceImpl
的代码了,前面看明白了这里看一个UserServiceImpl
就够了
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void save() {
System.out.println("我是userServiceImpl" + userDao);
userDao.save();
}
}
下面就是为什么Spring和IDEA都不推荐使用@Autowired注解的原因了
平常我们在代码中注入一个字段的时候,会用到@Autowired注解,但是这个注解有个问题,
@Autowired VS @Resource
他们的基本功能都是通过注解实现依赖注入,只不过@Autowired是Spring定义的,而@Resource是JSR-250定义的。大致功能基本相同,但是还有一些细节不同:
- 依赖识别方式:@Autowired默认是byType可以使用@Qualifier指定Name,@Resource默认ByName如果找不到则ByType
- 适用对象:@Autowired可以对构造器、方法、参数、字段使用,@Resource只能对方法、字段使用
- 提供方:@Autowired是Spring提供的,@Resource是JSR-250提供的
各种DI方式的优缺点:
- 构造器注入:强依赖性(即必须使用此依赖),不变性(各依赖不会经常变动)
- Setter注入:可选(没有此依赖也可以工作),可变(依赖会经常变动)
- Field注入:大多数情况下尽量少使用字段注入,一定要使用的话, @Resource相对@Autowired对IoC容器的耦合更低
Field注入的缺点:
会导致组件与IoC容器紧耦合(这是最重要的原因,离开了IoC容器去使用组件,在注入依赖时就会十分困难)
为什么IDEA只对@Autowired警告
Field注入虽然有很多缺点,但它的好处也不可忽略:那就是太方便了。使用构造器或者setter注入需要写更多业务无关的代码,十分麻烦,而字段注入大幅简化了它们。并且绝大多数情况下业务代码和框架就是强绑定的,完全松耦合只是一件理想上的事,牺牲了敏捷度去过度追求松耦合反而得不偿失。