Spring框架入门:从IoC到AOP

Spring框架的设计初衷是解决企业级Java开发中的复杂性问题,通过简化配置、降低组件耦合度,为开发者提供高效、灵活的开发体验。Spring框架的核心思想是控制反转和面向切面编程,控制反转颠覆了传统开发中开发者主动创建对象的模式,也就是不再需要去new对象了,将对象的创建、依赖管理等工作交给Spring容器负责------开发者只需通过注解(如@Component)或XML配置定义组件,容器便会在应用启动时自动初始化对象,并根据依赖关系如@Autowired完成组件注入,极大降低了代码间的耦合度。例如,在开发订单管理系统时,订单服务(OrderService)依赖用户服务(UserService),无需手动创建,Spring会自动将UserService实例注入到OrderService中,让开发者更专注于业务逻辑实现。

面向切面编程则解决了横切关注点的复用问题------对于日志记录、事务管理、权限校验等需要在多个模块中重复执行的功能,AOP可将其封装为切面,通过配置指定在哪些方法切入点的执行前后通知Advice自动触发,避免了代码冗余。以事务管理为例,只需在Service层方法上添加@Transactional注解,Spring就会通过AOP自动在方法执行前开启事务、执行后提交事务,若出现异常则回滚,无需手动编写事务控制代码。 除了核心的IoC和AOP,Spring还提供了丰富的子项目和扩展模块,覆盖开发全流程:Spring MVC是用于开发Web应用的MVC框架,支持RESTful API设计,可快速构建前后端分离的Web系统;Spring Data 简化了数据访问层开发,通过统一接口封装JPA、MyBatis等持久层技术,开发者无需编写复杂的SQL操作代码;Spring Security 专注于身份认证与授权,提供OAuth2.0、JWT等主流安全方案,保障应用的访问安全;Spring Boot则进一步简化了Spring应用的搭建与部署,通过"自动配置"消除大量XML配置,支持"一键打包"为可执行JAR包,大幅提升开发效率;而Spring Cloud则基于Spring Boot实现了微服务架构的核心能力,提供服务注册发现、配置中心等组件,助力企业构建分布式系统。

接下来从设计一个最简单的spring框架开始:有一个person类,里面有个穿衣服wear方法,但是需要穿什么衣服暂时还不确定,只有动态运行时才知道。穿衣服有sweater和shirt两种选择,那么就可以来个衣服cloth接口,sweater和shirt两个类分别实现了这个接口,同时都加上@Component,放入到容器中。Component顾名思义就是成为容器里面的一个组成部分

java 复制代码
@Component
public class Sweater implements Cloth{
    public void wear()
    {
        System.out.println("穿毛衣");
    }
}
java 复制代码
@Component
public class Shirt implements Cloth{
    public void wear()
    {
        System.out.println("穿T恤");
    }
}
java 复制代码
@Component
public interface Cloth {
    void wear();
}

在person类里面衣服是不确定的,可以通过注解@Autowired自动装配。同时也要避免产生二义性,可以暂时先只让一个实现接口,如果两个都实现编译器就不知道自动装配哪个,就会报错。为了测试是否完成自动装配,先在主函数中定义容器AnnotationConfigApplicationContext ,里面的参数是所在的包名,它会根据这个包名将所有加上@Component注解的类都放到容器中,今后想要用这个类,就可以直接从容器中拿,而且它还可以自动装配。那么这里就拿person类,看看里面是不是真的帮我们装配好了。

java 复制代码
@Component
public class Person {
    @Autowired
    private Cloth cloth;
    public void play()
    {
        System.out.println("出去玩");
        cloth.wear();
    }
}
java 复制代码
public class demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext("demo");
        Person person = context.getBean(Person.class);
        person.play();
    }
}

这里先只让sweater实现了接口,那么一看,确实是装配好了。但是以后也许会遇到这样的问题:可能sweater和shirt都要实现接口,那肯定会产生二义性。解决这个问题的方式也很简单,@Component()里面说明这个类的特性,然后在自动装配的时候给出这个特性就行了。比如在毛衣中加上winter作为特性,在衬衫中加上summer作为特性。在person中装配时就给出这个特性@Qualifier()

java 复制代码
@Component
public class Person {
    @Autowired
    @Qualifier("summer")
    private Clouth clouth;
    public void play()
    {
        System.out.println("出去玩");
        clouth.wear();
    }
}

除了@Component注解,@Repository、@Service、@Controller也是与@Component的功能类似,核心作用是将类标识为 Spring 容器管理的 Bean,同时还具有特定的语义和附加功能。但因为@Component是@Repository、@Service、@Controller的父类,所以平时用的更多。

容器如此强大,那它到底是怎样管理的呢,如果我创建两次person它真的会在内存中创建两个person对象吗?下面来进行验证:

java 复制代码
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext("demo");
        Person p1=context.getBean(Person.class);
        Person p2=context.getBean(Person.class);
        System.out.println(p1==p2);
    }
}

答案是不会。Spring 容器管理的 Bean 的默认作用域是单例,即容器中只会创建一个person实例,每次调用getBean()获取的都是同一个对象。但如果就是想要不同的实体对象,也是可以做到的,将person类被标注为@Scope("prototype"),则每次调用getBean()时,Spring 都会创建一个新的person实例。

还有一个问题,那么容器中每个Bean的生命周期是怎样的?或者说如何才能在合适的时机做到想做的事情,举个例子:在客户端与服务端的连接中,需要先得到url和port两个值,而连接想要断开的时候希望能够自动调用socket.close

对此,spring框架提供了两个接口InitializingBean和DisposableBean,InitializingBean接口中只包含了抽象方法void afterPropertiesSet() throws Exception,其调用时机是Bean 实例化且依赖注入完成后,它一般用于执行初始化逻辑,如参数校验、资源预加载等。而DisposableBean接口中仅包含抽象方法void destroy() throws Exception,其调用时机是Bean 被容器销毁前。常常用于执行资源释放,如关闭连接、释放文件等。

真正要连接客户端和服务端,还需要通过@PropertySource注解指定配置文件的路径(这里是("classpath:config.properties")),Spring 会在启动时自动读取该文件中的配置项,并将其添加到 Spring 的环境变量中。加载后的配置项可以通过@Value注解注入到 Bean 的属性中,实现 "配置与代码分离",避免硬编码。

java 复制代码
@Component
@PropertySource("classpath:config.properties")
public class Client implements InitializingBean, DisposableBean {
    private Socket socket;
    private int port;
    private String url;

    public int getPort() {
        return port;
    }

    @Value("${demo7.client.port}")
    public void setPort(int port) {
        this.port = port;
    }

    public String getUrl() {
        return url;
    }

    @Value("${demo7.client.url}")
    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("正在连接服务器");
        socket=new Socket(url,port);
        System.out.println("连接服务器成功");
    }

    @Override
    public void destroy() throws Exception {
        socket.close();
        System.out.println("socket close");
    }
}
java 复制代码
public class Demo {
    public static void main(String[] args) {
        System.out.println("正在初始化容器");
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext("demo7");
        System.out.println("初始化完毕");

        Person person = context.getBean(Person.class);
        person.sayHello();
        System.out.println("正在关闭容器");
        context.close();
        System.out.println("容器已经关闭");
    }
}

同时也在config.properties文件下设置好url和port来确保能连接成功。完成后先启动服务端然后启动客户端(顺序不能反)观察是否能连接成功。

相关推荐
无忧无虑Coding7 小时前
Spring Boot 多数据源(读写分离)入门
java·spring boot·后端
yBmZlQzJ7 小时前
在PostgreSQL中使用分区技术
数据库·postgresql
braised panda7 小时前
《架构师手记:SpringCloud整合Nacos实战·一》
后端·spring·spring cloud
小安同学iter7 小时前
Spring Cloud Gateway 网关(五)
java·开发语言·spring cloud·微服务·gateway
我有一颗五叶草7 小时前
MySQL 体系结构
数据库·mysql
码农小C8 小时前
idea2025.1.5安装+pj
java·开发语言·apache
David爱编程8 小时前
synchronized 全解析:从用法到底层原理的全面剖析
java·后端
yzx9910138 小时前
Java视觉跟踪入门:使用OpenCV实现实时对象追踪
java·开发语言·人工智能·opencv