Spring

Published 2026-07-30 19:32 Updated 2026-07-30 19:32 2623 words 14 min read ... Page views

This article systematically introduces the core ideas, configuration methods, Bean life cycle, automatic assembly, cyclic dependencies, proxy patterns and AOP mechanisms of Spring IoC and dependency injection, and demonstrates the integration process of Spring and MyBatis based on actual projects. It focuses on the implementation of dependency injection (Setter and constructor injection), Bean scope, automatic assembly strategy, life cycle callbacks, and the principles and practices of transaction management. It emphasizes the reasonable design of dependencies to avoid circular dependencies, and recommends the use of annotations and component scanning to improve development efficiency.

Spring

IoC and Dependency Injection

The core idea of IoC

IoC is inversion of control. Traditional code usually creates dependent objects by the objects themselves; after using Spring, the creation, configuration, and dependencies of objects are managed uniformly by the Spring IoC container.

Dependency injection is a concrete implementation of IoC. Objects only declare what dependencies they need, and the container injects the corresponding objects into them when creating the Bean.

For example, Driver relies on Vehicle. The business class no longer executes new Car() itself, but creates Car from the container and injects Driver.

public interface Vehicle {
    void run();
}

public class Car implements Vehicle {
    private String name;

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

    @Override
    public void run() {
        System.out.println(name + "正在行驶");
    }
}

public class Driver {
    private String name;
    private Vehicle vehicle;

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

    public void setVehicle(Vehicle vehicle) {
        this.vehicle = vehicle;
    }

    public void drive() {
        System.out.println(name + "准备出发");
        vehicle.run();
    }
}

Spring project construction

Creating the Maven Project

Introduce spring-context into pom.xml. The following versions only correspond to the original course example environment:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.20</version>
</dependency>

Create a core configuration file

Create applicationContext.xml in the Resource Catalog.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

start the container

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

Driver driver = context.getBean("driver", Driver.class);
driver.drive();

XML Configuration Bean

Register Bean

<bean id="driver" class="com.hyxy.Driver"/>
<bean id="car" class="com.hyxy.Car"/>

id is the name of the Bean in the container, and class is the fully qualified class name of the implementation class.

Setter injection

<bean id="car" class="com.hyxy.Car">
    <property name="name" value="小轿车"/>
</bean>

<bean id="driver" class="com.hyxy.Driver">
    <property name="name" value="老张"/>
    <property name="vehicle" ref="car"/>
</bean>

value is used to inject simple values, and ref is used to reference another Bean in the container.

Setter injection requires that the property have a corresponding Setter method. The Getter method is not necessary to complete the injection, but is usually provided in accordance with the JavaBean specification.

constructor injection

public class Driver {
    private final String name;
    private final Vehicle vehicle;

    public Driver(String name, Vehicle vehicle) {
        this.name = name;
        this.vehicle = vehicle;
    }
}
<bean id="driver" class="com.hyxy.Driver">
    <constructor-arg index="0" value="老张"/>
    <constructor-arg index="1" ref="car"/>
</bean>

Parameters can also be specified through name or type, but when the constructor is overloaded, it is easier to avoid ambiguity by using index or explicit parameter names.

Constructor injection is suitable for necessary dependencies to ensure that the object remains available after creation is complete.

Bean scope

Spring beans are singleton scope by default, that is, in the same container, only one instance is usually created by the same Bean definition.

<bean id="driver" class="com.hyxy.Driver" scope="singleton"/>

The prototype scope creates a new object each time a Bean is fetched:

<bean id="driver" class="com.hyxy.Driver" scope="prototype"/>

Common scopes include:

scopemeaning
singletonThere is usually only one instance in each Spring container.
prototypecreates a new instance each time it is retrieved.
requestOne instance per HTTP request.
sessionOne instance per HTTP Session.
applicationOne instance for each Web application.

Web scopes need to be used in the context of a Web application.

automatic assembly

XML Assemble by name

<bean id="driver"
      class="com.hyxy.Driver"
      autowire="byName">
    <property name="name" value="老张"/>
</bean>

<bean id="vehicle" class="com.hyxy.Car">
    <property name="name" value="小轿车"/>
</bean>

byName will find the Bean with the same name based on the attribute name. The above example requires that the attribute name of Driver be vehicle.

XML Assemble by Type

<bean id="driver"
      class="com.hyxy.Driver"
      autowire="byType">
    <property name="name" value="老张"/>
</bean>

<bean id="car" class="com.hyxy.Car"/>

byType will find beans based on attribute types. If there are multiple candidates of the same type, the container cannot determine the unique dependence and usually throws an exception.

Automatic assembly of XML can be used to learn principles, but actual projects more often use constructors to inject matching annotations or Java configurations.

circular dependency

Circular dependence refers to the mutual dependence between beans. For example, A relies on B, while B relies on A.

Spring can resolve Setter or field injection cyclic dependencies for some singleton beans, but cannot guarantee that all cyclic dependencies are resolved. The following situations are usually not resolved automatically:

  • Constructors depend on each other.
  • Circular dependence between prototype beans.
  • Circular dependencies caused by certain agents, initialization procedures, or custom life cycle logic.

Circular dependence usually reflects the unreasonable division of class responsibilities, which should be solved first by redesigning dependence relationships rather than relying on containers to cover the bottom.

Bean life cycle

XML lifecycle approach

<bean id="driver"
      class="com.hyxy.Driver"
      init-method="init"
      destroy-method="destroy"/>
public class Driver {
    public void init() {
        System.out.println("初始化 Driver");
    }

    public void destroy() {
        System.out.println("销毁 Driver");
    }
}

The singleton Bean’s destruction callback is called when the container is closed. Spring does not automatically and completely manage the destruction process of prototype beans, and the caller is usually responsible for cleaning up resources after the prototype object is used.

Annotate life cycle method

Common Spring 5 projects use javax.annotation, and Spring 6 and Spring Boot 3 use jakarta.annotation.

@PostConstruct
public void init() {
    System.out.println("初始化 Driver");
}

@PreDestroy
public void destroy() {
    System.out.println("销毁 Driver");
}

Overview of the initialization process

A typical initialization process for a singleton Bean can be summarized as:

  1. Instantiate the Bean.

  2. Inject attributes and dependencies.

  3. Call the relevant Aware interface.

  4. Perform pre-processing of BeanPostProcessor.

  5. Perform an initialization callback, such as @PostConstruct, InitializingBean, or custom init-method.

  6. Performs post-processing of BeanPostProcessor, and AOP agents are usually created at this stage.

  7. Bean becomes available.

When the container is closed, a corresponding destruction callback is executed.

BeanFactory and FactoryBean

BeanFactory

BeanFactory is the basic interface of the Spring IoC container and is responsible for the creation, acquisition and dependency management of beans. ApplicationContext adds functions such as event, internationalization, and resource loading on its basis.

FactoryBean

FactoryBean<T> itself is a Bean, but what the caller obtains through the ordinary Bean name is usually the object it produces.

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
    private String configLocation;

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    @Override
    public SqlSessionFactory getObject() throws Exception {
        try (InputStream inputStream =
                     Resources.getResourceAsStream(configLocation)) {
            return new SqlSessionFactoryBuilder().build(inputStream);
        }
    }

    @Override
    public Class<?> getObjectType() {
        return SqlSessionFactory.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
<bean id="sqlSessionFactory"
      class="com.hyxy.SqlSessionFactoryBean">
    <property name="configLocation" value="mybatis-config.xml"/>
</bean>

When obtaining sqlSessionFactory, you get the object produced by FactoryBean; to obtain the factory Bean itself, you need to add & before the name.

Bean registration method

Common Bean registration methods for Spring include XML, component scanning, and Java configuration.

XML registration

<bean id="car" class="com.hyxy.Car"/>

XML is suitable for centralized viewing of configuration and is also convenient for registering third-party classes whose source code cannot be modified. However, it will be cumbersome when the configuration volume is large.

Annotations and component scanning

Start scanning in the configuration file first:

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

Then mark the class with component annotations:

@Component
public class Car {
}

Common hierarchical annotations include:

  • @Controller: Control layer component.
  • @Service: Business layer components.
  • @Repository: Data access layer component.
  • @Component: Universal component.

These annotations can all register classes as Spring Beans, and hierarchical names are mainly used to express responsibilities; some annotations may also participate in mechanisms such as exception conversions in the framework.

Dependency injection annotation

@Autowired

@Autowired mainly finds dependencies by type. If there are multiple beans of the same type, you can combine @Qualifier or @Primary to eliminate ambiguity.

It is recommended to use constructor injection:

@Service
public class DriverService {
    private final Vehicle vehicle;

    public DriverService(@Qualifier("car") Vehicle vehicle) {
        this.vehicle = vehicle;
    }
}

When there is only one constructor, the @Autowired on the constructor can usually be omitted.

@Qualifier

@Component("car")
public class Car implements Vehicle {
}

@Component("bus")
public class Bus implements Vehicle {
}
@Service
public class DriverService {
    private final Vehicle vehicle;

    public DriverService(@Qualifier("bus") Vehicle vehicle) {
        this.vehicle = vehicle;
    }
}

@Primary

@Component
@Primary
public class Car implements Vehicle {
}

When there are multiple candidate beans of the same type, the Bean marked with @Primary will be used as the default selection.

@Resource

@Resource belongs to Jakarta or Java standard annotations and is usually matched by name first and then by type. It can explicitly specify the Bean name through name.

@Resource(name = "car")
private Vehicle vehicle;

@Value

@Value can inject simple configuration values or Spring expressions.

@Value("${car.name:默认车辆}")
private String carName;

Complex configuration objects are more suitable for using @ConfigurationProperties.

@Scope

@Component
@Scope("prototype")
public class Driver {
}

Java config

Define configuration classes

@Configuration
public class MyConfig {
    @Bean
    public Car car() {
        Car car = new Car();
        car.setName("小轿车");
        return car;
    }

    @Bean
    @Scope("prototype")
    public Driver driver(Car car) {
        Driver driver = new Driver();
        driver.setName("老张");
        driver.setVehicle(car);
        return driver;
    }
}

@Configuration indicates that this class is a configuration class, and the return object of the @Bean method will be registered in the container. Method parameters are automatically injected by the container.

Expressing dependencies through method parameters is clearer and easier to test than directly calling other @Bean methods within the method.

Launch the Java configuration container

ApplicationContext context =
        new AnnotationConfigApplicationContext(MyConfig.class);

Driver driver = context.getBean(Driver.class);
driver.drive();

proxy mode

The proxy pattern includes target objects, proxy objects, and abstract contracts that both parties follow. The proxy object adds common logic before and after calling the target method, such as logging, permission verification, or transaction management.

static agent

public interface HouseService {
    void rentHouse();
}

public class Owner implements HouseService {
    @Override
    public void rentHouse() {
        System.out.println("签合同并收款");
    }
}

public class Agent implements HouseService {
    private final HouseService target;

    public Agent(HouseService target) {
        this.target = target;
    }

    @Override
    public void rentHouse() {
        System.out.println("寻找房源并协商价格");
        target.rentHouse();
    }
}

Static proxies need to write corresponding proxy classes for different target types, and there are many duplicate codes.

JDK dynamic proxies

JDK dynamic proxy creates proxy objects based on interfaces.

HouseService target = new Owner();

HouseService proxy = (HouseService) Proxy.newProxyInstance(
        target.getClass().getClassLoader(),
        target.getClass().getInterfaces(),
        (proxyObject, method, args) -> {
            System.out.println("调用前处理");
            try {
                return method.invoke(target, args);
            } finally {
                System.out.println("调用后处理");
            }
        }
);

proxy.rentHouse();

CGLIB dynamic agent

CGLIB creates a proxy by generating a subclass of the target class, and does not require the target class to implement an interface. The target class cannot be final, and the method requiring a proxy cannot be final.

public class DaoMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(
            Object proxy,
            Method method,
            Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务");
        try {
            Object result = methodProxy.invokeSuper(proxy, args);
            System.out.println("提交事务");
            return result;
        } catch (Throwable e) {
            System.out.println("回滚事务");
            throw e;
        }
    }
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(EmpDao.class);
enhancer.setCallback(new DaoMethodInterceptor());

EmpDao empDao = (EmpDao) enhancer.create();
empDao.save();

Don’t use the outdated Class.newInstance(). When you need to create objects through reflection, you should use getDeclaredConstructor().newInstance().

AOP

AOP is area-oriented programming that is used to separate crosscutting logic such as logging, transactions, and permissions from business code.

common terms

  • Connection point: A location in program execution that can be enhanced, such as method invocations.
  • Entry points: Used to filter connection points that need to be enhanced.
  • Notification: Enhancement logic executed before and after the target method.
  • Aspect: A combination of entry points and notifications.
  • Target object: The enhanced business object.
  • Proxy object: The object after application of enhanced logic.

Introduce dependence

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.20</version>
</dependency>

Define the cut plane

@Aspect
@Component
public class LogAspect {
    @Pointcut("execution(* com.hyxy.service..*(..))")
    public void serviceMethod() {
    }

    @Before("serviceMethod()")
    public void before() {
        System.out.println("执行方法前记录日志");
    }

    @AfterReturning(pointcut = "serviceMethod()", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("方法正常返回:" + result);
    }

    @AfterThrowing(pointcut = "serviceMethod()", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        System.out.println("方法发生异常:" + exception.getMessage());
    }

    @After("serviceMethod()")
    public void after() {
        System.out.println("方法结束");
    }
}

Open annotation AOP:

<aop:aspectj-autoproxy/>

@After is similar to finally, and it will be executed regardless of whether the method returns normally or throws an exception; @AfterReturning will be executed only when it returns normally.

Overview of Spring transactions

Spring declarative transactions are based on AOP. Common transaction attributes include propagation behavior, isolation levels, timeouts, read-only, and rollback rules.

Common communication behaviors:

Communication BehaviorMeaning
REQUIREDJoin if there is a transaction, and create if there is no transaction.
REQUIRES_NEWalways creates new transactions and suspends current transactions.
SUPPORTSjoins if there is a transaction, and runs non-transaction if there is no transaction.
NESTEDuses nested transaction scopes in environments that support savepoints.
@Service
public class UserService {
    @Transactional(rollbackFor = Exception.class)
    public void saveUser(User user) throws Exception {
        // 数据库操作
    }
}

By default, Spring rolls back RuntimeException and Error, but does not automatically roll back ordinary checked exceptions. When you need to roll back the detected abnormality, rollbackFor should be configured.

Spring integrates with MyBatis

The integration goal is for Spring to manage data sources, SqlSessionFactory, Mapper agents and transactions.

Maven Depends

The following versions are examples of the original course combinations:

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.20</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.3.20</version>
    </dependency>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.1</version>
    </dependency>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.7</version>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.23</version>
    </dependency>
</dependencies>

database configuration

jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java2601?serverTimezone=UTC&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
jdbc.maxActive=300
jdbc.initialSize=2
jdbc.maxWait=60000
jdbc.minIdle=1

Spring configuration

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       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/tx
           https://www.springframework.org/schema/tx/spring-tx.xsd
           http://mybatis.org/schema/mybatis-spring
           http://mybatis.org/schema/mybatis-spring.xsd">

    <context:property-placeholder location="classpath:db.properties"/>
    <context:component-scan base-package="com.hyxy"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
    </bean>

    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations"
                  value="classpath*:com/hyxy/mapper/*.xml"/>
        <property name="typeAliasesPackage" value="com.hyxy.po"/>
    </bean>

    <mybatis-spring:scan base-package="com.hyxy.mapper"/>

    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

SqlSessionFactoryBean is responsible for creating MyBatis ‘SqlSessionFactory, and the Mapper scanner creates a proxy for the interface and registers it with the Spring container.

Mapper and Service

public interface UserMapper {
    int insert(User user);
}
@Service
public class UserService {
    private final UserMapper userMapper;

    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Transactional(rollbackFor = Exception.class)
    public void save(User user) throws Exception {
        userMapper.insert(user);
    }
}

The business layer only needs to inject Mapper and should not create SqlSession itself. When transactions are managed by Spring, commit() should not be called manually in Mapper or Service.

If you enjoyed this, leave a comment~

... Page views
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka