赞
踩
本篇博客简单介绍一下Spring FrameWork的相关概念。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sss</groupId> <artifactId>test_spring</artifactId> <version>1.0-SNAPSHOT</version> <properties> <encoding>UTF-8</encoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <scope>provided</scope> </dependency> <!-- Spring需要的依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- 日志需要的依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies> <build> <finalName>test_spring</finalName> <!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <pluginManagement> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
org.springframework.context.ApplicationContext
接口是我们常用的Spring IoC容器,负责实例化、配置、及装配bean的,它是org.springframework.beans.factory.BeanFactory
的子接口,也是我们通常所说的bean工厂。org.springframework.context.support.ClassPathXmlApplicationContext
进行我们的练习。Main.java
。package com.sss;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
}
}
resources
目录下新建applications.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
applications.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建一个字符串对象,对象名为myName,通过new String("sss")生成对象 -->
<bean id="myName" class="java.lang.String">
<constructor-arg value="sss" />
</bean>
</beans>
package com.sss; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml"); // 通过名字来获取bean对象 Object o = context.getBean("myName"); System.out.println(o.getClass()); System.out.println(o); } }
上述过程的原理如下图所示:
context.getBean("myName")
可以拿到我们需要的对象,对象的初始化过程我们再也不用参与了。Spring Bean容器:存放对象的一种技术实现,对象的生命周期由容器进行统一的管理。
Spring IoC容器可以管理一个或者多个bean,这些bean都是通过xml进行配置,这些Bean在Spring内部是通过BeanDefinition进行管理,每个bean对象都有一个BeanDefinition对象管理,管理包括但不限于如下的信息:
我们新建一个Book类,如下:
package com.sss; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Setter @Getter @ToString public class Book { private String name; private String author; public Book() {} public Book(String name) { this.name = name; } public Book(String name, String author) { this.name = name; this.author = author; } }
然后,我们再来新建一个BookStore类,如下:
package com.sss;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
@Setter
@Getter
@ToString
public class BookStore {
private String name;
private List<Book> books;
}
然后我么来修改applications.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="book1" class="com.sss.Book"> <property name="name" value="三国演义" /> <property name="author" value="罗贯中" /> </bean> <bean id="book2" class="com.sss.Book"> <constructor-arg name="name" value="西游记" /> <property name="author" value="吴承恩" /> </bean> <bean id="book3" class="com.sss.Book"> <constructor-arg name="name" value="水浒传" /> <constructor-arg name="author" value="施耐庵" /> </bean> <bean id="book4" class="com.sss.Book"> <constructor-arg index="0" value="红楼梦" /> <constructor-arg index="1" value="曹雪芹" /> </bean> <bean id="bookStore" class="com.sss.BookStore"> <property name="name" value="三味书屋" /> <property name="books"> <list> <ref bean="book1" /> <ref bean="book2" /> <ref bean="book3" /> <ref bean="book4" /> </list> </property> </bean> </beans>
最后,我们来修改一下Main.java
,如下:
package com.sss; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml"); // 通过名字来获取bean对象 Object book1 = context.getBean("book1"); System.out.println(book1); System.out.println(book1.getClass() + "\n"); Object book2 = context.getBean("book2"); System.out.println(book2); System.out.println(book2.getClass() + "\n"); Object book3 = context.getBean("book3"); System.out.println(book3); System.out.println(book3.getClass() + "\n"); Object book4 = context.getBean("book4"); System.out.println(book4); System.out.println(book4.getClass() + "\n"); // 通过类型获取对象:容器中只能有一个该类型的对象 Object bookStore = context.getBean(BookStore.class); System.out.println(bookStore); System.out.println(bookStore.getClass()); } }
注意事项:
package com.sss; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml"); // 通过类型获取对象:容器中只能有一个该类型的对象 Object book = context.getBean(Book.class); System.out.println(book); System.out.println(book.getClass()); } }
我们来修改一下applications.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: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">
<!--
扫描base-package的包,所有子包都会进行扫描包含Spring注解的类
-->
<context:component-scan base-package="com.sss.scan" />
</beans>
主要用到的注解如下:
@Controller
:做前端请求处理的类;@Service
:业务处理的类;@Component
:宽泛的概念,一般指组件;@Repository
:数据操作的类;@Configuration
:配置类,使用@Configuration+@Bean。我们先来使用一下@Controller注解:
package com.sss.scan;
import org.springframework.stereotype.Controller;
// @Controller和@Controller("Bean名称")
// @Controller使用类名首字母小写作为Bean名称
@Controller
public class LoginController {
}
package com.sss;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
Object o = context.getBean("loginController");
System.out.println(o);
System.out.println(o.getClass());
}
}
运行结果如下:
我们再来看一下@Service注解:
package com.sss.scan;
import org.springframework.stereotype.Service;
@Service
public class LoginService {
}
package com.sss;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
Object o = context.getBean("loginService");
System.out.println(o);
System.out.println(o.getClass());
}
}
我们来看一下@Configuration注解的使用:
package com.sss.scan; import com.sss.Book; import com.sss.BookStore; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; @Configuration public class Conf { @Bean public Book book1() { return new Book("三国演义", "罗贯中"); } @Bean("book2") public Book testBook1() { return new Book("水浒传", "施耐庵"); } @Bean public Book book3() { return new Book("西游记", "吴承恩"); } @Bean("book4") public Book testBook2() { return new Book("红楼梦", "曹雪芹"); } @Bean public BookStore bookStore1(Book book1, Book book2) { BookStore bookStore = new BookStore(); List<Book> books = new ArrayList<>(); books.add(book1); books.add(book2); bookStore.setName("三味书屋"); bookStore.setBooks(books); return bookStore; } @Bean("bookStore2") public BookStore testBookStore(@Qualifier("book3") Book book3, @Qualifier("book4") Book book4) { BookStore bookStore = new BookStore(); List<Book> books = new ArrayList<>(); books.add(book3); books.add(book4); bookStore.setName("新华书店"); bookStore.setBooks(books); return bookStore; } }
package com.sss; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml"); Object book1 = context.getBean("book1"); System.out.println(book1); System.out.println(book1.getClass()); Object book2 = context.getBean("book2"); System.out.println(book2); System.out.println(book2.getClass()); Object book3 = context.getBean("book3"); System.out.println(book3); System.out.println(book3.getClass()); Object book4 = context.getBean("book4"); System.out.println(book4); System.out.println(book4.getClass()); Object bookStore1 = context.getBean("bookStore1"); System.out.println(bookStore1); System.out.println(bookStore1.getClass()); Object bookStore2 = context.getBean("bookStore2"); System.out.println(bookStore2); System.out.println(bookStore2.getClass()); } }
Spring Bean的装配就是要使用Bean了。通常我们使用两个注解来实现Bean的装配:@Autowired
和@Resource
,二者的区别如下:
@Autowired
:Spring的注解,只能Spring框架使用;@Resource
:JDK的注解,是一种装配资源类的规范,Spring实现了这个规范,所以也可以使用该注解。我们先来看一下@Autowired注解的使用:
package com.sss.scan; import com.sss.Book; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; // @Controller和@Controller("Bean名称") // @Controller使用类名首字母小写作为Bean名称 @Controller @Getter @Setter @ToString public class LoginController { @Autowired private LoginService loginService; @Autowired private Book book1; }
package com.sss; import com.sss.scan.LoginController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml"); LoginController loginController = (LoginController) context.getBean("loginController"); System.out.println(loginController.getClass()); System.out.println(loginController); System.out.println(loginController.getLoginService()); System.out.println(loginController.getBook1()); } }
@Resource注解的使用方式和@Autowired一样,我们来看一下:
package com.sss.scan; import com.sss.Book; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.springframework.stereotype.Controller; import javax.annotation.Resource; // @Controller和@Controller("Bean名称") // @Controller使用类名首字母小写作为Bean名称 @Controller @Getter @Setter @ToString public class LoginController { @Resource private LoginService loginService; @Resource private Book book1; }
package com.sss; import com.sss.scan.LoginController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml"); LoginController loginController = (LoginController) context.getBean("loginController"); System.out.println(loginController.getClass()); System.out.println(loginController); System.out.println(loginController.getLoginService()); System.out.println(loginController.getBook1()); } }
除了上述两种装配方式,Spring中@Bean
注解修饰的方法参数会被自动装配。
Spring容器在初始化一个Bean的实例时,同时会指定该实例的作用域。Spring3为Bean定义了五种作用域,具体如下:
这里我们主要来看一下singleton和prototype:
我们修改一下applications.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.sss.Book" scope="singleton">
<property name="name" value="三国演义" />
<property name="author" value="罗贯中" />
</bean>
</beans>
我们修改一下Main.java
,如下:
package com.sss;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
Object book1 = context.getBean("book");
Object book2 = context.getBean("book");
System.out.println(book1 == book2);
}
}
可以看到,得到的两个Bean对象是同一个,我们修改一下Bean的作用域为prototype,如下:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="book" class="com.sss.Book" scope="prototype">
<property name="name" value="三国演义" />
<property name="author" value="罗贯中" />
</bean>
</beans>
可以看到返回的两个Bean对象不是同一个。
Spring容器可以管理singleton作用域Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁。
而对于prototype作用域的Bean,Spring只负责创建,当容器创建了Bean的实例后,Bean的实例就交给客户端代码管理,Spring容器将不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。
了解Spring生命周期的意义就在于,可以利用Bean在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,会在Bean被初始化后和被销毁前执行一些相关操作。
在Spring中,Bean的生命周期是一个很复杂的执行过程,我们可以利用Spring提供的方法定制Bean的创建过程。
当一个Bean被加载到Spring容器时,它就具有了生命,而Spring容器在保证一个Bean能够使用之前,会进行很多工作。Spring容器中Bean的生命周期流程如下图所示:
我们通过一个代码来看一下Bean的生命周期:
package com.sss; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class Lifecycle implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor, InitializingBean, DisposableBean { @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("BeanFactoryAware"); } @Override public void setBeanName(String s) { System.out.println("BeanNameAware"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("ApplicationContextAware"); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor before"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("BeanPostProcessor after"); return bean; } @Override public void destroy() throws Exception { System.out.println("DisposableBean"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean"); } }
我们来修改一下applications.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="book1" class="com.sss.Book"> <property name="name" value="三国演义" /> <property name="author" value="罗贯中" /> </bean> <bean id="book2" class="com.sss.Book"> <property name="name" value="西游记" /> <property name="author" value="吴承恩" /> </bean> <bean id="book3" class="com.sss.Book"> <property name="name" value="水浒传" /> <property name="author" value="施耐庵" /> </bean> <bean id="book4" class="com.sss.Book"> <property name="name" value="红楼梦" /> <property name="author" value="曹雪芹" /> </bean> <bean id="bookStore" class="com.sss.BookStore"> <property name="name" value="三味书屋" /> <property name="books"> <list> <ref bean="book1" /> <ref bean="book2" /> <ref bean="book3" /> <ref bean="book4" /> </list> </property> </bean> <bean id="lifeCycle" class="com.sss.Lifecycle" /> </beans>
最后我们来修改一下Main.java,如下:
package com.sss;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applications.xml");
}
}
运行结果如下:
IoC即Inversion of Control,即控制反转,不是什么技术,而是一种设计思想。在Java开发中,IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解IoC呢?理解好IoC的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就有正转),哪些方面反转了”。
DI,Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来的更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
IoC(控制反转):
DI(依赖注入):
IoC(控制反转)和DI(依赖注入)的理解:
Spring IoC的原理使用反射+ASM字节码技术。
反射的缺点:效率低,还有一些限制(无法获取方法传入参数名)。
Spring注册Bean、装载Bean的流程:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。