三、自动装配
自动装配,就是将一个bean自动地注入到其他Bean的property中。
<bean id="userDao" class="com.itheima.annotation.UserDaoImpl"/> <bean id="userService" class="com.itheima.annotation.UserServiceImpl" autowire="byName"/> <bean id="userController" class="com.itheima.annotation.UserController" autowire="byName"/>
注意:自动装配使用时是需要在对应的类中写其属性的Setter方法的,不然自动装配不了
package com.itheima.annotation; import javax.annotation.Resource; import org.springframework.stereotype.Controller; public class UserController { private UserService userService; /*Spring Bean的自动装配方式,需要在Controller和ServiceImpl增加setter方法, 即如下代码*/ public void setUserService(UserService userService) { this.userService = userService; } public void save() { this.userService.save(); System.out.println("userController...save..."); } }
通过注解和xml进行装配各有什么优缺点
1、注解
优点
- XML配置起来繁琐,注解最大的好处就是简化了XML配置
- 注解如果有问题,在编译期间,就可以验证正确性(类型安全的),如果出错更容易找,XML只能在运行期才能发现问题
- 使用起来直观且容易,提升开发的效率。
缺点
- 注解是一种分散式的元数据,与源代码紧绑定,所以耦合度比XML要高,修改不方便,不容易扩展和维护
- 对象之间的关系需要看源代码才可以知道,没有XML那么一目了然
2、Xml
优点
- xml是集中式的元数据,不需要和代码绑定的(xml配置文件和代码类是区分开的),耦合度低,修改方便
- 对象之间的关系一目了然
- 修改时,不用改源码,不涉及重新编译和部署。
缺点
- 配置冗长,需要额外维护,影响开发效率
- 类型不安全,校验不出来,出错不好排查
3、总结
注解简单概括:写起来比较简单、方便,看起来也简洁,但是修改麻烦
Xml配置概括:写起来比较灵活、修改方便,但是写和维护麻烦
如何使用IOC
好文参考:如何使用 IoC - 编程猎人
通常如何获取bean
要获取XML中配置的Bean,最关键的是获取.springframework.context.ApplicationContext
方法一:在初始化时保存ApplicationContext对象
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml"); ac.getBean("beanId"); ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); Student student = (Student) applicationContext.getBean("student"); System.out.println(student);
说明:这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。
方法二:通过Spring提供的utils类获取ApplicationContext对象
ApplicationContext ac1 = WebApplicationContextUtils .getRequiredWebApplicationContext(ServletContext sc); ApplicationContext ac2 = WebApplicationContextUtils .getWebApplicationContext(ServletContext sc); ac1.getBean("beanId"); ac2.getBean("beanId"); public class MyServlet extends HttpServlet { public void init() throws ServletException { super.init(); ServletContext servletContext = getServletContext(); WebApplicationContext ctx =WebApplicationContextUtils.getWebApplicationContext(servletContext); (Bean)ctx.getBean("bean"); } }
说明:这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,然后在通过它获取需要的类实例。上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。
方法三:继承自抽象类ApplicationObjectSupport
抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取ApplicationContext。
Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。
方法四:继承自抽象类WebApplicationObjectSupport
类似上面方法,调用getWebApplicationContext()获取WebApplicationContext
方法五:实现接口ApplicationContextAware
说明:实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。Spring初始化时,会通过该方法将ApplicationContext对象注入。
以下是实现ApplicationContextAware接口方式的代码,前面两种方法类似:
@Component public class SpringContextUtil implements ApplicationContextAware { /** * Spring应用上下文环境 */ private static ApplicationContext applicationContext; /** * 实现ApplicationContextAware接口的回调方法。设置上下文环境 * * @param applicationContext */ @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringContextUtil.applicationContext = applicationContext; } /** * @return ApplicationContext */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 获取对象 * * @param name * @return Object * @throws BeansException */ public static Object getBean(String name) throws BeansException { return applicationContext.getBean(name); } /** * 获取对象通过Class * * @param cls * @return Object * @throws BeansException */ public static <C> Object getBean(Class<C> cls) throws BeansException { return applicationContext.getBean(cls); }
虽然,spring提供的后三种方法可以实现在普通的类中继承或实现相应的类或接口来获取spring 的ApplicationContext对象,但是在使用是一定要注意实现了这些类或接口的普通java类一定要在Spring 的配置文件applicationContext.xml文件中进行配置。否则获取的ApplicationContext对象将为null。
方法六:通过Spring提供的ContextLoader
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean(beanID);
最后提供一种不依赖于servlet,不需要注入的方式。但是需要注意一点,在服务器启动时,Spring容器初始化时,不能通过上面的方法获取Spring 容器
除了autowire其他获取bean的注解的方法
还可以通过@Resource获取bean
@Resource作用:用来装配bean,可以写在字段上或者setter方法上。默认按照名称来装配注入,找不到名称时按照类型来装配注入,如果都没有那就报错
@Autowired()和@Resource的区别:
1.都是用来自动装配的,都可以放在属性上
2.@Autowired()通过byType的方式实现,而且必须要求这个对象存在
3.@Resource()默认通过byName的方式实现,如果找不到名字,则通过byType实现,如果两种都找不到就报错了
注解
使用@Autowired(啊4 te3 外 d)注解自动装配的过程是怎样的?@Resource装配顺序?
规则:@Autowired 先根据定义类型匹配再根据key匹配并返回
详细过程
@Autowired 首先会根据类型(By type)去SpringIOC容器中找对应的bean(跳过id(key)的匹配),如果找到的类型是唯一且匹配则返回这个对象并注入到用@Autowired标识的定义类型中。如果匹配到的类型是不唯一的,就是根据key来匹配!这个key是指的用 @Autowired 标识定义类型的变量名(也就是下面的abc),找到了就自动注入,如果上述查找的结果为空,那么会抛出异常。如果不想让它抛出异常那就使用required=false
@Autowired
IAccountDao abc;
解析:所谓的类型就是使用这个注解的对象及其子类对象就都是这一类型的,例如上面的IAccountDao 使用了这个注解,那么就会去ioc容器里找这个对象的bean及其子类的bean,他们都是IAccountDao 这个bean一类型的。
@@Autowired(required=false)
public BaseMapper baseMapper;
@Resource装配顺序
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则按类型进行匹配,如果匹配则自动装配。 否则就抛异常
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
注意:在根据类型来找时,发现找到多个符合要求的bean,这时可以用@Qualified(扩了 fai3 d)注解来指定注入某个具体id的bean,所以对于@Autowired 来说,如果没有使用这个注解就会根据变量名来找,但是如果使用了这个注解就会根据这个注解的值来找,@Resource也可以通过这个注解来指定注入某个具体的bean
什么是基于Java类的Spring注解配置? 给一些注解的例子
1、什么是基于Java类的Spring注解配置?
基于 Java 类的配置,允许你在少量的 Java 注解的帮助下,进行你的大部分 Spring
配置而非通过 XML 文件。
2、给一些注解的例子?
使用@Configuration 注解和@Bean 注解,@Configuration 注解表示标记这个类为配置类,相当于是Spring的XML配置文件,在这个类里可以配置bean,然后被Spring IOC 容器使用。@Bean 注解,它表示此方法将要返回一个对象,作为一个 bean 注册进 Spring 应用上下文。
@Configuration public class BeanConfiguration { @Bean public Account account(){ return new Account("001001001"); } @Bean public User user(Account account){ return new User("张三",18,account); } @Bean public User userNoAccount(){ return new User("张三",18,null); } }
怎样开启注解装配?
注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在 Spring 配置文件中配置<context:annotation-config/>元素。
开启自动装配
<!-- 开启自动装配 就是可以使用@Autowired,不能使用@component,@service,@Repository等注解-->
<context:annotation-config/>
开启自动装配并扫描指定包【建议使用,可以少扫描其他不必要的类】
<!-- 开启自动装配,扫描指定包,这里可以使用@component,@service,@Repository等注解 替代了 <context:annotation-config/>-->
<context:component-scan base-package="org.example"/>
<context:component-scan base-package="org.example"/>
常见的注解和作用如下
@Repository //作用在类上,表示数据层,此类的对象创建交由Spring管理
@Service //作用在类上,表示Service层,此类的对象创建交由Spring管理
@Controller //作用在类上,表示Controller,此类的对象创建交由Spring管理
@Component //作用在类上,表示普通层=类 ,此类的对象创建交由Spring管理
@Value //作用在属性上,此类创建对象时,当前属性的初始值
@Resource //作用在对象属性上,给当前属性赋值,根据对象名称查找对象赋值给当前对象属性【此注解不属于Spring,属于java拓展包<!-- @Resource注解 -->javax.annotation-api】
@Autowired //作用在对象属性上,给当前属性赋值,在Spring根据类型查找,相同类型对象赋值
@Qualifier("对象名") //作用在对象属性上,一般用于和@Autowired配合使用,当@Autowired在Spring容器中发现两个和当前属性对象相同的时候,使用此注解,根据对象名称来赋值
@Component, @Controller, @Repository, @Service 有何区别?
@Component,@Service, @Controller, @Repository是spring注解,使用这些注解的类可以被spring框架所扫描并注入到spring容器来进行管理,@Repository, @Service, @Controller就是针对不同的使用场景所采取的特定功能化的注解组件。
@Component是通用注解,没有特定的功能,其他三个注解是这个注解的拓展,并且具有了特定的功能
@Repository注解在持久层中,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能。
@Controller是标记为控制器的一个注解,主要就是用来处理请求的,具有将请求进行转发,重定向的功能。
@Service是服务层注解,这个注解只是标注该类处于业务逻辑层。
用这些注解对应用进行分层之后,就能将请求处理,业务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发。
@Required 注解有什么作用
@Required注解作用于Bean的setter方法上,用于检查一个Bean的属性的值在配置期间是否被赋予或设置,其实就是使用了这个注解就表明bean的这个属性必须在配置的时候设置,若@Required注解的bean属性未在配置的时候被设置,容器将抛出BeanInitializationException(bean初始化异常)
举个例子
使用<context:annotation-config/>隐式地注册RequiredAnnotationBeanPostProcessor,使@Required注解生效
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-lazy-init="true"> <context:annotation-config/> <bean id="resultMessage" class="com.jake.ResultMessage"> <!--此处只设置了code属性,并没有配置message属性--> <property name="code" value="001" /> </bean> </beans>
public class ResultMessage { private String code; private String message; public String getCode() { return code; } @Required //由于配置文件里配置这个属性,所以不会报错 public void setCode(String code) { this.code = code; } public String getMessage() { return message; } @Required //由于配置文件里没配置这个属性,所以会报错 public void setMessage(String message) { this.message = message; } } @Controller public class Test{ @Autowired private ResultMessage message; @RequestMapping("/testRequired") public String testRequired(HttpServletRequest req, HttpServletResponse resp) { System.out.println(message); } }
此时访问testRequired,可以看到,报BeanInitializationException异常。
org.springframework.beans.factory.BeanInitializationException: Properties 'message' are required for bean 'resultMessage'
@Autowired和@Resource之间的区别
1、相同点
都是用来自动装配的,两者都可以写在属性和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)提供方
@Autowired是Spring的注解;@Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供,需要JDK1.6及以上。
(2)注入方式
@Autowired()通过byType的方式实现,默认情况下,其依赖的对象必须存在(bean可用),如果允许null值,可以设置它的required属性为false,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法就是结合 @Qualifier注解一起使用来指定使用对应名称的bean。如下:
public class TestServiceImpl { // 下面两种@Autowired只要使用一种即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) { // 用于属性的方法上 this.userDao = userDao; } } public class TestServiceImpl { @Autowired @Qualifier("userDao") private UserDao userDao; }
@Resource()默认通过byName的方式实现,如果找不到名字,则通过byType实现,如果两种都找不到就报错了,@Resource有两个重要的属性:name和type,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
public class TestServiceImpl { // 下面两种@Resource只要使用一种即可 @Resource(name="userDao") private UserDao userDao; // 用于属性上 }
@Qualifier 注解有什么作用
有多个相同类型的 bean 并希望仅使用装配其中一个 bean 时,您可以使用@Qualifier 注解来指定使用哪一个bean,说白了就是当有很多相同类型的bean,通过这个注解可以通过bean的名称来指定使用哪个bean,通过是和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。