spring特点

(1)方便解耦,简化开发(对象与对象的调用)

(2)AOP的支持

(3)测试方便

(4)和其他框架进行整合

(5)方便对事物的操作

(6)降低api的开发难度。

spring核心组成部分

IOC 和 AOP

IOC

控制反转,也就是把创建对象的过程、对象与对象的调用过程统统交给spring来做,以后想用一个对象不需要new 了,本质上它是一个容器,而所谓的容器就是一个工厂,在工厂里边,获取xml解析的class属性值,通过反射来获取类字节码文件,进而获取配置类的相关属性。

目的

降低耦合。

IOC底层原理

(1) XML解析、工厂模式???、反射(通过反射获取更加灵活)

Spring 创建及配置、依赖注入_spring

IOC接口(BeanFactory)

IOC思想是基于IOC容器完成,而IOC容器最底层就是一个对象工厂。
spring 提供的IOC容器实现的两种方式,两个都是接口
(1)BeanFactory
它是spring内部使用的,不提供给开发用,在加载配置文件时,不会创建对象,而读取对象(使用)采取创建对象。
(2)ApplicationContext
 BeanFactory接口的子接口,提供了更加强大的功能,共开发人员使用,在加载配置文件的时候,就会把配置文件中的对象创建出来。
两个接口都可以通过工厂模式,通过加载配置文件,来创建对象。

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// 或
BeanFactory ctx = new ClassPathXmlApplicationContext("beans.xml");

下边着重看下,ApplicationContext接口的实现类:
(1)ClassPathXmlApplicationContext,这个类我们上边用到了,会去加载、创建一个对象
(2)FileSystemXmlApplicationContext,这个实现类跟第一个一样,会创建一个对象,

ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext区别:
ClassPathXmlApplicationContext 参数路径(配置文件)必须在src目录下,表示类路径;
FileSystemXmlApplicationContext 参数路径(配置文件)放在某个盘符的目录下,且必须时绝对路径才行。

Spring 创建及配置、依赖注入_属性值_02

IOC操作Bean管理

什么是Bean管理

指的是两个操作:

(1)spring创建对象
(2)spring对属性进行注入

Bean管理的两种方式

(1)基于xml配置文件的方式实现
(2)基于注解的方式

xml方式创建对象及属性的注入
id :标识符
class:类全路径

public class Person {
private String name;
public void name(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<bean id="person" class="com.lxc.domain.Person">
<property name="name" value="lxc" />
</bean>

传统的注入属性的方式,前提:必须呀new Person() 才能调用:

(1)通过set方法

public class Person {
private String name;
public void setName(String name) {
this.name = name;
}
}

(2)通过有参构造注入

public class Person {
private String name;
public void name(String name) {
this.name = name;
}
}

spring注入属性的方式,不需要new Person()即可完成对象的创建及属性注入:

(1)下边是无参构造的方式注入属性值(底层是调用的setName方法)

<bean id="person" class="com.lxc.domain.Person">
<!--注入属性-->
<property name="name" value="lxc" />
</bean>

(2)下边是有参构造的方式注入属性值(底层是调用有参的构造方法)
提示:当无参构造和有参构造同时给一个属性注入值时,无参构造优先级高。

<bean id="person" class="com.lxc.domain.Person">
<!--无参构造属性值的注入-->
<property name="name" value="lxc" />
<!--有参构造属性值的注入-->
<constructor-arg name="name" value="123" />
<!--下边效果与上边一样,著不过时通过参数索引注入值的-->
<constructor-arg index="0" value="123" />
</bean>

Bean的作用域

在spring里边,设置创建bean实例是单实例还是多实例。

默认情况下创建的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/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">

<bean id="person" class="com.lxc.domain.Person">
<property name="name" value="lxc" />
</bean>
</beans>
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person person = ctx.getBean("person", Person.class);
System.out.println(person == person); // true
}
}

多实例情况:
通过scope属性来设置。

<?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">

<bean id="person" class="com.lxc.domain.Person" scope="prototype">
<property name="name" value="lxc" />
</bean>
</beans>

********** Bean的生命周期 ********** 

从对象的创建到对象销毁的过程。
大致分7步:

(1)默认是通过无参构造器来创建bean实例;
(2)为Bean的属性和对引用的Bean 设置值;
把bean实例传递给bean后置处理器 postProcessBeforeInitialization 方法中处理; 
(3)调用Bean初始化方法(需要对其配置);
把bean实例传递给bean后置处理器 postProcessAfterInitialization 方法中处理;
(4)bean就可以使用了(对象已经获取到);
(5)当容器关闭时,调用bean里边销毁方法(需要配置销毁方法)。

演示生命周期
提示:在演示过程中,如果前后置处理器出发了多次,一定是xml中的配置有多个bean,因为一个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/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.lxc.domain.Cat" />
<bean id="dog" class="com.lxc.domain.Dog" />
<bean id="person" class="com.lxc.domain.Person" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="lxc" />
</bean>
<!--配置后置处理器-->
<bean id="beanPost" class="com.lxc.domain.MyBeanPost" />

</beans>
public class Person {
private String name;

public Person() {
System.out.println("第一步 调用无参构造创建Bean实例");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("第二步 调用set方法设置属性值");
this.name = name;
}
public void initMethod() {
System.out.println("第三步 调用初始化方法");
}
public void destroyMethod() {
System.out.println("最后 执行销毁方法");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}

 模拟bean的后值处理器,必须实现 BeanPostProcessor  接口,且重写里边两个方法。

// 模拟bean的前后置处理器 方法必须实现 BeanPostProcessor 接口,且重写里边的2个方法
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后置处理器方法 postProcessBeforeInitialization 执行了");
return null;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后置处理器方法 postProcessAfterInitialization 执行了");
return null;
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person person = ctx.getBean("person", Person.class);
System.out.println("第四步 获取创建的bean实例对象: "+person);
// 手动调用销毁方法
((ClassPathXmlApplicationContext) ctx).close();
}
}

Spring 创建及配置、依赖注入_属性值_03

IOC操作Bean管理(xml自动装配)
提示:在实际开发中,是基于注解方式做自动装配,很少用xml方式。

根据指定装配规则(属性名或者属性类型),spring自动将匹配的属性值进行注入。

先来看一个我们之前写的手动装配的简单小例子:

public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<bean id="person" class="com.lxc.domain.Person">
<property name="name" value="lxc" /> <!--属性和属性值手动装配-->
</bean>

自动装配
自定装配,需要在bean标签添加 autowire属性,配置自动装配。

autowire属性常用的两只属性值:
byname - 根据属性名称注入,;
byType - 根据属性类型注入,;

<bean id="person" class="com.lxc.domain.Person" autowire="byName" />
<bean id="father" class="com.lxc.domain.Father" />
public class Person {
private Father father1;
public Father getFather() {
return father1;
}
public void setFather(Father father) {
this.father1 = father;
}
@Override
public String toString() {
return "Person{" +
"father=" + father1 +
'}';
}
}
public class Father {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

IOC操作Bean管理(基于注解的)

代码的特殊标记。

用注解目的

简化操作。

在spring中,针对Bean管理中创建对象的注解有以下几个

@Component

@Service

@Controller

@Repository 或 @Mapper

以上几个注解,功能是一样的,都可以来创建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/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/>
<!--开启组件的扫描,扫描多个包,包之间要用逗号隔开, 或者扫描上层目录-->
<context:component-scan base-package="com.lxc.domain" />

</beans>

第二步

开启组件扫描,同上。

第三步

在类上添加创建对象的注解。
提示:注解中的value值可以不写,默认值是下边类名的首字母小写的形式,下边使用@Service或者 @Controller或者 @Repository 注解都可以。

// 里边的value相当于之前 <bean id="person"> id的值,当然value也可以省略,
// 如果省略,默认value值是下边类名的小写形式
@Component(value = "person")
public class Person {
public void t() {
System.out.println("123");
}
}

第四步

测试

public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person person = ctx.getBean("person", Person.class);
person.t();
}
}

Spring 创建及配置、依赖注入_属性值_04

属性注入

基于注解方式实现属性注入的注解有以下几个

@Autowired
根据属性类型进行注入;

用法
以Father和Person类为例,来看下以注解的方式如何进行属性注入

第一步:

在两个类上添加创建对象注解。

@Repository
public class Father {
public void say() {
System.out.println("我是爸爸");
}
}

 第二步:

天机注入属性的注解,即可

@Service
public class Person {
@Autowired
private Father father;

public void add() {
father.say();
}
}

第三步:

测试

public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person person = ctx.getBean("person", Person.class);
person.add();
}
}

Spring 创建及配置、依赖注入_xml_05

@Auqlifier
根据属性名称进行注入,这个必须要跟@Autowired 一起来使用;

@Service
public class Person {
@Autowired
@Qualifier("father")
private Father father;

public void add() {
father.say();
}
}

Spring 创建及配置、依赖注入_spring_06

Spring 创建及配置、依赖注入_xml_07

@Resource
根据属性类型或者名称都可以注入;

@Value
注入普通类型属性。

@Service
public class Person {
// 123 会被注入到属性中去name
@Value(value = "123")
private String name;
public void add() {
System.out.println(name);
}
}

测试

Spring 创建及配置、依赖注入_spring_08

在实际开发中,通过 @Value 可以动态读取配置文件中的端口号,或是一些配置属性等等。
前提:是springboot项目。

Spring 创建及配置、依赖注入_spring_09

@Value("${names}")
private String name;

完全用注解的方式开发,舍弃xml

(1)第一步

这种方式,需要创建一个配置类,来替代xml文件,

package com.lxc.domain;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration // 这个注解的作用就是告知spring,这是一个配置类,替换之前的 bean.xml的
@ComponentScan(basePackages = "com.lxc") // 这个就是要扫描哪一个包下的文件 ,等于之前 xml中的 <context:component-scan base-package="com.lxc" />
public class SpringConfig {
}

(2)第二步

测试, 注意的是与之前不同,下边 new  AnnotationConfigApplicationContext(xx.class) 获取配置类

public class Test {

ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
Person person = ctx.getBean("person", Person.class);
person.add();
}
}

===============================================================

以上是最近补充的内容,下边是之前记录的。

===============================================================

创建

记录创建过程,四步:

第一步:

想要使用spring,必须maven安装依赖:

<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
</dependencies>

第二步:

创建实体类

package com.lxc.domain;
public class User {
private String name;
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;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

第三步:

resource下创建 beans.xml文件,使用srping之前,必须配置的,配置如下:

<?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">
<!--
使用spring来创建对象
下边是无参构造方法设置:
<bean> 标签:====================================================
bean: 相当于一个对象 , new User()
id: 这个对象的唯一标识
class: 这个对象的类型,必须写 全路径名称
<property> 标签:====================================================
name属性:值就是实体类中的属性名
value属性: 为属性设置的值

有参构造方法设置:外层标签bean一样
里边标签<constructor-arg>
name:属性名;
value: 属性值。
index: 参数的索引,0代表: 参数一; 1代表:参数二; ··· ···
-->
<!--无参构造-->
<bean id="user" class="com.lxc.domain.User">
<property name="name" value="吕星辰" />
<property name="age" value="20" />
</bean>

<!--有参构造:方法一:通过参数名来设置-->
<!--<bean id="user" class="com.lxc.domain.User">
<constructor-arg name="name" value="lxc;" />
</bean>-->

<!--有参构造:方法二:通过参数的索引来设置参数-->
<!--<bean id="user" class="com.lxc.domain.User">
<constructor-arg index="0" value="l" />
<constructor-arg index="1" value="20" />
</bean>-->
</beans>

第四步:

测试调用,传统方式,如果想要使用User对象,必须把User类引入。然后new实例化之后,才能使用里边的方法属性等等,但是使用spring,上边配置完,就不需要实例化了,获取User类对象只需要两步,如下:

import com.lxc.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
public static void main(String[] args) {
// (1)spring容器 - 加载上边的配置文件 beans.xml
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// (2)getBean() 方法获取user对象,调用的时候,默认会执行 User类的无参构造方法,对User类实例化了
User user = (User) ctx.getBean("user");
System.out.println(user.toString()); // User{name='吕星辰', age=20}
}
}

配置

1、别名

<?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标签中 添加name属性,也可以添加多个别名中间以空格或逗号分隔-->
<bean id="user" class="com.lxc.domain.User" name="newUser,newUser1 newUser2">
<property name="name" value="吕星辰" />
<property name="age" value="20" />
</bean>
<!--方法二:通过别名 newUser也能获取到User类 -->
<alias name="user" alias="newUser"/>
</beans>

2、import

团队开发,将多个配置文件,导入合并为一个

ApplicationContext.xml 总文件,可以通过import 来引入分支文件

<!--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">
<import resource="beans.xml" />
<import resource="beans1.xml"/>
</beans>

分支文件

Spring 创建及配置、依赖注入_xml_10

依赖注入

1、set方式注入【重点】

之所以叫set注入,spring是通过实体类中各个属性的set方法为字段注入值的。

(1)注入null的情况
下边值注入一个name属性,其他的不注入,我们来看下输出的值:

创建实体类Student

package com.lxc.domain;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private Map<String, String> card;
private Set<String> games;
private Properties info;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
// 字符串相加,所有的变量默认会调用 toString() 方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", Address='" + address + '\'' + '\n' +
", books=" + Arrays.toString(books) +
", card=" + card + '\n' +
", games=" + games +
", info=" + info +
'}';
}
}

创建实体类Address

package com.lxc.domain;
public class Address {
private String country;
private String province;
private String city;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"country='" + country + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}

 beans.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="student" class="com.lxc.domain.Student">
<property value="Spring" name="name"/>
</bean>
</beans>

测试调用

import com.lxc.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
public static void main(String[] args) {
// spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans2.xml");
// 方法一:
Student student = (Student) ctx.getBean("student");
// 方法二:参数二定义类的类型,就不需要强制转换了
Student student = ctx.getBean("student", Student.class);
System.out.println(student.toString());
}
}

 输出如下:

Spring 创建及配置、依赖注入_spring_11

(2)注入  bean方式(引用)、数组类型、Map集合类型、Set集合类型、Properties类型

beans.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="address" class="com.lxc.domain.Address">
<property name="country" value="中国" />
<property name="province" value="山东省" />
<property name="city" value="烟台市" />
</bean>
<bean id="student" class="com.lxc.domain.Student">
<!--普通注入方式:通过value注入-->
<property name="name" value="Spring"/>

<!--通过引用方式注入,address属性值引用的上边的address-->
<property name="address" ref="address"/>

<!--String[] 数组类型注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>

<!--Map类型注入-->
<property name="card">
<map>
<entry key="学生卡" value="1100020" />
<entry key="银行卡" value="111222000" />
</map>
</property>

<!--Set类型注入-->
<property name="games">
<set>
<value>英雄联盟</value>
<value>地下城与勇士</value>
</set>
</property>

<!--properties-->
<property name="info">
<props>
<prop key="sex">男</prop>
<prop key="height">170</prop>
<prop key="class">三年级二班</prop>
</props>
</property>
</bean>
</beans>

测试调用与上边一样,不贴出来了。

输出如下:

Spring 创建及配置、依赖注入_xml_12