关于java:Spring学习笔记01

31次阅读

共计 8500 个字符,预计需要花费 22 分钟才能阅读完成。

1. Spring 简介

一句话概括 Spring,Spring 是一个开源的、轻量的 管制反转 (IOC)面向切面编程 (AOP) 的框架。

1.1 Spring、SpringMVC、Spring Boot 和 Spring Cloud 的区别

SpringMVC 是在 Spring 根底之上联合了 MVC 三层架构的框架,应用 DispatchServlet 和视图解析器做到了更不便的视图跳转和控制器调度解决,次要负责解决 WEB 利用开发。SpringMVC 是 Spring 的一个子模块。

SpringBoot 则应用了 约定大于配置 的理念,简化了 Spring 配置,专一于微服务的开发。

SpringCloud 专一于全局微服务的整合、治理。

2. 管制反转—IOC

2.1 基本概念

IOC:管制反转,是一种设计思维。即 将对象创立的控制权转移给了 Spring 容器

假如咱们创立了类 A,类 A 中有个成员变量类 B。

  • 传统开发:咱们须要手动创立类 B 的对象,将他赋给类 A 的对象,这叫被动管制对象的创立;
  • IOC:Spring 创立好 B 对象,而后存储到一个容器外面,当 A 对象须要应用 B 对象时,Spring 就从寄存对象的那个容器外面取出 A 要应用的那个 B 对象。

应用传统形式,A 对 B 产生了依赖,也就是 A 和 B 之间存在一种耦合关系(假如 B 的实现有了改变,咱们可能还须要改变 A 的源码),而通过 IOC 的思维,这种依赖关系就被容器取代了,所有对象的创立依赖于容器,在启动的时候,Spring 会读取配置文件,并将创立的对象先注入容器,之后咱们想要应用间接从容器中取就能够。

DI:依赖注入,能够了解为是 Spring 实现 IOC 的具体形式。

2.2 Spring 创建对象形式

class UserService {
    private UserDao dao;
    
    public void setUserDao(UserDao dao) {this.userDao = dao;}
}

class UserDao {
    private String name;
    
    public void setName(String name) {this.name = name;}
}

2.2.1 构造函数

<bean id="dao" class="com.dong.dao.UserDao">
    <!-- 应用 name 定位构造函数 -->
    <constructor-arg name="name" value="dongjh"></constructor-arg>
</bean>


<bean id="service" class="com.dong.service.UserService">
    <!-- 应用 type 定位构造函数 -->
    <constructor-arg type="com.dong.dao.UserDao" ref="dao"></constructor-arg>
</bean>

注:

  1. 如果某类有无参构造方法,则默认调用无参构造方法;
  2. 如果某类只有一个构造方法,则默认调用该构造方法。

2.2.2 property(调用 set 办法创立)

<bean id="dao" class="com.dong.service.UserDao">
    <!-- 根本类型应用 value 注入属性 -->
    <property name="name" value="dongjh"></property>
</bean>

<bean id="service" class="com.dong.service.UserService">
    <!-- 援用类型应用 ref 属性 -->
    <property name="userDao" ref="dao"></property>
</bean>

注:

  1. 应用属性注入时,容器会 默认调用无参构造方法创立实例,所以须要保障要创立的 bean 有无参的构造方法;
  2. 应用属性注入等价于调用了 setXXX 办法,XXX 是 property 标签中的 name,假如下面的代码的setName 办法改成了setName1,则第一个 bean 的 property 需这样写:
<property name="name1" value="dongjh"></property>

2.3 Bean 的主动拆卸(autowiring)

主动拆卸即 Spring 在上下文中主动寻找,主动给 bean 拆卸属性。

bean 的主动拆卸次要有三种策略:

  1. byname:把与某 bean(A)属性 具备雷同名字 的其余 bean(B)主动拆卸到该 bean(A)的属性中;
  2. byType:把与某 bean(A)属性 具备雷同类型 的其余 bean(B)主动拆卸到该 bean(A)的属性中;
  3. constructor:把与某 bean(A)的结构器入参 具备雷同类型 的其余 bean(B)主动拆卸到该 bean(A)的属性中;

2.3.1 byName

xml 配置形式
<!-- 因为该 bean 的 id 为 userDao,所以会间接主动拆卸上面 service 的 userDao 属性中 -->
<bean id="userDao" class="com.dong.service.UserDao">
    <property name="name" value="dongjh"></property>
</bean>

<!-- 该类有 setUserDao 办法,且设置了属性主动拆卸 byName-->
<bean id="service" class="com.dong.service.UserService" autowired="byName">
</bean>

2.3.2 byType

xml 配置形式
<!-- 该 bean 的类型为 com.dong.service.UserDao,所以会间接主动拆卸上面 service 的 userDao 属性中 -->
<bean id="userDao111" class="com.dong.service.UserDao">
    <property name="name" value="dongjh"></property>
</bean>

<!-- 该类一个 com.dong.service.UserDao 类型的属性,且设置了属性主动拆卸 byType-->
<bean id="service" class="com.dong.service.UserService" autowired="byType">
</bean>

2.4 Spring 注解配置

JDK1.5 开始反对注解,而 Spring 是 2.5 开始反对。

应用 Spring 注解配置须要先导入束缚: context 名称空间和注解扫描。

<?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">

    <!-- 注:只会寻找在同一个利用上下文中定义的注解,比方将这个申明搁置到 DispatcherServlet 的配置上下文中,它就只会查看 controllers 的上下文,而不会查看 services 的上下文,详情见 https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-annotation-config-->
    <context:annotation-config/>
    <!-- 指定注解扫描返回 -->
    <context:component-scan base-package="com.dong.dao"></context:component-scan>

</beans>

@Autowired

之后在须要主动拆卸的 属性或 set 办法 上应用 @AutoWired 注解,就能够实现主动拆卸。

// 具体的可润饰范畴
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

注:@Autowired 会先应用 byType,再按 byName 的程序主动拆卸。

@Autowired 有一个罕用属性:required,默认值为 true,这个属性示意润饰的成员变量是否肯定须要拆卸一个 bean:

  • 当 required 设置为 true 时,示意必须有一个能够主动拆卸到该成员变量的 bean,否则报错;
  • 当 required 设置为 false 时,则示意该成员变量能够为 null;

@Qualifier

当有多个 bean 满足主动拆卸条件时,能够应用这个指定拆卸的 bean,个别配合 @Autowired 应用。

@Qualifier 有个属性 value,给这个 value 设置想要拆卸的 bean 的 id 即可。

@Component

润饰类,示意将该类的 bean 对象注入到 Spring 容器中。

@Component 有个属性 value,示意注入时设置的 id。

如:

// 等价于 <bean id="user" class="User">
@Component(value="user")
public class User {}

@Component 有一些衍生注解,如 Web 开发会依照 MVC 架构分层,依据不同的性能层,衍生出了一些注解:

  • @Repository:针对 dao 层
  • @Service:针对 service 层
  • @Controller:针对 controller 层

这四个注解性能统一,都示意将某类的 bean 对象注入到 Spring 容器中。

@Value

给属性注入值,次要用于给成员变量注入值,简略的能够 注入根本类型的值,简单时能够注入一些操作系统属性或其余 bean 的属性。

@Scope

用于设置 bean 的作用域,先介绍最常见的:

  • singleton:全局惟一,单例;
  • prototype:每次注入创立一个新的对象。

注解和配置的区别

xml 性能更加弱小,方便管理保护。注解实用于简略的属性注入。

2.5 JavaConfig 配置

Spring 3.0之后开始反对了一种更优的配置形式:基于 Java 类的配置形式。通过减少 @Configuration 注解表明这是一个配置类,其底层为 @Component(即该类的 bean 对象也会被 Spring 容器接管)。

@Configuration 联合 @Bean 注解能够实现 XML 配置实现注入的性能,成果与

<beans>
    <bean>...</bean>
    <bean>...</bean>
</beans>

相似,在应用 @Bean 注解时,示意将该办法返回的对象加载进 Spring 容器,在应用 @Bean 润饰办法时须要留神:

  1. 办法带返回值,且返回类型为想要被治理的 bean 的类型;
  2. 办法名即为默认的 bean name,可应用 @Bean 的 name 属性自定义;
  3. 办法可带参数,则 此时传入的参数也必须是被 Spring 容器治理的 bean

以创立 UserService 和 UserDao 为例:

@Configuration
public class UserConfiguration {
    @Bean
    public UserService userService() {return new UserService();
    }
    
    @Bean
    public UserDao userDao() {return new UserDao();
    }
}

当 Spring 扫描该类时,会主动将 UserConfiguration、UserService、UserDao 的 bean 注入到 Spring 容器中进行托管。

3. 面向切面编程—AOP

3.1 基本概念

在软件开发中,咱们须要编写许多的业务代码,但还有很多代码是业务无关的,如

  • 日志性能
  • 缓存清理
  • 资源回收
  • 权限查看

为了实现业务代码和非业务代码的拆散,Spring 利用代理模式抽出了非业务代码,造成了 AOP 思维。

AOP 全称 Aspect Oriented Programming,意为面向切面编程,假如业务代码自身是一条纵向的逻辑流,咱们在其中找几个点(切点,Pointcut),插入非业务代码,就像是一个个切面(Aspect),这就是一个简略的 AOP 例子。

切面和切点很容易被混同,严格来说,切点是切面的一部分信息,切面次要蕴含两局部信息:

  • 切入点;
  • 切入后要执行的动作(Advice)。

而在具体的实现中,Spring 为切面还加了一个信息:在切点的哪个阶段执行?由此衍生出有:

  • 前置告诉(Before):在指标办法被调用之前调用告诉性能;
  • 后置告诉(After):在指标办法被调用之后调用告诉性能;
  • 返回告诉(After-returning):在指标办法胜利执行之后调用告诉;
  • 异样告诉(After-throwing):在指标办法抛出异样后调用告诉;
  • 盘绕告诉(Around):告诉包裹了被告诉的办法,在被告诉的办法调用之前和调用之后执行自定义的行为。

对于 AOP 在 Spring 的实现有三种形式:

  1. 实现原生的 Spring API 接口
  2. 自定义切面类
  3. 注解

3.2 实现原生 Spring API 接口

Spring 提供了诸如 MethodBeforeAdvice、AfterAdvice、AfterReturningAdvice 等原生接口,咱们能够通过实现这些接口构建切面,如

@Component
public class Log implements MethodBeforeAdvice {
    /**
     * @param method  要执行的指标对象办法
     * @param objects 参数
     * @param o       指标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println(o.getClass().getName() + "-" + method.getName() + "将要执行");
    }
}

该 Log 类实现了 MethodBeforeAdvice,成为了一个在办法执行前插入的切面,其次,咱们须要在 XML 中进行配置,设置切点并建设其和切面的连贯:

<aop:config>
    <!-- 切入点:execution 表达式,execution(要执行的办法!)-->
    <aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/>
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
</aop:config>

execution 示意在办法执行时触发;* 示意办法返回值能够是任意类型,com.dong.service.UserServiceImpl.* 应用全限定类名和办法名指定要增加前置告诉的办法,应用 * 示意对 com.dong.service.UserServiceImpl 类下的所有办法都执行切入;(..)示意办法的参数列表,应用 (..) 示意办法的入参能够是任意类型。

须要留神,Log 必须是一个被 Spring 容器治理的 bean 对象,否则 Spring 将无奈找到该切面的 bean,因而应用 @Component 注解润饰(也能够应用 XML 配置等形式注入)。

3.3 自定义切面类

自定义切面不须要实现 Spring 的原生接口,首先须要定义一个切面类,并将加载给 Spring 容器进行托管。

@Component(value="diy")
public class DiyPointCut {public void before() {System.out.println("办法执行前");
    }

    public void after() {System.out.println("办法执行后");
    }
}

与 3.2 类似,该形式须要通过 XML 配置设置切点以及其和切面的连贯。

<aop:config>
    <!-- 自定义切面,ref:要援用的 bean-->
    <aop:aspect ref="diy">
        <aop:pointcut id="pointcut" expression="execution(* com.dong.service.UserServiceImpl.*(..))"/>
        <!-- 指定 advice 和切点的关系 -->
        <aop:before method="before" pointcut-ref="pointcut"></aop:before>
        <aop:after method="after" pointcut-ref="pointcut"></aop:after>
    </aop:aspect>
</aop:config>

3.4 注解

3.4.1 启用注解性能

首先,应用注解有两种形式:

  1. 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
            https://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:annotation-config/>、<!-- 注解包扫描 -->
        <context:component-scan base-package="com.dong.dao"></context:component-scan>
        <context:component-scan base-package="com.dong.log"></context:component-scan>
        <!-- 启用 aop 注解 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    </beans>
  2. 应用 @EnableAspectJAutoProxy 注解须要被切的类

    @Component(value = "userService")
    @EnableAspectJAutoProxy
    public class UserServiceImpl implements UserService {}

其次,应用注解时,须要留神两局部:

  • 申明切面:申明某类是切面以及切入后理论要执行的动作(Advice);
  • 定位切点:须要通过 execution 表达式定位须要切入的办法。

上面介绍一下注解的应用形式。

3.4.2 注解应用

只有对一个类应用 @Aspect 注解润饰,就表明这是一个切面。能够对其中的办法应用

  • @Before
  • @After
  • @AfterReturning
  • @Around
  • @AfterThrowing

注解指定切入机会,切入点通过 execution 表达式指定,举例如下:

@Aspect
@Component
public class CustomPointCut {@Before("execution(* com.dong.service.UserServiceImpl.*(..))")
    public void before() {System.out.println("aop: 办法产生前");
    }
}

4. 参考资料

  1. Spring 最新 5 残缺教程
  2. Spring 入门(十):Spring AOP 应用解说
  3. Spring AOP – 注解形式应用介绍

正文完
 0