基于注解配置bean
- 💗自动装配
- 🍝`案例1:` @Autowired引出
- 🍝`案例2:` @Autowired解读
- 🍚`案例3:` @Resource解读
- 🍝小结
- 💗泛型依赖注入
- 🍝基本说明
- 🍝应用实例
💗自动装配
●基本说明
1.基于注解配置bean, 也可实现自动装配. 使用的注解是: @Autowired 或者 @Resource
- @Autowired的规则说明
-
在ioc容器中查找待装配的组件的类型, 如果有唯一的bean则匹配.
-
如果装配的类型对应的bean在ioc容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找, 找到就装配, 找不到就抛异常.
-
- @Resource的规则说明
-
@Resource有两个属性是有两个属性是比较重要的, 分别是name和type. Spring将@Resource注解的name属性解析为bean的名字, 将type属性解析为bean的类型.
-
如果使用name属性, 则使用byName的自动注入策略; 而使用type属性时, 则使用byType自动注入策略.
-
如果@Resource 没有指定 name 和 type, 则优先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找), 如果匹配不上, 再使用byType策略(即查找待装配的组件的类型). 如果都不成功, 就会报错.
-
- 不管是@Autowired 还是 @Resource 都保证属性名是规范写法就可以 注入.
🍝案例1:
@Autowired引出
@Service
public class UserService {
public void hi() {
System.out.println("UserService hi()...");
}
}
@Controller
public class UserAction {
private UserService userService;
public void sayOK() {
System.out.println("UserAction sayOK()....");
userService.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zzw.spring.component"/>
</beans>
public class SpringBeanTest {
@Test
public void setPropertyByAutowired() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans06.xml");
UserAction userAction = ioc.getBean("userAction", UserAction.class);
System.out.println("userAction=" + userAction);//这里会输出
userAction.sayOK();//这里会报空指针异常
}
}
加入@Autowired, 就不会报错了.
@Controller
public class UserAction {
@Autowired
private UserService userService;
public void sayOK() {
System.out.println("UserAction sayOK()....");
userService.hi();
}
}
小知识点: OOP中我们是通过这种方式注入对象的, 注意比对.
public class UserAction {
private UserService userService = new UserService();
public void sayOK() {
System.out.println("UserAction sayOK()....");
userService.hi();
}
}
🍝案例2:
@Autowired解读
下面的代码中, UserAction中的userService200 和 SpringBeanTest中的userService是同一个对象. 因为ioc容器中只有一个UserService类型的对象, 此时按照类型来匹配.
@Service
public class UserService {
public void hi() {
System.out.println("UserService hi()...");
}
}
@Controller
public class UserAction {
//原先的xml配置, 我们会配置ref. 但是注解配置的情况下, 我们会用@Autowired
//说明
//1.在ioc容器中查找待装配的组件的类型, 如果有唯一的bean匹配(按照类型), 则使用该bean匹配
//2.如果待装配的类型对应的bean在ioc容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找
// 找到就装配, 找不到就抛异常
@Autowired
private UserService userService200;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService200);
userService200.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写-->
<context:component-scan base-package="com.zzw.spring.component"/>
</beans>
public class SpringBeanTest {
@Test
public void setPropertyByAutowired() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans06.xml");
UserService userService = ioc.getBean("userService", UserService.class);
System.out.println("ioc容器中的userService=" + userService);
UserService userService200 = ioc.getBean("userService200", UserService.class);
System.out.println("ioc容器中的userService200=" + userService200);
UserAction userAction = ioc.getBean("userAction", UserAction.class);
userAction.sayOK();
}
}
如果在beans06.xml中加入了同一类型(UserService)的对象, 如下. 那么UserAction中的userService200 和 SpringBeanTest中的userService将不再是同一个对象. 因为ioc容器中UserService类型的对象有多个, 此时将按照待匹配属性的属性名作为id值来匹配, 匹配到的是id为userService200的对象.
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<bean class="com.zzw.spring.component.UserService" id="userService200"/>
<bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
如果UserAction改为如下情况, 那么将会报错. 因为此时是按照待匹配属性的属性名作为id值来匹配的, 但beans06.xml中并没有id=userService400的bean对象, 所以报错.
@Controller
public class UserAction {
//原先的xml配置, 我们会配置ref. 但是注解配置的情况下, 我们会用@Autowired
//说明
//1.在ioc容器中查找待装配的组件的类型, 如果有唯一的bean匹配(按照类型), 则使用该bean匹配
//2.如果待装配的类型对应的bean在ioc容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找
// 找到就装配, 找不到就抛异常
@Autowired
private UserService userService400;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService400);
userService400.hi();
}
}
指定id进行组装. 可以使用@Autowired和@Qualifier(value=“userService200”). 这时, 是装配的 id=userService200, 两个注解要配合使用, 类似于@Resource(name="userService200")
.
此时, UserAction中的userService 和 SpringBeanTest中的userService200是同一个对象.
@Controller
public class UserAction {
//指定id进行组装. 可以使用@Autowired和@Qualifier(value="userService200").
//这时, 是装配的 id=userService200, 类似于@Resource(name="userService200").
//前提是需要两个注解都写上.
@Autowired
@Qualifier(value = "userService200")
private UserService userService;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService);
userService.hi();
}
}
public class SpringBeanTest {
@Test
public void setPropertyByAutowired() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans06.xml");
UserService userService = ioc.getBean("userService", UserService.class);
System.out.println("ioc容器中的userService=" + userService);
UserService userService200 = ioc.getBean("userService200", UserService.class);
System.out.println("ioc容器中的userService200=" + userService200);
UserAction userAction = ioc.getBean("userAction", UserAction.class);
userAction.sayOK();
}
}
🍚案例3:
@Resource解读
@Resource源码解读: 通过解析注解来支撑, 底层是注解来支撑的.
String name() default "";
Class<?> type() default java.lang.Object.class;
@Resource(name = "userService")
代表把beans06.xml对应的容器里的id=userService的对象注入到属性上去, 所以UserAction中的userService400 和 SpringBeanTest中的userService是同一个对象
@Controller
public class UserAction {
//1.@Resource 有两个属性是比较重要的, 分别是name和type, Spring将@Resource注解的name属性解析为bean的名字,
// 将type属性解析为bean的类型. 所以如果使用name属性, 则使用byName的自动注入策略, 而使用type属性时则使用
// byType自动注入策略
// 比如 @Resource(name="userService") 表示装配id=userService的对象
// 比如 @Resource(type="UserService.class") 表示按照UserService.class类型进行装配. 这时要求容器中,只能有一个这样类型的对象
//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),
// 如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.
@Resource(name = "userService")
private UserService userService400;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService400);
userService400.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写, 这里是userService-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<bean class="com.zzw.spring.component.UserService" id="userService200"/>
<bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
public class SpringBeanTest {
@Test
public void setPropertyByAutowired() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans06.xml");
UserService userService = ioc.getBean("userService", UserService.class);
System.out.println("ioc容器中的userService=" + userService);
UserAction userAction = ioc.getBean("userAction", UserAction.class);
userAction.sayOK();
}
}
如果将代码改为@Resource(name = "userService200")
代表把beans06.xml对应的容器里的id=userService200的对象注入到属性上去, 那么UserAction中的userService400 和 SpringBeanTest中的userService200将是同一个对象,
@Controller
public class UserAction {
//1.@Resource 有两个属性是比较重要的, 分别是name和type, Spring将@Resource注解的name属性解析为bean的名字,
// 将type属性解析为bean的类型. 所以如果使用name属性, 则使用byName的自动注入策略, 而使用type属性时则使用
// byType自动注入策略
// 比如 @Resource(name="userService") 表示装配id=userService的对象
@Resource(name = "userService200")
private UserService userService400;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService400);
userService400.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写, 这里是userService-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<bean class="com.zzw.spring.component.UserService" id="userService200"/>
<bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
public class SpringBeanTest {
@Test
public void setPropertyByAutowired() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans06.xml");
UserService userService = ioc.getBean("userService", UserService.class);
System.out.println("ioc容器中的userService=" + userService);
UserService userService200 = ioc.getBean("userService200", UserService.class);
System.out.println("ioc容器中的userService200=" + userService200);
UserAction userAction = ioc.getBean("userAction", UserAction.class);
userAction.sayOK();
}
}
如果将代码改为@Resource(name = "userService600")
会直接报错. 因为beans06.xml对应的容器中没有id为userService600的对象, 所以报错.
@Controller
public class UserAction {
//1.@Resource 有两个属性是比较重要的, 分别是name和type, Spring将@Resource注解的name属性解析为bean的名字,
// 将type属性解析为bean的类型. 所以如果使用name属性, 则使用byName的自动注入策略, 而使用type属性时则使用
// byType自动注入策略
// 比如 @Resource(name="userService") 表示装配id=userService的对象
@Resource(name = "userService600")
private UserService userService400;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService400);
userService400.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写, 这里是userService-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<bean class="com.zzw.spring.component.UserService" id="userService200"/>
<bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
如果按照类型来装配, @Resource(type = UserService.class)
, 那么必须保证容器中该类型的对象只有一个.
像下面的情况就会报错.
public class UserAction {
@Resource(type = UserService.class)
private UserService userService400;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService400);
userService400.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<bean class="com.zzw.spring.component.UserService" id="userService200"/>
<bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错. 像下面的代码就会报错.
public class UserAction {
//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),
// 如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.
@Resource
private UserService userService400;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService400);
userService400.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<bean class="com.zzw.spring.component.UserService" id="userService200"/>
<bean class="com.zzw.spring.component.UserService" id="userService300"/>
</beans>
下面代码会成功
public class UserAction {
//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),
// 如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.
@Resource
private UserService userService;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService);
userService.hi();
}
}
下面代码依然会成功
public class UserAction {
//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),
// 如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.
@Resource
private UserService userService200;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService200);
userService200.hi();
}
}
下面代码会失败. 因为beans.xml中没有id=userServise600的bean对象, byName注入策略不成功, 并且byType注入策略也不成功, 所以会失败.
public class UserAction {
//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),
// 如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.
@Resource
private UserService userService600;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService600);
userService600.hi();
}
}
但是下面的代码会成功. 虽然beans.xml中没有id=userServise600的bean对象, 即byName注入策略不成功, 但是由于beans06.xml对应的容器中只有一个UserService类型的对象, 所以byType策略成功, 所以下面代码不会报错.
public class UserAction {
//2.如果@Resource 没有指定 name 和 type, 则先使用byName注入策略 (即使用待装配的属性的属性名作为id值进行查找),
// 如果匹配不上, 再使用byType策略. 如果都不成功, 就会报错.
@Resource
private UserService userService600;
public void sayOK() {
System.out.println("UserAction sayOK()....");
System.out.println("UserAction 装配的 userService属性=" + userService600);
userService600.hi();
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过注解获取的对象id是类名首字母小写-->
<context:component-scan base-package="com.zzw.spring.component"/><!--id=userService-->
<!--配置两个UserService对象-->
<!--<bean class="com.zzw.spring.component.UserService" id="userService200"/>-->
<!--<bean class="com.zzw.spring.component.UserService" id="userService300"/>-->
</beans>
🍝小结
1.如果装配的类型对应的bean在IOC容器中有多个, 则使用待装配的属性的属性名作为id值再进行查找, 找到就装配, 找不到就抛异常.
💗泛型依赖注入
🍝基本说明
1.为了更好地管理有继承和相互依赖的bean的自动装配, spring还提供基于泛型依赖的注入机制.
2.在继承关系复杂情况下, 泛型依赖注入就会有很大的优越性.
各个类关系图
传统方法是将 PhoneDao / BookDao 自动装配到 BookService / PhoneService中, 当这种继承关系多时, 就比较麻烦. 可以使用spring提供的泛型依赖注入.
🍝应用实例
public class Book {}
public class Phone {}
//自定义泛型类
public abstract class BaseDao<T> {
public abstract void save();
}
@Repository
public class BookDao extends BaseDao<Book> {
@Override
public void save() {
System.out.println("BookDao的 save方法....");
}
}
@Repository
public class PhoneDao extends BaseDao<Phone> {
@Override
public void save() {
System.out.println("PhoneDao的 save方法...");
}
}
//自定义泛型类
public class BaseService<T> {
@Autowired
private BaseDao<T> baseDao;
public void save() {
baseDao.save();
}
}
@Service
public class BookService extends BaseService<Book> {
//并没有写属性
}
@Service
public class PhoneService extends BaseService<Phone> {
//没有写属性
}
beans07.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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zzw.spring.depinjection"/>
</beans>
public class SpringBeanTest {
@Test
public void setProByDependencyInjection() {
ApplicationContext ioc =
new ClassPathXmlApplicationContext("beans07.xml");
PhoneService phoneService = ioc.getBean("phoneService", PhoneService.class);
phoneService.save();//PhoneDao的 save方法...
System.out.println("OK");
}
}
下乘: Spring系列四:AOP切面编程