目录
Spring的基本概念
🚩spring是什么?
spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
🚩spring项目的出发点:
- 目的:提供一种贯穿始终的解决方面,将各种专用框架整合成一个连贯的整体框架,简化企业级应用的开发(有点像粘合剂)
- 鼓励最佳实践: 例如spring将“针对接口编程”的成本降到了最小
- 非侵入性: 应用对象尽量避免依赖框架,IoC和AOP是避免依赖的关键技术
- 统一配置: 好的框架应该让应用配置灵活且统一
- 易于测试: 使单元测试尽可能简单
- 可扩展:
简单来说,spring就是一个轻量级的控制(IOC)和面向切面编程(AOP)的容器框架。
🚩spring的控制反转(依赖注入)
控制反转IOC(inversion of control),控制反转其实就是控制权的反转,我们都知道传统的,由代码直接操控的,那么反转了之后就是将控制权直接给到了外部的容器。这就是反转的概念 。控制反转又被称为依赖注入DI(Dependency Injection) ,意思就是由容器动态的将某种关系注入到组件中去。
AOP是什么 ?
- 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
- 目标(Target):被通知(被代理)的对象
- 通知(Advice):在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)
- 代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知),请注意:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的
- 切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)
- 适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)
示例:
🏳🌈创建web工程
这里我们直接创建一个web项目,并转换为web3.1,具体步骤见maven的安装和使用_一麟yl的博客-CSDN博客
🏳🌈配置pom文件
下面是我们需要的依赖配置。
注意:个人建议需要什么用到什么模块就使用什么模块的依赖。
<properties>
<hibernate.version>5.2.12.Final</hibernate.version>
<mysql.driver.version>5.1.44</mysql.driver.version>
<spring.version>5.2.6.RELEASE</spring.version>
<struts2.version>2.5.13</struts2.version>
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>2.9.1</log4j.version>
<disruptor.version>3.2.0</disruptor.version>
<junit.version>4.12</junit.version>
<servlet.version>4.0.1</servlet.version>
<jstl.version>1.2</jstl.version>
<standard.version>1.1.2</standard.version>
<tomcat-jsp-api.version>8.5.20</tomcat-jsp-api.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring 相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<!--4. log配置:Log4j2 + Slf4j -->
<!-- slf4j核心包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<!--用于与slf4j保持桥接 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!--核心log4j2jar包 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--web工程需要包含log4j-web,非web工程不需要 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<!--需要使用log4j2的AsyncLogger需要包含disruptor -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
<!--5. other -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!--6. jstl -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>${standard.version}</version>
</dependency>
<!-- 7. jsp自定义标签依赖 (必须与tomcat的版本一致) -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>${tomcat-jsp-api.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
注: 该依赖配置不仅包含了spring,同时也包含了hibernate,struts,mysql驱动等。
🏳🌈创建spring的配置文件
spring.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" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
</beans>
进行到这里我们的容器就算搭建好了,我们来两个实例。
🏳🌈示例
①示例一(普通)
🟠这里我们创建一个学生实体类(Student.java)
package com.ljq.spring01.model;
/**
* 学生实体
* @author 一麟
*
*/
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
}
🟢并在spring.xml文件中配置好
<bean id="demo" class="com.ljq.spring01.model.Student">
<property name="name">
<value>一麟</value>
</property>
</bean>
配置文件说明:
ID:在容器中确保了bean的唯一(不能以/开头)
class:bean的权限类名
name:在容器中标记bean的名字(唯一,不能以/开头,能够存在多个值,用逗号隔开)
scope:(singleton|prototype)默认是singleton
singleton: 单例模式, 在每个Spring IoC容器中一个bean定义对应一个对象实例
prototype: 原型模式/多例模式, 一个bean定义对应多个对象实例parent:指定一个父bean(必须要有继承关系才行)
abstract: 将一个bean定义成抽象bean(抽象bean是不能实例化的),抽象类一定要定义成抽象bean,非抽象类也可以定义成抽象bean
🟤main方法测试
package com.ljq.spring01.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ljq.spring01.model.Student;
/**
* 测试
*
* @author 一麟
*
*/
public class Test {
public static void main(String[] args) {
// 获取spring.xml文件的上下文
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
// 用上下文获得ID为student的bean
Student stu = (Student) ctx.getBean("student");
System.out.println(stu);
}
}
运行结果:
②示例二(sbstract和parent的使用)
这里我们给student实体类安排一个父类(person):
package com.ljq.spring01.model;
/**
* 人物实体类
*
* @author 一麟
*
*/
public abstract class Person {
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
并让student实体类继承这个类:
package com.ljq.spring01.model;
/**
* 学生实体
*
* @author 一麟
*
*/
public class Student extends Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
}
然后就是配合spring.xml文件:
<!-- parent填的是父类bean的ID -->
<bean id="student" class="com.ljq.spring01.model.Student" parent="person">
<property name="name">
<value>一麟</value>
</property>
</bean>
<!-- ps:这个地方的abstract一定要填,而且要填true,因为person类是一个抽象类,abstract默认是false -->
<bean id="person" class="com.ljq.spring01.model.Person" abstract="true">
<property name="age">
<value>18</value>
</property>
</bean>
继续运行我们上面的test类的main方法:
public static void main(String[] args) {
// 获取spring.xml文件的上下文
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
// 用上下文获得ID为student的bean
Student stu = (Student) ctx.getBean("student");
System.out.println("学生名字:"+stu.getName());
System.out.println("学生年龄:"+stu.getAge());
}
运行结果:
③示例三(有参构造创建javaBean)
注意:这种方式我们一定在实体类中写好有参构造
这里我们在student类中加入有参构造:
public Student(String name) {
super();
this.name = name;
}
这种方式跟第二种方式是差不多的,只需要将bean中的property改为constructor即可,配置文件如下:
<bean id="student" class="com.ljq.spring01.model.Student" parent="person">
<!-- <property name="name">
<value>一麟</value>
</property> -->
<constructor-arg index="0">
<value>一麟</value>
</constructor-arg>
</bean>
这种构造方法是根据属性的下标来植入值的,我们也可以直接设置属性名字如:
<bean id="student" class="com.ljq.spring01.model.Student" parent="person">
<!-- <property name="name">
<value>一麟</value>
</property> -->
<!--
<constructor-arg index="0">
<value>一麟</value>
</constructor-arg>
-->
<constructor-arg name="name">
<value>一麟</value>
</constructor-arg>
</bean>
运行之前的main方法,运行结果如下:
④实例四(init-method的使用)
这种方式其实也很简单,就是在实体类中写一个初始化的方法,然后在容器中的bean直接配置即可。
首先,我们在student类中加入init方法:
/**
* 初始化方法,表示正常启动
*/
public void init() {
System.out.println("我是"+this.name+"大帅哥");
}
然后配置spring.xml文件:
<bean id="student" class="com.ljq.spring01.model.Student" parent="person" init-method="init">
<!-- <property name="name">
<value>一麟</value>
</property> -->
<!--
<constructor-arg index="0">
<value>一麟</value>
</constructor-arg>
-->
<constructor-arg name="name">
<value>一麟</value>
</constructor-arg>
</bean>
最后还是使用之前的main方法测试,结果如下:
⑤示例五(复杂属性-引入属性的使用ref)
这里我们先创建一个地址实体类方便测试address.java:
package com.ljq.spring01.model;
/**
* 地址实体类
*
* @author 一麟
*
*/
public class Address {
private String city;// 城市
private String street;// 街道
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public String toString() {
return "Address [city=" + city + ", street=" + street + "]";
}
}
并在student中加入address属性:
private Address address;//地址
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
spring.xml配置:
<!-- parent填的是父类bean的ID -->
<bean id="student" class="com.ljq.spring01.model.Student"
parent="person" init-method="init">
<property name="name">
<value>一麟</value>
</property>
<!-- <constructor-arg index="0"> <value>一麟</value> </constructor-arg> -->
<!-- <constructor-arg name="name"> <value>一麟</value> </constructor-arg> -->
<property name="address" ref="address" />
</bean>
<!-- ps:这个地方的abstract一定要填,而且要填true,因为person类是一个抽象类,abstract默认是false -->
<bean id="person" class="com.ljq.spring01.model.Person"
abstract="true">
<property name="age">
<value>18</value>
</property>
</bean>
<bean id="address" class="com.ljq.spring01.model.Address">
<property name="city">
<value>长沙</value>
</property>
<property name="street">
<value>天顶街道</value>
</property>
</bean>
main方法:
public static void main(String[] args) {
// 获取spring.xml文件的上下文
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
// 用上下文获得ID为student的bean
Student stu = (Student) ctx.getBean("student");
System.out.println("学生名字:" + stu.getName());
System.out.println("学生年龄:" + stu.getAge());
System.out.println("学生地址:" + stu.getAddress());
}
运行结果:
⑥示例六(集合、数组的使用)
不仅如此,spring容器还能够注入集合、数组。
直接在student类中声明属性:
private int[] arr;
private List list;
private Map map;
private Properties prop;
public Properties getProp() {
return prop;
}
public void setProp(Properties prop) {
this.prop = prop;
}
public int[] getArr() {
return arr;
}
public void setArr(int[] arr) {
this.arr = arr;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
修spring.xml配置文件:
<bean id="student" class="com.ljq.spring01.model.Student"
parent="person">
<!-- init-method="init" -->
<!-- <property name="name"> <value>一麟</value> </property> -->
<!-- <constructor-arg index="0"> <value>一麟</value> </constructor-arg> -->
<!-- <constructor-arg name="name"> <value>一麟</value> </constructor-arg> -->
<!-- <property name="address" ref="address" /> -->
<property name="list">
<list>
<value>湖南</value>
<value>湖北</value>
<value>意大利</value>
</list>
</property>
<property name="arr">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
<property name="map">
<map>
<entry key="A" value="a" />
<entry key="B" value="b" />
<entry key="C" value="c" />
</map>
</property>
<property name="prop">
<props>
<prop key="中国">Chain</prop>
<prop key="意大利">Italy</prop>
<prop key="美国">US</prop>
</props>
</property>
</bean>
main方法:
public static void main(String[] args) {
// 获取spring.xml文件的上下文
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
// 用上下文获得ID为student的bean
Student stu = (Student) ctx.getBean("student");
// System.out.println("学生名字:" + stu.getName());
// System.out.println("学生年龄:" + stu.getAge());
// System.out.println("学生地址:" + stu.getAddress());
System.out.println(Arrays.toString(stu.getArr()));
System.out.println(stu.getList());
System.out.println(stu.getMap());
System.out.println(stu.getProp());
}
运行结果:
⑦多配置文件的处理
当我们的工程存在多个模块需要配置多个配置文件的时候,一个一个获取上下文实属是不方便。
这个时候我们可以将各个模块全部集成到一个配置文件里面。
首先,我们将之前的spring.xml文件改为spring-a.xml假设这个文件负责a模块,然后重新创建一个新的spring.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 引入a模块的配置文件 -->
<import resource="spring-a.xml" />
</beans>
我们每次新增一个模块配置文件的时候只需要将其配置在总配置文件中即可,这个时候就能够解决多模块配置文件的问题了。
🏳🌈简化bean的获取
在我们之前的代码里面我们是这样获取bean对象的:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
Student stu = (Student) ctx.getBean("student");
这样写是存在问题的而且也不是方便,每次需要配置文件上下文的时候都需要new 一个
ClassPathXmlApplicationContext,使得我们的工程的性能损耗,这样会导致什么问题呢?
有没有想过如果我们的线程配合也在这个配置文件中,那么这个线程就会被启动两次,一次是web容器初始化启动的时候,另一次就是这个地方,被new的时候会被启动一次,就相当于被重新初始化了,这就是明显的冗余。
那我们要这么解决这个问题呢?
这个时候我们就选择不直接new ClassPathXmlApplicationContext,而是新建一个工具类实现ApplicationContextAware接口,实现之后就大大方便了我们获取ApplicationContext中的所有Bean,接下来我们来看看这个接口具体是使用。
ApplicationContextAware的使用:
package com.ljq.spring01.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 简化获取bean
*
* @author 一麟
*
*/
@Component
public class SpringBeanUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* spring通过注入的方式自动调用该方法将上下文设置好
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 根据bean的ID获取bean
*
* @param <T>
* @param id
* @return
*/
public static <T> T getBean(String id) {
Object object = null;
object = applicationContext.getBean(id);
return (T) object;
}
}
在这个类中我们通过实现ApplicationContextAware这个感知接口实现重写setApplicationContext方法,这个方法会被spring自动注入上下文,只需要在配置文件中加入
<!-- 配置获取bean的工具类 -->
<bean id="springBeanUtil"
class="com.ljq.spring01.util.SpringBeanUtil"></bean>
或者在工具类中也需要加入注册注解:
@Component
这样我们的工具类就搞定了。
下面是关于工具类的调用:main方法测试 :
new ClassPathXmlApplicationContext("spring.xml");
Student stu = (Student) SpringBeanUtil.getBean("student");
System.out.println(Arrays.toString(stu.getArr()));
System.out.println(stu.getList());
System.out.println(stu.getMap());
System.out.println(stu.getProp());