Loading... # SSM-回炉重造-Spring IOC Spring 主要两部分: 1. IOC (Inversion Of Control): 控制反转 2. AOP (Aspect Oriented Programming) : 面向切面编程 ## IOC 控制: 资源的获取方式 - 主动式:手动创建资源 ````java BookController{ BookService bs = new BookService(); public void test(){ bs.check(); } } ```` - 被动式: 交给容器来创建资源 ````java BookController{ BookService bs; //由容器来帮助我们创建此对象,并且赋值 public void test(){ bs.check(); } } ```` 获取资源的方式由主动`new`变成被动接收 DI(Dependency Injection): 依赖注入 容器通过反射的形式,将需要的对象注入到相应的变量中,只要容器管理的组件,即可使用容器提供的功能 ## HelloWorld 示例 1. 创建空的Java工程 2. 导入必需的**Spring Jar包依赖包** > spring-beans-4.0.0.RELEASE.jar > > spring-context-4.0.0.RELEASE.jar > > spring-core-4.0.0.RELEASE.jar > > spring-expression-4.0.0.RELEASE.jar 3. 创建JavaBean对象 ````java package com.oylong.bean; /** * @ProjectName: springioc * @Description: * @Author: OyLong * @Date: 2020/9/10 0:35 */ public class Person { private String name; private Integer age; private String email; ...... } ```` 4. 创建**Spring Bean配置文件** ````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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="person" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> </bean> </beans> ```` 5. 通过**ApplicationContext对象**以及对象的`id`来获取容器中的对象 ````java public class IOCTest { @Test public void test(){ ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Person person = ioc.getBean("person", Person.class); //person为bean对象的id System.out.println(person); } } ```` 6. 打印了由IOC容器管理的对象 > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com'} `ClassPathXmlApplicationContext`: 从当前的类路径下去寻找*IOC配置文件* ## 注意事项 1. 容器中的对象在容器创建完成的时候,也创建好了 2. ioc容器中的对象是单例的 3. 容器中若没有相应的组件,则获取组件时会报异常 4. ioc容器创建对象时会利用相应的`setter`方法进行赋值(property标签) 5. javaBean的属性名,由getter/setter方法决定(尽量自动生成setter/getter方法) ## 通过类型直接获取Bean实例 当`ioc容器`中只有一个同类型的bean对象时,如下: ````java ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Person person = ioc.getBean(Person.class); System.out.println(person); ```` 我们将可以直接得到对应的bean对象 > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com'} 但是当容器中有多个相同类型的bean对象时,将报如下的错误: ````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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="person" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> </bean> <bean id="person1" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> </bean> </beans> ```` 报错: > No qualifying bean of type [com.oylong.bean.Person] is defined: expected single matching bean but found 2: person,person1 报错提示的也很明显,没有匹配的bean对象,因为找到了2个,不知道你需要的是哪一个,因此此时不适合使用类型来获取`bean`对象 ## 通过调用有参构造器创建bean对象 前提是这个bean对象本身有对应的构造器,如下: ````java public class Person { private String name; private Integer age; private String email; public Person() { } public Person(String name, Integer age, String email) { this.name = name; this.age = age; this.email = email; } ... } ```` 然后通过xml配置: ````xml <bean id="person2" class="com.oylong.bean.Person"> <constructor-arg name="age" value="16"></constructor-arg> <constructor-arg name="email" value="ouyanglong721@gmail.com"></constructor-arg> <constructor-arg name="name" value="oyLong"></constructor-arg> </bean> ```` 在获取bean对象即可 ````java ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Person person = ioc.getBean("person2", Person.class); System.out.println(person); } ```` ## property标签 复杂属性赋值 复杂类型不能直接通过`value`属性来赋值 ### 1.空值(null) 如果不设置,则对象属性默认为null,其他基本类型属性则根据类的创建规则来自动赋值,也可手动设空值 如下: ````xml <bean id="person1" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email"> <null/> </property> </bean> ```` ### 2.对象赋值 car对象: ````java public class Car { private String name; private String color; private BigDecimal price; ...... } ```` person对象: ````java public class Person { private String name; private Integer age; private String email; private Car car; ...... ```` 通过引用为Person的Car赋值,如下: ````xml <bean id="car" class="com.oylong.bean.Car"> <property name="name" value="大卡车"></property> <property name="color" value="绿色"></property> <property name="price" value="2800"></property> </bean> <bean id="person2" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> <property name="car" ref="car"></property> </bean> ```` ````java public void test1(){ ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); Person person = ioc.getBean("person2", Person.class); System.out.println(person); System.out.println(person.getCar() == ioc.getBean("car", Car.class)); } ```` 输出如下: > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com', car=Car{name='大卡车', color='绿色', price=2800}} > true 同时可以看到,引用赋值成功了,同时从ioc容器获取的bean对象与赋值的对象是同一个 如果要设置一个不同的car,则需要内部创建一个 如下: ````xml <bean id="person2" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> <property name="car"> <bean class="com.oylong.bean.Car"> <property name="name" value="测试车"></property> </bean> </property> </bean> ```` 输出如下: > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com', car=Car{name='测试车', color='null', price=null}} > false **注:这个内部创建的bean对象不能通过ioc容器获取到,只能在内部使用** ### 3.集合赋值 更改Person对象: ````java public class Person { private String name; private Integer age; private String email; private Car car; private List list; private Map map; private Properties properties; ...... } ```` bean配置如下: ````xml <bean id="person3" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> <property name="car" ref="car"></property> <!-- list赋值--> <property name="list"> <list> <value>中文</value> <value>英文</value> <ref bean="car"></ref> </list> </property> <!-- map赋值 --> <property name="map"> <map> <entry key="m1" value="m1"></entry> <entry> <key> <value>car</value> </key> <ref bean="car"></ref> </entry> </map> </property> <!-- properties赋值 --> <property name="properties"> <props> <prop key="aa">aa</prop> <prop key="bb">bb</prop> </props> </property> </bean> ```` 运行结果 > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com', car=Car{name='大卡车', color='绿色', price=2800}, list=[中文, 英文, Car{name='大卡车', color='绿色', price=2800}], map={m1=m1, car=Car{name='大卡车', color='绿色', price=2800}}, properties={aa=aa, bb=bb}} ### 4. 通过xml为JavaBean属性的属性赋值 如下xml ````xml <bean id="person4" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> <property name="car" ref="car"></property> <property name="car.name" value="大车"></property> </bean> ```` 其中`car.name`就是将引用的car的名字更改为相应的值,需要注意的就是,假如更改了,那么本身在ioc容器里面的这个`car`对象的属性也会随之更改 如下: > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null} car的名字变成了*大车* ### 5. xml中bean标签的继承 通过`parent`属性就可以直接将对应的属性的值直接拷贝一份,然后将需要更改的再使用`property`标签更改即可,如下: ````xml <bean id="person4" class="com.oylong.bean.Person"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> <property name="car" ref="car"></property> <property name="car.name" value="大车"></property> </bean> <bean id="person5" class="com.oylong.bean.Person" parent="person4"> <property name="name" value="parent=p4"></property> </bean> ```` 输出如下: > Person{name='parent=p4', age=15, email='ouyanglong721@gmail.com', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null} ## 创建单实例和多实例的bean对象 **singleton**: 单实例(默认) - 容器启动时就创建好对象,保存在容器中 - 任何时候获取都是之前创建好的对象 **prototype**:多实例 - 容器启动时默认不创建对象 - 每次获取时再创建对象 - 每次获取都会创建一个新的对象 通过设置`bean`标签的`scope`属性即可设置不同的创建方式 ````xml <bean id="person4" class="com.oylong.bean.Person" scope="singleton"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> <property name="car" ref="car"></property> <property name="car.name" value="大车"></property> </bean> ```` ## 自动装配 默认情况下,xml中设置的`autowired`属性的值为`default/no`,即不自动装配,不为指定的属性赋值 - byName 以属性名作为id,去容器中找到这个组件,为其赋值,找不到则为null ````xml <bean id="car" class="com.oylong.bean.Car"> <property name="name" value="大卡车"></property> <property name="color" value="绿色"></property> <property name="price" value="2800"></property> </bean> <bean id="person6" class="com.oylong.bean.Person" autowire="byName"> <property name="age" value="15"></property> <property name="name" value="oyLong"></property> <property name="email" value="ouyanglong721@gmail.com"></property> </bean> ```` 如上xml中,由于`Person`类中的`Car`属性的属性名为`car`,所以将在ioc容器中去寻找一个**id**为car的组件,找到了之后为其赋值 输出: > Person{name='oyLong', age=15, email='ouyanglong721@gmail.com', car=Car{name='大车', color='绿色', price=2800}, list=null, map=null, properties=null} - byType 以属性的类型为依据去ioc容器寻找组件,如果容器中有多个同类型的组件,那么就会报异常,没找到则赋值为`null` - constructor 按照构造器进行赋值 1. 按照有参构造器的类型进行装配(在ioc中寻找指定类型然后通过构造器赋值) 2. 如果按照类型找到了多个,参数名作为id,继续装配,找不到就为`null` 3. 如果有List集合,且在容器中有多个匹配的类型,那么它们会被封装进List 4. 基本类型不会自动装配 5. 不会报错 ## SPEL ## 通过注解将组件加入ioc容器 ### Controller 控制器:推荐给控制器层的组件加这个注解 ### Service 业务逻辑:推荐给业务逻辑层的组件加这个注解 ### Repository 持久层(dao层):给数据库层(持久化,dao)的组件添加这个注解 ### Component 给除以上外的组件添加这个注解 ### component-scan 标签 在xml中配置标签 ````xml <context:component-scan base-package="com.oylong"/> ```` 只有配置了component-scan后,才能扫描指定的目录下的注解,才能将相应的组件添加至ioc容器 > **使用注解之前,需要先导入Spring的aop包** 默认情况下添加的组件的id为**类名首字母小写** 更改id则在相应的注解后面添加即可,如: ````java @Service("myGoodService") @Scope(value = "prototype") public class GoodService{ } ```` 同样,如果需要更改组件的作用域,添加Scope注解在设置其`value`值即可 ## @AutoWired注解 Spring通过`@AutoWired`这个注解自动为属性赋值(从ioc容器中去查找相应的值),如下代码: ````java @Controller public class CarServlet { @Autowired private CarService carService; } ```` 将自动从ioc容器中找到carService并为其赋值 ### @AutoWired注入流程 1. 首先按照类型去ioc容器中寻找对应的组件,类似: ````java bookService = ioc.getBean(BookService.class); ```` 2. 如果没找到,会抛异常 3. 如果找到多个,按照变量名作为id继续进行匹配(BookService[bookService]) 4. 如果没有匹配的变量名,就抛异常 5. 可以使用`@Qualifier`注解进行指定id匹配 ````java @Controller public class CarServlet { @Autowired @Qualifier("carService") private CarService carServiceTmp; } ```` 上面的代码就是通过`@Qualifier`这个注解,让`carServiceTmp`这个变量去指定装配id为`carService`的组件 6. 如果使用`@Qualifier`注解指定id还是没找到,那还是会抛异常 ## @Resource注解 这个注解的功能与`@Autowired`注解功能类似,也是用于自动注入,如下: ````java @Controller public class CarServlet { @Resource private CarService carService; } ```` `@AutoWired`是最强大的,由Spring规定。`@Resource`是j2ee规定的Java标准,与之类似的还有一个`@Inject`注解,由EJB规定。 - @Resource: 因为是java标准,所以拓展性更强,切换不同的容器框架,都可使用,而`@AutoWired`则不行,但是目前市面上的容器基本上也就只有Spring一家。默认按照id进行匹配,如果没有匹配的id,则会进行类型匹配,也可以单独指定其`name`属性的值去按指定名称进行注入 最后修改:2020 年 09 月 14 日 05 : 37 PM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 ×Close 赞赏作者 扫一扫支付 支付宝支付 微信支付