Spring
1.简介
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJE完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性,可测试性、和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
也就是从实际开发中抽取出开的框架,找到可以通用的步骤,把特定的部分空出开,留给开发者自主操作,这样也就提高了开发效率。但是Spring的优点不仅限于此,Spring独立于各种应用服务器,基于Spring开发的应用,可以做到Write Once,Run Anywhere。还有Spring的IOC容器,提高了组件之间的解耦。AOP支持允许将一些通用的任务、事务、日志等进行集中式管理,提高了代码的复用性。ORM和DAO与第三方持久层框架进行良好整合,简化了底层数据库的访问。Spring还有高度的开放性,因为这后面也就产生了各种各样的Spring升级版。
Spring框架图:
ORM:(Object Relation Mapping)对象关系映射,是一种程序设计技术,用于实现面向对象语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在里使用的“虚拟对象数据库”。如今已有很多免费和付费的ORM产品,而有些程序员更倾向于创建自己的ORM工具。
OXM:Object/XML Mapping 就是Java对象和XML文档之间的映射。
ORM:Object Relation Mapping 是一种程序设计技术,用于面向对象编程语言里不同类型系统的数据之间的转换。
JMS:Java Message Service 是一个Java平台中关于消息中间件的API,用于两个应用程序之间,或分布式系统中发送消息,进行异步通讯。JMS是与具体平台无关的API,绝大多数的中间件都是支持的。
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
总括:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
Spring-IOC
1.控制反转
控制反转也就是控制权的交换,在普通的程序中,我们是直接在对象的内部通过new来创建对象,程序主动去创建对象。在IOC容器中,对象的创建和调用都是由Spring管理,IOC容器来创建和控制对象,不再使用new。这样想就是IOC控制住了对象。
传统程序中:传统程序,就是我们自己在对象中主动去获取依赖的对象。
有了IOC之后:IOC帮我们找到并且创建项目所依赖的对象。
2.IOC和DI
控制反转和依赖注入,其实是同一个概念的不同角度的描述,控制反转的描述。依赖注入,是组件之间的依赖关系,是由容器在运行时决定的,这样说也可以理解成,DI包含于IOC,容器动态的将依赖关系注入到组件中,DI也可以认为是实现IOC的一种方式。
3.IOC的目的
目的就是降低组件之间的耦合度:
4.IOC的实现方式
控制反转的底层实现原理:工厂设计模式,Java反射,XML解析。
(1)BeanFactory:IOC容器的基本接口,不供给开发者使用,加载文件时不会创建对象,在获取时才会进行创建。
(2)ApplicationContext:Beanfactory的子接口,提供更多更强大的功能,加载配置文件时,就会把在配置文件中的对象进行创建。
4.简单体现
(1)导jar包(必须要有commons-logging)
创建User实体
public class User {
private String name;
public User(){
System.out.println("...创建成功...");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void add(){
System.out.println("...添加成功...name:"+name);
}
}
配置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="user" class="entity.User">
<property name="name" value="张三"/>
</bean>
</beans>
Test
@Test
public void Test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
System.out.println(user);
user.add();
User user1 = context.getBean("user",User.class);
System.out.println(user1);
user1.add();
}
这里的user和user1,均是由Spring容器创建的,对应的name属性也是Spring设置的。
控制反转:这里的对象是由Spring创建,而不是和传统程序一样,由程序本身new出一个对象,对象的创建的权利交给了Spring,程序变成了对象的接受者。
5.IOC创建对象
5.有参构造方法
//无参-见上
User_1
public class User_1 {
private String name;
public User_1(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addUser(){
System.out.println("...添加成功...name:"+name);
}
}
XML
<!-- Test2-->
<!-- <bean id="user_1" class="com.spring.demos.entity.User_1">-->
<!--<!– 参数名注入–>-->
<!-- <constructor-arg name="name" value="李四"/>-->
<!-- </bean>-->
<bean id="user_1" class="com.spring.demos.entity.User_1">
<!-- 参数类型注入-->
<constructor-arg type="java.lang.String" value="李五"/>
</bean>
Test
@Test
public void Test2(){
User_1 user_1 = (User_1) context.getBean("user_1");
user_1.addUser();
}
输出:
6.Spring配置
6.1 设置别名
用alias标签为bean设置别名,一个bean可以有多个别名
(也可以认为是,一个类在不同场景下应用时,不同的名字)
<alias name="user_1" alias="userNew"/>
6.2 Bean的配置
<!-- bean就相当于由Spring控制的是Java对象-->
<!-- id 是bean的标识符,是惟一的,如果没有设置id,会默认使用name,如果id和name同时存在,name就相当于是别名的作用-->
<!-- name里可以设置多个别名,可以用空格,都好,分号隔开-->
<!-- 如果id,name均没有配置,可以使用getBean(XX.class)来获取对象-->
<!-- class中是全类名-->
<bean id="user" name="user2 user3,user4;user5" class="com.spring.demos.entity.User">
<property name="name" value="张三"/>
</bean>
6.3 获取其他人的XML
import获取
<import resource="{path}/beans.xml"/>
7.依赖注入(DI)
所谓依赖注入,是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。
Dependency Injection 分为两种,设值注入,构造注入。
7.1构造注入:
见前构造方法那
7.2Set注入
被注入的属性必须要有set方法,并且set的方法名要由set+属性首字母大写,如果属性是boolean类型,要有is方法。
Student类:
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = 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 List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
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 String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public void show(){
System.out.println("name="+ name
+ ",address="+ address.getAddress()
+ ",books="
);
for (String book:books){
System.out.print("<<"+book+">>\t");
}
System.out.println("\n爱好:"+hobbys);
System.out.println("card:"+card);
System.out.println("games:"+games);
System.out.println("wife:"+wife);
System.out.println("信息:"+info);
}
}
XML配置:
<bean id="adr" class="com.spring.demos.entity.Address">
<property name="address" value="SD"/>
</bean>
<bean id="student" class="com.spring.demos.entity.Student">
<property name="name" value="张六"/>
<property name="address" ref="adr"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>12345</value>
<value>西游记</value>
<value>水浒传</value>
<value>红楼梦</value>
<value>三国演义</value>
</array>
</property>
<!-- Map注入-->
<property name="card">
<map>
<entry key="schoolCard" value="2020000001"/>
<entry key="IDCard" value="20210909"/>
</map>
</property>
<!-- Set注入-->
<property name="games">
<set>
<value>PUMG</value>
<value>LoL</value>
</set>
</property>
<!-- 注入null-->
<property name="wife"><null/></property>
<!-- List注入-->
<property name="hobbys">
<list>
<value>看电影</value>
<value>钓鱼</value>
<value>吃东西</value>
</list>
</property>
<!-- Properties注入-->
<property name="info">
<props>
<prop key="学号">20210909</prop>
<prop key="性别">男</prop>
<prop key="别名">小六</prop>
</props>
</property>
</bean>
Test:
7.3 P注入 和C注入
这里的p、c注入都需要在头文件里添加约束:
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
这里的p就是Properties,直接注入属性:
<bean id="user" name="user2 user3,user4;user5" class="com.spring.demos.entity.User" p:name="zhangzhang"/>
c就是Constructor,构造器注入:
<bean id="user" name="user2 user3,user4;user5" class="com.spring.demos.entity.User" c:name="zhangzhang"/>
8.Bean的作用域
8.1Single(单例模式)
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域,就是默认是单例模式。
public void Test(){
User user = (User) context.getBean("user");
System.out.println(user);
user.add();
User user1 = context.getBean("user",User.class);
System.out.println(user1);
user1.add();
System.out.println(user==user1);
}
8.2 Prototype(原型模式)
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
XML配置
<bean id="user_1" class="com.spring.demos.entity.User_1" scope="prototype">
<!-- 参数名注入-->
<constructor-arg name="name" value="李四"/>
</bean>
Test:
public void Test2(){
User_1 user_1 = (User_1) context.getBean("user_1");
System.out.println(user_1);
User_1 user_11 = (User_1) context.getBean("user_1");
System.out.println(user_11);
System.out.println(user_11==user_1);
user_1.addUser();
}
输出:
8.3 Request
当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
<bean id="user_1" class="com.spring.demos.entity.User_1" scope="request">
<!-- 参数名注入-->
<constructor-arg name="name" value="李四"/>
</bean>
针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
8.4Session
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
<bean id="user_1" class="com.spring.demos.entity.User_1" scope="session">
<!-- 参数名注入-->
<constructor-arg name="name" value="李四"/>
</bean>
9.Bean的生命周期
Bean实例生命周期的执行过程如下:
-
Spring对bean进行实例化,默认bean是单例;
-
Spring对bean进行依赖注入;
-
如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法;
-
如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来;
-
如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
-
如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization方法将被调用;
-
如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
-
如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法将被调用;
-
此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
-
若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;
10.Bean的自动装配
自动转配,就是Spring在上下文中查找某个bean依赖的bean
Bean的装配机制有三种:
- 在xml中显式配置;
- 在java中显式配置;
- 隐式的bean发现机制和自动装配。
自动装配时,会扫描组件,自动发现向下文中所创建的Bean,找到之后就是自动注入。
XML配置:
<bean id="UserDao" class="com.spring.demos.dao.UserDaoImp"/>
<bean id="UserService" class="com.spring.demos.service.UserServiceImp">
<!-- 这里是set注入 name中是set方法后面的首字母小写后的-->
<!-- 这里是引用的bean里的UserDao,引用用的是ref,而不是value -->
<property name="userDao" ref="UserDao"/>
</bean>
public class UserDaoImp implements UserDao{
@Override
public void addUser() {
System.out.println("...添加成功....");
}
}
public class UserServiceImp implements UserService{
private UserDaoImp userDaoImp = new UserDaoImp();
@Override
public void setUserDao(UserDaoImp userDaoImp) {
this.userDaoImp = userDaoImp;
}
@Override
public void getUserDao() {
userDaoImp.addUser();
}
}
Test:
public void Test1(){
UserServiceImp userServiceImp = (UserServiceImp) context.getBean("UserService");
userServiceImp.getUserDao();
}
10.1byName
autowire byName (按名称自动装配) 由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误。(由方法中的参数名称来查找bean里的对象)
<bean id="student" class="com.spring.demos.entity.Student" autowire="byName">
按照 Bean 名称自动装配存在错误装配 JavaBean 的可能,如果配置文件中定义了与需要自动装配的 JavaBean 的名称相同而类型不同的 JavaBean ,则会错误的注入不同类型的 JavaBean。
10.2 byType
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
<bean id="student" class="com.spring.demos.entity.Student" autowire="byType">
无论是按照 Bean 的类型,还是按照 Bean 的名称,自动装配有时候都会出现无法自动装配的情况。例如在配置文件中再添加一个 User 类的实现对象,byType 自动装配类型会因为无法自动识别装配那一个JavaBean 而抛出 org.springframework.beans.factory.UnsatisfiedDependencyException 异常。要解决此类问题,只能通过混合使用手动装配来指定装配哪个 JavaBean 。
10.3使用注解
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
@Autowired XXX 代替set方法;
@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
@Qualifier不能单独使用。
11.使用注解开发
需要导包;
进行约束:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
8.1.Bean的实现
我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
<!--指定注解扫描包-->
<context:component-scan base-package="com.kuang.pojo"/>
2、在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
public String name = "张张";
}
3、测试
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
8.2.属性注入
使用注解注入属性
1、可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("张张")
// 相当于配置文件中 <property name="name" value="秦疆"/>
public String name;
}
2、如果提供了set方法,在set方法上添加@value(“值”);
@Component("user")
public class User {
public String name;
@Value("张张")
public void setName(String name) {
this.name = name;
}
}
8.3.衍生注解
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Controller:controller层
- @Service:service层
- @Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配了!
8.4.自动装配注解
在Bean的自动装配已经讲过了,可以回顾!
8.5.作用域
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")
@Scope("prototype")
public class User {
@Value("张张")
public String name;
}
8.6.小结
XML与注解比较
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践
- xml管理Bean
- 注解完成属性注入
- 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>
作用:
-
进行注解驱动注册,从而使注解生效
-
用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
-
如果不扫描包,就需要手动配置bean
-
如果不加注解驱动,则注入的值为null!
总结
SpringIOC控制反转,指就是说原先在对象中要使用另一个对象就必须要显式的去创建另一个对象的实例,例如通过构造方法或者是调用工厂方法(工厂方法最终也是需要new,因为这是Java创建对象所必须的)来获得。而Spring提供了IOC容器来帮我们生成所需要的对象。也就是说在我们原先的对象中有用到其他对象的地方Spring会帮我们来注入。不用我们再去考虑这些问题。
DI主要就是配置注入(通过XML文件),另一种就是注解