跳至主要內容

Spring

Yang大约 19 分钟

是企业开发复杂性的一站式解决方案

  • 核心是 Ioc 容器AOP 面向切面编程
  • Spring Ioc 负责创建与管理系统对象,并在此基础上扩展功能
<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
  </dependency>
</dependencies>

整合 Junit4

  • Maven 工程依赖 spring-test

  • 利用 @RunWith@ContextConfiguration 描述测试用例类

    • @RunWith:将 Junit 的运行过程交给 Spring 来完成
    • @ContextConfiguration:说明在初始化 IoC 容器时要加载哪个配置文件
  • 测试用例类从容器获取对象完成曾测试用例执行

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.6.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
</dependencies>
import com.imooc.spring.ioc.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

//将Junit4的执行权交由Spring Test,在测试用例执行前自动初始化IoC容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringTestor {
    @Resource
    private UserService userService;

    @Test
    public void testUserService(){
        userService.createUser();
    }
}

组成模块

Spring IoC 容器

IoC 容器是 Spring 生态的地基,用于统一创建与管理对象依赖

  • 对象的控制权交由 第三方 统一管理(IOC 控制反转)
    • 控制反转全称 Inverse of Control,是一种设计理念,是现代程序设计遵循的标准,是宏观目标
    • 通过加入 Ioc 容器 将对象统一管理,让对象关联变为弱耦合
  • 利用 Java 反射 技术,实现运行时对象创建与关联(DI 依赖注入)
    • 依赖注入全称 Dependency Injection,是具体语言技术实现,是微观实现
  • 基于配置提高应用程序的可维护性与扩展性

基于 XML 配置 Bean

基于构造方法实例化对象

  • <bean> 标签默认通过默认构造方法创建对象
    • idname 属性相同点
      • 都是在设置对象在 IoC 容器中唯一标识
      • 两者在同一配置文件中都不允许出现重复
      • 两者允许在多个配置文件中出现重复,新对象覆盖旧对象
      • 注意
        • 两者命名要求有意义,按驼峰命名书写
        • 在没有 idname 的情况下,默认使用类名全称作为 bean 标识
    • idname 属性不同点
      • (推荐)id 要求更严格,一次只能定义一个对象标识
      • name 要求宽松,一次允许定义多个对象标识
  • <bean> 标签内部添加 <contract-arg> 标签使用带参构造方法实例化对象
<!-- ApplicationContext.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="sweetApple" class="com.spring.ioc.entity.Apple">
        <constract-arg name="title" value="红富士"></constract-arg>
        <constract-arg name="color" value="红色"></constract-arg>
        <constract-arg name="origin" value="欧洲"></constract-arg>
    </bean>
    <bean id="luna" class="com.spring.ioc.entity.Child">
        <constract-arg name="name" value="露娜"/>
        <constract-arg name="apple" ref="sweetApple"/>
    </bean>
</beans>
// SpringApplication.java
package com.spring.ioc;
import com.spring.ioc.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
        Child lily = context.getBean("luna", Child.class);
        lily.eat();
      	// 销毁
      	((ClassPathXmlApplication) context).registerShutdownHook();
    }
}
// Apple.java
package com.spring.ioc.entity;
public class Apple {
    private String title;
    private String color;
    private String origin;
    public Apple() {
    }
    public Apple(String title, String color, String origin) {
        this.title = title;
        this.color = color;
        this.origin = origin;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String getOrigin() {
        return origin;
    }
    public void setOrigin(String origin) {
        this.origin = origin;
    }
}
// Child.java
package com.spring.ioc.entity;
public class Child {
    private String name;
    private Apple apple;
    public Child(){}
    public Child(String name, Apple apple) {
        this.name = name;
        this.apple = apple;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Apple getApple() {
        return apple;
    }
    public void setApple(Apple apple) {
        this.apple = apple;
    }
    public void eat(){
        System.out.println(name + "吃到了" + apple.getOrigin() + "种植的" + apple.getTitle());
    }
}

基于静态工厂实例化对象

<!-- ApplicationContext.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="sweetApple" class="com.spring.ioc.factory.AppleStaticFactory" factory-method="createSweetApple"></bean>
</beans>
// AppleStaticFactory.java
package com.spring.ioc.factory;
import com.spring.ioc.entity.Apple;
public class AppleStaticFactory {
    public static Apple createSweetApple() {
        Apple apple = new Apple();
        apple.setTitle("红富士");
        apple.setOrigin("欧洲");
        apple.setColor("红色");
        return apple;
    }
}
// SpringApplication.java
package com.spring.ioc;
import com.spring.ioc.entity.Apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
        Apple sweetApple = context.getBean("sweetApple", Apple.class);
        System.out.println(sweetApple.getTitle());
    }
}

基于工厂实例方法实例化对象

IoC 容器对工厂类进行实例化并调用对应的实例方法创建对象的过程

<!-- ApplicationContext.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="factoryInstance" class="com.spring.ioc.factory.AppleFactoryInstance"></bean>
    <bean id="apple" factory-bean="factoryInstance" factory-method="createSweetApple"></bean>
</beans>
// SpringApplication.java
package com.spring.ioc.factory;
import com.spring.ioc.entity.Apple;
public class AppleFactoryInstance {
    public Apple createSweetApple() {
        Apple apple = new Apple();
        apple.setTitle("红富士");
        apple.setOrigin("欧洲");
        apple.setColor("红色");
        return apple;
    }
}
// AppleStaticFactory.java
package com.spring.ioc;
import com.spring.ioc.entity.Apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:ApplicationContext.xml");
        Apple apple = context.getBean("apple", Apple.class);
        System.out.println(apple.getTitle());
    }
}
对象依赖注入

指运行时将容器内对象利用 反射 赋给其他对象的操作

基于 setter 方法注入对象
<bean id="sweetApple" class="com.spring.ioc.entity.Apple">
  <property name="title" value="红富士"></property>
  <property name="color" value="红色"></property>
  <property name="origin" value="欧洲"></property>
</bean>

<bean id="luna" class="com.imooc.spring.ioc.entity.Child">
  <property name="name" value="露娜"/>
  <property name="apple" ref="sweetApple"/>
</bean>
基于构造方法注入对象
<bean id="sweetApple" class="com.spring.ioc.entity.Apple">
  <property name="title" value="红富士"></property>
  <property name="color" value="红色"></property>
  <property name="origin" value="欧洲"></property>
</bean>

<bean id="luna" class="com.imooc.spring.ioc.entity.Child">
  <constract-arg name="name" value="露娜"/>
  <constract-arg name="apple" ref="sweetApple"/>
</bean>
注入集合对象
<!-- 注入 List -->
<bean id="..." class="...">
  <property name="someList">
    <list>
      <value>具体指</value>
      <ref bean="beanld"></ref>
    </list>
  </property>
</bean>
<!-- 注入 Set -->
<bean id="..." class="...">
  <property name="someSet">
    <set>
      <value>具体指</value>
      <ref bean="beanld"></ref>
    </set>
  </property>
</bean>
<!-- 注入 Map -->
<bean id="..." class="...">
  <property name="someMap">
    <map>
      <entry key="k1" value="v1"></entry>
      <entry key="k2" value-ref="beanld"></entry>
    </map>
  </property>
</bean>
<!-- 注入 Properties -->
<bean id="..." class="...">
  <property name="someProperties">
    <props>
      <prop key="k1">v1</prop>
      <prop key="k2">v2</prop>
    </props>
  </property>
</bean>
获取容器内对象
//获取容器内所有beanId数组ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");String[] beanNames = context.getBeanDefinitionNames();for (String beanName:beanNames){  System.out.println(beanName);  System.out.println("类型:" + context.getBean(beanName).getClass().getName());  System.out.println("内容:" + context.getBean(beanName));}
路径表达式
表达式实例说明
classpath:config.xml扫描 classpath 根路径(不包含 jar)的 config.xml
classpath:com/imooc/config.xml扫描 classpath 下(不包含 jar) com.imooc 包中的 config. xml
classpath*:com/imooc/config.xml扫描 classpath 下(包含 jar) com.mooc 包中的 config. xml
classpath:config-*.xml扫描 classpath 根路径下所有以 config- 开头的 XML 文件
classpath:com/**/config.xml扫描 com 包下(包含任何子包)的 config. xml
file:c:/config.xml扫描 C 盘根路径 config.xm
Scope

用于决定对象何时被创建与作用范围

  • 将影响容器内对象的数量
<bean id="..." class="..." scope="singleton">
属性说明
singleton单例 (默认值),每一个容器有且只有唯一的实例,实例被全局共享(单例多线程执行,存在线程风险安全)
prototype多例,每次使用时都是创建一个实例(占用更多资源,但不存在线程安全问题)
requestweb 环境下,每一次独立请求存在唯一实例
sessionweb 环境下,每一个 session 存在有唯一实例
applicationweb 环境下,ServletContext 存在唯一实例
websocket每一次 WebSocket 连接中存在唯一实例
---singletonprototype
对象数量全局唯一存在多个
实例化时机IoC 容器启动时getBean() 或 对象注入时
线程安全问题存在不存在
执行效率
bean 生命周期

基于注解配置 Bean

优势

  • 摆脱繁琐的 XML 形式的 bean 与依赖注入配置
  • 基于 声明式 的原则,更适合轻量级的现代企业应用
  • 让代码可读性变得更好,研发人员拥有更好的开发体验

分类

组件类型注解

声明当前类的功能与职责

注解说明
@Component组件注解,通用注解,被该注解描述的类将被 IoC 容器管理并实例化
@Controller语义注解,说明当前类是 MVC 应用的控制器类
@Service语义注解,说明当前类是 Service 业务服务类
@Repository语义注解,说明当前类用于业务持久层,通常描述对应的 Dao 类,bean id 默认为首字母小写的类名,也可通过 @Repository(BeanId) 设置
<?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">
  <!--通知Spring IoC容器初始化时加载属性文件-->
  <context:property-placeholder location="classpath:config.properties"/>
  <!-- 在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
        @Repository
        @Service
        @Controller
        @Component
     -->
  <!-- XML 配置开启组件扫描,才能使用注解 -->
  <context:component-scan base-package="com.imooc">
    <!-- 排除:使一些文件不被扫描 -->
    <context:exclude-filter type="regex" expression="com.imooc.exl.*" />
  </context:component-scan>
</beans>
# config.properties
metaData=imooc.com
connection.driver=xxxxx
connection.url=xxx
connection.username=xxx
connection.password=xxx
自动装配注解

根据属性特征自动注入对象

  • Spring IoC 容器会自动通过反射技术将属性 private 修饰符修改为 public 直接进行赋值
  • 如果装配注解在属性上将不执行 set 方法
  • 如果装配注解在 set 方法上,则自动按类型/名称对 set 方法参数进行注入
  • @Primary:如果在 IoC 容器中出现多个相同类型的对象,则默认采取 @Primary 注解所描述的对象完成注入
  • @Resource
    • 设置 name 属性,则按 name 在 IoC 容器中对 bean 注入
    • 未设置 name 属性
      • 以属性名作为 bean name 在 IoC 容器中匹配 bean ,如果有匹配则注入
      • 未匹配到,则按类型进行匹配,需要加入 @Primary 解决类型冲突
    • 使用建议:在使用时推荐设置 name 或保证属性名与 bean 名称一致
      • @Resource(name="userDao")
分类注解说明
按类型装配@Autowired按容器内对象类型动态注入属性,由 Spring 机构提供
@Inject基于 JSR-330(Dependency Injection for Java) 标准,其他同 @Autowired,但不支持 required 属性
按名称装配@Named@Inject 配合使用,JSR-330 规范,暗属性名自动装配属性
@Resource基于 JSR-250 规范,优先按名称,再按类型只能匹配
元数据注解

更细化的辅助 IoC 容器管理对象的注解

注解说明
@Primary按类型装配时出现多个相同类型对象,拥有此注解对象优先被注入
@PostConstruct描述方法,相当于 XML 中 init- method 配置的注解版本
@PreDestory描述方法,相当于 XML 中 destroy- method 配置的注解版本
@Scope设置对象的 scope 属性
@Value为属性注入静态数据(可以以 @Value("${metaData}"))的形式访问配置文件加载的 config.properties 文件内的属性

基于 Java 代码配置 Bean

  • 完全摆脱 XML 的束缚,使用独立的 Java 类管理对象与依赖
  • 注解配置相对分散,利用 Java Config 可对配置集中管理
  • 可以在编译时进行依赖检查,不容易出错
注解说明
@Configuration描述类,说明当前类是 Java Config 配置类,完全替代 ⅩML 文件
@Bean描述方法,方法返回对象将被 loC 容器管理,beanId 默认为方法名
@ImportResource描述类,加载静态文件,可使用 @ Value 注解获取
@ComponentScan描述类,同 XML 的 < context: compoment-scan> 标签
package com.imooc.spring.ioc;

import com.imooc.spring.ioc.controller.UserController;
import com.imooc.spring.ioc.dao.EmployeeDao;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.*;

@Configuration //当前类是一个配置类,用于替代applicationContext.xml
@ComponentScan(basePackages = "com.imooc")
public class Config {
    @Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
    public UserDao userDao(){
        UserDao userDao = new UserDao();
        System.out.println("已创建" + userDao);
        return userDao;
    }

    @Bean //Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
    @Primary
    public UserDao userDao1(){
        UserDao userDao = new UserDao();
        System.out.println("已创建" + userDao);
        return userDao;
    }

    @Bean
    //先按name尝试注入,name不存在则按类型注入
    public UserService userService(UserDao udao , EmployeeDao employeeDao){
        UserService userService = new UserService();
        System.out.println("已创建" + userService);
        userService.setUserDao(udao);
        System.out.println("调用setUserDao:" + udao);
        userService.setEmployeeDao(employeeDao);
        return userService;
    }

    @Bean //<bean id="xxx" clas="xxx">
    @Scope("prototype")
    public UserController userController(UserService userService){
        UserController userController = new UserController();
        System.out.println("已创建" + userController);
        userController.setUserService(userService);
        System.out.println("调用setUserService:" + userService);
        return userController;
    }
}
package com.imooc.spring.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        //基于Java Config配置IoC容器的初始化
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        System.out.println("=========================");
        String[] ids = context.getBeanDefinitionNames();
        for(String id : ids){
            System.out.println(id + ":" + context.getBean(id));
        }
    }
}

Spring AOP

面向切面编程:Aspect Oriented Programming

  • 将通用的、与业务无关的功能抽象封装为切面类

  • 在不修改源码的情况下,对程序行为进行扩展

  • Spring AOP 与 AspectJ 的关系

    • Eclipse AspectJ 是一种基于 Java 平台的面向切面编程语言
    • Spring AOP 使用 AspectJWeaver 实现类与方法匹配
    • Spring AOP 利用 代理模式 实现对象运行时功能扩展
<!-- aspectjweaver 是 Spring AOP 的底层依赖 -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.5</version>
</dependency>
<!-- AOP配置 -->
<bean id="methodAspect" class="com.imooc.spring.aop.aspect.MethodAspect"></bean>
<aop:config>
  <!--
		PointCut 切点,使用execution表达式描述切面的作用范围
		execution(public * com.imooc..*.*(..)) 说明切面作用在com.imooc包下的所有类的所有方法上
		<aop:pointcut id="pointcut" expression="execution(public * com.imooc..*.*(..))"></aop:pointcut>
	-->

  <!--只对所有Service类生效-->
  <aop:pointcut id="pointcut" expression="execution(* com.imooc..*Service.*(..))"></aop:pointcut>

  <!-- 定义切面类 -->
  <aop:aspect ref="methodAspect">
    <!--
				before通知(Advice),代表在目标方法运行前先执行methodAspect.printExecutionTime()
		-->
    <aop:before method="printExecutionTime" pointcut-ref="pointcut"/>
    <aop:after-returning method="doAfterReturning" returning="ret" pointcut-ref="pointcut"/>
    <aop:after-throwing method="doAfterThrowing" throwing="th" pointcut-ref="pointcut"/>
    <aop:after method="doAfter" pointcut-ref="pointcut"></aop:after>
  </aop:aspect>
</aop:config>
package com.imooc.spring.aop.aspect;

import org.aspectj.lang.JoinPoint;

import java.text.SimpleDateFormat;
import java.util.Date;

//切面类
public class MethodAspect {
    //切面方法,用于扩展额外功能
    //JoinPoint 连接点,通过连接点可以获取目标类/方法的信息
    public void printExecutionTime(JoinPoint joinPoint){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String now = sdf.format(new Date());
        String className = joinPoint.getTarget().getClass().getName();//获取目标类的名称
        String methodName = joinPoint.getSignature().getName();//获取目标方法名称
        System.out.println("---->" + now + ":" + className + "." + methodName);
        Object[] args = joinPoint.getArgs();
        System.out.println("---->参数个数:" + args.length);
        for(Object arg:args){
            System.out.println("---->参数:" + arg);
        }
    }

    public void doAfterReturning(JoinPoint joinPoint,Object ret){
        System.out.println("<----返回后通知:" + ret);
    }
    public void doAfterThrowing(JoinPoint joinPoint,Throwable th){
        System.out.println("<----异常通知:" + th.getMessage());
    }
    public void doAfter(JoinPoint joinPoint){
        System.out.println("<----触发后置通知");
    }
}

注解

注解说明
@Aspect切面,具体的可插拔组件功能类,通常一个切面只实现一个通用功能
@Target Class/Method目标类、目标方法,指真正要执行的与业务相关对的方法
@PointCut切入点,使用 execution 表达式说明切面要作用在系统的那些类上
@JoinPoint连接点,切面运行过程中是包含了 目标类/方法 元数据的对象
@Adivce通知,说明具体的切面的执行时机,Spring 包含了五种不同类型通知

JoinPoint

(返回类型)方法说明
Object getTarget()获取 IoC 容器内目标对象
Signature getSignature()获取目标方法
Object[] getArgs()获取目标方法参数

PointCut

<aop:pointcut expression="execution(* com.imooc..*Service.*(..))" />

<!--只对无返回方法生效-->
<aop:pointcut expression="execution(String com.imooc..*Service.*(..))" />

<!--只对所有返回值为String类型方法生效-->
<aop:pointcut expression="execution(String com.imooc..*Service.*(..))" />

<!--对方法名进行约束 -->
<aop:pointcut expression="execution(* com.imooc..*Service.create*(..))" />

<!-- 对参数进行约束 -->
<aop:pointcut expression="execution(* com.imooc..*Service.*(String,*))" />
  • public 可以去掉

Advice

通知类型说明
Before Advice前置通知,目标方法运行前执行
After Returning Advice返回后通知,目标方法返回数据后执行,可以接收目标方法的返回值
After Throwing Advice异常通知,目标方法抛出异常后执行(无论成功与否都会通知)
After Advice后置通知,目标方法运行后执行(无论成功与否都会通知)
Around Advice最强大通知,自定义通知执行时机,可决定目标方法是否运行
  • 后置通知、返回后通知、异常通知 的执行顺序由配置的先后顺序决定
  • 特殊的“通知” - 引介增强
    • 是对类的增强,而非方法
    • 允许在运行时为目标类增加新属性或方法
    • 允许在运行时改变类的行为,让类随运行环境动态变更
<bean id="methodChecker" class="com.imooc.spring.aop.aspect.MethodChecker"></bean>
<aop:config>
  <aop:pointcut id="pointcut" expression="execution(* com.imooc..*.*(..))"></aop:pointcut>
  <aop:aspect ref="methodChecker">
    <!--环绕通知-->
    <aop:around method="check" pointcut-ref="pointcut"/>
  </aop:aspect>
</aop:config>
public class MethodChecker {
    //ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
    public Object check(ProceedingJoinPoint pjp) throws Throwable {
        try {
            long startTime = new Date().getTime();
            Object ret = pjp.proceed();// 执行目标方法
            long endTime = new Date().getTime();
            long duration = endTime - startTime; // 执行时长
            if(duration >= 1000){
                String className = pjp.getTarget().getClass().getName();
                String methodName = pjp.getSignature().getName();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
                String now = sdf.format(new Date());
                System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
            }
            return ret;
        } catch (Throwable throwable) {
            System.out.println("Exception message:" + throwable.getMessage());
            throw throwable;
        }
    }
}

基于注解

注解说明
@Around
@Before
@After
@AfterThrowing
@AfterReturning
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--初始化IoC容器-->
    <context:component-scan base-package="com.imooc"/>
    <!--启用Spring AOP注解模式-->
    <aop:aspectj-autoproxy/>
</beans>
package com.imooc.spring.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
@Component //标记当前类为组件
@Aspect    //说明当前类是切面类
public class MethodChecker {
    //环绕通知,参数为PointCut切点表达式
    @Around("execution(* com.imooc..*Service.*(..))")
    //ProceedingJoinPoint是JoinPoint的升级版,在原有功能外,还可以控制目标方法是否执行
    public Object check(ProceedingJoinPoint pjp) throws Throwable {
        try {
            long startTime = new Date().getTime();
            Object ret = pjp.proceed();//执行目标方法
            long endTime = new Date().getTime();
            long duration = endTime - startTime; //执行时长
            if(duration >= 1000){
                String className = pjp.getTarget().getClass().getName();
                String methodName = pjp.getSignature().getName();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
                String now = sdf.format(new Date());
                System.out.println("=======" + now + ":" + className + "." + methodName + "(" + duration + "ms)======");
            }
            return ret;
        } catch (Throwable throwable) {
            System.out.println("Exception message:" + throwable.getMessage());
            throw throwable;
        }
    }
}

实现原理

基于 代理模式 实现功能扩展,包含两种形式

  • 目标类拥有接口:通过 JDK 动态代理实现功能扩展
  • 目标类没有接口:通过 CGLib 组件实现功能扩展

代理模式

通过代理对象对原对象实现功能扩展

Spring JDBC

是 Spring 框架用于处理关系型数据库的模块

  • 对 JDBC API 进行封装,极大简化开发工作量
  • JdbcTemplate 是 Spring JDBC 核心类,提供数据 CRUD 方法

使用步骤

  • Maven 工程引入 spring-jdbc
  • applicationContext.xml 配置 DataSource 数据源
  • Dao 注入 JdbcTemplate 对象,实现数据 CRUD
<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.6.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
  </dependency>
</dependencies>
<?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">
    <!-- 数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/imooc?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--JdbcTemplate提供数据CRUD的API-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="employeeDao" class="com.imooc.spring.jdbc.dao.EmployeeDao">
        <!--为Dao注入JdbcTemplate对象-->
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <bean id="employeeService" class="com.imooc.spring.jdbc.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"/>
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>
// EmployeeDao,java
package com.imooc.spring.jdbc.dao;

import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;
import java.util.Map;

public class EmployeeDao {
    private JdbcTemplate jdbcTemplate;
		/**
		 * jdbcTemplate
		 * 方法:
		 		* queryForObject 将查询结果中单条记录转换为对应的对象进行返回
		 		* query 将查询结果中多条记录每一条转换为对应的实体对象放入 List 进行返回
		 		* queryForList 返回 Map 完成对数据的封装
		 		* update 泛指所有写入操作(添加、删除、修改)
		 * 参数
		 		* sql SQL语句
		 		* new Object[]{参数1,参数2,...}
		 		* new BeanPropertyRowMapper<Employee>(Employee.class) 可选,返回数据的类
        */
    public Employee findById(Integer eno){
        String sql = "select * from employee where eno = ?";
        //查询单条数据
        Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{eno}, new BeanPropertyRowMapper<Employee>(Employee.class));
        return employee;
    }

    public List<Employee> findByDname(String dname){
        String sql = "select * from employee where dname = ?";
        //查询复合数据
        List<Employee> list = jdbcTemplate.query(sql, new Object[]{dname}, new BeanPropertyRowMapper<Employee>(Employee.class));
        return list;
    }

    public List<Map<String, Object>> findMapByDname(String dname){
        String sql = "select eno as empno , salary as s from employee where dname = ?";
        //将查询结果作为Map进行封装
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, new Object[]{dname});
        return maps;
    }

    public void insert(Employee employee){
        String sql = "insert into employee(eno,ename,salary,dname,hiredate) values(?,?,?,?,?)";
        //利用update方法实现数据写入操作
        jdbcTemplate.update(sql,new Object[]{
           employee.getEno() , employee.getEname(),employee.getSalary(),employee.getDname() , employee.getHiredate()
        });
    }

    public int update(Employee employee){
        String sql = "UPDATE employee SET ename = ?, salary = ?, dname = ?, hiredate = ? WHERE eno = ?";
        int count = jdbcTemplate.update(sql, new Object[]{employee.getEname(), employee.getSalary(), employee.getDname(), employee.getHiredate(), employee.getEno()});
        return count;
    }

    public int delete(Integer eno){
        String sql = "delete from employee where eno = ?";
        return jdbcTemplate.update(sql, new Object[]{eno});
    }


    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}
// EmployeeService.java
package com.imooc.spring.jdbc.service;

import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Date;

public class EmployeeService {
    private EmployeeDao employeeDao;
    private DataSourceTransactionManager transactionManager;

    public void batchImport(){
        //定义了事务默认的标准配置
        TransactionDefinition definition = new DefaultTransactionDefinition();
        //开始一个事务,返回事务状态,事务状态说明当前事务的执行阶段
        TransactionStatus status = transactionManager.getTransaction(definition);
        try {
            for (int i = 1; i <= 10; i++) {
                /*if (i == 3) {
                    throw new RuntimeException("意料之外的异常");
                }*/
                Employee employee = new Employee();
                employee.setEno(8000 + i);
                employee.setEname("员工" + i);
                employee.setSalary(4000f);
                employee.setDname("市场部");
                employee.setHiredate(new Date());
                employeeDao.insert(employee);
            }
            //提交事务
            transactionManager.commit(status);
        }catch (RuntimeException e){
            //回滚事务
            transactionManager.rollback(status);
            throw e;
        }

    }
    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    public DataSourceTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(DataSourceTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
}
// SpringApplication.java
package com.imooc.spring.jdbc;

import com.imooc.spring.jdbc.dao.EmployeeDao;
import com.imooc.spring.jdbc.entity.Employee;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        EmployeeDao employeeDao = context.getBean("employeeDao", EmployeeDao.class);
        Employee employee = employeeDao.findById(3308);
        System.out.println(employee);
    }
}

声明式事务

  • 配置 TransactionManager 事务管理器
  • 配置事务通知与事务属性
  • 为事务绑定 PointCut 切点
<!-- 1.事务管理器,用于创建事务/提交/回滚 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<!--2.事务通知配置,决定哪些方法使用事务,哪些方法不使用事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <!-- 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚 -->
    <tx:method name="batchImport" propagation="REQUIRED"/>
    <tx:method name="batch*" propagation="REQUIRED"/>
    <!-- 设置所有findXXX方法不需要使用事务 -->
    <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
    <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>

    <tx:method name="importJob1" propagation="REQUIRES_NEW"/>
    <tx:method name="importJob2" propagation="REQUIRES_NEW"/>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

<!--3. 定义声明式事务的作用范围-->
<aop:config>
  <aop:pointcut id="pointcut" expression="execution(* com..*Service.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>

Spring MVC

是 Spring 体系的轻量级 Web MVC 框架

核心是 Controller 控制器,用于处理请求产生响应

基于 Spring IOC 容器运行,所有对象被 IOC 管理

  • Model:模型
  • View:视图
  • Controller:控制器

环境配置

  1. Maven 依赖 spring-mvc
  2. web.xml 配置 Dispatcherservlet
  3. 配置 applicationcontext 的 mvc 标记
  4. 开发 Controller 控制器
<!-- pom.xml -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--DispatchServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <!--
            DispatcherServlet是Spring MVC最核心的对象
            DispatcherServlet用于拦截Http请求,
            并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
        -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--applicationContext.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <!--
            在Web应用启动时自动创建Spring IOC容器,
            并初始化DispatcherServlet
        -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--"/" 代表拦截所有请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>characterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
上次编辑于:
贡献者: sunzhenyang