Spring基础学习一 -- IOC, DI, Bean
Spring成员
狂神说Spring
Spring是一个轻量级
的控制反转(IoC)
和面向切面(AOP)
的容器框架
.
支持事务
的处理, 对框架整合的支持
解决企业应用开发的复杂性, 使现有的技术更容易使用.
SSH: Struct2 + Spring + Hibernate 旧
SSM: SpringMVC + Spring + Mybatis 新
Spring七大组件
Spring Framework
在每一层都可以有自己的选择, 都会提供支持
Spring Boot
一个快速开发的脚手架; 可以快速开发一个微服务; 约定大于配置
把很多实践都放到了框架里面, 可以开箱即用
不用生成代码, 没有xml配置
Spring Cloud
基于Spring Boot实现
把最佳实践固化到系统实践中
IOC
狂神说-Spring IOC
原来的业务分层:
- UserDao Dao层
- UserDaoImpl 实现类
- UserService 业务接口
- UserServiceImpl 业务实现类
public class UserServiceImpl implements UserService{
// 主动创建对象
private UserDao userDao = new UserDaoImpl(); // 需要根据需求主动更改new UserDaoMysqlImpl
// new UserDaoOracleImpl等等等
// 需要用到他的地方 , 不去实现它 , 而是留出一个接口 , 利用set
// 不去自己new,将主动权从业务层交给用户
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void getUserDao(){
userDao.getUser;
}
}
使用
public class MyTest{
public static void main(String[] args){
// 用户实际接触的是业务层, dao层不需要接触
UserService userService = new UserServiceImpl(); // 主动创建
(UserServiceImpl userService).setUserDao(new UserDaoSqlImpl); // 被动调用
}
}
用户的需求可能会影响原来的代码, 修改一次的成本十分昂贵
从主动的创建对象 --> 被动的接受对象: 控制反转
程序员不需要管理对象的创建, 系统的耦合性大大降低
控制反转IoC(Inversion of Control)
,是一种设计思想,DI(依赖注入)
是实现IoC的一种方法
没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制;
控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制(反射机制)发现和操作这些JavaBean 的属性。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
对象的具体注入从代码中被移除, 取而代之在xml文件中定义.
Hello Spring - IOC实现
Hello实体类 Hello.java
public class Hello {
private String name;
public String getName() {
return name;
}
// set方法进行注入
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
applicationContext.xml
Spring的配置文件. Maven下是pom.xml
POM
什么是pom?
POM是项目对象模型(Project Object Model). 该文件用于管理:源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。
https://pocnblogs.com/wkrbky/p/6353285.html
包含元素:
- maven的协作相关属性: groupdId:artifactId:version的形式来唯一确定一个项目
- POM之间的关系: 继承(parent)、聚合(packaging, modules)与依赖(dependencies)
- 属性: ${propertyName}的形式引用属性
applicationContext.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">
java:
Hello hello = new Hello();
--> Spring
id: 变量名; class: new的对象; property: 对象的属性设置
<!--bean就是java对象 , 由Spring创建和管理-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
测试使用myTest.java
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml ");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
对象的创建和属性控制都是交给Sping控制
这个过程就叫控制反转 :
- 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
- 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
依赖注入 : 就是利用set方法来进行注入的.
IOC思想 – 由主动的编程, 变成被动的接收
<?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="MysqlImpl" class="com.kuang.dao.impl.UserDaoMySqlImpl"/>
<bean id="OracleImpl" class="com.kuang.dao.impl.UserDaoOracleImpl"/>
<bean id="ServiceImpl" class="com.kuang.service.impl.UserServiceImpl">
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
<!--引用另外一个bean , 不是用value 而是用 ref-->
<property name="userDao" ref="OracleImpl"/>
</bean>
@Test
public void test2(){
// 拿到Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 需要什么,直接get
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl"); // 对象已经被创建
serviceImpl.getUser();
}
要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
依赖注入(DI)
Set注入
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .
address.java
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
student.java
package com.kuang.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
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;
public void setName(String name) {
this.name = name;
}
...... set for all ......
public void show(){
System.out.println("name="+ name + ",address="+ address.getAddress() + ",books=");
for (String book:books){
System.out.print("<<"+book+">>\t");
}
System.out.println("\n爱好:"+hobbys);
System.out.println("card:"+card);
System.out.println("games:"+games);
System.out.println("wife:"+wife);
System.out.println("info:"+info);
}
}
- 常量注入
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
</bean>
- Bean注入
<bean id="addr" class="com.kuang.pojo.Address">
<property name="address" value="重庆"/>
</bean>
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
</bean>
- 数组注入
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</array>
</property>
</bean>
- List注入
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
<value>爬山</value>
</list>
</property>
- Map注入
<property name="card">
<map>
<entry key="中国邮政" value="456456456465456"/>
<entry key="建设" value="1456682255511"/>
</map>
</property>
- Set注入
<property name="games">
<set>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</set>
</property>
- Null值注入
<property name="wife"><null/></property>
<property name-"wife" value=""/> // 空字符串
- Properties注入
<property name="info">
<props>
<prop key="学号">20190604</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
</props>
</property>
P namespace注入和C namespace注入
在配置文件中引入p,c 命名空间
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>
<!--C(构造: Constructor)命名空间 , 需要有有参数构造器, 构造器注入, 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>
</bean>
Bean的作用域
https://blog.csdn.net/kongmin_123/article/details/82048392
- singleton
如果bean的作用域的属性被声明为singleton,那么Spring Ioc容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那么Spring在每次需要时都返回同一个bean实例。
Spring的默认模式
<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
<!-- collaborators and configuration for this bean go here -->
</bean>
- prototype
当一个bean的作用域为prototype,表示一个bean定义对应多个对象实例。声明为prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
以下scope只会在web中应用
- request
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
Spring容器会在每次用到loginAction来处理每个HTTP请求的时候都会创建一个新的LoginAction实例。
当http请求调用作用域为request的bean的时候,每增加一个HTTP请求,Spring就会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。
- session
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
Spring容器会在每次调用到userPreferences时,在一个单独的HTTP会话周期来创建一个新的UserPreferences实例。
Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。
- application
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring容器会在整个web应用范围使用到appPreferences的时候创建一个新的AppPreferences的实例。
这种作用域在一些程度上来说和Spring的单例作用域相似,但是也有如下不同之处:
- application作用域是每个ServletContext中包含一个,而不是每个SpringApplicationContext之中包含一个(某些应用中可能包含不止一个ApplicationContext)。
- application作用域仅仅作为ServletContext的属性可见,单例Bean是ApplicationContext可见。
Bean的自动装配
https://blog.csdn.net/qq_33369905/article/details/105828918?spm=1001.2014.3001.5501
自动装配是使用spring满足bean依赖的一种方法. spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
- 在xml中显式配置
- 在java中显式配置
- 隐式的bean发现机制和
自动装配
注解自动装配比自动装配xml配置更加推荐
XML显式装配
使用注解需要:
- 导入约束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/>
<bean id="cat" class="com.sss.ddd.cat">
... 简洁
</beans>
@Autowired
public class people{
@Autowired
private Cat cat;
@Autowired
@Qualifier(value="dog2222")
// 自动装配无法通过一个注解完成的时候,可以指定一个名字Qualifier(value=“xxx”)
// 来配合Autowired来指定一个唯一的bean对象注入
private Dog dog;
...
}
@Autowired(required = false)
显式的定义了autowired的required属性为false -- 说明这个对象可以为空
可以不需要set方法!
对比:
@Autowired
@Autowired通过byType的方式实现, 要求这个字段必须存在
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
@Resource
相似功能
@Resource(name=“cat22”);
@Resource默认通过byName方式实现, 如果找不到, 按byType实现
注解开发
在spring4之后,想要使用注解形式,必须得要引入aop的包
在配置文件当中,还得要引入一个context约束
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
- bean
User.java
package com.spring.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
// bean
/*变成组件 == <bean id="user" class="com.spring.pojo.User"/>*/
@Component
@Scope("singleton")
public class User {
public String name = "hanhaha";
//属性注入
// 相当于bean - property name="gender" value="male"
@Value("male")
public String gender;
}
@Component
: 组件, 放在类上, 说明这个类被Spring管理了, 变成bean
- 属性如何注入
@Value(“xxxx“)
- @Value(“${}”)
获取配置文件application.properties中的属性值
@Value(“${user.userName}”) // 写在application.properties中的属性配置
String name; // name就会被注入为上面的值, 可以直接使用这个值
application-test.yml:
spring:
profiles: test
user:
userName: 王立国-test
sex: 男
age: 18
author:
name: wlg-test
height: 174
- @Value("#{}")
表示常量值 或者 获取bean中的属性
@Value("#{1}")
private int number; //获取数字 1
@Value("#{'Spring Expression Language'}") //获取字符串常量
private String str;
@Value("#{dataSource.url}") //获取bean的属性
private String jdbcUrl;
- 衍生注解
@Component
有几个衍生注解, web开发中, 会按照MVC三层架构分层
- dao -
@Repository
- service -
@Service
- controller -
@Controller
这四个注解的功能一样, 都是将某个类注册到Spring中, 装配bean
- 作用域
@Scope(“singleton”)
xml和注解的最佳实现:
xml管理bean; 注解完成属性的注入
java显式装配
用一个xxxxConfig.java文件来代替.xml文件来完成bean的装配. 在spring boot中常见
编写一个实体类,Dog
@Component //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
2、新建一个config配置包,编写一个MyConfig配置类
@Configuration //代表这是一个配置类
@import(“导入其他config”)
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public Dog dog(){
return new Dog();
}
}
3、测试
获取上下文的方式也发生了变化
@Test
public void test2(){
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("dog");
System.out.println(dog.name);