动力节点—2020最新Spring教程笔记(上)

根据动力节点2020Spring笔记整理而成,视频地址https://www.bilibili.com/video/BV1nz4y1d7uy/。

1 Spring 概述

Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。

官网:https://spring.io/

Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、Web、面向切面编程(AOP, Aspects)、提供JVM的代理(Instrumentation)、消息发送(Messaging)、核心容器(Core Container)和测试(Test)。

2 IOC 控制反转

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA 对 classB 有依赖。

依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。

依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

Spring **框架使用依赖注入(**DI)实现IoC。

Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

具体分为两种,基于 XML 和基于注解的 DI。

2.1 基于XML的DI

2.1.1 注入分类

2.1.1.1 set注入
public class Student{
	private String name;
	private int age;
	private School school;
  public Student(String name, Integer age, School school) {
    this.name = name;
    this.age = age;
    this.school = school;
  }
  public void setName(String name) {
    this.name = name;
  }

  public void setAge(Integer age) {
    this.age = age;
 }

  public void setSchool(School school) {
    this.school = school;
 }
}

class School{
  private String name;
  private String address;
  public void setAddress(String address) {
    this.address = address;
  }

  public void setName(String schoolName) {
    this.schoolName = schoolName;
  }
}

set 注入,相当于调用 set 的方法,使用 <property />,分为两种,一种是简单类型,即基本类型和String,另一种是引用类型。基本类型和String使用value,引用类型是ref。

<bean id="student" class="com.bjpowernode.ba01.Student" autowire="byName">
    <property name="name" value="" />
    <property name="age" value="20" />
    <property name="school" ref="school"/>
</bean>

<bean id="school" class="com.bjpowernode.ba01.School">
    <property name="name" value="北大" />
    <property name="address" value="北京" />
</bean>
2.1.1.2 构造注入(了解)

构造注入相当于调用构造器,使用 <constructor-arg />

<bean id="student" class="com.bjpowernode.ba01.Student">
    <constructor-arg name="name" value="" />
    <constructor-arg name="age" value="20" />
    <constructor-arg name="school" ref="school"/>
</bean>

<bean id="school" class="com.bjpowernode.ba01.School">
    <property name="name" value="北大" />
    <property name="address" value="北京" />
</bean>

2.1.2 自动注入

对于某个property是ref类型的,可以自动注入,不用自己写,byName是按照名字注入。在这里指的是Student类的属性school和下面School类的id一样。

<bean id="student" class="com.bjpowernode.ba01.Student" autowire="byName">
    <property name="name" value="" />
    <property name="age" value="20"></property>
<!--自动注入,省略        <property name="school" ref="school"/>-->
</bean>

<!--id和上面省略的属性名一致-->
<bean id="school" class="com.bjpowernode.ba01.School">
    <property name="schoolName" value="北大"></property>
</bean>

byType按照类型注入,不要求注入的id和属性名一致。

<bean id="student" class="com.bjpowernode.ba01.Student" autowire="byType">
    <property name="name" value="" />
    <property name="age" value="20"></property>
<!--自动注入,省略        <property name="school" ref="school"/>-->
</bean>

<!--id和上面的属性名不同-->
<bean id="school1234" class="com.bjpowernode.ba01.School">
    <property name="schoolName" value="北大"></property>
</bean>

2.2 基于注解的DI

需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。

掌握下面几个注解,@Component,@Repository,@Service,@Controller,@Value,@Autowired,@Qualifier,@Resource。

其中前四个都是定义Bean,@Repository是对DAO实现类使用,@Service对Service实现类使用,@Controller对Controller实现类使用。@Value用于基本类型和String。@Autowired用于自动注入,默认byType,结合@Qualifier实现byName。@Resource默认byName,找不到则byType。

具体如下,第一个例子是byName,School类的对象名称为school1,要求自动注入的名称也是school1.

@Component("school1")
public class School {
    @Value("北大")
    private String schoolName;
}

@Component("student")
public class Student {
    @Value("王坤")
    private String name;
    @Value("20")
    private Integer age;
    @Resource(name="school1") //对应School的名字
    //@Resource(..)可换成下面两行
  	//@Autowired
    //@Qualifier("school1")
    private School school;
}

第二个例子是byType,按照School类去查找,不关心名称。

@Component("school1")
public class School {
    @Value("北大")
    private String schoolName;
}

@Component("student")
public class Student {
    @Value("王坤")
    private String name;
    @Value("20")
    private Integer age;
    @Autowired //或者@Resource
    private School school;
}

3 AOP面向切面编程

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。

3.1 AOP术语

(1) 切面(Aspect)

切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。

(2) 连接点(JoinPoint)

连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。

(3) 切入点(Pointcut)

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

(4) 目标对象(Target)

目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。

(5) 通知(Advice)

通知表示切面的执行时间,Advice 也叫增强。通知定义了增强代码切入到目标代码的时间点,是目标方

法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。

切入点定义切入的位置,通知定义切入的时间。

3.2 AspectJ

3.2.1 切入点表达式

下面为切入点表达式,用中文表述为

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

execution(modifiers-pattern? ret-type-pattern 
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)

举例

execution(public * *(…))

指定切入点为:任意公共方法。

execution(* set*(…))

指定切入点为:任何一个以“set”开始的方法。

execution(* com.xyz.service..(…))

指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.xyz.service….(…))

指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后

面必须跟“*”,表示包、子包下的所有类。

execution(* …service..*(…))

指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

3.2.2 5种通知类型

所有通知方法可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。

AspectJ 中常用的通知有五种类型:

(1)前置通知 @Before

(2)后置通知 @AfterReturing

该注解的 returning 属性就是用于指定接收方法返回值的变量名的,可作为通知的参数

(3)环绕通知 @Around

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

(4)异常通知 @AfterThrowing

该注解的 throwing 属性用于指定所发生的异常类对象,可作为通知的参数

(5)最终通知 @After

无论目标方法是否执行,该通知都会执行。

(6) 定义切入点 @PointCut

可以用一个方法名表示一个切入点表达式,其他通知使用的时候可以用对应的方法取代切入点表达式。

注:在5种通知都有的情况下,如果目标对象的方法本身有异常,执行顺序是Around->Before->方法->Around->After->AfterThrowing,如果没有异常,执行顺序是Around->Before->方法->Around->After->AfterReturing。如果其他的通知出现异常,AfterThrowing同样会执行。特别的,如果 AfterReturing 抛出异常,则5种通知都会执行。

示例如下:

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:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--        <context:component-scan base-package="com.bjpowernode.service" />-->
    <bean id="someServiceImpl" class="com.bjpowernode.service.SomeServiceImpl"></bean>
    <bean id="myAspect" class="com.bjpowernode.service.MyAspect"/>

    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

接口:

package com.bjpowernode.service;

public interface SomeService {
    String doOther(int num);
}

实现类:

package com.bjpowernode.service;

public class SomeServiceImpl implements SomeService {
    @Override
    public String doOther(int num) {
        System.out.println("业务方法"+1/num);
        return String.valueOf(num);
    }
}

切面类:

package com.bjpowernode.service;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;

@Aspect
public class MyAspect {

    @Before("mypt()")
    public void myBefore(JoinPoint jp){
        Signature signature = jp.getSignature();
        System.out.println("方法定义"+signature);
        for(Object arg:jp.getArgs()){
            System.out.println("参数"+arg+","+arg.getClass());
        }
        System.out.println("Before");
    }

    @AfterReturning(value = "mypt()",returning = "retu")
    public void myAfterReturning(JoinPoint jp,Object retu){ ;
        System.out.println("返回的是"+retu);
        System.out.println("afterReturning");
        System.out.println(1/0);
    }

    @Around(value = "mypt()")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        Object obj = null;
        System.out.println("Around");
        System.out.println("环绕通知,通知之前");

        obj = pjp.proceed();

        System.out.println("环绕通知,通知之后");

        return obj;
    }

    @AfterThrowing(value = "mypt()",throwing="ex")
    public void myAfterThrowing(Throwable ex){
        System.out.println("AfterThrowing:"+ex.getMessage());

    }

    @After("mypt()")
    public void myAfter(){
        System.out.println("After");
//        System.out.println(1/0);
    }

    @Pointcut("execution(* com.bjpowernode.service.SomeServiceImpl.do*(..))")
    private void mypt(){
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值