程序笔记   发布时间:2022-07-16  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Java程序员必备框架—Spring全家桶的前世今生详细梳理大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

在工作中经常会发现很多同事连最常用的SSM框架使用起来也经常忘这忘那的࿰c;关于spring甚至只记得IOC、DI、AOP࿰c;然后就在网上找资料浪费大部分时间࿰c;所以本文重温了一遍帮大伙加深理解࿰c;同时做个整理࿰c;以后再忘来看这篇文章就好了。

我平时也会收集一些不错的spring学习书籍PDF࿰c;毕竟程序员的书都挺贵的࿰c;不可能每本都买实体࿰c;自己啃完也会梳理学习笔记࿰c;都在这了>>spring一网打尽c;直接点击就可以无偿获取。

1. 体系结构

Java程序员必备框架—Spring全家桶的前世今生详细梳理

Spring是模块化的࿰c;可以选择合适的模块来使用࿰c;其体系结构分为5个部分࿰c;分别为:

Core Container

核心容器:Spring最主要的模块࿰c;主要提供了IOC、DI、BeanFactory、Context等࿰c;列出的这些学习过Spring的同学应该都认识

Data Access/Integration

数据访问/集成:即有JDBC的抽象层、ORM对象关系映射API、还有事务支持(重要)等

Web

Web:基础的Web功能如Servlet、http、Web-MVC、Web-Socket等

Test

测试:支持具有Junit或TestNG框架的Spring组件测试

其他

AOP、Aspects(面向切面编程框架)等

2. IOC

2.1 引入耦合概念

耦合:即是类间或方法间的依赖关系࿰c;编译时期的依赖会导致后期维护十分困难࿰c;一处的改动导致其他依赖的地方都需改动࿰c;所以要解耦

解耦:解除程序间的依赖关系࿰c;但在实际开发中我们只能做到编译时期不依赖࿰c;运行时期才依赖即可࿰c;没有依赖关系即没有必要存在了

解决思路:使用Java的反射机制来避免new关键字(通过读取配置文件来获取对象全限定类名)、使用工厂模式

2.2 IOC容器

Spring框架的核心࿰c;主要用来存放Bean对象࿰c;其中有个底层BeanFactory接口只提供最简单的容器功能(特点延迟加载)࿰c;一般不使用。常用的是其子类接口ApplicationContext接口(创建容器时立即实例化对象࿰c;继承BeanFactory接口)࿰c;提供了高级功能(访问资源࿰c;解析文件信息࿰c;载入多个继承关系的上下文࿰c;拦截器等)。

ApplicationContext接口有三个实现类:ClassPathXmlApplicationContext、FileSystemoXmlApplication、AnnotionalConfigApplication࿰c;从名字可以知道他们的区别࿰c;下面讲解都将围绕ApplicationContext接口。

容器为Map结构࿰c;键为id࿰c;值为Object对象。

2.2.1 Bean的创建方式

无参构造

只配了id、class标签属性(此时一定要有无参函数࿰c;添加有参构造时记得补回无参构造)

普通工厂创建

可能是别人写好的类或者jar包࿰c;我们无法修改其源码(只有字节码)来提供无参构造函数࿰c;eg:

// 这是别人的jar包是使用工厂来获取实例对象的
public class InstanceFactory {
    public User getUser() {
        return new User();
    }
}

 <!--  工厂类  -->
<bean id="userFactory" class="com.howl.entity.UserFactory"></bean>
<!--  指定工厂类及其生产实例对象的方法  -->
<bean id="user" factory-bean="userFactory" factory-method="getUser"></bean>

静态工厂创建

<!--  class使用静态工厂类,方法为静态方法生产实例对象  -->
<bean id="user" class="com.howl.entity.UserFactory" factory-method="getUser"></bean>

2.2.2 Bean标签

该标签在applicationContext.xml中表示一个被管理的Bean对象࿰c;Spring读取xml配置文件后把内容放入Spring的Bean定义注册表࿰c;然后根据该注册表来实例化Bean对象将其放入Bean缓存池中࿰c;应用程序使用对象时从缓存池中获取

属性描述
class指定用来创建bean类
id唯一的标识符࿰c;可用 ID 或 name 属性来指定 bean 标识符
scope对象的作用域࿰c;singleton(默认)/prototype
lazy-init是否懒创建 true/false
init-method初始化调用的方法
destroy-methodx销毁调用的方法
autowire不建议使用࿰c;自动装配byType、byName、constructor
factory-bean指定工厂类
factory-method指定工厂方法
元素描述
constructor-arg构造函数注入
properties属性注入
元素的属性描述
type按照类型注入
index按照下标注入
name按照名字注入࿰c;最常用
value给基本类型和String注入
ref给其他bean类型注入
元素的标签描述
<list>
<Set>
<Map>
<props>

2.2.3 使用

注意:默认使用无参构造函数的࿰c;若自己写了有参构造࿰c;记得补回无参构造

XML

<bean id="user" class="com.howl.entity.User"></bean>

ApplicationContext ac = new ClassPathXmlApplicationContext
("applicationContext.xml");
User user = (User) ac.getBean("user");
user.getName();

注解

前提在xml配置文件中开启bean扫描

<context:component-scan base-package="com.howl.entity"></context:component-scan>

// 默认是类名首字母小写
@Component(value="user")
public class User{
    int id;
    String name;
    String eamil;
}

2.2.4 生命周期

单例:与容器同生共死

多例: 使用时创建࿰c;GC回收时死亡

3. DI

Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系࿰c;能注入的数据类型有三类:基本类型和String࿰c;其他Bean类型࿰c;集合类型。注入方式有:构造函数࿰c;set方法࿰c;注解

3.1 基于构造函数的注入

<!--  把对象的创建交给Spring管理  -->
<bean id="user" class="com.howl.entity.User">
    <constructor-arg type="int" value="1"></constructor-arg>
    <constructor-arg index="1" value="howl"></constructor-arg>
    <constructor-arg name="email" value="xxx@qq.com"></constructor-arg>
    <constructor-arg name="birthday" ref="brithday"></constructor-arg>
</bean>

<bean id="brithday" class="java.util.Date"></bean>

3.2 基于setter注入(常用)

被注入的bean一定要有setter函数才可注入࿰c;而且其不关心属性叫什么名字࿰c;只关心setter叫什么名字

<bean id="user" class="com.howl.entity.User">
    <property name="id" value="1"></property>
    <property name="name" value="howl"></property>
    <property name="email" value="XXX@qq.com"></property>
    <property name="birthday" ref="brithday"></property>
</bean>

<bean id="brithday" class="java.util.Date"></bean>

3.3 注入集合

内部有复杂标签、、、这里使用setter注入

<bean id="user" class="com.howl.entity.User">

	<property name="addressList">
		<list>
			<value>INDIA</value>
            <value>Pakistan</value>
            <value>USA</value>
            <ref bean="address2"/>
        </list>
    </property>

    <property name="addressSet">
        <set>
            <value>INDIA</value>
            <ref bean="address2"/>
            <value>USA</value>
            <value>USA</value>
        </set>
    </property>

    <property name="addressmap">
        <map>
            <entry key="1" value="INDIA"/>
            <entry key="2" value-ref="address1"/>
            <entry key="3" value="usA"/>
        </map>
    </property>

    <property name="addressProp">
        <props>
            <prop key="one">INDIA</prop>
            <prop key="two">Pakistan</prop>
            <prop key="three">USA</prop>
            <prop key="four">USA</prop>
        </props>
    </property>

</bean>

3.4 注解

@Autowired:自动按照类型注入(所以使用注解时setter方法不是必须的࿰c;可用在变量上࿰c;也可在方法上)。若容器中有唯一的一个bean对象类型和要注入的变量类型匹配就可以注入;若一个类型匹配都没有࿰c;则报错;若有多个类型匹配时:先匹配全部的类型࿰c;再继续匹配id是否有一致的c;有则注入࿰c;没有则报错

@Qualifier:在按照类型注入基础上按id注入࿰c;给类成员变量注入时不能单独使用࿰c;方法参数注入时可以单独使用

@resource:上面二者的结合

注意:以上三个注入只能注入bean类型数据࿰c;不能注入基本类型和String࿰c;集合类型的注入只能通过XMl方式实现

@Value:注入基本类型和String数据

承接上面有个User类了

@Component(value = "oneUser")
@Scope(value = "singleton")
public class OneUser {

    @Autowired	// 按类型注入
    User user;

    @Value(value = "注入的String类型")
    String str;

    public void UserToString() {
        System.out.println(user + str);
    }
}

3.5 配置类(在SpringBoot中经常会遇到)

配置类等同于aplicationContext.xml࿰c;一般配置类要配置的是需要参数注入的bean对象࿰c;不需要参数配置的直接在类上加@Component

/**
 * 该类是个配置类࿰c;作用与applicationContext.xml相等
 * @Configuration表示配置类
 * @ComponentScan(value = {""})内容可以传多个࿰c;表示数组
 * @Bean 表示将返回值放入容器࿰c;默认方法名为id
 * @Import 导入其他配置类
 * @EnableAspectJAutoProxy 表示开启注解
 */
@Configuration
@Import(OtherConfiguration.class)
@EnableAspectJAutoProxy
@ComponentScan(value = {"com.howl.entity"})
public class SpringConfiguration {

    @Bean(value = "userFactory")
    @Scope(value = "prototype")
    public UserFactory createUserFactory(){

        // 这里的对象容器管理不到࿰c;即不能用@Autowired࿰c;要自己new出来
        User user = new User();

        // 这里是基于构造函数注入
        return new UserFactory(user);
    }
}

@Configuration
public class OtherConfiguration {

    @Bean("user")
    public User createUser(){
        User user = new User();

        // 这里是基于setter注入
        user.setId(1);
        user.setName("howl");
        return user;
    }
}

4. AOP

4.1 动态代理

动态代理:基于接口(invoke)和基于子类(Enhancer的create方法)࿰c;基于子类的需要第三方包cglib࿰c;这里只说明基于接口的动态代理࿰c;笔者 动态代理的博文

Object ob = Proxy.newProxyInstance(mydog.getClass().getClassLoader(), mydog.getClass().geTinterfaces(),new InvocationHandler(){

    // 参数依次为:被代理类一般不使用、使用的方法、参数的数组
    // 返回值为创建的代理对象
    // 该方法会拦截类的所有方法࿰c;并在每个方法内注入invoke内容
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 只增强eat方法
            if(method.getName().equals("eat")){
                System.out.println("吃肉前洗手");
                method.invoke(mydog, args);
            }else{
                method.invoke(mydog, args);
            }
            return proxy;
        }
})

4.2 AOP

相关术语:

连接点:这里指被拦截的方法(Spring只支持方法)

通知:拦截到连接点要执行的任务

切入点:拦截中要被增强的方法

织入:增强方法的过程

代理对象:增强功能后返回的对象

切面:整体的结合࿰c;什么时候࿰c;如何增强方法

xml配置

<!-- 需要额外的jar包࿰c;aspectjweaver表达式需要 -->

<!--  被切入的方法  -->
<bean id="accountserviceImpl" class="com.howl.interfaces.impl.AccountserviceImpl"></bean>

<!--  通知bean也交给容器管理  -->
<bean id="logger" class="com.howl.util.Logger"></bean>

<!--  配置aop  -->
<aop:config>

    <aop:pointcut id="pt1" expression="execution(* com.howl.interfaces..*(..))"/>

    <aop:aspect id="logAdvice" ref="logger">
        <aop:before method="beforeLog" pointcut-ref="pt1"></aop:before>
        <aop:after-returning method="afterReturningLog" pointcut-ref="pt1"></aop:after-returning>
        <aop:after-throwing method="afterThrowingLog" pointcut-ref="pt1"></aop:after-throwing>
        <aop:after method="afterLog" pointcut="execution(* com.howl.interfaces..*(..))"></aop:after>

        <!--  配置环绕通知,测试时请把上面四个注释掉࿰c;排除干扰  -->
        <aop:around method="aroundLog" pointcut-ref="pt1"></aop:around>

    </aop:aspect>
</aop:config>

<!-- 切入表达式 -->
<!-- 访问修饰符 . 返回值 . 包名 . 包名 . 包名。。。 . 类名 . 方法名(参数列表) -->
<!-- public void com.howl.service.Userservice.deleteUser() -->
<!-- 访问修饰符可以省略 -->
<!-- * 表示通配࿰c;可用于修饰符࿰c;返回值࿰c;包名࿰c;方法名 -->
<!-- .. 标志当前包及其子包 -->
<!-- ..可以表示有无参数࿰c;*表示有参数 -->
<!-- * com.howl.service.*(..) -->

<!-- 环绕通知是手动编码方式实现增强方法合适执行的方式࿰c;类似于invoke? -->

即环绕通知是手动配置切入方法的࿰c;且Spring框架提供了ProceedingJoinPoint࿰c;该接口有一个proceed()和getArgs()方法。此方法就明确相当于调用切入点方法和获取参数。在程序执行时࿰c;spring框架会为我们提供该接口的实现类供我们使用

// 抽取了公共的代码(日志)
public class Logger {

    public void beforeLog(){
        System.out.println("前置通知");
    }

    public void afterReturningLog(){
        System.out.println("后置通知");
    }

    public void afterThrowingLog(){
        System.out.println("异常通知");
    }

    public void afterLog(){
        System.out.println("最终通知");
    }

    // 这里就是环绕通知
    public Object aroundLog(ProceedingJoinPoint pjp){

        Object rtValue = null;

        try {

            // 获取方法参数
            Object[] args = pjp.getArgs();

            System.out.println("前置通知");

            // 调用业务层方法
            rtValue = pjp.proceed();

            System.out.println("后置通知");

        } catch (Throwable t) {
            System.out.println("异常通知");
            t.printStackTrace();
        } finally {
            System.out.println("最终通知");
        }
        return rtValue;
    }

}

基于注解的AOP

<!-- 配置Spring创建容器时要扫描的包,主要扫描被切入的类࿰c;以及切面类 -->
<context:compinent-scan base-package="com.howl.*"></context:compinent-scan>

<!-- 这二者的类上要注解 @Compinent / @service -->

<!-- 开启AOP注解支持 -->
<aop:aspectj:autoproxy></aop:aspectj:autoproxy>>

注意要在切面类上加上注解表示是个切面类࿰c;四个通知在注解中通知顺序是不能决定的且乱序࿰c;不建议使用c;不过可用环绕通知代替 。即注解中建议使用环绕通知来代替其他四个通知

// 抽取了公共的日志
@Component(value = "logger")
@Aspect
public class Logger {

    @Pointcut("execution(* com.howl.interfaces..*(..))")
    private void pt1(){}

    @Before("pt1()")
    public void beforeLog(){
        System.out.println("前置通知");
    }

    @AfterReturning("pt1()")
    public void afterReturningLog(){
        System.out.println("后置通知");
    }

    @AfterThrowing("pt1()")
    public void afterThrowingLog(){
        System.out.println("异常通知");
    }

    @After("pt1()")
    public void afterLog(){
        System.out.println("最终通知");
    }

    @Around("pt1()")
    public Object aroundLog(ProceedingJoinPoint pjp){

        Object rtValue = null;

        try {

            // 获取方法参数
            Object[] args = pjp.getArgs();

            System.out.println("前置通知");

            // 调用业务层方法
            rtValue = pjp.proceed();

            System.out.println("后置通知");

        } catch (Throwable t) {
            System.out.println("异常通知");
            t.printStackTrace();
        } finally {
            System.out.println("最终通知");
        }
        return rtValue;
    }

}

5. 事务

Spring提供了声明式事务和编程式事务࿰c;后者难于使用而选择放弃࿰c;Spring提供的事务在业务层࿰c;是基于AOP的

5.1 声明式事务

从业务代码中分离事务管理࿰c;仅仅使用注释或 XML 配置来管理事务࿰c;Spring 把事务抽象成接口 org.springframework.transaction.PlatformtransactionManager ࿰c;其内容如下࿰c;重要的是其只是个接口࿰c;真正实现类是:org.springframework.jdbc.datasource.DatasourcetransactionManager

public interface PlatformtransactionManager {
    // 根据定义创建或获取当前事务
   transactionStatus gettransaction(transactionDefinition definition);
   void commit(transactionStatus status);
   void rollBACk(transactionStatus status);
}

transactionDefinition事务定义信息

public interface transactionDefinition {
   int getPropagationBehavior();
   int getisolationLevel();
   String getName();
   int getTimeout();
   Boolean isReadOnly();
}

因为不熟悉所以把过程全部贴下来

5.2 xml配置

建表

create table `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `money` int(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

entity

public class Account {

    private int id;
    privatE int money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public Account(int id, int money) {
        this.id = id;
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", money=" + money +
                '}';
    }
}

Dao层

public interface AccountDao {

    // 查找账户
    public Account SELEctAccountById(int id);

    // 更新账户
    public void updateAccountById(@Param(value = "id") int id, @Param(value = "money") int money);

}

@H_581_7@mapper层

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.howl.dao.AccountDao">

    <SELEct id="selectAccountById" resultType="com.howl.entity.Account">
        select * from account WHERE id = #{iD};
    </SELEct>

    <update id="updateAccountById">
        updatE account SET money = #{money} WHERE id = #{iD}
    </update>

</mapper>

service层

public interface Accountservice {

    public Account SELEctAccountById(int id);

    public void transfer(int fid,int sid,int money);

}

service层Impl

public class AccountserviceImpl implements Accountservice {

    @Autowired
    private AccountDao accountDao;

    public Account SELEctAccountById(int id) {
        return accountDao.SELEctAccountById(id);
    }

    // 这里只虑事务࿰c;不关心钱额是否充足
    public void transfer(int fid, int sid, int money) {

        Account sourceAccount = accountDao.SELEctAccountById(fid);
        Account targetAccount = accountDao.SELEctAccountById(sid);

        accountDao.updateAccountById(fid, sourceAccount.getMoney() - money);

        // 异常
         int i = 1 / 0;

        accountDao.updateAccountById(sid, targetAccount.getMoney() + money);
    }
}

applicationContext.xml配置

<!--  配置数据源,spring自带的没有连接池功能  -->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriveRMANagerDatasource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring"></property>
    <property name="username" value="root"></property>
    <property name="password" value=""></property>
</bean>

<!--  配置sqlSessionFactory工厂  -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLOCATIOn" value="classpath:mybatis-config.xml"></property>
    <property name="datasource" ref="datasource"></property>
</bean>

<!--  业务层bean  -->
<bean id="accountserviceImpl" class="com.howl.service.impl.AccountserviceImpl" lazy-init="true">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

<!--  事务管理器  -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DatasourcetransactionManager">
    <property name="datasource" ref="datasource"></property>
</bean>

<!--  配置事务通知࿰c;可以理解为Logger  -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--  配置事务的属性
      isolation:隔离界别࿰c;默认使用数据库的
      propagation:转播行为࿰c;默认requIRED
      read-only:只有查询方法才需要设置true
      timeout:默认-1永不超时
      no-rollBACk-for
      rollBACk-for

      -->
    <tx:attributes>
        <!--  name中是选择匹配的方法  -->
        <tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
        <tx:method name="*" propagation="requIRED" read-only="false"></tx:method>
    </tx:attributes>
</tx:advice>

<!--  配置AOP  -->
<aop:config>
    <aop:pointcut id="pt1" expression="execution(* com.howl.service.impl.AccountserviceImpl.transfer(..))"/>
    <!--  建立切入点表达式与事务通知的对应关系  -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>

测试

public class UI {

    public static void main(String[] args) {

        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

        Accountservice accountservice = (AccountservicE) ac.getBean("accountserviceImpl");

        Account account = accountservice.SELEctAccountById(1);
        System.out.println(account);

        accountservice.transfer(1,2,100);
    }
}

正常或发生异常都完美运行

个人觉得重点在于配置事务管理器(而像数据源这样是日常需要)

事务管理器:管理获取的数据库连接

事务通知:根据事务管理器来配置所需要的通知(类似于前后置通知)

上面两个可以认为是合一起配一个通知࿰c;而下面的配置方法与通知的映射关系

AOP配置:用特有的<aop:advisor>标签来说明这是一个事务࿰c;需要在哪些地方切入

5.3 注解事务

  1. 配置事务管理器(和xml一样必须的)
  2. 开启Spring事务注解支持<tx:Annotation-driven transaction-manager="transactionManager"></tx:Annotation-driven>
  3. 在需要注解的地方使用@transaction
  4. 不需要AOP࿰c;是因为@transaction注解放在了哪个类上就说明哪个类需要切入࿰c;里面所有方法都是切入点࿰c;映射关系已经存在了

在AccountserviceImpl中简化成,xml中可以选择方法匹配࿰c;注解不可࿰c;只能这样配

@service(value = "accountserviceImpl")
@transactional(propagation = Propagation.SUPPORTS, readOnly = truE)
public class AccountserviceImpl implements Accountservice {

    // 这里为了获取Dao层
    @Autowired
    private AccountDao accountDao;

    // 业务正式开始

    public Account SELEctAccountById(int id) {
        return accountDao.SELEctAccountById(id);
    }

    // 这里只虑事务࿰c;不关心钱额是否充足
    @transactional(propagation = Propagation.requIRED,readOnly = falsE)
    public void transfer(int fid, int sid, int money) {

        Account sourceAccount = accountDao.SELEctAccountById(fid);
        Account targetAccount = accountDao.SELEctAccountById(sid);

        accountDao.updateAccountById(fid, sourceAccount.getMoney() - money);

        // 异常
        // int i = 1 / 0;

        accountDao.updateAccountById(sid, targetAccount.getMoney() + money);
    }
}

6. Test

应用程序的入口是main方法࿰c;而JUnit单元测试中࿰c;没有main方法也能执行࿰c;为其内部集成了一个main方法࿰c;该方法会自动判断当前测试类哪些方法有@Test注解࿰c;有就执行。

JUnit不会知道我们是否用了Spring框架࿰c;所以在执行测试方法时࿰c;不会为我们读取Spring的配置文件来创建核心容器࿰c;所以不能使用@Autowired来注入依赖。

解决方法:

  1. 导入JUnit包
  2. 导入Spring整合JUnit的包
  3. 替换Running࿰c;@RunWith(SpringJUnit4ClassRunner.class)
  4. 加入配置文件࿰c;@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
//@ContextConfiguration(LOCATIOns = "classpath:applicationContext.xml")
public class UITest {

    @Autowired
    UserFactory userFactory;

    @Test
    public void User(){
        System.out.println(userFactory.getUser().toString());
    }
}

7. 注解总览

@Component
@Controller
@service
@Repository

@Autowired
@Qualifier
@resource
@Value
@Scope

@Configuration
@ComponentScan
@Bean
@Import
@Propertysource()

@RunWith
@ContextConfiguration

@transactional

8. 总结

学完Spring之后感觉有什么优势呢?

  • IOC、DI:方便降耦

  • AOP:重复的功能形成组件࿰c;在需要处切入࿰c;切入出只需关心自身业务甚至不知道有组件切入࿰c;也可把切入的组件放到开发的最后才完成

  • 声明式事务的支持

  • 最小侵入性:不用继承或实现他们的类和接口࿰c;没有绑定了编程࿰c;Spring尽可能不让自身API弄乱开发者代码

  • 整合测试

  • 便集成其他框架


好了࿰c;本文就写到这里吧࿰c;我的学习秘籍都在这了>>spring一网打尽c;直接点击就可以直接白嫖了!

大佬总结

以上是大佬教程为你收集整理的Java程序员必备框架—Spring全家桶的前世今生详细梳理全部内容,希望文章能够帮你解决Java程序员必备框架—Spring全家桶的前世今生详细梳理所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。