SSM框架学习笔记6—Spring

1 背景

原始时代我们用一个jsp搞定一切,但如此开发大型项目时我们遇到了问题,前端美化的代码和后端的代码交织,代码中又有html、js、css样式,又有业务逻辑和数据库访问代码,杂乱不清晰,美工和开发打架。

于是mvc分层架构封建时代出现,把我们写代码的地方硬性分成3个地方,Model层封装数据,View视图层页面展现,Controller控制层访问转发。代码之间的耦合度降低。

前端框架(View):struts1、struts2、SpringMVC
后端框架(Model):dbutils、jdbcTemplate、hibernate、ibatis、mybatis。
控制框架(Controller):Spring

Spring为什么那么强?

  1. 接手对前端框架和后端框架对象的管辖
  2. 让不同技术之间能够简单的相互配合

Spring的核心:

  1. 控制反转(Inversion of Control,缩写为IoC)
  2. DI依赖注入
  3. Bean工厂
  4. SpringAOP面向切面编程
  5. 事务控制。

随着时代的优胜劣汰,最终形成了一套新的三大框架,即SpringMVC、Spring、MyBatis(SSM)框架

Spring官网: http://spring.io
Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。Spring框架的不光是技术牛,而是它的核心思想更牛,它不重复发明轮子,而是“拿来主义”,把业界做的最好的技术黏合起来形成一个强大的企业级的应用框架。
Spring 框架是一个分层架构,由7个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:

在这里插入图片描述

2 控制反转与依赖注入

2.1 定义

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,而是由容器来进行管理

通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

而依赖注入(Dependency Injection,缩写为DI)则是实现控制反转的一种方式之一。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过Spring容器控制程序,来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。

简单来说,依赖注入需要清除以下四点:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

2.2 一个例子

2.2.1 项目结构

在这里插入图片描述

2.2.2 pom.xml

<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>cn.tedu</groupId>
	<artifactId>spring</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<!-- 集中定义依赖版本号 -->
	<properties>
		<junit.version>4.10</junit.version>
		<spring.version>4.1.3.RELEASE</spring.version>
	</properties>

	<dependencies>
		<!-- 单元测试 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>

		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

	</dependencies>
</project>

2.2.3 Dept.java

package spring;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component	//@Component注解将此类交给容器进行管理,从而可以由容器创建并注入对象
public class Dept {
	@Value(value = "软件部")
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Dept [name=" + name + "]";
	}
}

2.2.4 User.java

package spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component	//@Component注解将此类交给容器进行管理,从而可以由容器创建并注入对象
public class User {
	@Autowired	//通过@Autowired注解自动注入dept对象
	private Dept dept;
	
	@Value(value = "tony")	//设定name默认值为tony
	private String name;
	public Dept getDept() {
		return dept;
	}
	public void setDept(Dept dept) {
		this.dept = dept;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "User [dept=" + dept + ", name=" + name + "]";
	}
}

2.2.5 applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	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-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!-- 扫描包 -->
	<context:component-scan base-package="spring" />
</beans>

2.2.6 TestSpringDI.java

package spring;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpringDI {
	@Test
	public void show() {
		//获得上下文容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		//通过容器获得user对象
		User u = (User)ac.getBean("user");
		System.out.println(u);
	}
}

3 SpringAOP

3.1 AOP定义及作用

Spring框架实现了AOP面向切面编程,是面向对象编程(OOP)的一种补充,其引入了第三方AspectJ框架来具体实现。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

通过AOP,可以对原有的方法进行扩展,将重复但又不得不做的事情,放到AOP中执行,从而减少代码的耦合性.降低维护的成本。

如下图(图片来源:《Spring实战(第4版)》)
在这里插入图片描述

3.2 五种AOP切入方式

AspectJ提供了五种切入方式,分别是:

  1. @Before: 前置通知。将拦截符合切点表达式的方法,在这些方法执行之前,执行通知方法,常用于记录方法执行前的状态,比如初始执行时间等。方法体有JoinPoint 参数:用来连接当前连接点的连接细节,一般包括方法名和参数值。
  2. @After: 后置通知。将拦截符合切点表达式的方法,在这些方法执行完毕后,无论是否发生异常,均会执行通知方法,可用于记录方法执行后的状态,或业务执行后的清理操作。在后置通知中,不能访问目标方法的执行结果
  3. @AfterRunning: 返回通知。将拦截符合切点表达式的方法,在这些方法执行返回后,执行通知方法,可用于在业务方法执行返回后,对返回值进行操作和记录。返回通知只有在目标方法正常结束时,才会执行的通知,并且可以访问到方法的返回值
  4. @AfterThrowing: 异常通知。将拦截符合切点表达式的方法,在这些方法执行抛出异常时,执行通知方法,可用于拦截程序抛出的异常,并对异常进行处理
  5. @Around: 环绕通知: 将拦截符合切点表达式的方法,在这些方法执行前后,执行通知方法,常用于改变执行步骤以满足业务需求

异常通知是指当程序运行出现异常时,则执行通知方法,另外四种通知的执行时机如下图所示,分别在业务方法(Business Method)的执行前后进行拦截,执行指定的代码。
在这里插入图片描述

3.3 一个例子

3.3.1 项目结构

在这里插入图片描述

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.0.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.tedu</groupId>
	<artifactId>aop</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>aop</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 添加切面依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3.3.2 HelloController.java

package cn.tedu.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
	@RequestMapping("/hello")
	public String hello() {
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return "hi aop";
	}
	@RequestMapping("/welcome")
	public String welcome() {
		try {
			Thread.sleep(800);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
		return "welcome aop";
	}
}

3.3.3 TimeAspect.java

package cn.tedu.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component		//组件,交给spring容器
@Aspect				//切面
public class TimeAspect {
	String methodName;      	//方法名
	long startTime;         		//开始时间
	
	//切点表达式,处理所有controller下包括其子目录中的*.*所有类的所有方法
	//匹配所有目标类的public方法,第一个*为返回类型,第二个*为方法名
	@Pointcut("execution( public * cn.tedu.controller..*.*(..))")
	public void aopPointCut() {}
	
	@Before("aopPointCut()")		//前置通知,业务执行前
    public void doBefore(JoinPoint joinPoint) {
    	System.out.println(joinPoint.getSignature().getDeclaringTypeName() );
		System.out.println(joinPoint.getSignature().getName() );
        methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        startTime = System.currentTimeMillis();
    }

    @After("aopPointCut()")		//后置通知,业务执行后
    public void doAfter() {
        long endTime = System.currentTimeMillis() - startTime;
        System.out.println("执行 " + methodName + " 耗时为:" + endTime + "ms");
    }

    //正常返回通知,返回后执行
    @AfterReturning(returning = "object", pointcut = "aopPointCut()")
    public void doAfterReturning(Object object) {
    	System.out.println("response={"+object.toString()+"}");
    }   

     @Around("aopPointCut()")		//环绕通知
     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();	//回调业务方法
            long end = System.currentTimeMillis();
            System.out.println("==around " + joinPoint + "\t耗时 : " + (end - start) + " ms!");
            return result; 

        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            System.out.println("==around " + joinPoint + "\t耗时 : " + (end - start) + " ms with exception : " + e.getMessage());
            throw e;
        }

     }	
}

3.3.4 RunApp.java

package cn.tedu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RunApp {
	public static void main(String[] args) {
		SpringApplication.run(RunApp.class, args);
	}
}

3.3.5 运行结果

在浏览器通过域名http:localhost:8080/hello访问网站,能正常返回,所以没有执行切面AfterThrowing,而是执行了AfterReturning切面。控制台将输出以下信息:
在这里插入图片描述
如果我们再HelloController.java中的hello方法执行1/0,则在浏览器通过域名http:localhost:8080/hello访问网站时,将会抛出异常,未能正常返回,所以没有执行AfterReturning切面,而是执行了AfterThrowing切面。控制台输出信息如下图所示
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_36302584/article/details/107654721