毕设学习第六天SSM框架之Spring5

虽然目前spring已经出现了6但是现如今大多数应用的还是spring5,因此毕设学习选择Spring5而非6

spring简介

Spring 是一个开源的 Java 企业级应用开发框架,旨在简化企业级 Java 应用的开发过程。它通过控制反转(IOC)和面向切面编程(AOP)等核心技术,帮助开发人员构建松耦合、高度模块化的应用。
Spring的主要内容
  • 控制反转(IOC):通过控制反转实现对象的解耦,提高代码的可维护性和可测试性。
  • 面向切面编程(AOP):简化了横切关注点(如事务管理、日志记录等)的处理。
  • 事务管理:为各种事务管理提供统一的 API,支持声明式事务控制。
  • 数据访问:简化了数据库操作,支持 JDBC、ORM(如 Hibernate、JPA)等技术。
  • Spring MVC:一个基于模型-视图-控制器(MVC)设计模式的 Web 框架,广泛用于构建 Web 应用。
  • Spring Boot:一个简化 Spring 应用开发的框架,通过约定优于配置的理念,使得开发者可以快速构建和部署 Spring 应用。
  • Spring Cloud:为微服务架构提供支持,解决分布式系统中的常见问题,如服务注册与发现、配置管理、负载均衡等。

SSM框架包含Spring(业务层框架)+SpringMVC(表现层框架)+Mybatis(持久化数据层框架),简单来说就是SpringMVC负责和视图交互,由SpringMVC实现页面动态变化,SpringMVC和Spring交互,由Spring进行业务处理,Spring和Mybatis进行交互,由Mybatis把内存中的数据持久保存到数据库中,这些框架简化了传统的Javaweb开发过程,包括Servlet,JSP,JDBC等技术的优化

Spring框架中IOC和AOP是两个核心内容,是Spring框架的核心思想

控制反转(IOC)

IOC 是一种设计原则,指的是控制权的转移,将对象的创建和依赖关系的管理从程序代码中抽离出来,交给框架或容器来负责。在传统的程序设计中,对象的创建和依赖关系是由程序员手动控制的,而在 IOC 中,控制权由容器(如 Spring 框架中的 ApplicationContext)来接管,程序员只需定义好依赖关系,容器会在运行时将依赖注入到相应的对象中,从而实现低耦合和高内聚。这种设计有助于提高程序的可维护性、可测试性和扩展性。

控制反转(Inversion of Control,IOC) 和 依赖注入(Dependency Injection,DI)

控制反转(IOC)是一个广泛的概念,强调将控制权转移到外部容器中。

依赖注入(DI)是控制反转的一种实现方式,专注于通过外部容器注入依赖对象来达到控制反转的效果。

IOC是一种设计理念,DI是IOC的一种实现方式

假设如下场景,假设我们一开始的需求是针对普通用户业务模块而实现的两个功能,而后需要新增一个VIP用户的,但是业务逻辑不变只是具体操作变了,针对需求变更以下是传统设计理念和IOC设计理念的不同

 

package org.example.dao;

public interface UserDao {
    public void function1();
    public void function2();
}
package org.example.dao;

public class UserDaoCommonImpl implements UserDao {
    @Override
    public void function1() {
        System.out.println("执行普通用户操作1");
    }

    @Override
    public void function2() {
        System.out.println("执行普通用户操作2");
    }
}
package org.example.service;

public interface UserService {
    public void service1();
}
package org.example.service;

import org.example.dao.UserDao;
import org.example.dao.UserDaoCommonImpl;

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoCommonImpl();
    @Override
    public void service1() {
        System.out.println("执行业务操作1");
        userDao.function1();
        userDao.function2();
    }
}
package org.example;

import org.example.service.UserService;
import org.example.service.UserServiceImpl;
import org.junit.Test;

public class Main {
    @Test
    public void mainTest(){
        UserService userService = new UserServiceImpl();
        userService.service1();
    }

}

可以看到一开始没有VIP用户这个业务需求时我们的代码只是针对普通用户的,然而当我们需要新添加VIP用户时,我们不得不改变service的源代码

package org.example.service;

public interface UserService {
    public void service1(String level);
}

 

package org.example.service;

import org.example.dao.UserDao;
import org.example.dao.UserDaoCommonImpl;
import org.example.dao.UserDaoVipImpl;

public class UserServiceImpl implements UserService {
    private UserDao userDao ;
    @Override
    public void service1(String level) {
        System.out.println("执行业务操作1");
        switch (level) {
            case "common":
                userDao = new UserDaoCommonImpl();
                break;
            case "vip":
                userDao = new UserDaoVipImpl();
                break;
        }
        userDao.function1();
        userDao.function2();

    }
}
package org.example;

import org.example.service.UserService;
import org.example.service.UserServiceImpl;
import org.junit.Test;

public class Main {
    @Test
    public void mainTest(){
        UserService userService = new UserServiceImpl();
        userService.service1("vip");
    }

}

如果我们还要新增其他类型的用户依然需要改动业务层的源代码,可见其扩展性是极其差的,如果我们采用如下的设计方案

package org.example.service;

import org.example.dao.UserDao;

public interface UserService {
    public void setUserDao(UserDao userDao);
    public void service1();
}
package org.example.service;

import org.example.dao.UserDao;


public class UserServiceImpl implements UserService {
    private UserDao userDao ;
    
    @Override
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void service1() {
        System.out.println("执行业务操作1");
        userDao.function1();
        userDao.function2();

    }
}
package org.example;

import org.example.dao.UserDaoVipImpl;
import org.example.service.UserService;
import org.example.service.UserServiceImpl;
import org.junit.Test;

public class Main {
    @Test
    public void mainTest(){
        UserService userService = new UserServiceImpl();
        userService.setUserDao(new UserDaoVipImpl());
        userService.service1();
    }

}

这样如果后去再有新的用户类型,我们只需改变注入的对象类型即可,用户自己选择注入即可,而不涉及核心业务层代码,底层应该尽可能不变动,使得底层内容只会扩展增加而不需要改变原来的,只要不涉及核心业务,代码的变更造成的影响就小,解耦之后业务的主动权由程序员变为用户本身,用户可以根据自己去调用对应的业务,而不是由程序员改变对应的业务代码来满足用户需求

IOC容器

IOC(控制反转)容器是Spring框架的核心组件之一,它负责管理对象的生命周期和依赖关系。IOC容器的主要作用是通过依赖注入(DI)机制将对象的创建、配置和依赖关系的管理交给容器来完成,而不是由程序员显式地去实例化和管理。这样,开发者可以通过声明式的方式配置对象和它们之间的依赖关系,从而实现了控制反转。

本质:基于工厂模式,所有创建对象的操作由Spring配置文件完成,包括管理,这样配置文件就是一个IOC容器,生产存储本程序所有的对象实例,某一个类需要实例只需要到该容器中通过id去查找然后使用即可,以前是各个类想要使用某个功能必须自己创建实例,这样耦合度极高,为了解耦合,Spring进行了统一管理对象,而程序中的类无需关系实例,只需注重自己的业务功能即可

对象管理:IOC容器负责创建、初始化和销毁对象。在容器启动时,容器会根据配置文件或注解创建相应的对象,并在整个应用生命周期内进行管理。

依赖注入:IOC容器根据定义的依赖关系自动注入对象所需要的其他对象。这种依赖关系可以通过构造器注入、setter方法注入或字段注入来实现。通过依赖注入,减少了类之间的耦合,使得系统更加灵活和可扩展。

导入spring所需的依赖 ,使用spring框架进行实例的创建

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.18</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

配置文件applicationcontext和bean

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置文件的导入,用于集成不同的配置文件 -->
    <import resource="bean.xml"/>
</beans>
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    一个bean相当于一个实例,所有实例的创建由本配置文件进行,该配置文件就是IOC容器,
    统一管理Spring上下文的所有实例,程序需要对象就到该容器里面去取,通过id来识别想要的实例,
    如此之后创建实例不需要各个类去创建,而是由本配置文件统一创建
    所管理的实例对应的类型必须要有set方法
    通过反射创建实例Class.forName("bean标签对应class的内容为所创建类的路径"),然后通过newInstance创建实例
    -->

    <!--
    进行有参构造创建实例
    id是唯一标识
    name是别名
    class是类的全名
    -->
    <bean id="user1" class="org.example.pojo.User" name="user_one">
        <constructor-arg name="name" value="小明"/>
        <constructor-arg name="age" value="18"/>
        <constructor-arg name="gender" value="男"/>
    </bean>
    <!--
    property相当于属性,使用set方法进行赋值
    -->
    <bean id="user2" class="org.example.pojo.User">
        <property name="name" value="李华" />
        <property name="age" value="20"/>
        <property name="gender" value="男"/>
    </bean>
    <bean id="userDaoCommonImpl" class="org.example.dao.UserDaoCommonImpl"/>
    <bean id="userDaoVipImpl" class="org.example.dao.UserDaoVipImpl"/>
    <bean id="userService" class="org.example.service.UserServiceImpl">
        <!--
         value是具体的值,即基本属性,ref是引用IOC容器中其他的bean,即引用类型
         -->
        <property name="userDao" ref="userDaoCommonImpl"/>
    </bean>

</beans>
package org.example.pojo;

public class User {
    private String name;
    private int age;
    private String gender;

    public User() {
    }

    public User(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}
package org.example;

import org.example.dao.UserDaoVipImpl;
import org.example.pojo.User;
import org.example.service.UserService;
import org.example.service.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    @Test
    public void mainTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
        User user1= (User) context.getBean("user1");
        User user2= (User) context.getBean("user2");
        UserService userService = (UserService) context.getBean("userService");
        System.out.println(user1.toString());
        System.out.println(user2.toString());
        userService.service1();
    }

}

依赖注入(ID)

三种方式:构造器注入,set方法注入,扩展注入

bean对象创建依赖于容器,bean对象的所有属性由容器来注入,即创建依赖容器,容器注入属性

以下不同类型值的注入,记得要导入新的bean容器

package org.example.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class Person {
    private String name;
    private User user;
    private String[] friends;
    private List<String> clothes;
    private Map<String, Integer> scores;
    private Set<String> hobbies;
    private Properties info;

    public String getName() {
        return name;
    }

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

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String[] getFriends() {
        return friends;
    }

    public void setFriends(String[] friends) {
        this.friends = friends;
    }

    public Map<String, Integer> getScores() {
        return scores;
    }

    public void setScores(Map<String, Integer> scores) {
        this.scores = scores;
    }

    public Set<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(Set<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public List<String> getClothes() {
        return clothes;
    }

    public void setClothes(List<String> clothes) {
        this.clothes = clothes;
    }
}

 

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="bean.xml"/>
    <bean  id="person" class="org.example.pojo.Person">
        <!-- 以下是各个类型的属性值的注入 -->
        <property name="name" value="李雷"/>
        <property name="user" ref="user1"/>
        <property name="friends">
            <array>
                <value></value><value>张三</value>
                <value>李四</value>
                <value>王五</value>
                <value>赵六</value>
                <value>钱七</value>
            </array>
        </property>
        <property name="clothes">
            <list>
                <value>蓝色上衣</value>
                <value>黑色裤子</value>
            </list>
        </property>
        <property name="scores">
            <map>
                <entry key="数学" value="150"/>
                <entry key="英语" value="150"/>
                <entry key="语文" value="150"/>
            </map>
        </property>
        <property name="hobbies">
            <set>
                <value>打篮球</value>
                <value>唱</value>
            </set>
        </property>
        <property name="info">
            <props>
                <prop key="学号">1234</prop>
                <prop key="班级">5班</prop>
                <prop key="性别">男</prop>
            </props>
        </property>
    </bean>
</beans>

扩展方式注入

p 命名空间

p 命名空间是 Spring 的注入简化方式,通常用于设置 Bean 的属性值。通过 p 命名空间,你可以以更简洁的方式为 Bean 设置属性,而无需显式使用 <property> 标签

c 命名空间

c 命名空间是 构造函数注入简化方式,用于简化通过构造函数传入参数的配置。通常,构造函数注入在配置中需要使用 <constructor-arg> 标签,但 c 命名空间提供了一种更简洁的方式

bean的作用域

singleton:整个容器中只有一个 Bean 实例。

prototype:每次请求都会生成一个新的 Bean 实例。

request:每个 HTTP 请求一个新的 Bean 实例。

session:每个 HTTP 会话一个新的 Bean 实例。

application:整个应用共享一个 Bean 实例。

websocket:每个 WebSocket 会话一个新的 Bean 实例。

bean的自动装配

装配(Wiring)是指将对象之间的依赖关系建立起来的过程。在 Spring 中,装配通常指的是将一个 Bean 的依赖注入到另一个 Bean 的过程。装配使得 Spring 容器能够创建并管理对象之间的关系,从而支持松耦合的设计,使得各个组件之间可以独立开发和替换,而不需要修改核心业务逻辑

依赖(Dependency)在软件开发中指的是一个对象或类依赖于另一个对象或类才能完成其功能。具体来说,一个类 A 如果需要使用类 B 的某些功能或数据,A 就依赖于 B。依赖关系通常出现在两个类之间,一个类通过引用或调用另一个类的属性或方法来实现某个功能

Spring支持bean的自动装配,会自动在上下文中查找给bean装配属性,即自动在上下文也就是容器根据给出的规则查找和本set方法中相同名称或类型的bean对象进行装配

 

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置文件的导入,用于集成不同的配置文件 -->
    <import resource="bean.xml"/>
    <import resource="bean_one.xml"/>
    <import resource="bean_two.xml"/>
</beans>
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userDao" class="org.example.dao.UserDaoVipImpl"/>
    <!-- 通过名称和类型自动装配,byName依靠id且唯一,set方法后面的名称也要一致,byType依靠类型唯一,且属性的类型也要一样 -->
    <bean id="userServiceImpl1" class="org.example.service.UserServiceImpl" autowire="byName"/>
    <!--
    <bean id="userServiceImpl2" class="org.example.service.UserServiceImpl" autowire="byType"/>
    -->
</beans>

 

package org.example;


import org.example.pojo.Person;
import org.example.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    @Test
    public void mainTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
        UserService userService1 = context.getBean("userServiceImpl1",UserService.class);
        userService1.service1();
    }

}

还可以直接通过注解的方式实现,通过注解可以省略set方法

package org.example.service;



public interface UserService {
    public void service1();
}
<?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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 注解自动配置的支持 -->
    <context:annotation-config/>
    <!-- 配置文件的导入,用于集成不同的配置文件 -->
    <import resource="bean.xml"/>
    <import resource="bean_one.xml"/>
    <import resource="bean_two.xml"/>
</beans>
package org.example.service;

import org.example.dao.UserDao;
import org.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;


public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao ;
    @Qualifier("user1")
    @Autowired
    private User user;

    @Override
    public void service1() {
        System.out.println("执行业务操作1");
        System.out.println(user.toString());
        userDao.function1();
        userDao.function2();

    }
}
package org.example;


import org.example.pojo.Person;
import org.example.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    @Test
    public void mainTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
        UserService userService = context.getBean("userService",UserService.class);
        userService.service1();
    }

}

Spring注解开发

Spring注解开发的出现是为了提升开发效率、简化配置,并使代码更加简洁、易于理解和维护。

@Component:将一个普通的Java类标识为Spring的bean,并让Spring管理它的生命周期,通过@value赋值。

@Autowired:自动注入依赖的bean。

@Controller:标识一个类是Spring MVC的控制器。

@Service:标识一个类是Spring服务层的bean。

@Repository:标识一个类是Spring的数据访问层的bean。

<?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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="org.example.pojo"/>
    <context:component-scan base-package="org.example.dao"/>
    <bean id="userService" class="org.example.service.UserServiceImpl"/>
</beans>
package org.example.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

// 单例模式
@Scope("prototype")
@Component
public class User {
    @Value("李华")
    private String name;
    @Value("18")
    private int age;
    @Value("男")
    private String gender;

    public User() {
    }

    public User(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}
package org.example.dao;

import org.springframework.stereotype.Component;

@Component
public class UserDaoVipImpl implements UserDao{

    @Override
    public void function1() {
        System.out.println("执行VIP用户操作1");
    }

    @Override
    public void function2() {
        System.out.println("执行VIP用户操作2");
    }
}
package org.example.service;

import org.example.dao.UserDao;
import org.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


public class UserServiceImpl implements UserService {
    @Resource
    private UserDao userDao ;
    @Autowired
    private User user;

    @Override
    public void service1() {
        System.out.println("执行业务操作1");
        System.out.println(user.toString());
        userDao.function1();
        userDao.function2();

    }
}
package org.example;


import org.example.pojo.Person;
import org.example.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    @Test
    public void mainTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
        UserService userService = context.getBean("userService",UserService.class);
        userService.service1();
    }

}

@Resource@Autowired的区别是前者先按照name然后再按照type找寻,而后者直接按照type查询,结合@Qualifier来区分相同类型的实例

类型和名称的对应关系,变量名称和bean中的id字段进行匹配,变量类型和bean中的class字段进行匹配自动装配得到对应的类

@Component结合配置扫描包可以实现创建bean实例的效果,并且可以统一管理,扫描相当于把包下的类进行注册到配置里面,即<bean></bean>,通过注解把实例注册到容器中,扫描了的包注解才能生效

JavaConfig实现配置

javaconfig拜托了xml配置文件,由一个java配置类实现所有的配置通过@Configuration这个注解

package org.example.config;

import org.example.controller.UserController;
import org.example.controller.UserControllerImpl;
import org.example.dao.UserDao;
import org.example.dao.UserDaoCommonImpl;
import org.example.dao.UserDaoVipImpl;
import org.example.pojo.User;
import org.example.service.UserService;
import org.example.service.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;

// 使用java类型作为配置文件,相当于applicationContext.xml
@Configuration
// 扫描组件
@ComponentScan(basePackages = {"org.example.controller","org.example.dao","org.example.pojo","org.example.service"})
public class JavaConfig {
    /*
    * @Bean相当于xml内注册一个<bean></bean>
    * 方法名称为实例的id
    *返回值类型为实例的class
    * */
    @Bean
    public User user() {
        return new User();
    }
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }

    @Bean
    public UserDao userDao() {
        return new UserDaoVipImpl();
    }
    @Bean
    public UserController userController() {
        return new UserControllerImpl();
    }

}
package org.example.dao;


import org.springframework.stereotype.Repository;

@Repository
public class UserDaoVipImpl implements UserDao{

    @Override
    public void function1() {
        System.out.println("执行VIP用户操作1");
    }

    @Override
    public void function2() {
        System.out.println("执行VIP用户操作2");
    }
}
package org.example.service;

import org.example.dao.UserDao;
import org.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier(value = "userDaoVip")
    private UserDao userDao ;
    @Autowired
    private User user;

    @Override
    public void service1() {
        System.out.println("执行业务操作1");
        System.out.println(user.toString());
        userDao.function1();
        userDao.function2();

    }
}
package org.example.controller;

import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserControllerImpl implements UserController {
    @Autowired
    private UserService userService;
    @Override
    public void connect() {
        userService.service1();
    }
}

AOP(面向切面编程)

AOP(面向切面编程)思想是通过将横切关注点(如日志记录、事务管理、安全控制等)从业务逻辑中分离出来,形成独立的切面,从而使得系统更加模块化、解耦。在AOP中,业务逻辑和切面通过代理模式进行交互,切面在特定的连接点(如方法执行时)插入额外的功能,达到增强功能而不改变原有业务代码的目的。这种方式提高了代码的可维护性、可重用性和灵活性。
 
 
AOP的本质是代理模式,代理模式一般分为真实角色,客户和代理角色,真实角色为客户提供真正的业务活动,代理模式的好处的避免了真实角色和客户直接交互,因为每个业务是各不相同的,但是除了业务外还有许多公开的业务每个角色都需要完成,代理模式的好处是作为中介,由代理角色来完成所有真实角色公共的业务部门,而每个角色只需要专注于自己独特的业务即可,在后续如果有什么公共的业务需要扩展的话只需交给代理来进行而不用让每个真实角色都进行该业务,降低了代码复杂度,方便对于公共业务集中管理,对于特殊业务分块管理,二者之间解耦合。
代理模式有两种静态和动态
 
AOP和OOP相比,OOP是通过多个类共同实现某一个程序,而AOP则是在一个类的基础上把别的类添加进来,传统OOP在编写程序时就已经把所有需要的类都关联起来的了,而后续很难改动,但AOP可以进行横向切入,一般切面为一个类,包含了切入点和通知,切入点用于定义该切面需要切入到哪些类之中,而通知则是定义需要切入的行为,连接点是程序执行过程中的一个点,常见的连接点包括方法调用、方法执行、构造函数调用等,在连接点上切入通知,这就是织入
 

静态代理: 代理类在编译时就确定,无法灵活扩展;适用于代理类较少的场景。

动态代理: 代理类在运行时动态生成,能自动代理任意实现接口的目标类,适合于代理类较多的场景。

package org.example.pojo;

public interface RealRole {
    public void service();
}
package org.example.pojo;

public class Role1 implements RealRole{
    @Override
    public void service() {
        System.out.println("Role1 service");
    }
}
package org.example.pojo;

public class Role2 implements RealRole{
    @Override
    public void service() {
        System.out.println("Role2 service");
    }
}
package org.example.pojo;

public class ProxyRole {
    private RealRole realRole;

    public void setRealRole(RealRole realRole) {
        this.realRole = realRole;
    }
    public void proxyService(){
        // 切入公共业务
        System.out.println("Common service");
        realRole.service();
    }
}
package org.example.pojo;

public class Customer {
    public static void main(String[] args) {
        ProxyRole proxyRole = new ProxyRole();
        proxyRole.setRealRole(new Role1());
        proxyRole.proxyService();
        proxyRole.setRealRole(new Role2());
        proxyRole.proxyService();
    }
}

 

以上就是静态代理模式的举例说明,但是实际当中一般采用动态代理,利用反射的原理这样可以避免修改源代码

package org.example.pojo;

import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler {
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 反射获取代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces() , this);
    }
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        // 通过反射在下原来的业务前后添加新的业务
        addService1();
        addService2();
        System.out.println(method.getName()+"执行前");
        Object result = method.invoke(target, objects);
        System.out.println(method.getName()+"执行后");
        addService3();
        return result;
    }
    public void addService1(){
        System.out.println("Proxy service1");
    }
    public void addService2(){
        System.out.println("Proxy service2");
    }
    public void addService3(){
        System.out.println("Proxy service3");
    }

}
package org.example.pojo;

public class Customer {
    public static void main(String[] args) {
        // 真实角色的业务执行
        RealRole realRole = new Role1();
        realRole.service();
        System.out.println("==================");
        DynamicProxy dynamicProxy = new DynamicProxy();
        dynamicProxy.setTarget(realRole);
        // 此时的对象已经的通过反射得到的添加完新业务的对象
        realRole = (RealRole) dynamicProxy.getProxy();
        // 代理角色的业务执行
        realRole.service();
    }
}

SpringAOP

切面(Aspect): 模块化横切关注点(如日志、事务)的组件,包含 通知 和 切点。

连接点(Join Point): 程序执行过程中可插入切面的点(如方法调用、异常抛出)。

通知(Advice): 切面在连接点执行的具体动作,分为 @Before@After@Around@AfterReturning@AfterThrowing 五种类型。

切点(Pointcut): 通过表达式(如 execution(...))定义哪些连接点会被切面拦截,用于筛选目标方法。

目标对象(Target Object): 被切面增强的原始对象(如业务逻辑类实例)。

代理(Proxy): Spring AOP 动态生成的对象(JDK 动态代理或 CGLIB 代理),用于包裹目标对象并织入切面逻辑。

织入(Weaving): 将切面应用到目标对象的过程,Spring AOP 通过 运行时动态代理 实现。

 

配置实现AOP

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


    <bean id="AfterLog" class="org.example.log.AfterLog"/>
    <bean id="BeforeLog" class="org.example.log.BeforeLog"/>
    <context:annotation-config>true</context:annotation-config>
    <context:component-scan base-package="org.example"/>


    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* org.example.service.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="BeforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="AfterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>
package org.example.dao;

import org.springframework.stereotype.Repository;


public interface UserDao {
    public void add();
    public void delete();
    public void update();
    public void query();
}
package org.example.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("添加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void query() {
        System.out.println("查找一个用户");
    }
}
package org.example.service;




public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
package org.example.service;

import org.example.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Qualifier("userDaoImpl")
    @Autowired
    private UserDao userDao;

    @Override
    public void add() {
        userDao.add();
    }

    @Override
    public void delete() {
        userDao.delete();
    }

    @Override
    public void update() {
        userDao.update();
    }

    @Override
    public void query() {
        userDao.query();
    }
}
package org.example.log;


import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;


public class BeforeLog implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Before 日志记录");
        System.out.println("执行方法: " + method.getName());
        System.out.println("参数: " + Arrays.toString(args));
        System.out.println("业务逻辑 : " + target);
    }
}
package org.example.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;


public class AfterLog implements AfterReturningAdvice {
    
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("After 日志记录");
        System.out.println("执行方法: " + method.getName());
        System.out.println("参数: " + Arrays.toString(args));
        System.out.println("业务逻辑 : " + target);
        System.out.println("返回值: " + returnValue);
    }
}
package org.example;


import org.example.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Main {
    @Test
    public void mainTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService =context.getBean("userServiceImpl", UserService.class);
        userService.add();
        userService.delete();
        userService.update();
        userService.query();
    }
}

另一种形式

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



    <context:annotation-config>true</context:annotation-config>
    <context:component-scan base-package="org.example"/>
    <bean id="testAspect" class="org.example.log.TestAspect"/>

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* org.example.service.UserServiceImpl.*(..))"/>
        <aop:aspect ref="testAspect">
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>

    </aop:config>


</beans>
package org.example.log;

public class TestAspect {
    public void before() {
        System.out.println("before");
    }
    public void after() {
        System.out.println("after");
    }
}

 

简化使用注解实现

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



    <context:annotation-config/>
    <aop:aspectj-autoproxy/>
    <context:component-scan base-package="org.example"/>


</beans>
package org.example.log;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class TestAspect {
    @Before("execution(* org.example.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("before");
    }
    @After("execution(* org.example.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("after");
    }
}

AspectJ 

AspectJ 是一个功能强大的 面向切面编程(AOP) 框架,它是基于 Java 的 AOP 解决方案,允许开发者将横切关注点(如日志、事务、安全等)与业务逻辑代码解耦,从而提高代码的模块化和可维护性。AspectJ 提供了一个完整的 AOP 编程模型,包括切面、切入点、通知等,并支持多种织入方式。

SpringAOP是基于AspectJ在Spring框架的实现形式

package org.example.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class LoggingAspect {

    // 定义一个切入点,切入到UserService之中
    @Pointcut("execution(* org.example.aspectj.UserService之中.*(..))")
    public void serviceMethods() {}

    // 前置通知:在方法执行前输出日志
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    // 后置通知:在方法执行后输出日志
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }

    // 环绕通知:控制方法的执行
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around method: Before");
        Object result = joinPoint.proceed(); // 执行目标方法
        System.out.println("Around method: After");
        return result;
    }
}

 

OOP和AOP的总结感悟

OOP是面向对象编程,AOP是面向切面编程,二者的重点不一样,面向对象编程是指把所有业务细化分为多个独立的小业务模块,每个模块就是一个类,这些类实例化之后各种组合调用就可以实现多个大业务模块,一般OOP里面的业务都是核心业务,是各不相同的,而AOP则是把一些多个业务都需要的干的事情单独拿出来,如日志,权限,事物等,把共同的业务写为一个模块然后切入到需要的 核心业务里面。

假设你开发一个用户管理系统,每个方法(如添加用户、删除用户)都需要权限检查。如果用传统方式,你会在每个方法里写一遍权限代码,导致代码冗余且难维护。而用AOP:

切面(Aspect):创建一个“权限检查”模块,专门处理权限逻辑。

切入点(Pointcut):通过配置(如表达式 * UserService.*(..))指定哪些方法需要权限检查。

通知(Advice):在目标方法执行前(或后、异常时)自动触发权限检查。

AOP就像程序的“管家”,把那些琐碎但必要的功能(如日志、权限)统一打理,让开发者专注于核心业务。它通过切面模块化动态织入代码,让系统更干净、灵活。

posted @ 2025-02-02 00:04  突破铁皮  阅读(53)  评论(0)    收藏  举报