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.
example,Driver dependent Vehicle。Business categories no longer execute themselves new Car(), but is created by containers Car, re-inject 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
in pom.xml introduced in spring-context。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 in the Resource Catalog applicationContext.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"
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,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 Used to inject simple values,ref 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 it 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>can also name or type Specify parameters, but when the constructor is overloaded, use the index Or explicit parameter names make it easier to avoid ambiguity.
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:
| scope | meaning |
|---|---|
singleton | There is usually only one instance in each Spring container. |
prototype | Create a new instance each time you retrieve it. |
request | One instance for each HTTP request. |
session | One instance for each HTTP Session. |
application | One 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 Beans with the same name are found based on the property name. Requirements in the above example Driver The attribute name of is 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 Beans are found based on property 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, such as A dependent B, at the same time B turn, relies 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.
prototypeCircular dependence between 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
Use of Spring 5 common projects javax.annotationSpring 6 and Spring Boot 3 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:
-
Instantiate the Bean.
-
Inject attributes and dependencies.
-
invoking the relevant
AwareInterface. -
implementation
BeanPostProcessorPre-processing of. -
Perform an initialization callback, such as
@PostConstruct、InitializingBeanor custominit-method。 -
implementation
BeanPostProcessor, AOP proxies are usually created at this stage. -
Bean becomes available.
When the container is closed, a corresponding destruction callback is executed.
BeanFactory with FactoryBean
BeanFactory
BeanFactory It is the basic interface of the Spring IoC container and is responsible for the creation, acquisition and dependency management of beans.ApplicationContext On this basis, functions such as event, internationalization, and resource loading have been added.
FactoryBean
FactoryBean<T> It is a Bean itself, 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>obtain sqlSessionFactory What you get when you FactoryBean Produced object; to get the factory Bean itself, you need to add &。
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 components.@Service: Business layer components.@Repository: Data access layer components.@Component: Common components.
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 Find dependencies mainly by type. If there are multiple beans of the same type, you can combine @Qualifier or @Primary 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, you can usually omit the @Autowired。
@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,@Primary Marked beans will be the default selection.
@Resource
@Resource Belonging to Jakarta or Java standard annotations, usually matching by name first and then by type. it can name Explicitly specify the Bean name.
@Resource(name = "car")
private Vehicle vehicle;@Value
@Value Simple configuration values or Spring expressions can be injected.
@Value("${car.name:默认车辆}")
private String carName;Complex configuration objects are more suitable for use @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,@Bean The return object of the method is registered in the container. Method parameters are automatically injected by the container.
Expressing dependencies through method parameters is better than directly calling others within a method @Bean The method is clearer and easier to test.
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. Target class cannot be final, the method that requires an agent 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 outdated ones Class.newInstance()。When you need to create an object through reflection, you should use the 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 similar finally, will be executed whether the method returns normally or throws an exception; it will only be executed when it returns normally @AfterReturning。
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 behavior | meaning |
|---|---|
REQUIRED | Join if there are transactions, and create if there are no transactions. |
REQUIRES_NEW | Always create a new transaction and pause the current transaction. |
SUPPORTS | If there is a transaction, join, and if there is no transaction, it will run non-transactional. |
NESTED | Use 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 sets RuntimeException and Error Rollback. Ordinary detected exceptions are not automatically rolled back. When you need to roll back a detected exception, you should configure rollbackFor。
Spring integrates with MyBatis
The integration goal is to have Spring 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=1Spring 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 responsible for creating MyBatis SqlSessionFactory, 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 it by itself SqlSession。When transactions are managed by Spring, they should not be called manually in Mapper or Service commit()。
If you enjoyed this, leave a comment~