侧边栏壁纸
  • 累计撰写 262 篇文章
  • 累计创建 139 个标签
  • 累计收到 16 条评论

目 录CONTENT

文章目录

[笔记]SpringBoot实战(1)-Spring 4.x

Sherlock
2018-12-07 / 0 评论 / 0 点赞 / 1526 阅读 / 0 字
温馨提示:
本文最后更新于2023-10-09,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

系列笔记:

简单记录下《JavaEE开发的颠覆者SpringBoot实战》的学习心得。 先贴下读者学习时的一些代码记录:


本书基于 Spring 4.XSpring Boot 1.3.0 展开(最低要求Java1.6,推荐1.8),主打基于注解和 Java 配置的零配置(无xml配置),由于读者学习时是基于Spring Boot 2.1.1 进行的,故文中 Spring的源码是基于Spring 5.1的,Spring Boot 的源码是基于2.1.1的 。

概念

控制反转(Inversion of Control, IoC)不等同于依赖注入(Dependency Injection, DI),实际上它们有着本质上的不同:控制反转是一种思想(设计原则),用来降低代码之间耦合度,而依赖注入是实现控制反转的一种形式。

Spring @Bean

Java配置是Spring 4.x、Spring Boot 推荐的配置方式,可以完全替代想,xml配置。

在 Spring 中是通过@Configuration和@Bean来实现的:

  • @Configuration 声明当前类时一个配置类,相当于一个Spring配置的xml文件
  • @Bean 注解在方法上,声明当前方法的返回值为一个Bean(Bean的名称可以是方法名)。

    在Spring容器中,只要容器中存在某个Bean,就可以在另外一个Bean的声明方法的参数中注入。@Bean 代表将当前方法返回的 POJO 装配到 IoC 容器中,而其属性 name 定义这个 Bean 的名称,如果没有配置它,则将当前方法名称作为 Bean 的名称保存到 Spring IoC 容器中 。

Spring 对 Bean的声明周期朝族偶支持两种方式:

  • Java 配置方式:使用@Bean的 initMethod 和 destoryMethod(相当于xml配置的init-method和destory-xml)。例如:

    @Bean(initMethos="init",destoryMethod="destory")

  • 注解方式:利用JSR-250的 @PostConstruct和@PreDestory(需要引入 jsr250-api jar包)。

分别代表在构造函数执行完之后进行和在Bean销毁之前执行。

条件注解@Conditional

@Confition 可根据满足某一特定条件创建一个特定的Bean(可以根据特定条件来控制Bean的创建行为)。在Spring Boot 中有大量应用。

相关特定条件类可通过实现 Condition 接口的 matches() 方法来定义判断条件,然后通过@Conditional(XXXCondition.calss) 来使用。

Profile

Profile 为在不同环境下使用不同配置提供了支持。
通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。在开发中使用@Profile注解类或者方法,达到在不同情况下选择实例化不同的Bean。
可通过设定jvm的spring.profiles.active 参数来设置配置环境。

使用时应该先设置活动的Profile,后注册Bean配置;类,不然会报Bean未定义的错误。

Spring 中简单使用示例:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();  
ctx.getEnvironment().setActiveProfiles("prod");  
ctx.register(XXXConfig.class);  
ctc.refresh();  

Spring AOP

AOP: 面向切面的编程,相对于OOP面向对象编程。
SpringAOP存在的目的是为了解耦。AOP可以让一组类共享相同的行为,在OOP中只能通过继承类(且为单继承)和实现接口,会使代码的耦合度增强,AOP弥补了OOP的不足。

Spring 支持 AspectJ 的注解式切面编程:

  • 1、使用@Aspect 声明是一个切面
  • 2、使用@After、@Before、@Around 定义建言(advice),可直接将拦截规则(切点)作为参数。
  • 3、其中@After、@Before、@Around 参数的兰姐规则称为切点(PointCut),为了使切点复用,可使用 @PointCut 专门定义拦截规则,然后在@After、@Before、@Around 的参数中调用。
  • 4、其中符合条件的每一个被拦截处为连接点(JoinPoint)

Spring 支持基于注解拦截和基于方法规则拦截两种方式,注解式拦截可以很好的控制要拦截的粒度和获得更丰富的信息,Spring本身在事务处理(@Transcational)和数据缓存(@Cacheable)等上面都是使用此种形式的拦截。

启用Spring AOP 需要引入 spring-aop、aspectjrt、aspectjweaver 包。
可通过 @Aspect 声明一个切面,使用 @Component 让该切面成为Spring容器管理的Bean。
通过 @PointCut 声明切点。
通过 @After、@Before、@Around 等声明一个建言,可以使用@PointCut定义的切点,如:如:@Pointcut("@annotation(com.xxx.aop.Action)");也可以直接使用拦截规则作为参数,如:@Before("execution(*com.xxx.aop.XXXService.*(..))")
通过 @EnableAspectJAutoProxy 注解开启Spring对AspectJ的支持。

后记

  • 如果被代理的目标对象实现了接口,那么Spring会默认使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。
  • 如果是被代理类的方法自调用,在自调用的过程中,是类自身的调用,而不是代理对象去调用,那么就不会产生 AOP,因为这样Spring就不能把你的代码织入到约定的流程中。
  • 需要代理的对象方法不能是private的,因为Spring不管使用的是JDK动态代理还是CGLIB动态代理,一个是针对接口实现的类,一个是通过子类实现。无论是接口还是父类,显然都不能出现 private 方法,否则子类或实现类都不能覆盖到。如果方法为private,那么在代理过程中,根本找不到这个方法,引起代理对象创建出现问题,也就可能会导致有的对象没有注入成功。

元注解

所谓元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解。

Spring 中有很多组合注解,比如:@Configuration 就是一个组合 @Component 注解,表名这个类其实也是一个Bean。

Java注解之 @Target、@Retention、@Documented

先来看一个Spring中的一个常用注解示例:

package org.springframework.stereotype;  
import java.lang.annotation.Documented;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 组合 @Component 注解
public @interface Controller {  
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any
     */
        // 覆盖 value 参数
    String value() default "";
}

@Target({ElementType.TYPE})

ElementType 这个枚举类型的常量提供了一个简单的分类:注释可能出现在Java程序中的语法位置(这些常量与元注释类型(@Target)一起指定写入注释的合法位置在何处)。

源码摘要:

package java.lang.annotation;  
// @since 1.5
public enum ElementType {  
    /** 类, 接口 (包括注释类型), 或 枚举 声明 Class, interface (including annotation type), or enum declaration */
    TYPE,
    /** 字段声明(包括枚举常量)Field declaration (includes enum constants) */
    FIELD,
    /** 方法声明 Method declaration */
    METHOD,
    /** 正式的参数声明 Formal parameter declaration */
    PARAMETER,
    /** 构造函数声明 Constructor declaration */
    CONSTRUCTOR,
    /** 局部变量声明 Local variable declaration */
    LOCAL_VARIABLE,
    /** 注释类型声明 Annotation type declaration */
    ANNOTATION_TYPE,
    /** 包声明 Package declaration */
    PACKAGE,
    /**
     * 类型参数声明 Type parameter declaration
     * @since 1.8
     */
    TYPE_PARAMETER,
    /**
     * 使用的类型 Use of a type
     * @since 1.8
     */
    TYPE_USE
}

@Retention({RetentionPolicy.Runtime})

RetentionPolicy 这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间。
源码摘要:

package java.lang.annotation;  
// @since 1.5
public enum RetentionPolicy {  
    /**
     * 注释只在源代码级别保留,编译时被忽略 (Annotations are to be discarded by the compiler)
     */
    SOURCE,
    /**
     * 注释将被编译器在类文件中记录,但在运行时不需要VM保留。这是默认的行为。
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,
    /**
     * 注释将被编译器记录在类文件中,在运行时被VM保留,因此可以反读。
     * (Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.)
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

@Documented

表示默认情况下,javadoc 和类似工具将记录带有类型的注释。 此类型应用于注释类型的声明,其注释会影响其客户端对带注释元素的使用。 如果使用 Documented 注释类型声明,则其注释将成为带注释元素的公共API的一部分。

Spring 单元测试

Spring 提供了一个 SpringJUnit4ClassRunner 类,他提供了 Spring TestContect Framework 的功能。通过 @ContextConfiguration 来配置 Application Context,通过 @ActiveProfiles 确定活动的profile。

import org.junit.runner.RunWith;  
import org.springframework.test.context.ActiveProfiles;  
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

// RunWith是指定使用的单元测试执行类(指定的类需继承org.junit.runners.BlockJUnit4ClassRunner,@since junit 4.4)
// 在junit环境下提供 Spring TestContect Framework 的功能,
@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = {XXXConfig.class}) // 用来加载配置 ApplicationContext,其中 classes 属性用来加载配置类。
@ActiveProfiles("prod") // 用来声明活动的 profile
// 这个用于指定在测试类执行之前,可以做的一些动作,TransactionalTestExecutionListener.class用于对事务进行管理
@TestExecutionListeners({ TransactionalTestExecutionListener.class })
/**
 * 不是必须的,这里是和@TestExecutionListeners中的TransactionalTestExecutionListener.class 
 * 配合使用,用于保证插入的数据库中的测试数据,在测试完后,事务回滚,将插入的数据给删除掉,保证数 
 * 据库的干净。如果没有显示的指定@Transactional,那么插入到数据库中的数据就是真实的插入了。
 */
@Transactional
public class XXXTests {  
    // xxxx
}

Spring 常用概念

IOC

IOC(Inversion Of Controll,控制反转)是一种设计思想,将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。

这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂,当需要创建一个对象,只需要配置好配置文件/注解即可,不用考虑对象是如何被创建出来的,大大增加了项目的可维护性且降低了开发难度。

AOP

AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。使用AOP之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样可以大大简化代码量,提高了系统的扩展性。

Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。

Spring AOP / AspectJ AOP 的区别?

Spring AOP属于运行时增强,而AspectJ是编译时增强。

Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。

AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。如果切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区