Without IoC and AOP
Without Spring, we first manually implement the business logic and layer it:
- DAO
- Service
- Controller
Introduction to IoC (Inversion of Control)
Inversion of Control (IoC) is a design principle used to achieve decoupling between components, and it is one of the most important concepts in object-oriented programming. The core idea of IoC is to transfer the control of objects from the caller to the framework or container, so that the dependency relationships between objects are managed by the container.
Features of IoC
Decoupling
IoC reduces coupling between modules by handing over the responsibility of dependency management and creation to the container, enhancing system maintainability and scalability.
Dynamic Dependency Management
The container dynamically injects dependencies into objects based on configuration or annotations, without hard-coded dependency relationships.
Flexibility
Object dependencies can be dynamically modified at runtime by simply changing the configuration, without modifying the code.
Application Scenarios of IoC
IoC is widely used in various software development frameworks and projects. Here are some typical applications:
- Spring Framework: The Spring framework uses the IoC container to manage the lifecycle and dependencies of Beans
- Guice and Dagger: Lightweight DI frameworks
- Frontend frameworks (such as Angular): Managing dependencies between components and services through a dependency injection system
Advantages of IoC
- Enhanced modularity: By reducing direct dependencies between modules, modular design is promoted
- Improved testability: Dependency injection makes it easy to replace dependency objects, thereby supporting unit testing
- Enhanced flexibility: Dependencies can be dynamically injected through configuration or annotations
- Easier maintenance: Decoupling minimizes the impact when adding or modifying functionality
Limitations of IoC
- Learning curve: Beginners need time to understand the concepts of IoC and DI
- Runtime performance overhead: The IoC container parsing dependencies at runtime may introduce some performance overhead
- Complexity: In large projects, excessive use of IoC may make configuration and dependency relationships complex
Utils
WzkDruidUtils
/**
* Druid utility class
* Uses singleton design pattern, static block initialization
* @author wzk
* @date 16:38 2024/11/18
**/
public class WzkDruidUtils {
private static DruidDataSource druidDataSource = new DruidDataSource();
static {
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://172.16.1.130:3306/wzk-mybatis");
druidDataSource.setUsername("hive");
druidDataSource.setPassword("hive@wzk.icu");
}
private WzkDruidUtils() {
}
public static DruidDataSource getInstance() {
return druidDataSource;
}
}
WzkConnectionUtils
/**
* Current thread SQL connector
* Uses ThreadLocal to pass connection
* @author wzk
* @date 16:43 2024/11/18
**/
public class WzkConnectionUtils {
private final ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
public Connection getCurrentConnection() throws SQLException {
Connection connection = threadLocal.get();
if (null == connection) {
connection = WzkDruidUtils.getInstance().getConnection();
threadLocal.set(connection);
}
return connection;
}
}
DAO Layer
WzkAccountDao
package wzk.dao;
import wzk.model.WzkAccount;
public interface WzkAccountDao {
WzkAccount selectWzkAccount(String card) throws Exception;
int updateWzkAccount(WzkAccount wzkAccount) throws Exception;
}
JdbcWzkAccountDaoImpl
public class JdbcWzkAccountDaoImpl implements WzkAccountDao {
private WzkConnectionUtils wzkConnectionUtils;
public void setWzkConnectionUtils(WzkConnectionUtils wzkConnectionUtils) {
this.wzkConnectionUtils = wzkConnectionUtils;
}
@Override
public WzkAccount selectWzkAccount(String card) throws Exception {
Connection connection = wzkConnectionUtils.getCurrentConnection();
String sql = "select * from wzk_account where card = ? limit 1";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
prepareStatement.setString(1, card);
ResultSet resultSet = prepareStatement.executeQuery();
WzkAccount wzkAccount = new WzkAccount();
while (resultSet.next()) {
wzkAccount.setCard(resultSet.getString("card"));
wzkAccount.setName(resultSet.getString("name"));
wzkAccount.setMoney(resultSet.getInt("money"));
}
resultSet.close();
prepareStatement.close();
return wzkAccount;
}
@Override
public int updateWzkAccount(WzkAccount wzkAccount) throws Exception {
Connection connection = wzkConnectionUtils.getCurrentConnection();
String sql = "update wzk_account set money =? where card =?";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
prepareStatement.setInt(1, wzkAccount.getMoney());
prepareStatement.setString(2, wzkAccount.getCard());
int result = prepareStatement.executeUpdate();
prepareStatement.close();
return result;
}
}
Service Layer
WzkTransferService
package wzk.service;
public interface WzkTransferService {
void transfer(String fromCard, String toCard, int money) throws Exception;
}
WzkTransferServiceImpl
public class WzkTransferServiceImpl implements WzkTransferService {
private WzkAccountDao wzkAccountDao;
public void setWzkAccountDao(WzkAccountDao wzkAccountDao) {
this.wzkAccountDao = wzkAccountDao;
}
@Override
public void transfer(String fromCard, String toCard, int money) throws Exception {
WzkAccount from = wzkAccountDao.selectWzkAccount(fromCard);
WzkAccount to = wzkAccountDao.selectWzkAccount(toCard);
from.setMoney(from.getMoney() - money);
to.setMoney(to.getMoney() + money);
int fromResult = wzkAccountDao.updateWzkAccount(from);
int toResult = wzkAccountDao.updateWzkAccount(to);
System.out.println("transfer fromResult: " + fromResult + " toResult: " + toResult);
}
}
Controller Layer
WzkServlet
@WebServlet(name="wzkServlet", urlPatterns = "/wzkServlet")
public class WzkServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("=== WzkServlet doGet ===");
// Without Spring's help, we need to manually create and maintain dependency relationships
// Assemble DAO, DAO layer depends on ConnectionUtils and DruidUtils
JdbcWzkAccountDaoImpl jdbcWzkAccountDaoImpl = new JdbcWzkAccountDaoImpl();
jdbcWzkAccountDaoImpl.setConnectionUtils(new WzkConnectionUtils());
// Assemble Service
WzkTransferServiceImpl wzkTransferService = new WzkTransferServiceImpl();
wzkTransferService.setWzkAccountDao(jdbcWzkAccountDaoImpl);
// Execute business logic
try {
wzkTransferService.transfer("1", "2", 100);
} catch (Exception e) {
System.out.println("=== transfer error ====");
}
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print("=== WzkServlet doGet ===");
}
}
Test Run
Start the service and access the URL in a browser or Postman:
http://localhost:8999/wzkServlet
In a scenario without IoC and AOP, we need to manually create and maintain dependency relationships. This demonstrates the core value of IoC—transferring control of object creation and dependency management from application code to the container.