简介
Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,能够让编码变的更加简单,核心组件IOC容器和Aop面向切面编程。
spring优点:
- 方便解耦,简化开发
- 管理对象
- 集成其他框架
- Junit单元测试
- 方便进行事务操作
- 降低API开发难度
Spring核心特性:
依赖注入 (DI):
Inversion of Control (IoC) 控制反转是一个通用概念,可以用多种不同的方式表达。 依赖注入只是控制反转的一个具体例子。在编写复杂的 Java 应用程序时,应用程序类应尽可能独立于其他 Java 类,以增加重用这些类的可能性,并在单元测试时独立于其他类对其进行测试。 依赖注入有助于将这些类粘合在一起,同时保持它们的独立性。
依赖注入简单来说,例如,依赖,A 类依赖于B类。注入就意味着,B 类将被 IoC 注入到 A 类中。
面向切面编程(AOP)
跨越应用程序多个点的功能称为横切关注点,这些横切关注点在概念上与应用程序的业务逻辑分开。 有各种常见的好例子,包括日志记录、声明式事务、安全性、缓存等。
OOP 中模块化的关键单元是类,而 AOP 中模块化的单元是方面。 DI 可帮助您将应用程序对象彼此分离,而 AOP 可帮助您将横切关注点与它们影响的对象分离。
面向切面是一种思想,不是具体的框架,也不是具体的代码。
Spring体系结构图:
spring的官网:
Spring官方下载依赖jar包地址:
Spring核心知识点
SpringIOC
IOC(Inversion of Control) 控制反转是一种面对对象编程的设计原则,用于降低代码之间的耦合度。IOC容器主要作用就是创建对象和处理对象之间的依赖关系。
Bean的管理
- 使用spring创建对象
- 使用spring注入属性
创建对象
- 单独new方式---耦合度太高
每次单独new对象,没有实现统一管理对象,如果后期userDao的名称信息发生变化的情况下,需要改变的引用地方比较多,耦合度太高。
- 工厂模式---降低我们耦合度
概念:统一的管理和维护我们每个对象创建与使用的过程。
不需要自己new对象。
- 反射的方式
降低代码的-耦合度
反射创建对象
方法一:调用无参数构造器创建对象
- 利用无参构造器
- 给类添加一个无参构造器(缺省构造器)
- 配置bean元素
- 调用容器的getbean
public class Fast { public Fast() { System.out.println("spring创建对象方式一:通过无参构造方法"); } }
<!-- 使用无参构造器创建对象 id属性:bean的名称,要求唯一 class属性:类的全名 --> <bean id="Fast" class="Fast"></bean> @Test public void test01(){ ApplicationContext applicationContext= new ClassPathXmlApplicationContext("bean.xml"); }
输出:
方法二:调用有参构造方法来创建对象
public class Slow { private Integer id; private String name; public Slow(Integer id,String name){ //super(); this.id = id; this.name = name; System.out.println("spring创建对象方式2:通过有参构造方法"+"\n"+id+"-"+name); } }
<bean id="slow" class="Slow"> <!-- constructor-arg:是有参构造方法的使用标签 index:参数的索引,从 0 开始 name: 参数名 type:参数类型(区分开关键字和封装类 int 和 Integer) 此时的创建对象相当于:SomeBean2 bean=new SomeBean2(1,"alice"); --> <constructor-arg index="0" name="id" type="java.lang.Integer" value="1"/> <constructor-arg index="1" name="name" type="java.lang.String" value="alice"/> </bean>
@Test public void test01(){ ApplicationContext applicationContext= new ClassPathXmlApplicationContext("bean.xml"); }
输出:
方法三:使用静态工厂方法创建对象
通过调用类的静态工厂方法来创建对象
public class Fast { public Fast() { System.out.println("spring创建对象第3种方式:通过静态方法工厂创建"); } }
public class FastFactory { public static Fast getInstance(){ System.out.println("通过静态方法工厂创建对象"); return new Fast(); //相当于Fast.getInstance(); } }
<!-- 使用静态工厂方法创建对象 factory-method属性:指定一个静态方法,spring容器会调用这个静态方法来创建对象 --> <bean id="FastFactory" class="FastFactory" factory-method="getInstance"></bean> @Test public void test01(){ ApplicationContext applicationContext= new ClassPathXmlApplicationContext("bean.xml"); }
输出:
方法四:使用实例工厂方法来创建对象
调用对象的实例方法来创建对象
public class cost { public cost(){ System.out.println("通过实例工厂创建对象"); } }
public class costFactory { public cost getInstance(){ return new cost(); } }
<!--通过实例工厂创建对象 使用实例工厂方法来创建对象 factory-bean属性:指定一个bean的ID, factory-method属性:指定一个方法 spring容器会调用这个bean的对应的方法来创建对象。 costFactory factory=new costFactory(); cost cost=factory.getInstance();--> <bean id="factory" class="costFactory"></bean> <bean id="cost" factory-bean="factory" factory-method="getInstance"></bean> @Test public void test01(){ ApplicationContext applicationContext= new ClassPathXmlApplicationContext("bean.xml"); }
输出:
方法五:使用实例工厂创建对象
public class messageBean { public messageBean(){ System.out.println("通过实现FactoryBean接口实现对象的创建"); } }
public class bean implements FactoryBean<messageBean> { @Override public messageBean getObject() throws Exception { return null; } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return true; } }
<!--通过factorybean 来创建对象--> <bean id="factotybean" class="bean"></bean> @Test public void test01(){ ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml"); messageBean bean=applicationContext.getBean("factotybean", messageBean.class); }
输出:
DI依赖注入
spring容器通过调用对象提供的set方法或者构造器来建立依赖关系。
依赖关系的创建流程
spring 容器启动后读取配置文件
基于对象属性set方法进行注入
在Bean标签下 在定义一个属性<property>标签
- property元素:表示使用set方法来注入依赖关系
属性值包含特殊符号
1 把<>进行转义 < >
2 把带特殊符号内容写到CDATA
<!--<property name="bookName" value="<<武汉>>"></property> --> <property name="bookName"> <value><![CDATA[<<武汉>>]]></value> </property>
- name属性:指定属性值
- ref属性:指定属性值(是被注入的bean的ID)
<!-- property元素:表示使用set方法来注入依赖关系 name属性:指定属性值 ref属性:指定属性值(是被注入的bean的ID) <!--null值--> <property name="address"><null/></property> <!--属性值包含特殊符号 1 把<>进行转义 < > 2 把带特殊符号内容写到CDATA --> <property name="address"><value><![CDATA[<<上海>>]]></value> </property> -->
//注入属性 外部bean <bean id="dc1" class="ioc.dc"> <bean id="db1" class="ioc.db"> <property name="di" ref="dc1"></property> </bean> public class db { private IB di; private String name; public void setName(String name) { this.name = name; } public void setDi(IB di) { this.di = di; System.out.println("调用了方法"); } } public class di implements IB{ private String diname; public di(){ } public void setDiname(String diname) { this.diname = diname; } public void f1(){ System.out.println("注入bean的属性"); } }
/*test01 测试set方式的注入 * */ @Test public void test01(){ db db=abstractApplicationContext.getBean("db1",ioc.db.class); logger.info("依赖注入成功。"); }
输出:
注入属性---外部bean
//注入属性 外部bean <bean id="dc1" class="ioc.dc"> <bean id="db1" class="ioc.db"> <property name="di" ref="dc1"></property> </bean>
注入属性---内部bean
//注入属性---内部bean <property name="name" value="mysql"></property> <property name="di" > <bean id="di2" class="ioc.di"> <property name="diname" value="MYSQL AB"></property> </bean> </property> </bean>
利用构造器方式进行注入
在Bean标签下 在定义一个属性<constructor-arg >标签
constructor-arg 元素:构造器方式注入
name 指定参数列表名称,index 指定参数列表索引,参数的下标(从0 开始)
public class A { private B b; public A(){ System.out.println("A"); } public A(B b){ System.out.println("构造器方式注入"); this.b=b; } public void execute(){ System.out.println("成功注入"); b.f1(); } }
public class B { public B(){ System.out.println("对象被创建"); } public void execute(){ System.out.println("execute"); } public void f1(){ System.out.println("被成功注入"); } } <!-- constructor-arg 元素:构造器方式注入,其中,index 属性指定参数的下标(从0 开始) --> <bean id="b1" class="DI.B"></bean> <bean id="a1" class="DI.A"> <constructor-arg index="0" ref="b1"></constructor-arg> </bean>
/* * 构造器注入 * */ @Test public void test01(){ A a1=applicationContext.getBean("a1",A.class); a1.execute(); logger.info("构造器方式注入完成"); }
输出:
p名称空间注入
Xml头部引入P标签:
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </bean> 使用p标签注入属性: <bean id="Book" class="book" p:bookName="books" p:bookPrice="66"> </bean>
p名称注入也是调用了set 方法注入属性。
集合类型属性注入
1. 注入数组类型
2. 注入list集合类型
3. 注入Map集合类型属性
4. 注入set集合属性
public class stu { //四种类型属性 private String[] courses; private List<String> list; private Map<String,String> map; private Set<String> set; //学生所学多门课程 private List<course> courseList; public void setCourseList(List<course> courseList) { this.courseList = courseList; } public String[] getCourses() { return courses; } public void setCourses(String[] courses) { this.courses = courses; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public Set<String> getSet() { return set; } public void setSet(Set<String> set) { this.set = set; } }
<!--1 集合类型属性注入--> <bean id="stu" class="bean_stu.stu"> <!--数组类型属性注入--> <property name="courses"> <array> <value>体育</value> <value>数学</value> </array> </property> <!--list类型属性注入--> <property name="list"> <list> <value>张三</value> <value>小三</value> </list> </property> <!--map类型属性注入--> <property name="map"> <map> <entry key="soccer" value="足球"></entry> <entry key="basketball" value="篮球"></entry> </map> </property> <!--set类型属性注入--> <property name="set"> <set> <value>SQL</value> <value>Java</value> </set> </property>
@Test public void test01(){ AbstractApplicationContext abstractApplicationContext=new ClassPathXmlApplicationContext("bean2.xml"); stu stu=abstractApplicationContext.getBean("stu", bean_stu.stu.class); String str= JSONObject.toJSONString(stu); logger.info(str); }
输出
集合里设置对象类型的值
<!--创建多个course对象--> <bean id="course1" class="bean_stu.course"> <property name="cname" value="PHP"></property> </bean> <bean id="course2" class="bean_stu.course"> <property name="cname" value="C++"></property> </bean> <!--注入list集合类型,值是对象--> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property> </bean>
util标签注入
util :命名空间,用以区分。
- 先引入一个util名称空间,将集合类型的值配置成一个bean,借用引用的方式注入集合,set等
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> <!--1 提取list集合类型属性注入--> <util:list id="bookList"> <value>A</value> <value>B</value> <value>C</value> </util:list> <!--2 提取list集合类型属性注入使用 单例和多例--> <bean id="book" class="bean_stu.book" scope="prototype"> <property name="list" ref="bookList"></property> </bean> </beans>
- 使用util标签读取properties 文件的内容
classpath:按照类的路径来搜索
spring 容器会依据路径来找对对应的properties文件,然后读取该文件的内容到properties
<util:properties id="config" location="classpath:config.properties"></util:properties>
- 注入spring表达式
<bean id="sp1" class="res.SpelBean"> <property name="name" value="#{vb1.name}"></property> <property name="age" value="#{vb1.age}"></property> <property name="city" value="#{vb1.city[1]}"></property> <property name="score" value="#{vb1.score.english}"></property> <property name="pageSize" value="#{vb1.pageSize}"></property> </bean>
bean的常见属性
作用域
作用域 |
描述 |
singleton |
在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。 |
prototype |
每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。 |
request |
每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。 |
session |
同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。 |
application |
限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。 |
<!--scope 属性: 用来配置作用域,缺省值为 singleton(即一个bean只能创建一个实例) 如果值为prototype(即一个bean会创建多个实例)--> <bean id="s2" class="cost" scope="prototype"></bean> <bean id="s4" class="cost" scope="singleton"></bean> <bean id="s5" class="cost" scope="session"></bean> <!-- A bean definition with singleton scope --> <bean id="..." class="..." scope="singleton"> <!-- collaborators and configuration for this bean go here --> </bean>
生命周期
生命周期:初始化,分配资源 销毁 释放资源。
<!--生命周期:初始化,分配资源 销毁 释放资源。 init-method属性:指定初始化方法: destroy-method属性:指定销毁方法 不过如果作用域为单例时,销毁方法才执行。 lazy-init属性:指定是否延迟加载,如果为TRUE,表示延迟加载,容器不会直接创建bean实例。 --> <bean id="messageBean1" class="init.messageBean" init-method="init" destroy-method="destroy" lazy-init="true"> </bean> <bean id="ExampleBean1" class="init.ExampleBean" init-method="init" destroy-method="destroy" lazy-init="true"></bean> 自动装配
自动装配指的是spring容器依据某种规则,自动建立对象之间的依赖关系
(默认情况下,容器不会自动装配。)可以通过指定autowire属性来告诉容器进行自动装配(容器实际上还是通过调用set方法或者构造器来完成依赖关系的建立。)
- autowire属性: 自动装配,属性有三个值
- byName :容器依据属性名来查找对对应的bean,然后调用对应的set方法来完成注入。如果找不到对应的bean,注入为null。不可能找到多个符合条件。
- byType:容器依据属性类型来查找对应的bean,然后调用对应的set方法来完成注入。如果找不到对应的bean,注入null,有可能找到多个符合条件的bean,此时会 出错。
- constructor: 与byType类似,不同的是调用对应的构造器的来完成注入。
<bean id="wt" class="res.Waiter"></bean> <bean id="res1" class="res.Restaurant" autowire="byType"> </bean>
SpringBean的注解形式
注解:注解是JDK5中推出的新特性,代码的特殊标记。
Bean的管理操作方式
1. 基于XML方式实现
2. 基于注解方式实现
注解可以使用在类、方法、属性上面。
使用注解的目的,简化xml的配置方式。
配置组件扫描
base-package属性:指定要扫描的包名,spring容器会扫描该包及其子包下面的所有的类,如果该类前面有特定的注解。
(比如@Component),则spring容器会将其纳入容器进行管理(相当于这儿配置了一个bean元素)
通过注解来指定作用域
<context:component-scan base-package="somebean"></context:component-scan>
自动扫描的注解标记
Spring提供的常用注解
- @Component 将对象注入Spring容器中
- @Service 注入业务逻辑对象
- @Controller 控制器类
- @Repository 注入dao对象
以上该四个注解底层都是基于@Component注解封装的,只是用于区分。
部分其他注解
@Autowired @Qualifier
- 该注解支只持setter方式和构造器方式注解。当采用set方式注入时,可以将@Autowired添加到set方法前面,如果不使用@Qualifier,则容器会使用byType方式来注入,有可能出错。
- 使用@Qualifier 明确要注入的对象类型id的bean的ID
- 也可以直接将两个注解直接添加到属性前面,这样利用Java的反射机制,即使没有set方法,也可以。不过只会赋值,不会进行其他操作。
- 当采用构造器注入时,可以将该注解添加到对应的构造器前面。
@Component("res") public class res { /* @Autowired @Qualifier("wt3")*/ private Waiter wt; public Waiter getWt() { return wt; }
@Autowired public void setWt(//注入进来的对象,类似于byName的属性。 @Qualifier("wt3") Waiter wt) { this.wt = wt; System.out.println("测试:服务员"); } public res(){ System.out.println("res()"); } }
@Resource
只支持set方式注入
可以将该注解添加属性前,使用name属性指定要注入的bean的ID,(不指定会按照byType方式进行注入)
可以将该注解添加到属性前。
@Component("bar") public class Bar { private Waiter wt; public Waiter getWt() { return wt; } @Resource(name = "wt3") public void setWt(Waiter wt) { this.wt = wt; } public Bar(){ System.out.println("11"); } }
@Value注解
可以使用该注解来注入基本类型的值
也可以使用该注解来使用spring表达式
该注解可以添加到属性前,或者添加到对应的set方法前。
@Value("#{config.pagesize}")
private String pageSize;
@Configuration 注解
作为配置类,替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = { "bean"})
public class SpringConfig {
}
注解实例
@Component("b1") @Scope("singleton")//单例和多例 @Lazy(true)//延迟加载 public class bean { @Value("#{config.pagesize}") private String pageSize; @Value("alice") private String name; @PostConstruct //初始化方法 public void init(){ System.out.println("初始化"); } @PreDestroy //销毁方法 public void destroy(){ System.out.println("销毁"); } public bean(){ System.out.println("bean()"); } @Bean(name="Bean") public bean getBean(){ bean bean = new bean(); System.out.println("调用方法:"+bean); return bean; } }