原始时代我们用一个jsp搞定一切,但如此开发大型项目时我们遇到了问题,前端美化的代码和后端的代码交织,代码中又有html、js、css样式,又有业务逻辑和数据库访问代码,杂乱不清晰,美工和开发打架。
于是mvc分层架构封建时代出现,把我们写代码的地方硬性分成3个地方,Model层封装数据,View视图层页面展现,Controller控制层访问转发。代码之间的耦合度降低。
前端框架(View):struts1、struts2、SpringMVC
后端框架(Model):dbutils、jdbcTemplate、hibernate、ibatis、mybatis。
控制框架(Controller):Spring
Spring为什么那么强?
Spring的核心:
随着时代的优胜劣汰,最终形成了一套新的三大框架,即SpringMVC、Spring、MyBatis(SSM)框架
Spring官网: http://spring.io
Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。Spring框架的不光是技术牛,而是它的核心思想更牛,它不重复发明轮子,而是“拿来主义”,把业界做的最好的技术黏合起来形成一个强大的企业级的应用框架。
Spring 框架是一个分层架构,由7个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,而是由容器来进行管理,
通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
而依赖注入(Dependency Injection,缩写为DI)则是实现控制反转的一种方式之一。采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,不需要直接new来获得这个对象,而是通过Spring容器控制程序,来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如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.0modelVersion>
<groupId>cn.tedugroupId>
<artifactId>springartifactId>
<version>0.0.1-SNAPSHOTversion>
<properties>
<junit.version>4.10junit.version>
<spring.version>4.1.3.RELEASEspring.version>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
dependencies>
project>
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 + "]";
}
}
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 + "]";
}
}
<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>
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);
}
}
Spring框架实现了AOP面向切面编程,是面向对象编程(OOP)的一种补充,其引入了第三方AspectJ框架来具体实现。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
通过AOP,可以对原有的方法进行扩展,将重复但又不得不做的事情,放到AOP中执行,从而减少代码的耦合性.降低维护的成本。
AspectJ提供了五种切入方式,分别是:
异常通知是指当程序运行出现异常时,则执行通知方法,另外四种通知的执行时机如下图所示,分别在业务方法(Business Method)的执行前后进行拦截,执行指定的代码。
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.0.RELEASEversion>
<relativePath />
parent>
<groupId>cn.tedugroupId>
<artifactId>aopartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>aopname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
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";
}
}
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;
}
}
}
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);
}
}
在浏览器通过域名http:localhost:8080/hello访问网站,能正常返回,所以没有执行切面AfterThrowing,而是执行了AfterReturning切面。控制台将输出以下信息:
如果我们再HelloController.java中的hello方法执行1/0,则在浏览器通过域名http:localhost:8080/hello访问网站时,将会抛出异常,未能正常返回,所以没有执行AfterReturning切面,而是执行了AfterThrowing切面。控制台输出信息如下图所示