spring特点
(1)方便解耦,简化开发(对象与对象的调用)
(2)AOP的支持
(3)测试方便
(4)和其他框架进行整合
(5)方便对事物的操作
(6)降低api的开发难度。
spring核心组成部分
IOC 和 AOP
IOC
控制反转,也就是把创建对象的过程、对象与对象的调用过程统统交给spring来做,以后想用一个对象不需要new 了,本质上它是一个容器,而所谓的容器就是一个工厂,在工厂里边,获取xml解析的class属性值,通过反射来获取类字节码文件,进而获取配置类的相关属性。
目的
降低耦合。
IOC底层原理
(1) XML解析、工厂模式???、反射(通过反射获取更加灵活)
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 参数路径(配置文件)放在某个盘符的目录下,且必须时绝对路径才行。
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是单例模式。
<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属性来设置。
<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初始化时生命周期就会加载一次!
<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("最后 执行销毁方法");
}
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
模拟bean的后值处理器,必须实现 BeanPostProcessor 接口,且重写里边两个方法。
// 模拟bean的前后置处理器 方法必须实现 BeanPostProcessor 接口,且重写里边的2个方法
public class MyBeanPost implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后置处理器方法 postProcessBeforeInitialization 执行了");
return null;
}
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();
}
}
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;
}
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的实例。
基于注解的方式来创建对象
第一步
引入依赖
<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值是下边类名的小写形式
(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();
}
}
属性注入
基于注解方式实现属性注入的注解有以下几个
@Autowired
根据属性类型进行注入;
用法
以Father和Person类为例,来看下以注解的方式如何进行属性注入
第一步:
在两个类上添加创建对象注解。
public class Father {
public void say() {
System.out.println("我是爸爸");
}
}
第二步:
天机注入属性的注解,即可
public class Person {
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();
}
}
@Auqlifier
根据属性名称进行注入,这个必须要跟@Autowired 一起来使用;
public class Person {
("father")
private Father father;
public void add() {
father.say();
}
}
@Resource
根据属性类型或者名称都可以注入;
@Value
注入普通类型属性。
public class Person {
// 123 会被注入到属性中去name
(value = "123")
private String name;
public void add() {
System.out.println(name);
}
}
测试
在实际开发中,通过 @Value 可以动态读取配置文件中的端口号,或是一些配置属性等等。
前提:是springboot项目。
@Value("${names}")
private String name;
完全用注解的方式开发,舍弃xml
(1)第一步
这种方式,需要创建一个配置类,来替代xml文件,
package com.lxc.domain;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 这个注解的作用就是告知spring,这是一个配置类,替换之前的 bean.xml的
(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;
}
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>
分支文件
依赖注入
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() 方法
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;
}
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());
}
}
输出如下:
(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>
测试调用与上边一样,不贴出来了。
输出如下: