Spring框架总结(四)——注解

 

前面几节,通过配置xml文件,可以实现Spring 控制反转的依赖注入。不过,如果一个类中,存在很多属性、依赖不止一个目标,此时这种基于xml配置的方式就显得非常繁琐。幸好,Spirng提供了几种技巧,可以帮助我们减少xml的配置数量

  • 自动装配(autowiring)有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让spring自动识别如何装配Bean的依赖关系
  • 自动检测(autodiscovery)比自动装配更进了一步,让Spring能够自动识别哪些类需要被配置成Spring Bean,从而减少对<bean>元素的使用

一、自动装配Bean属性

1、四中类型的自动装配

  • byName——把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。
  • byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean自动装配到Bean的对应属性中。如果没有跟属性的类型相匹配的Bean,则该属性不被装配。
  • constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。
  • autodetect——首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。

这里,我们只演示第一个byName:

第一步:创建持久层UserDao

1 package com.justnow.dao;
2 
3 import com.justnow.domain.User;
4 
5 public class UserDao {
6     public void findAll(User user){
7         System.out.println(user);
8     }
9 }

第二步:创建业务层的实现类UserServiceImpl,通过  set方法实现依赖注入。

 1 package com.justnow.service.impl;
 2 
 3 import com.justnow.dao.UserDao;
 4 import com.justnow.domain.User;
 5 import com.justnow.service.IUserService;
 6 
 7 public class UserServiceImpl implements IUserService {
 8     private UserDao userDao;
 9     private String otherProperty;
10 
11     //必须有属性的set方法,否则不能实现依赖注入
12     public void setUserDao(UserDao userDao) {
13         this.userDao = userDao;
14     }
15 
16     public void setOtherProperty(String otherProperty) {
17         this.otherProperty = otherProperty;
18     }
19 
20     public void findAll(User user) {
21         userDao.findAll(user);
22         System.out.println(otherProperty);
23     }
24 }

第三步:编写Spring框架的配置文件

在bean标签中设置autowire的属性为byName:如果在该配置文件中出现一个Bean的id是userDao(userDao为userServiceImpl的属性名字),就可以实现自动装配

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 6         <bean id="userService" class="com.justnow.service.impl.UserServiceImpl" autowire="byName">
 7             <property name="otherProperty" value="利用property标签注入的值"/>
 8         </bean>
 9 
10         <bean id="userDao" class="com.justnow.dao.UserDao"/>
11 </beans>

第四步:测试

 1     @Test
 2     public void test(){
 3         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 4         IUserService userService = (IUserService) context.getBean("userService");
 5         User user = new User();
 6         user.setId(1);
 7         user.setName("justnow");
 8         user.setMoney(1.0f);
 9         userService.findAll(user);
10     }

结果:

Spring框架总结(四)——注解

 

修改userDao这个bean的id值为userDao2,如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 6         <bean id="userService" class="com.justnow.service.impl.UserServiceImpl" autowire="byName">
 7             <property name="otherProperty" value="利用property标签注入的值"/>
 8         </bean>
 9 
10 <!--
11         <bean id="userDao" class="com.justnow.dao.UserDao"/>
12 -->
13         <bean id="userDao2" class="com.justnow.dao.UserDao"/>
14 </beans>

看一下测试结果:

Spring框架总结(四)——注解

 

 在以后的开发中,基本上不怎么用这四种方式实现自动装配,而使用注解装配!所以,后面三个就不再演示了。

https://github.com/justn0w/javawebdemo/tree/master/spring/Spring_Demo3

c6e5dccfcacb13c42db41a2ed7b1145ddef700b7

2、使用注解实现自动装配

使用注解自动装配 Bean的属性,可以不再编写繁琐的xml文件,从而将注意力转向项目的业务逻辑。在将来的开发中,注解发挥着中流砥柱的作用!

Spring容器默认禁用注解装配。所以,在使用基于注解的自动装配前,我们需要在Spring配置文件中启用它。最简单的启用方式是使用Spring的context命名空间配置中的<context:annotation-config>元素,如下所示:

<?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:annotation-config />

红底白字是新加入的命名规则。

(1)使用@Autowired

场景一:在set方法上使用

第一步:修改UserServiceImpl实现类,在setUserDao这个方法上添加注解

 1 public class UserServiceImpl implements IUserService {
 2     private UserDao userDao;
 3     private String otherProperty;
 4     //必须有属性的set方法,否则不能实现依赖注入
 5     @Autowired
 6     public void setUserDao(UserDao userDao) {
 7         this.userDao = userDao;
 8     }
 9     public void setOtherProperty(String otherProperty) {
10         this.otherProperty = otherProperty;
11     }
12 
13     public void findAll(User user) {
14         userDao.findAll(user);
15         System.out.println(otherProperty);
16     }
17 }

第二步:修改配置文件,如下:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xmlns:context="http://www.springframework.org/schema/context"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd
 7                            http://www.springframework.org/schema/context
 8                            http://www.springframework.org/schema/context/spring-context.xsd">
 9         <context:annotation-config />
10         <bean id="userService" class="com.justnow.service.impl.UserServiceImpl">
11             <property name="otherProperty" value="利用property标签注入的值"/>
12         </bean>
13 
14         <bean id="userDao10" class="com.justnow.dao.UserDao" />
15 </beans>

第三步:测试

 1     @Test
 2     public void test(){
 3         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 4         IUserService userService = (IUserService) context.getBean("userService");
 5         User user = new User();
 6         user.setId(1);
 7         user.setName("justnow");
 8         user.setMoney(1.0f);
 9         userService.findAll(user);
10     }

结果:

Spring框架总结(四)——注解

 

 

该结果,与按名字实现自动装配的结果一致!

场景二:标注在需要自动装配Bean引用的任意方法

比如说,下面的方法:

@Autowired
pubilc void differentWay(UserDao userdao){
   this.userdao=userdao;    
}

该方法并不是set方法,但是通过注解实现自动装配userDao这个Bean

场景三:标注在构造函数

如下:

1     @Autowired
2     public UserServiceImpl(UserDao userDao){
3         this.userDao = userDao;
4     }

场景四:直接标注属性,并删除setter方法

在我们的开发过程中,利用这种方法实现自动装配,使用的非常频繁。

第一步:修改UserServiceImpl实现类,在私有属性上添加注解,并删除setter方法

 1 public class UserServiceImpl implements IUserService {
 2     @Autowired
 3     private UserDao userDao;
 4 
 5     private String otherProperty;
 6 
 7     public void setOtherProperty(String otherProperty) {
 8         this.otherProperty = otherProperty;
 9     }
10 
11     public void findAll(User user) {
12         userDao.findAll(user);
13         System.out.println(otherProperty);
14     }
15 }

测试下:

Spring框架总结(四)——注解

 

 @Autowired不会受限于private关键字。即使属性是私有的实例变量,它仍然可以被自动装配!

自动装配,只是帮助我们不再编写<property>和<constructor-arg>元素,我们仍需要使用<bean>元素显示定义Bean。

二、自动检测Bean

这是Spring的另一种技巧,<context:component-scan>元素除了完成<context:annotation-config>一样的工作外,还允许Spring自动检测Bean和定义Bean。也就说,不再使用<bean>元素,Spring应用中的大多数Bean都能够实现定义和装配。

使用方法如下:

<context:component-scan

  base-package="com.justnow">

</context:component-scan>

<context:component-scan>元素会扫描指定的包及其所有自曝,并查找出能够自动注册为Spring Bean的类。base-package属性标识了<context:component-scan>元素所扫描的包。

那么,<context:component-scan>又是如何知道哪些类需要注册为Spring Bean呢?

2.1 使用注解,为自动检测标注Bean

(1) @Component

作用:把资源让spring管理。相当于在xml中配置一个bean

属性:value:指定了bean的id。如果不指定value属性,默认bean的id是当前类的类名(首字母小写)。

(2) @Controller @Service @Repository

这三个注解都是针对第一个的衍生注解,他们的作用级属性都是一模一样。不过提供了更加明确的语义化。

@Controller:一般用于表现层的注解。即SpringMVC中

@Service:一般用于业务层的注解。

@Repository:一般用于持久层的注解。

(3) @Autowired

这个注解相当于使用第一节中的autodetect类型的自动装配。首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。

当使用该注解注入属性时,setter方法可以省略!

2.2 过滤组件扫描

如果出现接下来的需求,我们使用<context:component-scan>扫描了com.justnow,但是对于com.justnow.controller中的包,我们不希望被扫描到,此时,就需要使用<context:include-filter>和<context:exclude-filter>子元素来调整扫描的行为。

(1) <context:include-filter>

该元素还包括以下属性:type和expression,使用方式如下

Spring框架总结(四)——注解

 

 

type和expression的

第一种搭配:过滤器扫描使用指定注解所标注的那些类。通过expression属性指定要扫描的注解

第二种搭配:过滤器扫描派生与(实现或者继承)expression属性所指定类型的那些类。

第三种搭配:过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类。

第四种搭配:过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类。

第五种搭配:使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定。

举个例子:

1     <context:component-scan base-package="com.justnow">
2         <context:include-filter type="assignable" expression="com.justnow.service.IUserService"/>
3     </context:component-scan>

只扫描,在com.justnow包下实现com.justnow.service.IUSerService接口的类。

(2) <context:exclude-filter>,为哪些类不需要注册为Spring Bean。

如:

    <!--1、 配置 spring 创建容器时要扫描的包  -->
    <context:component-scan base-package="com.justnow">
        <!--制定扫包规则,不扫描@Controller 注解的 JAVA 类,其他的还是要扫描 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

即不扫描@Controller注解的java类,但是其他的还要扫描。

参考:


https://docs.spring.io/spring/docs/4.3.25.RELEASE/spring-framework-reference/htmlsingle/

 

 

三、总结

1、最小化Spring XML配置是通过自动装配和自动检测实现的。前者让我们不再依赖<property>和<constructor-org>元素,而后者可以解放为每个类创建Bean这个繁琐的过程。

2、还有一种基于Java的Spring的配置没有总结,这种方法当然也非常实用。

上一篇:通俗易懂spring之singleton和prototype


下一篇:mybatis学习:mybatis的注解开发CRUD操作