IoC
控制反转(IoC,Inversion of Control) 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现 方式是依赖注入,比如之前文章调用
依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完 成功能,即 classA 对 classB 有依赖。
Ioc 的实现:依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些 工作由容器自行完成。
idea新建一个maven-quickstart项目
pom.xml中加入依赖文件
1 2 3 4 5 6 7 8 9 10 11 12 13 <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.16.RELEASE</version> </dependency> </dependencies>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package xyz.shi.service; import xyz.shi.entity.User; import java.util.ArrayList; import java.util.List; public class UserService { public List<User> getUserList(){ ArrayList<User> users = new ArrayList<>(); User user1 = new User(1, "test1", "123456"); User user2 = new User(2, "test2", "123456"); User user3 = new User(3, "test3", "123456"); users.add(user1); users.add(user2); users.add(user3); return users; } }
resources/application.xml
1 2 3 4 5 6 7 <?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="userService" class="xyz.shi.service.UserService"></bean> </beans>
1 2 3 4 <bean id ="mailService" name ="mailService2" class ="com.mszlu.service.MailService" scope ="singleton" /> <bean id ="mailService" name ="mailService2" class ="com.mszlu.service.MailService" scope ="prototype" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import xyz.shi.entity.User; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import xyz.shi.service.UserService; public class userTest { public static void main(String[] arg) { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); // UserService userService =(UserService) context.getBean("userService"); UserService userService1 = context.getBean(UserService.class); for (User user : userService1.getUserList()) { System.out.println(user.getName()); } } }
ioc注解
使用XML方式管理Bean以及Bean的依赖,非常直观,但是配置相对繁琐一些,尤其是当Bean多了之后,有没有更简便的方式呢?
Annotation配置 使用注解的方式,可以极大的简化配置
1 2 3 4 5 6 7 8 <?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="xyz.shi.service" /> </beans>
件扫描的作用: 让Spring知道该去哪个包中扫描注解, 以及扫描什么样的注解, 其中用到的标签就是<context:component-scan>
, 其属性base-package
指定扫描哪个包下的类, 多个包可以用逗号隔开, 比如:
1 <context:component-scan base-package="com.yjzzjy4.learning.beans, com.yjzzjy4.learning.test"/>
1 2 3 <context:component-scan base-package="com.yjzzjy4.learning" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
或者示例二:
1 2 3 <context:component-scan base-package="com.yjzzjy4.learning"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
其中<context:component-scan>
的use-default-filters属性指定是否使用Spring默认的过滤器, 是一个布尔属性, 默认为true, 即扫描所有注解. 当设为false的时候, 则需要自己配置扫描规则, 通常用<context:include-filter>
来包含要扫描的注解类型(示例一), 如果使用默认过滤器, 则通常会使用<context:exclude-filter>
注解来排除掉不想要扫描的注解类型(示例二), 其中:
org.springframework.stereotype.Component:
即@Component注解;
org.springframework.stereotype.Service:
即@Service注解;
而type属性值设为annotation即说明过滤器的作用对象是注解.
创建对象 Spring提供了以下四个注解用于创建对象:
@Component: 建议用于普通Bean;
@Controller: 建议用在Web层的Controller;
@Repository: 建议用在DAO层的仓储Bean;
@Service: 建议用在Service层的Bean.
实际上这四个注解都是实现相同的功能, Spring将它们区分开来仅仅是为了应用开发的逻辑清晰, 并没有硬性规定这几个注解的使用场合, 这些注解都有一个属性: value, 用于指定Bean的id, 即:
1 2 @Component(value = "employee") 值得注意的是, 默认情况下若不给value设置值, 则value自动取类名首字母小写为值
1 <bean id="employee" .../>
练习 resources-application.xml
1 2 3 4 5 6 7 8 9 <?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="xyz.shi" /> </beans>
1 2 3 4 5 6 7 package xyz.shi.controller; import org.springframework.stereotype.Controller; @Controller public class PersonController { }
1 2 3 4 5 package xyz.shi.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package xyz.shi.service; import xyz.shi.entity.User; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; @Service public class UserService { public static List<User> getUserList(){ ArrayList<User> users = new ArrayList<>(); User user1 = new User(1, "test1", "123456"); User user2 = new User(2, "test2", "123456"); User user3 = new User(3, "test3", "123456"); users.add(user1); users.add(user2); users.add(user3); return users; } }
1 2 3 4 5 6 7 8 9 10 package xyz.shi.entity; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component(value = "userBean") @Scope(value = "prototype") public User () { }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class userTest { public static void main(String[] arg) { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); System.out.println(context.getBean("personController")); System.out.println(context.getBean("userService")); System.out.println(context.getBean("userDao")); System.out.println(context.getBean("userBean")); System.out.println(context.getBean("role")); } }
1 2 3 4 5 xyz.shi.controller.PersonController@1cab0bfb xyz.shi.service.UserService@5e955596 xyz.shi.dao.UserDao@50de0926 xyz.shi.entity.User@2473b9ce xyz.shi.entity.Role@60438a68
注入属性 IoC的第二个任务就是注入属性, Spring提供了三个注解用于完成此任务:
@Autowired
: 按类型进行自动装配, 不可以有多个相同类型的Bean;
@Qualifier
: 按名称进行装配, 要和@Autowired
一起使用;
@Value
: 注入值类型属性.
还有一个注解是由Java提供的:
@Resource
: 既可以根据类型, 也可以根据名称注入, 相当于@Autowired
和@Qualifier
.
通常建议使用Spring提供的注解, 因为在以后的Spring版本迭代中将会更可控一些.
使用@AutoWired进行自动注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package xyz.shi.dao; import org.springframework.stereotype.Repository; import xyz.shi.entity.User; import java.util.ArrayList; import java.util.List; @Repository public class UserDao { public List<User> getUserList(){ ArrayList<User> users = new ArrayList<>(); User user1 = new User(1, "test1", "123456"); User user2 = new User(2, "test2", "123456"); User user3 = new User(3, "test3", "123456"); users.add(user1); users.add(user2); users.add(user3); return users; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package xyz.shi.service; import org.springframework.beans.factory.annotation.Autowired; import xyz.shi.dao.UserDao; import xyz.shi.entity.User; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; public List<User> getUserList(){ return userDao.getUserList(); } }
Autowired 加入后,不用在实例化了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package xyz.shi.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import xyz.shi.entity.User; import xyz.shi.service.UserService; import java.util.List; @Controller public class UserController { @Autowired private UserService userService; public List<User> getUserList() { return userService.getUserList(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import xyz.shi.controller.UserController; import xyz.shi.entity.User; import java.util.List; public class userTest { public static void main(String[] arg) { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserController userController = context.getBean("userController", UserController.class); List<User> users = userController.getUserList(); for(User user: users) { System.out.println(user.getName()); } } }
@Qualifier注解
修改UserControll.java加上@Qualifer注解,指定id为userService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package xyz.shi.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import xyz.shi.entity.User; import xyz.shi.service.UserService; import java.util.List; @Controller public class UserController { @Autowired @Qualifier("userService") private UserService userService111; public List<User> getUserList() { return userService111.getUserList(); } }
这样运行测试代码是没有问题,如果是@Qualifier(“userService1”),就会报错,因为找不到对应的name
1 2 3 4 @Autowired public void test(PersonDao personDao){ System.out.println("personDao:"+personDao); }
@Qualifier注解也可以作用在属性上,用来被当作id去匹配容器中的对象,如果没有 此注解,那么直接按照类型进行匹配
1 2 3 public void test1(@Qualifier("personServiceSon") PersonService personService){ personService.getPerson(); }