文档

Overview

  • Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
  • Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。

容器,非侵入,IOC,AOP,事务管理

7大模块

image.png

  1. Spring Core

核心模块, IOC & AOP

  1. Spring Aspects

该模块为与 AspectJ 的集成提供支持。

  1. Spring AOP

提供了面向切面的编程实现。

  1. Spring Data Access/Integration :

Spring Data Access/Integration 由 5 个模块组成:

  • spring-jdbc : 提供了对数据库访问的抽象 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需要和 JDBC API 交互,这样就屏蔽了数据库的影响。
  • spring-tx : 提供对事务的支持。
  • spring-orm : 提供对 Hibernate 等 ORM 框架的支持。
  • spring-oxm : 提供对 Castor 等 OXM 框架的支持。
  • spring-jms : Java 消息服务。
    1. Spring Web

Spring Web 由 4 个模块组成:

  • spring-web :对 Web 功能的实现提供一些最基础的支持。
  • spring-webmvc : 提供对 Spring MVC 的实现。
  • spring-websocket : 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。
  • spring-webflux :提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步.
    1. Spring Test

Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。
Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好。

IOC

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法。
image.png
将对象之间的相互依赖关系交Spring框架( IOC 容器)来管理,并由 IOC 容器完成对象的注入。

配置元数据的方式

  • XML配置:Bean的定义信息是和实现分离的
  • 注解:零配置
  • Java 配置类

    xml配置

    参见附件xml配置

    applicationContext.xml
    sub.xml
    idea configure application context for xml file.
    image.png

    ApplicationContext

    对象由Spring创建,类被Spring托管

    The location path or paths supplied to an ApplicationContext constructor are resource strings that let the container load configuration metadata from a variety of external resources, such as the local file system, the Java CLASSPATH, and so on.

ApplicationContext的实现:

  • ClassPathXmlApplicationContext
  • AnnotationConfigApplicationContext
  • ……

    1. @Test
    2. public void test(){
    3. //解析beans.xml文件 , 生成管理相应的Bean对象
    4. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    5. //getBean : 参数即为spring配置文件中bean的id .
    6. Hello hello = (Hello) context.getBean("hello");
    7. hello.show();
    8. }

    image.png
    在配置文件加载的时候。其中管理的对象都已经初始化了

    依赖注入

    <property></property>

    构造器注入

    1. <bean id="xxx" class="a.b.c">
    2. <constructor-arg name="kkk" value="vvv"></constructor-arg>
    3. </bean>

    Set注入(各种数据类型)

  • 常量注入

  • Bean注入
  • 数组注入
  • List注入
  • Map注入
  • Set注入
  • Null注入
  • Properties注入:Java.Util.Properties: 配置类
  • ……
    <bean id="xxx" class="a.b.c">
      <property name="kkk">
                  <!--各种类型的注入-->
        </property>
    </bean>
    

    p命名和c命名注入

    Spring5中p命名空间需要类中有无参构造才能使用,c命名空间的使用需要有对应的含参构造器。 ```xml
<a name="Rmjfz"></a> ## Bean 那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。java @SpringBootApplication public class Application { private static ApplicationContext applicationContext; public static void main(String[] args) { applicationContext = SpringApplication.run(Application.class, args); displayAllBeans(); } public static void displayAllBeans() { String[] allBeanNames = applicationContext.getBeanDefinitionNames(); for(String beanName : allBeanNames) { System.out.println(beanName); } } } <a name="wCRtm"></a> ### Bean的作用域 对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。 | **Scope** | **Description** | | | --- | --- | --- | | [singleton](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-singleton) | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. | 在创建起容器时就同时自动创建<br />了一个bean的对象,不管你是否<br />使用,他都存在了。<br />关闭工厂 ,所有的对象都会销毁。 | | [prototype](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-prototype) | Scopes a single bean definition to any number of object instances. | <br />1. 将其注入到另一个bean中,或者<br /> 以程序的方式调用容器的getBean()<br />方法)时都会创建一个新的bean实例。<br />2. 在我们创建容器的时候并没有实例<br /> 化,而是当我们获取bean的时候才会去<br />创建一个对象。<br />3. 大部分 bean 实际都是无状态(没<br /> 有实例变量)的(比如 Dao、Service),<br />这种情况下, bean 是线程安全的。<br />4. 关闭工厂 ,所有的对象不会销毁。<br /> 内部的垃圾回收机制会回收。 | | [request](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-request) | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. | | | [session](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-session) | Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. | | | [application](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-application) | Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext. | | | [websocket](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-websocket-scope) | Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext. | | <a name="cQIE5"></a> ## 自动装配 **推荐不使用自动装配xml配置 , 而使用注解。**<br />自动装配是使用spring满足**bean依赖**的一种方法,spring会在应用上下文中为**某个bean寻找其依赖的bean**。<br />Spring中bean的三种装配机制。 1. 在xml中显式配置;(ref="") 1. 在java中显式配置; 1. 隐式的bean发现机制和自动装配。 Spring的自动装配需要从两个角度来实现,或者说是两个操作: 1. **组件扫描(**component scanning):spring会自动发现应用上下文中所创建的bean; 1. **自动装配**(autowiring):spring自动满足bean之间的依赖,也就是我们说的IOC/DI <a name="wZoGz"></a> ### <bean> byName 当一个bean节点带有 autowire byName的属性时。 1. 将查找其类中**所有的set方法名**,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。 1. 去spring容器中寻找是否有此字符串名称id的对象。 1. 如果有,就取出注入;如果没有,就报空指针异常。 <a name="eqkwN"></a> ### <bean> byType 首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/436938/1650177279271-1fa658cd-4b2c-40ad-9853-131159c67370.png#clientId=u0fd793ef-3aae-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1080&id=u1052f009&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1080&originWidth=1920&originalType=binary&ratio=1&rotation=0&showTitle=false&size=583936&status=done&style=none&taskId=u5d9377be-d595-426c-915a-3e2b95eacc0&title=&width=1920) <a name="OvXau"></a> ### 使用注解 在xml文件配置了<context:component-scan>标签后,spring容器可以自动去扫描base-pack所指定的包或其子包下面的java类文件,如果扫描到有@Component、@Controller、@Service 、@Repository等注解修饰的Java类,则将这些类注册为spring容器中的bean。xml <?xml version=”1.0” encoding=”UTF-8”?> !--https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-annotation-config-- <a name="oXqIL"></a> #### @Autowired、@Quanlifier、@Resource对比 | | 按什么装配 | <br /> | 注意 | | --- | --- | --- | --- | | @Autowired | byType | @Autowired(required = false)<br />等同于@Nullable | 通过反射实现的,<br />依赖不一定需要Setter | | @Qualifier<br />[@Qualifier tutorial](https://www.baeldung.com/spring-qualifier-annotation) | byName | @Autowired<br />@Qualifier(value = "cat2") | 必须配合@Autowired使用 | | @Resource | byName (指定name)<br />-><br />byName(默认name)<br />-><br />byType | @Resource(name = "dog1")<br />或<br />@Resource | **不常用**<br />- 属于J2EE复返,引入javax.annotation-api<br />- 如果name属性一旦指定,<br /> 就只会按照名称进行装配 |java public class AutowiredDemo { @Resource(name = “logCenterProducer”) private AliyunMqTcpProducer logCenterProducer; } <a name="e7hEC"></a> #### [Null-safety](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#null-safety) @Nullable, @NoneNull等 <a name="zOhR2"></a> #### @Component代替<bean> 衍生注解:@Repository,@Service, @Controller <a name="mjvRh"></a> #### @Value 相当于<property><br />简单的增删改查可以用,复杂的依赖注入建议还是用xml配置。 <a name="rRlRr"></a> #### @Scope <a name="eWkga"></a> ### xml和注解最佳实践 - XML可以适用任何场景 ,结构清晰,维护方便 - 注解更简洁,开发方便。不能在外部引用的类上使用注解,但可以在xml配置,解決方式是JavaConfig配置。 **xml与注解整合开发** :推荐最佳实践 - xml管理Bean - 注解完成属性注入 不是绝对的,根据实际项目。 <a name="gKboo"></a> ### JavaConfig配置java @Configuration @ComponentScan(“com.yanjing”) @Import(BeanConfig2.class) //合并另一个配置类 public class BeanConfig { // 注册一个bean,就相当于之前些的一个标签 // 返回:bean的类型 // 方法名: 是bean的id @Bean public House bigHouse() { return new House(“yuanda”); } } ![image.png](https://cdn.nlark.com/yuque/0/2022/png/436938/1650178831943-990080e9-def4-4eaa-905f-48c41167a6c0.png#clientId=u0fd793ef-3aae-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1080&id=u2b8574a2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1080&originWidth=1920&originalType=binary&ratio=1&rotation=0&showTitle=false&size=977200&status=done&style=none&taskId=u970dd92f-88d5-4444-9382-bb5b24b9567&title=&width=1920)sql @Configuration public class ChainedTransactionConfig { /* @param mdmTransactionManager 也是可注入的bean,不用field autowire @param defaultTransactionManager 也是可注入的bean,不用field autowire @return chainedTransactionManager bean / @Bean public PlatformTransactionManager chainedTransactionManager( @Qualifier(“myVendorTransactionManager”) PlatformTransactionManager mdmTransactionManager, @Qualifier(“defaultTransactionManager”) PlatformTransactionManager defaultTransactionManager) { return new ChainedTransactionManager(mdmTransactionManager, defaultTransactionManager); } } <a name="Qgv7a"></a> # AOP <a name="S4YrB"></a> ## 概念 <a name="W3yzL"></a> ### 面试时AOP聊动态代理 - AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 - Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理。 ![image.png](https://cdn.nlark.com/yuque/0/2021/png/436938/1639917466161-4d91acce-2d16-4ea0-929f-1c7b31211386.png#clientId=u4587bc52-5a84-4&crop=0.2788&crop=0.0405&crop=0.9517&crop=0.9047&from=paste&height=345&id=u9986060b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1080&originWidth=1920&originalType=binary&ratio=1&rotation=0&showTitle=false&size=719399&status=done&style=none&taskId=uf2b075c1-6434-4fcb-8f44-3b3d4e33501&title=&width=613)<br />连接点:`ProceedingJoinPoint`<br />SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/436938/1639917961492-4167de91-0cde-4562-a207-eefd89efabcd.png#clientId=u4587bc52-5a84-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=332&id=ua0fefdb1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=474&originWidth=762&originalType=binary&ratio=1&rotation=0&showTitle=false&size=173410&status=done&style=none&taskId=ud8c4fa10-2f9c-4d76-9e11-30ff83ebb72&title=&width=534) <a name="m7ncb"></a> ## Spring实现AOP <a name="hRDLG"></a> ### org.springframework.aopxml // 织入 org.aspectj aspectjweaver 1.9.7 <a name="UvXEL"></a> ### 使用Spring API 每个advice定义后一个类,使用SpringAPIjava public class BeforeLog implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object target) throws Throwable { System.out.println(target.getClass().getName() + “的” + method.getName() + “被执行了”); } } public class AfterLog implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(“执行了” + method.getName() + “方法,返回结果为:” + returnValue); } } xml <?xml version=”1.0” encoding=”UTF-8”?> <— 这里省略了beans中的属性 —>
<!--方式一,使用Spring原生API接口 -->
<!--配置AOP,需要导入AOP的约束-->
<aop:config>
    <!--切入点: expression:表达式,execution(要执行的位置)-->
    <aop:pointcut id="pointcut" expression="execution(* com.yanjing.service.UserServiceImpl.*(..))"/>

    <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"></aop:advisor>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>

<a name="e62Tc"></a>
### 自定义切面
切面是纯粹的pojo类,通过xml配置切入。在xml中相对SpringAPI实现更简单点,但是不能使用反射的一些东西。切入点可以复用。
```xml
<?xml version="1.0" encoding="UTF-8"?>
<-- 这里省略了beans中的属性 -->
<beans>

    <!--方式二,自定义切面,使用AOP的标签-->
    <bean id="userService2" class="com.yanjing.service.UserServiceImpl2"></bean>
    <bean id="diy" class="com.yanjing.aspect.DiyAspect"></bean>
    <aop:config>
        <!--自定义切面,ref:要引用的类-->
        <aop:aspect ref="diy">
            <aop:pointcut id="diyPointcut" expression="execution(* com.yanjing.service.UserServiceImpl2.*(..))"/>
            <aop:before method="before" pointcut-ref="diyPointcut"></aop:before>
            <aop:after method="after" pointcut-ref="diyPointcut"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

注解方式

在专门的一个类里设置Aspect
详细:AnnotationPointCut.java

@Aspect
public class AnnotationPointCut {

    @Before("execution(* com.yanjing.service.UserServiceImpl3.*(..))")
    public void before(){
        System.out.println("---------注解方式方法执行前---------");
    }

    @After("execution(* com.yanjing.service.UserServiceImpl3.*(..))")
    public void after(){
        System.out.println("---------注解方式方法执行后---------");
    }

    // 用得较少
    @Around("execution(* com.yanjing.service.UserServiceImpl3.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("-------环绕前------");
        // 执行方法
        Signature signature = jp.getSignature();
        jp.proceed();
        System.out.println("--------环绕后--------");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<-- 这里省略了beans中的属性 -->
<beans>
    <!--方式三,注解方式-->
    <bean id="userService3" class="com.yanjing.service.UserServiceImpl3"></bean>
    <bean id="annotationPointcut" class="com.yanjing.aspect.AnnotationPointCut"></bean>
    <!--    如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,-->
    <!--    就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理-->
    <!--    proxy-target-class默认false,使用JDK动态代理-->
          <!--不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>

点击查看【bilibili】