Spring 之自动装配及注解(三)

Spring 之自动装配及注解(三)

一、DI依赖注入

  • 构造器注入

  • Set注入

    • 依赖注入!Set注入方式
    • 依赖:bean对象依赖Spring容器
    • 注入:bean对象的所有属性,由容器来注入!

    【环境配置】

    • 导入Spring官方配置
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
         https://www.springframework.org/schema/beans/spring-beans.xsd">
    </beans>
    
  • 1、【简单类型注入】eg:String

    public class Address {
     private String address;
    //getter and setter 方法
    。。。。
    //toString()方法     
    }
    
    public class Student {
     private String name;
    
    //getter and setter 方法
    。。。。
    //toString()方法  
    
    }
    

    Test测试类

    public class MyTest {
     public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
         //Student student = (Student) context.getBean("student");
         /*应用反射则不用强制转换*/
         Student student = context.getBean("student", Student.class);
         System.out.println(student.toString());
     }
    }
    

    beans.xml

    <bean id="student" class="com.study.dao.Student">
          <!--第一种普通值注入-->
          <property name="name" value="yyb"></property>
    </bean>
    

    输出:yyb

  • 2、【复杂类型的注入】

    在原有的Student类上添加其他类型的属性

    public class Student {
     private String name;
     private Address address;
     private String[] books;
     private List<String> hobbys;
     private Map<String,String> card;
     private Set<String> games;
     private String wife;
     private Properties info;
    
     //getter and setter 方法
    。。。。
    //toString()方法  
    }
    

    beans.xml

    <bean id="address" class="com.study.dao.Address">
           <property name="address" value="西安"/>
     </bean>
     <bean id="student" class="com.study.dao.Student">
          <!--第一种普通值注入-->
          <property name="name" value="yyb"></property>
    
          <!--第二种(引用类型)bean注入,ref-->
           <property name="address" ref="address"></property>
           <!--数组类型的bean注入,array-->
           <property name="books">
               <array>
                   <value>java</value>
                   <value>php</value>
                   <value>cpp</value>
                   <value>python</value>
               </array>
           </property>
           <!--list类型的bean注入-->
           <property name="hobbys">
               <list>
                   <value>篮球</value>
                   <value>足球</value>
               </list>
           </property>
         <!--map类型的bean注入-->
          <property name="card">
              <map>
                  <entry key="身份证" value="1233333444"></entry>
                  <entry key="学生证" value="1910321930"></entry>
              </map>
          </property>
          <!--set类型的bean注入-->
          <property name="games">
              <set>
                  <value>LOL</value>
                  <value>BOB</value>
                  <value>COC</value>
              </set>
          </property>
         <!--null值的bean注入-->
          <property name="wife">
              <null></null>
          </property>
         <!--properties的bean注入-->
         <property name="info">
             <props>
                 <prop key="学号">1910212108</prop>
                 <prop key="姓名">yyb</prop>
                 <prop key="性别">男</prop>
             </props>
         </property>
     </bean>
    

    Test测试输出:

    Student{

    name='yyb', address=Address{address='西安'},

    books=[java, php, cpp, python], hobbys=[篮球, 足球],

    card={身份证=1233333444, 学生证=1910321930},

    games=[LOL, BOB, COC], wife='null', info={学号=1910212108, 性别=男, 姓名=yyb}

    }

  • 第三方注入:

    • 【c命名和p命名注入】

    首先在原有的Spring官方配置中加入c和p命名的配置

xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"

User类

public class User {
 private String name;
 private String  sex;
 public User() {
 }

 public User(String name, String sex) {
     this.name = name;
     this.sex = sex;
 }
  //getter and setter 方法
。。。。
//toString()方法  
}

beans..xml

 <!--第三方注入c命名通过构造器方法注入construct-args-->
 <bean id="user" class="com.study.dao.User" c:name="yyb" c:sex="男"/>
 <!--第三方注入p命名直接赋值注入property-->
 <bean id="user2" class="com.study.dao.User" p:name="yyb2" p:sex="女"/>

二、Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式!
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种配置的方式

  • 1、在xml中显示的配置
  • 2、在java中显示的配置
  • 3、隐式的自动装配bean【重要】
1、ByName自动装配
public class Cat {
 public void sound(){
     System.out.println("my name is cat miao~");
 }
}
public class Dog {
 public void sound(){
     System.out.println("my name is dog wang~");
 }
}
public class Person {
 private Dog dog;
 private Cat cat;
 private String name;

 //getter and setter 方法
。。。。
//toString()方法 
}

 <bean id="dog" class="com.study.aotowired.Dog" />
 <bean id="cat" class="com.study.aotowired.Cat" />
 <!--byName的自动装配会,自动在容器上下文查找,==》和自己对象set方法后面的值对应的bean id!-->
 <!--byType:会自动在容器上下文查找,和自己对象属性类型相同的bean!这是就与bean 的 id无关了-->
 <bean id="person" class="com.study.aotowired.Person" autowire="byName">
     <property name="name" value="yyb"/>
 </bean>

2、ByType自动装配
	<bean class="com.study.aotowired.Dog" />
 <bean class="com.study.aotowired.Cat" />
 <!--byName的自动装配会,自动在容器上下文查找,==》和自己对象set方法后面的值对应的bean id!-->
 <!--byType:会自动在容器上下文查找,和自己对象属性类型相同的bean!这是就与bean 的 id无关了-->
 <bean id="person" class="com.study.aotowired.Person" autowire="byType">
     <property name="name" value="yyb"/>
 </bean>
//Test测试
import com.study.aotowired.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author yangyanbin
 * @date 2021/3/7 14:28
 */
public class MyTest {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean("person", Person.class);
        person.getCat().sound();
        person.getDog().sound();
    }
}

输出:

my name is cat miao~
my name is dog wang~

小结:

  • byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
  • bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

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

1、在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
  https://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
</beans>
image-20210307150400135
1、@Autowired注解

在类的属性上使用和set方法使用(set方法可以省略)

1、xml配置

	<bean id="dog" class="com.study.aotowired.Dog" />
 <bean id="cat" class="com.study.aotowired.Cat" />
 <bean id="person" class="com.study.aotowired.Person"/>
 <!--开启注解的支持-->
 <context:annotation-config/>

2、使用注解

public class Person {
 @Autowired
 private Dog dog;
 @Autowired
 private Cat cat;
 private String name;

 //getter and setter 方法
。。。。
//toString()方法 
}

Test类

输出:

my name is cat miao~
my name is dog wang~

注:使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(spring)容器中存在,且符合名字byname!

科普:

  • @Nullable : 字段标记了该参数可以为空null
  • @Autowired (required = false):如何显示定义了Autowired的required的属性为false,说明这个对象可以为null,否则不可以为空

如果@Autowired自动配装的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以是有@Qualifier(value="xxxx")去配置 @Autowired的使用,指定一个唯一的bean对象注入!

public class Person {
 @Autowired
 @Qualifier(value = "dog11")
 private Dog dog;

 @Autowired
 @Qualifier (value="cat111")
 private Cat cat;
<bean id="dog" class="com.study.aotowired.Dog" />
 <bean id="dog11" class="com.study.aotowired.Dog" />
 <bean id="dog22" class="com.study.aotowired.Dog" />
 <bean id="cat" class="com.study.aotowired.Cat" />
 <bean id="cat111" class="com.study.aotowired.Cat" />
2、@Resource:Java的注解使用
import javax.annotation.Resource;

/**
 * @author yangyanbin
 * @date 2021/3/7 14:05
 */
public class Person {
    @Resource (name = "dog11")
    private Dog dog;
    @Resource
    private Cat cat;
    private String name;
    ......

 <!--<bean id="dog" class="com.study.aotowired.Dog" />-->
    <bean id="dog11" class="com.study.aotowired.Dog" />
    <bean id="dog22" class="com.study.aotowired.Dog" />
    <bean id="cat" class="com.study.aotowired.Cat" />
    <bean id="cat111" class="com.study.aotowired.Cat" />
    <bean id="person" class="com.study.aotowired.Person"/>
    <!--开启注解的支持-->
    <context:annotation-config/>

小结:

@Resource与@Autowired的区别:

  • 都是用来自动装配的,都可以都放在属性字段上

  • @Autowired 通过byType的方式实现,而且必须要求这个对象存在【常用】

  • @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下就会报错!

    【常用】还可以通过name指定对象。

  • 执行顺序不同:

    • @Autowired 通过byType的方式实现
    • @Resource默认通过byName的方式实现

四、使用注解开发

1、首先在pom.xml加入SpringMVC的依赖jar在JDK1.5之后aop的依赖也会自动加入

image-20210307182023326

2、在配置文件application.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
     https://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.study.pojo"/>
 <!--开启注解支持驱动-->
 <context:annotation-config/>
</beans>
1、bean对象的创建
  • 在类上使用@Component:等价于在中bean对象的创建

  • <bean id="user" class="com.study.pojo.dao.User"/>
    
  • 注:bean 的id默认使用类的小写

    /*等价于在中bean对象的创建:<bean id="user" class="com.study.pojo.dao.User"/>*/
    @Component
    public class User {
     public String name="yyb";
    }
    

    Test测试:

    import com.study.pojo.dao.User;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author yangyanbin
     * @date 2021/3/7 18:11
     */
    public class MyTest {
        @Test
        public  void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            User user = context.getBean("user", User.class);
            System.out.println(user.name);
        }
    }
    输出:yyb
    
2、属性的注入
  • 在属性上使用@Value注解等价于

    bean中的property的设置

    <property name="name" value="yyb"></property>
    
    /*等价于在中bean对象的创建:<bean id="user" class="com.study.pojo.dao.User"/>*/
    @Component
    public class User {
     public String name;
    
     /*等价于bean中的property的设置<property name="name" value="yyb"></property>
      */
     @Value("yyb")
     public void setName(String name) {
         this.name = name;
     }
    
3、衍生的注解:
  • @Conponent有几个衍生注解,通常在web开发中按照mvc三层架构
    • Dao: @Repository
    • Service:@Service
    • Controller:@Controller
    • 这四个注解功能都一样的都是代表将某个类注册到Spring中,来装配Bean
4、自动装配配置:
  • @Autowired:在类的属性上使用和set方法使用(set方法可以省略)
    • 自动装配通过类型,名字。如果@Autowired不能唯一自动装配上属性,则需要通过@Qualifier(valuer="XXXX")
  • @Resource:自动装配通过名字,类型。
  • @Nullable : 字段标记了该参数、字段可以为空null
5、作用域:
  • @Scope("Singleton")//单例模式
    @Scope("prototype")//原型模式
    
6、小结:
  • xml与注解:

    • xml更加的万能,适用于任何场合!维护的更加方便
    • 注解不是自己类使用不了,维护相对复杂!
  • xml与注解的最佳实践

    • xml可以只用来管理Bean
    • 注解只负责完成属性的注入
    • 在使用过程中一定要开启注解的支持。

五、使用Java的方式配置Spring

  • 完全用注解配置不依赖(不用)与xml的方式

  • 配置类:

    package com.study.config;
    
    import com.study.pojo.User;
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.Bean;
    
    /**
     * @author yangyanbin
     * @date 2021/3/7 21:58
     */
    /*标记这这是一个配置类等同于application.xml,这个也会被Spring托管,注册到容器中
    * 因为他本来就是一个@Component*/
    @Configurable
    public class JavaConfig {
        /*相当于创建了一个该类的Bean就是之前的bean标签,
        * 方法名就是==id的getUser
        * 返回值就是bean中class属性(全类名限定),就是要注入到Bean的对象*/
        @Bean
        public User getUser(){
    
            return  new User();
        }
    }
    
    
  • User实体类:

    package com.study.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    /**
     * @author yangyanbin
     * @date 2021/3/7 21:58
     */
    /*这个注解就说明了将这个类由Spring托管了并注册到了容器中*/
    @Component
    public class User {
        private  String name;
    
        public String getName() {
            return name;
        }
        /*给属性输入值*/
        @Value("lijing")
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    
  • Test测试类

    import com.study.config.JavaConfig;
    import com.study.pojo.User;
    import org.junit.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
     * @author yangyanbin
     * @date 2021/3/7 22:02
     */
    public class MyTest {
        @Test
        public void mytest(){
            /*如果完全使用了配置方式去做,我们就只能通过 AnnotationConfig上下文来获取容器,并通过类的class对象加载!*/
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
            User user = context.getBean("getUser", User.class);
            System.out.println(user.getName());
        }
    }
    
    
扩展:

@Import注解

  • @ImportSpring基于 Java 注解配置的主要组成部分。@Import注解提供了@Bean注解的功能,同时还有原来Spring基于 xml 配置文件里的<import>标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration的类。

下面将分别说明@Import注解的功能。

  • 1、引入其他的@Configuration

  • 假设有如下接口和两个实现类:

package com.test
interface ServiceInterface {
    void test();
}

class ServiceA implements ServiceInterface {

    @Override
    public void test() {
        System.out.println("ServiceA");
    }
}

class ServiceB implements ServiceInterface {

    @Override
    public void test() {
        System.out.println("ServiceB");
    }
}
  • 两个@Configuration,其中ConfigA``@Import``ConfigB:
package com.test
@Import(ConfigB.class)
@Configuration
class ConfigA {
    @Bean
    @ConditionalOnMissingBean
    public ServiceInterface getServiceA() {
        return new ServiceA();
    }
}

@Configuration
class ConfigB {
    @Bean
    @ConditionalOnMissingBean
    public ServiceInterface getServiceB() {
        return new ServiceB();
    }
}
  • 通过ConfigA创建AnnotationConfigApplicationContext,获取ServiceInterface,看是哪种实现:
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigA.class);
    ServiceInterface bean = ctx.getBean(ServiceInterface.class);
    bean.test();
}

输出为:ServiceB.证明@Import的优先于本身的的类定义加载。

  • 2、直接初始化其他类的Bean

    Spring 4.2之后,@Import可以直接指定实体类,加载这个类定义到context中。 例如把上面代码中的ConfigA@Import修改为@Import(ServiceB.class),就会生成ServiceBBean到容器上下文中,之后运行main方法,输出为:ServiceB.证明@Import的优先于本身的的类定义加载.

posted @ 2021-03-07 22:42  yyb1024  阅读(648)  评论(1编辑  收藏  举报