1、spring基本概念
spring官方文档:
https://docs.spring.io/spring-framework/docs/current/reference/html/index.html
Spring是为了构建企业应用的轻量级框架。然而,Spring是模块化的,允许你只是使用其中的一部分,不需要引入其他的。你可以在任何web框架上使用IoC容器,也可以只使用Hibernate集成代码或JDBC抽象层。Spring框架支持声明式事务管理、通过RMI远程访问、WebService和持久化数据。它还提供全功能的MVC框架,和将AOP应用到你的系统。
Spring的优点:
(1)方便解耦,简化开发
Spring就是一个大工厂,可以将所有对象创建和依赖的关系维护,交给Spring管理。
(2)AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
(3)声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程。
(4)方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序。
(5)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。
(6)降低JavaEE API的使用难度
Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。
2、spring基本结构
Spring框架由组织为约20个模块的功能组成。这些模块分为核心容器,数据访问/集成,Web,AOP(面向方面的编程),检测,消息传递和测试,如下图所示。
2.1 核心容器
核心容器由spring-core、spring-beans、spring-context、spring-context和spring-expression 5个模块构成。
spring-core和spring-beans模块为框架提供最基本的功能,包括IoC和依赖注入。BeanFactory是一个工厂的实现类。它可以免除用户手动创建单例类并且对象之间解耦。
context(spring-core)模块基于spring-beans和BeanFactory两个模块,允许以框架方式访问对象,这类似于JNDI。Context模块继承了Beans模块,并支持国际化、事件传播、资源加载等。Context模块支持Java EE特性,如EJB、JMX、远程访问。spring-context接口是Contet模块的重点。spring-context-support提供将第三方库集成到Spring的功能,如缓存(EhCache,JCache)和任务调度(CommonJ,Quartz)等。
spring-expression模块提供强有力的在运行时查询和操作对象的语言。这种语言支持获取和设置属性、方法执行、获取数组或集合中的对象、逻辑计算、命名变量,在Spring的IoC容器中获得对象。
2.2 AOP 和 Instrumentation
spring-aop模块提供切面编程的实现。可以自定义方法拦截和切入点。
spring-aspects模块提供与AspectJ的集成。
spring-instrument模块为特定的服务器提供类加载服务。spring-instrument模块是集成了Tomcat。
2.3 消息
Spring4提供了spring-messaging模块,主要类有Message,MessageChannel,MessageHandler。这个模块还包含一些映射消息到方法的注解,类似于Spring MVC基于编程模式的注解。
2.4 数据访问/集成
这一层由JDBC、ORM、OXM、JMS、和事物模块组成。
spring-jdbc模块,主要为了解决数据库繁多的问题,应用此可不需要关注使用的数据库。
spring-tx模块提供编程式或声明式事务处理。
spring-orm模块提供流行的对象关系映射的APIs,包含JPA和Hibernate.
spring-oxm模块提供对Object/XML映射的支持,例如JAXB,Castor,JiBX和XStream。
spring-jms模块(Java消息服务)包含生成和消费消息的功能。在Spring4.1以后,它集成了spring-messaging模块。
2.5 Web
Web层包含spring-web、spring-webmvc和spring-websocket 3个模块。
spring-web模块提供面向Web方法的集成特性,例如多部分文件上传、通过监听初始化IoC容器和面向Web的Context,还包含HTTP客户端和对远程的支持。
spring-webmvc模块(也被称作Web-Servlet模块)包含Spring MVC框架。
spring-websocket模块提供对socket的全面支持。
2.6 测试
spring-test模块通过JUnit或者TestNG来对Spring的模块进行单元测试和集成测试。它提供一致的Spring 的ApplicationContexts 和context的缓存。它还提供mock对象让你测试你的代码。
3、spring IoC容器和bean的简介
3.1 IoC容器和bean简介
控制翻转(IoC)也成为依赖注入(DI)。在这个过程中,对象仅通过构造函数参数,工厂方法的参数或在构造从工厂方法返回后在对象实例上设置的属性来定义其依赖项,然后在容器创建bean时注入那些依赖项。此过程就是控制其依赖项的实例化或位置的bean本身的你过程。
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。Bean是由Spring IoC容器实例化,组装和以其他方式管理的对象。否则,bean仅仅是应用程序中许多对象之一。Bean及其之间的依赖关系反映在容器使用的配置元数据中。
3.2 容器概述
该org.springframework.context.ApplicationContext接口代表Spring IoC容器,并负责实例化,配置和组装Bean。容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令。配置元数据以XML,Java批注或Java代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。
配置元数据:使用xml文件配置元数据,但不是唯一方式。
实例化容器:提供给ApplicationContext构造函数的一个或多个位置路径是资源字符串,这些资源字符串使容器可以从各种外部资源(例如本地文件系统,Java等)加载配置元数据CLASSPATH。
例:ApplicationContext context = new ClassPathXmlApplicationContext(“services.xml”, “daos.xml”);
使用容器:该ApplicationContext是一个维护bean定义以及相互依赖的注册表的高级工厂的接口。通过使用方法 T getBean(String name, Class requiredType),您可以检索bean的实例。
例:ApplicationContext context = new ClassPathXmlApplicationContext(“services.xml”, “daos.xml”);
PetStoreService service = context.getBean(“petStore”, PetStoreService.class);
3.3 依赖注入(DI)
依赖注入是一个过程,通过该过程,对象仅通过构造函数参数,工厂方法的参数在构造活创建对象实例后在对象实例上设置的属性来定义其依赖关系(即,与他们一起工作的其他对象)。使用DI原理,代码更加简洁。
依赖注入的三种方式:
构造函数注入:就是通过构造方法把需要的实体注入进来
setter注入:通过setter方法注入
注解注入:通注解@Autowired注入
4、AOP面向切面
4.1 AOP基本概念
面向切面编程,是spring重要的功能之一。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总的来说就是把非业务性代码抽离出来,降低代码的耦合度提高代码的可重用性,例如记录日志,权限验证、事务管理等功能。
4.2 AOP的相关属性
1、通知(Advice)
通知描述了切面要完成的工作以及何时执行。比如我们的日志切面需要记录每个接口调用时长,就需要在接口调用前后分别记录当前时间,再取差值。
前置通知(Before):在目标方法调用前调用通知功能。
后置通知(After):在目标方法调用之后调用通知功能,不关心方法的返回结果。
返回通知(AfterReturning):在目标方法成功执行之后调用通知功能。
异常通知(AfterThrowing):在目标方法抛出异常后调用通知功能。
环绕通知(Around)**:通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为。
连接点(JoinPoint)
通知功能被应用的时机。比如接口方法被调用的时候就是日志切面的连接点。
切点(Pointcut)
切点定义了通知功能被应用的范围。比如日志切面的应用范围就是所有接口,即所有controller层的接口方法。
切面(Aspect)
切面是通知和切点的结合,定义了何时、何地应用通知功能。
引入(Introduction)
在无需修改现有类的情况下,向现有的类添加新方法或属性。
织入(Weaving)
把切面应用到目标对象并创建新的代理对象的过程。
5、spring快速开始
5.1 项目搭建
spring的使用比较简单,大致流程如下:
搭建此项目的前提,安装开发环境jdk、maven
1、使用idea创建spring项目,点击file -> new ->project
如图勾选spring点击下一步
2、输入项目名称点击下一步
3、等待相关以来下载完成即可
5.2 控制反转(IOC)和依赖注入(DI)
在使用IOC容器获取bean对象之前,我们首先得获取IOC容器。
// 获取IOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
创建bean对象user类
package com.test;
public class User {
private String id;
private String username;
private String password;
public User(String id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public User() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
将实体类交给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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--两种不同的注入方式-->
<!--使用set方法注入普通类型-->
<bean id="user" class="com.test.bean.User">
<property name="id" value="1"/>
<property name="username" value="admin"/>
<property name="password" value="123456"/>
</bean>
<!--使用构造方法-->
<bean id="user1" class="com.test.bean.User">
<constructor-arg name="id" value="1"/>
<constructor-arg name="username" value="admin1"/>
<constructor-arg name="password" value="123456"/>
</bean>
</beans>
获取user对象,并打印用户名
// 获取IOC容器,xml的地址不能有误
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("/applicationContext.xml");
// 获取user对象
User user = (User) applicationContext.getBean("user");
System.out.println(user.getUsername());
User user1 = (User) applicationContext.getBean("user1");System.out.println(user1.getUsername());
运行效果 如下图:
5.3 AOP实现日志输出
AOP允许定义方法拦截器和切入点,将代码按照功能进行分离,以便干净地解耦,日志功能与业务逻辑无关,可以抽离出来。
https://maven.aliyun.com/mvn/search
实现aop有两种方式一种,一种是@Aspect注解的形式,另一种是使用xml的形式,xml的方式需要另导入aspectjweaver-1.5.3.jar依赖包,导入依赖的方式如下。
1、先下载依赖包,阿里maven仓库地址:https://maven.aliyun.com/mvn/search
2、导入步骤如下图:
导入依赖包之后我们就可以以xml文件的形式实现aop了,具体步骤如下:
1、创建具体业务的实现类,切面要切入的类,本次一登录记录日志为例
创建UserLoginService及实现类,如图:
具体实现代码:
public interface IUserLoginService {
String login(String username);
String loginout(String username);
}
public class UserLoginServiceImpl implements IUserLoginService{
@Override
public String login(String username) {
System.out.println("用户登录.....");
return "admin".equals(username) ? "登录成功" : "登录失败";
}
@Override
public String loginout(String username) {
System.out.println("用户退出.....");
return"admin".equals(username) ? "退出成功" : "退出失败";
}
}
2、编写切面类,具体实现代码:
public class LogAspect {
public void before(JoinPoint joinPoint){
System.out.println("前置通知"+joinPoint.getSignature().getName());
}
public void afterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知"+joinPoint.getSignature().getName()+",-->"+ret);
}
public Object arount(ProceedingJoinPoint joinPoint) throws Throwable {
//手动执行目标方法
System.out.println("环绕通知开始。。。");
Object object = joinPoint.proceed();
System.out.println("环绕通知结束。。。");
return object;
}
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知:"+e.getMessage());
}
public void after(){
System.out.println("最终通知。。。");
}
}
3、编写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"
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.xsd">
<!--两种不同的注入方式-->
<!--使用set方法注入普通类型-->
<bean id="user" class="com.test.bean.User">
<property name="id" value="1"/>
<property name="username" value="admin"/>
<property name="password" value="123456"/>
</bean>
<!--使用构造方法-->
<bean id="user1" class="com.test.bean.User">
<constructor-arg name="id" value="1"/>
<constructor-arg name="username" value="admin1"/>
<constructor-arg name="password" value="123456"/>
</bean>
<!-- 注入目标类-->
<bean id="userLogin" class="com.test.service.UserLoginServiceImpl" />
<!-- 注入切面类-->
<bean id="logAspect" class="com.test.aop.LogAspect" />
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut expression="execution(* com.test.service.UserLoginServiceImpl.*(..))" id="pointcut"/>
<!-- 前置通知
<aop:before method="myBefore" pointcut="" pointcut-ref=""/>
method : 通知,及方法名
pointcut:切入点表达式,此表达式只能当前通知
pointcut-ref:切入点引用,可以与其他通知共享切入点
通知方法可以有参数
-->
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pointcut"/>
<!-- 后置通知 -->
<aop:after-returning method="afterReturning" returning="ret" pointcut-ref="pointcut"/>
<!-- 环绕通知 -->
<aop:around method="arount" pointcut-ref="pointcut"/>
<!-- 抛出异常通知 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="pointcut" throwing="e"/>
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
4、运行测试,效果入下图: