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>
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的依赖也会自动加入
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注解
@Import
是Spring
基于 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)
,就会生成ServiceB
的Bean
到容器上下文中,之后运行main
方法,输出为:ServiceB
.证明@Import
的优先于本身的的类定义加载.