依赖查找、依赖注入
IoC的实现分为两种:依赖查找和依赖注入
依赖查找和依赖注入 依赖查找:创建的 Bean 都是不带属性的 byName 1 2 3 4 5 6 7 <bean id="person" class="com.linkedbear.spring.basic_dl.a_quickstart_byname.bean.Person" ></bean> public static void main (String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext ("quickstart-byname.xml" ); Person person = (Person) ctx.getBean("person" ); System.out.println(person); }
byType 1 2 3 4 5 6 7 8 9 10 11 12 <bean class="com.linkedbear.spring.basic_dl.b_bytype.bean.Person" ></bean> public static void main (String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext ("quickstart-bytype.xml" ); Person person = ctx.getBean(Person.class); Map<String, DemoDao> beans = ctx.getBeansOfType(DemoDao.class); System.out.println(person); ObjectProvider<Dog> dogProvider = ctx.getBeanProvider(Dog.class); }
通过接口与实现类 1 2 3 4 5 6 7 8 9 10 <bean class="com.linkedbear.spring.basic_dl.b_bytype.dao.impl.DemoDaoImpl" /> public static void main (String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext ("quickstart-bytype.xml" ); DemoDao demoDao = ctx.getBean(DemoDao.class); System.out.println(demoDao.findAll()); }
依赖注入: xml配置注入: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Person { private String name; private Integer age; } public class Cat { private String name; private Person master; } <?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" ><bean id="person" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Person" > <property name="name" value="test-person-byset" /> <property name="age" value="18" /> </bean> <bean id="cat" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Cat" > <property name="name" value="test-cat" /> <!-- ref引用上面的person对象 --> <property name="master" ref="person" /> </bean> public class QuickstartInjectBySetXmlApplication { public static void main (String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext ("inject-set.xml" ); Person person = ctx.getBean(Person.class); System.out.println(person); Cat cat = ctx.getBean(Cat.class); System.out.println(cat); } } output: Person{name='test-person-byset' , age=18 } Cat{name='test-cat' , master=Person{name='test-person-byset' , age=18 }}
注解注入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Configuration public class QuickstartConfiguration { @Bean(name = "aaa") public Person person () { return new Person (); } } @Component("user") public class User { @Value("秦疆") public String name; } public class AnnotationConfigApplication { public static void main (String[] args) throws Exception { ApplicationContext ctx = new AnnotationConfigApplicationContext (QuickstartConfiguration.class); Person person = ctx.getBean(Person.class); System.out.println(person); } }
组件扫描 因为Bean可能需要添加很多,所以为了简化,就可以在类上添加@Component
。如果不指定 Component的名称,它的默认规则是 “类名的首字母小写” (例如 Person
的默认名称是 person
,DepartmentServiceImpl
的默认名称是 departmentServiceImpl
)。
xml启用组件扫描 1 <context:component-scan base-package ="com.linkedbear.spring.annotation.c_scan.bean" />
为了迎合咱在进行 Web 开发时的三层架构,它额外提供了三个注解:@Controller
、@Service
、@Repository
,分别代表表现层、业务层、持久层。这三个注解的作用与 @Component
完全一致,其实它们的底层也就是 @Component
xml引入注解(在 xml 中要引入注解配置,需要开启注解配置,同时注册对应的配置类) 1 2 3 4 5 6 7 8 9 10 11 12 13 <?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 /> <bean class="com.linkedbear.spring.annotation.d_importxml.config.AnnotationConfigConfiguration" /> </beans>
依赖查找与依赖注入区别:
作用目标不同
依赖注入的作用目标通常是类成员
依赖查找的作用目标可以是方法体内,也可以是方法体外
实现方式不同
依赖注入通常借助一个上下文被动的接收
依赖查找通常主动使用上下文搜索
几种注入方式: setter属性注入:
1 2 3 4 <bean id="person" class="com.linkedbear.spring.basic_di.a_quickstart_set.bean.Person" > <property name="name" value="test-person-byset" /> <property name="age" value="18" /> </bean>
1 2 3 4 5 6 7 @Bean public Person person () { Person person = new Person (); person.setName("test-person-anno-byset" ); person.setAge(18 ); return person; }
构造器注入
1 2 3 4 <bean id="person" class="com.linkedbear.spring.basic_di.b_constructor.bean.Person" > <constructor-arg index="0" value="test-person-byconstructor" /> <constructor-arg index="1" value="18" /> </bean>
1 2 3 4 @Bean public Person person () { return new Person ("test-person-anno-byconstructor" , 18 ); }
注解式属性注入 @Component下的属性注入 1 2 3 4 5 6 7 @Component("user") public class User { @Value("秦疆") public String name; }
外部配置文件引入-@PropertySource 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 red.name=red-value-byproperties red.order=1 @Configuration @PropertySource("classpath:basic_di/value/red.properties") public class InjectValueConfiguration { @Value("${red.name}") private String name; @Value("${red.order}") private Integer order; } <bean class="com.linkedbear.spring.basic_di.c_value_spel.bean.Red" > <property name="name" value="${red.name}" /> <property name="order" value="${red.order}" /> </bean>
SpEL属性注入 1 2 3 4 5 6 7 8 9 10 11 12 @Component public class Green { @Value("#{'copy of ' + blue.name}") private String name; @Value("#{blue.order + 1}") private Integer order; } <bean class="com.linkedbear.spring.basic_di.c_value_spel.bean.Green" > <property name="name" value="#{'copy of ' + blue.name}" /> <property name="order" value="#{blue.order + 1}" /> </bean>
自动注入: xml中的autowire 1 2 3 4 5 6 7 8 9 <bean id="user" class="com.kuang.pojo.User" autowire="byName" > <property name="str" value="qinjiang" /> </bean> <bean id="user" class="com.kuang.pojo.User" autowire="byType" > <property name="str" value="qinjiang" /> </bean>
注解中的autowire 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class User { @Autowired private Cat cat; @Autowired(required = false) private Dog dog; private String str; } <context:annotation-config/> <bean id="dog" class="com.kuang.pojo.Dog" /> <bean id="cat" class="com.kuang.pojo.Cat" /> <bean id="user" class="com.kuang.pojo.User" />
注解中的@Qualifier 1 2 3 4 5 6 7 8 9 10 11 12 13 <bean id="dog1" class="com.kuang.pojo.Dog" /> <bean id="dog2" class="com.kuang.pojo.Dog" /> <bean id="cat1" class="com.kuang.pojo.Cat" /> <bean id="cat2" class="com.kuang.pojo.Cat" /> @Autowired @Qualifier(value = "cat2") private Cat cat;@Autowired @Qualifier(value = "dog2") private Dog dog;
注解中的@Resource 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <bean id="dog" class="com.kuang.pojo.Dog" /> <bean id="cat1" class="com.kuang.pojo.Cat" /> <bean id="cat2" class="com.kuang.pojo.Cat" /> <bean id="user" class="com.kuang.pojo.User" /> public class User { @Resource(name = "cat2") private Cat cat; @Resource private Dog dog; private String str; }
1 2 3 4 5 6 7 8 <bean id="dog" class="com.kuang.pojo.Dog" /> <bean id="cat1" class="com.kuang.pojo.Cat" /> @Resource private Cat cat;@Resource private Dog dog;
一些问题: @Autowired注入的原理逻辑 先拿属性对应的类型,去 IOC 容器中找 Bean ,如果找到了一个,直接返回;如果找到多个类型一样的 Bean , 把属性名拿过去,跟这些 Bean 的 id 逐个对比,如果有一个相同的,直接返回;如果没有任何相同的 id 与要注入的属性名相同,则会抛出 NoUniqueBeanDefinitionException 异常。
依赖注入的目的和优点? 首先,依赖注入作为 IOC 的实现方式之一,目的就是解耦 ,我们不再需要直接去 new 那些依赖的类对象(直接依赖会导致对象的创建机制、初始化过程难以统一控制);而且,如果组件存在多级依赖,依赖注入可以将这些依赖的关系简化,开发者只需要定义好谁依赖谁即可。
除此之外,依赖注入的另一个特点是依赖对象的可配置 :通过 xml 或者注解声明,可以指定和调整组件注入的对象,借助 Java 的多态特性,可以不需要大批量的修改就完成依赖注入的对象替换(面向接口编程与依赖注入配合近乎完美)。
谁把什么注入给谁了? 由于组件与组件之间的依赖只剩下成员属性 + 依赖注入的注解,而注入的注解又被 SpringFramework 支持,所以这个问题也好回答:IOC 容器把需要依赖的对象注入给待注入的组件 。