Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
官方文档
IOC介绍
什么是IOC(控制反转)
- 把对象创建和对象之间的调用过程,交给Spring进行管理。
- 使用IOC目的:为了降低耦合度
IOC本质
控制反转loC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现loC的一种方法,也有人认为DlI只是loC的另一种说法。没有loC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是loC容器,其实现方法是依赖注入(Dependency Injection,Dl)。
IOC底层
Spring提供的IOC容器实现的两种方式(两个接口)
- BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)
- ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!
简单IOC例子
没用IOC思想的代码
假如有这些东西,
部分代码如下
public class UserDao1 implements UserDao {@Overridepublic void getUser() {System.out.println("张三");}}
public class UserServiceImpl implements UserService{UserDao user= new UserDao1();@Overridepublic void getUser() {user.getUser();}}
下面属于用户调用了,用户调用不是指客户那种,就好比,我写一个模块,让其他人调用我写的模块。这里的main方法,就是调用的我写的模块。调用的人就相当于用户,因为可以不同的人调用我这个模块,不同的用户。
public class testUser {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();userService.getUser();}}
最终会输出UserDao1张三。
这时候有个问题,用户想要输出UserDao2怎么办,UserDao3呢?
这不得去改源码吗,UserServiceImpl.java里面UserDao user= new UserDao2();``UserDao user= new UserDao3();
每当调用的人,就是用户,有新需求了,就去改源码,不是一个好程序,需要解耦。这些东西要让用户自定义。
使用IOC思想的代码
我们这样修改源码,多一个set方法。
使用了set注入后,程序不在具有主动性,而是变成了被动的接受对象。
public class UserServiceImpl implements UserService{UserDao user= null;public void setUser(UserDao user) {this.user = user;}@Overridepublic void getUser() {user.getUser();}}
用户调用时,用set方法自己设置一个对象,我要用什么对象,我直接设置,这样就不用去修改源码了,用户用什么,自己传入什么。
main这里是用户调用,不属于源码范围了。我是程序员,我用别人写的Spring,我就是Spring的用户。
public class testUser {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();userService.setUser(new UserDao2());//李四userService.getUser();}}
使用Spring IOC
Maven创建项目后,导入依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.0.RELEASE</version></dependency>
这是测试的目录结构
package com.lyd.pojo;/*** @author liyadong* @create 2022-03-31-14:31*/public class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}}
Spring配置文件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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--使用Spring来创建对象,在Spring中这些都称为Bean.一个对象就相当于一个beanid:变量名class:对象类的全类名--><bean id="user" class="com.lyd.pojo.User"><property name="name" value="张三"></property><property name="age" value="23"></property></bean></beans>
bean标签(核心)
<!--使用Spring来创建对象,在Spring中这些都称为Bean.一个对象就相当于一个beanid:变量名class:对象类的全类名name:取多个别名,各种常用的分隔符隔开,可以通过这些别名进行访问对象--><bean id="user" class="com.lyd.pojo.User" name="u u1,u2"><property name="name" value="张三"></property><property name="age" value="23"></property></bean>
alias标签
取个别名,可以通过别名获取对象
- name:被取别名的对象名
alias:别名
<alias name="user" alias="u"></alias>
import标签
用于团队合作的时候,引入其他的Spring配置文件,就可以用当前的配置文件访问其他文件里面的对象了
<import resource="beans1.xml"></import><import resource="beans2.xml"></import>
依赖注入(DI)
public class Student {private String name;private School school;private String[] books;private List<String> hobbys;private Map<String,String> card;private Set<String> games;private Properties info;private String wife;//......get,set,toString省略}
set注入
对象里面这些参数一定要有set方法才能注入,不然注入不了。 ```xml <?xml version=”1.0” encoding=”UTF-8”?>
红楼梦 西游记 水浒传 听歌 看电影 学习 王者荣耀 原神 yyyyyyy xxxxxxxx root 123456
<a name="EjZyW"></a>### 根据构造器注入可以通过注入构造器的参数进行创建对象。**默认是使用无参构造器**<br />当然构造器注入也是可以向set注入那样多种类型注入。```xml<bean id="user" class="com.lyd.pojo.User"><!--1.属性名--><constructor-arg name="age" value="23"/><constructor-arg name="name" value="王武"/><!--2.形参类型通过type的方式可以用指定参数类型的方式,没有别名。这种方式不推荐,要是多个类型一样的参数呢--><!-- <constructor-arg type="int" value="24"/>--><!-- <constructor-arg type="java.lang.String" value="张三"/>--><!--3.参数索引--><!-- <constructor-arg index="0" value="王二"/>--><!-- <constructor-arg index="1" value="25"/>--></bean>
p命名和c命名空间注入
需要引入
xmlns:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"


<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!--p命名空间注入,可以直接注入属性值:property--><bean id="user" class="com.lyd.pojo.User" p:age="23" p:name="张三"/><!--c命名空间注入,通过构造器注入:construct-args--><bean id="user2" class="com.lyd.pojo.User" c:age="32" c:name="李思"/></beans>
使用对象
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
通过XML获取对象,还可以通过注解,文件等。
执行这句话时,就创建了ApplicationContext.xml里面的全部对象,默认执行的无参构造函数。
把对象都写在程序之外的地方,用户可以直接修改,想要什么对象就自己写什么对象,参数都可以自己配置。使用时只用传一个字符串就得到了。
public class testSpringIOC {public static void main(String[] args) {//获取Spring的上下文对象ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");//全部对象都是spring中管理了,要使用就去Spring中取出来。给用户一个容器,要什么就去取什么//User user = (User)context.getBean("user");User user = context.getBean("user",User.class);System.out.println(user.toString());//结果:User{name='张三', age=23}}}
bean作用域
在bean标签的scope属性里面设置
- 单例模式(Spring默认机制)
只会创建一个实例,翻来覆去用这个实例。
<bean id="user" class="com.lyd.pojo.User" scope="singleton"/>
原型模式:每次从容器中get的时候,都会产生一个新的对象
<bean id="user" class="com.lyd.pojo.User" scope="prototype"/>
其余的request,session这些只能在web开发中使用
bean的自动装配
使用autoaire属性自动装配
这是手动装配bean,gei每个set都手动注入值
<bean id="cat" class="com.lyd.pojo.Cat"/><bean id="dog" class="com.lyd.pojo.Dog"/><bean id="people" class="com.lyd.pojo.People"><property name="string" value="张三"/><property name="cat" ref="cat"/><property name="dog" ref="dog"/></bean>
使用自动装配
<bean id="cat" class="com.lyd.pojo.Cat"/><bean id="dog" class="com.lyd.pojo.Dog"/><!--autowire属性:可以设置主动装配bean,自动在容器上下文中查找byName:会自动在容器上下文中查询,和自己对象set方法后面的值对应的beanidbyType:会自动在容器山下文中查找,和自己对象属性类型相同的bean--><bean id="people" class="com.lyd.pojo.People" autowire="byName"><property name="string" value="张三"/></bean>
小结:
- byName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set的方法的值一致
byType的时候,需要保证所有的bean的class唯一,并且这个bean需要自动注入的属性类型一致
使用注解自动装配
导入约束
配置注解支持
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--配置注解支持--><context:annotation-config/><!--xml里面什么都不配置,id和class还是要的--><bean id="cat" class="com.lyd.pojo.Cat"/><bean id="dog" class="com.lyd.pojo.Dog"/><bean id="people" class="com.lyd.pojo.People"/></beans>
@Autowired:
直接放在属性上就可以,也可以放在set方法上
- 使用Autowired注解就可以不用边写set方法了,注解都是用反射获取值得,提前是自动装配的属性在IOC容器中,且符合规范 ```java package com.lyd.pojo;
import org.springframework.beans.factory.annotation.Autowired;
/**
- @author liyadong
@create 2022-03-31-17:42 */ public class People { @Autowired private Cat cat; //如果显示的定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Dog dog; private String name;
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
} public Dog getDog() {
return dog;
} public String getString() {
return name;
}
@Override public String toString() {
return "People{" +"cat=" + cat +", dog=" + dog +", name='" + name + '\'' +'}';
} }
<a name="Lzc9r"></a>#### @Qualifier如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候、我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入!<br />指定不同的beanid,但是类型不一样是要报错的。```xml<bean id="cat222" class="com.lyd.pojo.Cat"/>
@Autowired@Qualifier(value = "cat222")private Cat cat;
@Resource
这是java自带的注解,可以指定name,type进行名字,类型的指定装配
@Resource(name = "cat222",type = Cat.class)private Cat cat;@Resource(name="dog",type=Dog.class)private Dog dog;
小结
@Resource和@Autowired的区别:
都是自动装配的,都可以放在属性字段上
@Autowired默认通过byType的方式实现,然后再去byName
@Resource默认通过byName的方式实现,然后再去byType
使用注解开发
使用注解需要导入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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--指定要扫描的包.该包下所有带有@Component 注解的类都等价于bean标签,装在容器里面--><context:component-scan base-package="com.lyd.pojo"/><!--配置注解支持--><context:annotation-config/></beans>
@Component和@Value
- 直接在类上面加上
@Component注解,配合配置文件里面的指定扫描包<context:component-scan base-package="com.lyd.pojo"/>,这个类就被装到了IOC容器里面。 - 在属性或者set方法上面,可以通过
@Value注入值 ```java package com.lyd.pojo;
import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;
//@Component组件,等价于
private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}@Value("23")public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}
调用```javapublic class testSpringIOC {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");User user = context.getBean("user",User.class);System.out.println(user.toString());}}
衍生注解
@Component有几个衍生注解,根据分层不同,会有不同的注解标记
- dao层:@Repository
- service层:@Service
- controller层:@Controller
这四个注解功能一样,不同层对应不同的注解,都是代表将某个类注册到Spring中,装配Bean




需要在配置文件扫描指定包改成base-package="com.lyd"
配置作用域@Scope
@Component@Scope("singleton")//作用域单例模式//@Scope("prototype")//原型模式public class Dog {}
JavaConfig实现配置
完全用java来配置,就不用xml了。
接着上面的步骤,单独建一个配置类
package com.lyd.config;import com.lyd.pojo.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component//@Component代表这是一个配置类,就是和我们之前的ApplicationContext.xml一样的@Configuration//扫描包 <context:component-scan base-package="com.lyd.pojo"/>@ComponentScan("com.lyd.pojo")//导入其他的配置类,合并成一个配置类@Import({MyConfig2.class,MyConfig3.class})public class MyConfig {//注册一个bean,就相当于之前写的bean标签//1.方法的名字,就相当于bean标签的id属性//2.方法的返回类型,就相当于bean标签中的class属性//3.返回值就是要注入到bean的对象@Beanpublic User user(){return new User();}}
然后通过注解的方式AnnotationConfigApplicationContext获取上下文对象,来从IOC容器中取出对象
@Testpublic void Test01(){//如果完全使用了配置类方法取做,我们就只能通过AnnotationConfigApplicationContext上下文来获取容器,通过配置类的class对象加载ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);User user = (User)context.getBean("user");System.out.println(user.toString());}
