Spring框架总结
Spring的设计理念
- spring 是一个集众多设计模式(工厂模式、单例模式、适配器模式…)于一身的开源轻量级框架。
- 初衷不是创造轮子,而是使现用的轮子更好的运转,致力于整合现有的框架技术(mybatis、struts…)。
Spring作用
- 接管项目中组件(dao、service、action)的创建、销毁和生产。
- 通过依赖注入降低耦合、AOP编程方式简化java开发,解放生产力。
Spring核心思想
- IOC(Inversion of Control):控制权利反转,把手动创建对象的权利交给框架
DI(Dependency Injection): 依赖注入 ,由IOC容器维护对象之间的依赖关系 - AOP(Aspect-Oriented Programming):面向切面编程
Spring体系结构
Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入。下面的部分对在 Spring 框架中所有可用的模块给出了详细的介绍。
Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。
核心容器
核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
spring-core模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
context模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。spring-context-support提供了对第三方库集成到Spring上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。。
数据访问/集成
数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
JDBC 模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。
ORM 模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。
OXM 模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
JMS 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。。
事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。
(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细)
Web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。
Web-MVC 模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。
Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
Web-Portlet 模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。
其他
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
AOP 模块提供了面向方面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
Spring开发流程
- step1 引入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
step2 引入spring配置文件
名称:任意,一般为applicationContext.xml
位置:项目根目录下,一般放在resources下step3 开发类对象
public class UserDaoImpl implements UserDao {
@Override
public void add(User user) {
System.out.println("Dao 添加用户成功~~~~~~~");
}
@Override
public void queryAll() {
System.out.println("查询所有用户成功~~~~~~~~~");
}
}
- step4 在配置文件注册bean
<bean id="userDao" class="cn.gjxblog.dao.UserDaoImpl" />
- .step5 测试
Spring容器
Spring容器(IOC容器)是Spring框架的核心,容器将创建对象,把它们连接在一起,配置它们,并管理它们的整个生命周期从创建到销毁,Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为Spring Beans。
Spring容器分类
- BeanFactory
这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持 - ApplicationContext
注:ApplicationContext容器包括BeanFacotry容器所有功能,且添加了许多功能。一般开发使ApplicationContext容器
ApplicationContext(I)
常用的接口实现:
- FileSystemXmlApplicationContext
该容器从从完整路径下加载xml文件已被定义的 bean - ClassPathXmlApplicationContext
该容器从从类路径下加载xml文件已被定义的 bean - WebXmlApplicationContext
该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean
- FileSystemXmlApplicationContext
bean对象
bean对象是被实例化,组装,并通过springIoc容器所管理的对象 ,是构成应用程序的支柱。
bean类型,生命周期
bean类型 | 创建时间 | 销毁时间 |
---|---|---|
单例 | 工厂启动时 | 工厂关闭时 |
多例 | 每次使用时 | jvm负责销毁 |
<!--单例 默认-->
<bean class="ioc.UserServiceImpl" id="userService" scope="singleton">
<!--多例-->
<bean class="ioc.UserServiceImpl" id="userService" scope="prototype">
spring配置bean对象方式
- 基于Xml的配置文件
- 基于注解的配置*
依赖注入
Spring依赖注入方式
1 set注入
public class UserServiceImpl implements UserService {
private UserDao userDao;
//1 set注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser(User user) {
System.out.println("---业务逻辑---");
userDao.add(user);
}
}
- applicationContext.xml
<!--配置userDaoImpl对象-->
<bean id="userDao" class="cn.gjxblog.dao.UserDaoImpl" />
<!--配置UserServiceImpl对象 set注入-->
<bean id="userService" class="cn.gjxblog.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
2 构造方法注入
public class UserServiceImpl implements UserService {
private UserDao userDao;
//2 构造方法注入
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser(User user) {
System.out.println("---业务逻辑---");
userDao.add(user);
}
}
- applicatonContext.xml
<!--配置userDaoImpl对象-->
<bean id="userDao" class="cn.gjxblog.dao.UserDaoImpl" />
<!--配置UserServiceImpl对象 构造方法注入-->
<bean id="userService" class="cn.gjxblog.service.impl.UserServiceImpl">
<constructor-arg name="userDao" value="userDao"/>
</bean>
3 自动装配 autowire
byName
根据注入的属性名与配置文件中的id匹配,一致则注入,注入失败抛NPE
<bean id="userService" class="包名.UserServiceImpl" autowire="byName"></bean>
byType
根据注入的属性类型,与配置文件中的类型匹配,一致则注入,失败则抛NPE
<bean id="userService" class="包名.UserServiceImpl" autowire="byType"></bean>
Spring基于注解的配置
由于Spring采用xml配置,一旦项目中存在大量bean对象,书写和维护都比较麻烦,所以有为了简化开发,提高效率,从Spring2.5后可以采用注解来实例化bean,DI….。
注解开发前置条件
当我们使用注解时,必须先在容器中声明
如@Autowired注解,必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor的Bean:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
如果使用的注解过多,这样声明未免过于繁琐,Spring为我们提供了2种方法来解决
方法1:配置文件引入
<context:annotation-config/>
方法2(推荐):开启包扫描,<context:component-scan/>
该配置项其实也包含了自动注入上述processor的功能,因此当使用<context:component-scan/>
后,即可将<context:annotation-config/>
省去。
<!-- 开启包扫描 package:将要扫描的包-->
<context:component-scan base-package="cn.gjxblog.service,cn.gjxblog.action"/>
常注解元素及属性
元素 | 修饰范围 | 作用 | 属性 |
---|---|---|---|
@Component | 类 | 对当前组件实例化 | [value]:用来指定当前创建的实例在工厂中的唯一标识 |
@Repository | 类 | 对当前DAO组件实例化 | [value] |
@Service | 类 | 对当前Service组件实例化 | [value] |
@Controller | 类 | 对当前Action组件实例化 | [value] |
- | - | - | - |
@scope | 类 | 决定当前组件是否为单例 | [value]: singleton/prototype |
- | - | - | - |
@Autorwired | 属性 | 注入值:根据类型注入 | - |
@Qualifier | 属性 | 注入值:根据组件唯一标识注入 | [value]:组件在工厂中的唯一标识 |
@Resource | 属性 | 注入值:先根据name寻找ID注入,再根据类型注入 | [name]:默认属性名 |
AOP
AOP是对OOP的补充和完善,OOP引入封装,继承,多态的概念来建立一种对象层次的结构,用以模拟公共行为的一个集合。
当我们需要为分散的对象引入公共的行为的时候,OOP则显的无能为力。
AOP技术则恰恰相反,它利用一种为’横切的技术’,破解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用的模块,并将其名为”Aspect”。
AOP术语
项 | 描述 |
---|---|
通知(Advice) | 处理目标方法以外的操作都称之为通知 |
切入点(PointCut) | 要为那些类中的那些方法加入通知 |
切面(Aspect) | 通知+切入点 |
通知的类型
Spring方面可以使用下面提到的5种通知工作
通知 | 类 | 描述 |
---|---|---|
前置通知 | MethodBeforeAdvice | 在目标方法执行之前,执行通知 |
返回后通知 | AfterReturningAdvice | 目标方法正常结束时,执行通知。 |
后置通知 | AfterAdvice | 目标方法无论是否发生异常,都会执行该输出 |
环绕通知 | MethodInterceptor | 在目标方法调用之前和之后,执行通知 |
异常通知 | ThrowsAdvice | 如果目标方法抛出异常,执行通知。 |
切入点表达式
- execution(返回值 包.类.方法(参数表))
使用原则:切入尽量精确,避免不必要的切入
eg1. execturion(* cn.gjxblog.service.impl.UserServiceImpl.*(..))
返回值:任意
包:cn.gjxblog.service.impl
类:UserServiceImpl
方法:任意
参数:任意 - within 针对方法的切入点表达式
eg2. within(cn.gjxblog.service.impl.UserServiceImpl)
效果等效于eg2,都是将通知加在UserserviceImpl下的所有方法
Spring中AOP开发步骤
基于xml配置文件开发
- 开发通知类并实现相应的通知接口
@Aspect
public class LogAdvice implements MethodBeforeAdvice,MethodInterceptor,AfterReturningAdvice {
/**
* 前置通知
* @param method 目标类中当前调用的方法对象
* @param args 当前调用方法的参数
* @param target 目标对象
*/
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置通知:目标类方法执行前执行该方法");
}
/**
* 环绕通知
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("前置通知");
System.out.println("目标类:"+invocation.getThis());
System.out.println("调用的方法:"+invocation.getMethod());
invocation.proceed();//执行目标方法
System.out.println("后置通知");
return null;
}
/**
* 返回后通知
* @param returnValue 目标方法返回值,如果方法是void,则返回为null
* @param method 目标方法
* @param args 方法参数表
* @param target 目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("返回后通知:目标类方法正常结束后执行该方法");
}
}
- xml中配置相应通知
<!--AOP配置-->
<aop:config>
<!--配置切入点 expression:切入点表达式-->
<aop:pointcut id="pc" expression="within(cn.gjxblog.service.impl.UserServiceImpl)"/>
<!--组装切面 切入点+通知 -->
<aop:advisor advice-ref="logBeforeAdvice" pointcut-ref="pc"/>
</aop:config>
基于注解开发
1.引入相应的配置
<!--开启代理标识,在程序运行期,动态创建目标对象代理对象 -->
<aop:aspectj-autoproxy/>
2.开发通知类
@Component
//切面注解,声明这个类为切面
@Aspect
public class CacheAdvice {
//切入点注解,定义一个切入点
@Pointcut("execution(* cn.gjxblog.service.impl.UserServiceImpl.*(..))")
public void point(){}
@Before(value = "point()")
public void before(){
System.out.println("前置通知:目标类方法执行前执行该方法");
}
@AfterReturning("within(cn.gjxblog.service.impl.UserServiceImpl)")
public void afterReturn(){
System.out.println("返回后通知:目标类方法正常结束后执行该方法");
}
@After("point()")
public void after(){
System.out.println("后置通知,方法无论是否发生异常,都会执行该输出");
}
@AfterThrowing(value = "point()",throwing = "e")
public void afterThrow(Exception e){
System.out.println("异常通知:如果目标方法执行发生异常,执行该输出"+e);
}
@Around("point()")
public Object around(ProceedingJoinPoint pjp){
Object result = null;
System.out.println("环绕通知--前置通知");
Object[] args = pjp.getArgs();
if(null != args && args.length >0) {
for (Object arg : args) {
System.out.println(arg.toString());
}
}
System.out.println("目标类"+pjp.getTarget());
System.out.println("方法签名"+pjp.getSignature());
try {
result = pjp.proceed();//执行目标对象的方法
System.out.println("环绕通知--后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
}
IOC容器创建复杂对象
- 简单对象:含有无参构造方法,可以直接通过new创建的对象
可以通过xml配置bean,或者组件注解,直接交给工厂管理 - 复杂对象,不含无参构造方法,不能直接通过new创建的对象,不能直接交给工厂管理
如:抽象类,接口
创建步骤
- 创建一个类,并实现FactoryBean接口,泛型为复杂对象类型
- 在xml文件注册bean,或者使用注解元素
@Component
public class CalendarFacotyBean implements FactoryBean<Calendar> {
/**
*
* @return 返回创建的对象
* @throws Exception
*/
@Override
public Calendar getObject() throws Exception {
Calendar instance = Calendar.getInstance();
return instance;
}
/**
* @return 返回创建对象的类型
*/
@Override
public Class<?> getObjectType() {
return Calendar.class;
}
/**
* 创建对象是否为单例
* @return true 单例
*/
@Override
public boolean isSingleton() {
return false;
}
}
其他
基于注解的Spring测试类
- 前置条件:引入Spring-test模块和Junit4.12以上版本
//RunWith 注解指定JUnit使用的单元测试执行类
@RunWith(SpringJUnit4ClassRunner.class)
//指定Spring配置文件所在的路径,可以同时指定多个文件
@ContextConfiguration("/applicationContext.xml")
public class TestBean {
@Autowired
private UserService userService;
@Test
public void testUserService(){
userService.addUser(new User());
}
}