想学Spring?看看这篇文章吧!

Spring5

想学Spring?看看这篇文章吧!_第1张图片

看完B站狂神的视频,小编获益匪浅,在学习途中记下了这篇文章
小伙伴也可以从B站获取狂神的完整视频哦!
狂神B站主页

1 入门

1.1 简介

Spring:春天------>给软件行业带来了春天!

  • 2002,首次推出了Spring框架的雏形:interface21框架!

  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。

  • Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。

  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

  • SSH:Struct2 + Spring + Hibernate!

  • SSM:SpringMVC + Spring + Mybatis!

  • 官网:https://spring.io/projects/spring-framework#overview

  • 官方下载地址:https://repo.spring.io/release/org/springframework/spring/

  • GitHub:https://github.com/spring-projects/spring-framework


<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.9version>
dependency>

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.3.9version>
dependency>

1.2 优点

  • Spring是一个开源的免费的框架(容器)!
  • Spring是一个轻量级的、非入侵式的框架!
  • 控制反转(IOC),面向切面编程(AOP)!
  • 支持事务的处理,对框架整合的支持!

总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!

1.3 组成

想学Spring?看看这篇文章吧!_第2张图片

1.4 拓展

现代化的Java开发!说白就是基于Spring的开发!

  • Spring Boot

    一个快速开发的脚手架。
    基于SpringBoot可以快速的开发单个微服务。
    约定大于配置。

  • Spring Cloud
    SpringCloud是基于SpringBoot实现的。

因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!

弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”

2 IOC

2.1 IOC理论推导

我们先用我们原来的方式写一段代码


  • DaoUser接口

    public interface DaoUser {
        /**
         * 输出语句测试
         * */
        public void printText();
    }
    
  • DaoUser的实现类DaoUserMyBatisImpl

    public class DaoUserMyBatisImpl implements DaoUser{
        @Override
        public void printText() {
            System.out.println("我是MyBatis的执行!");
        }
    }
    
  • ServiceUser接口

    public interface ServiceUser {
        /**
         * 输出打印特定的语句
         * */
        public void printText();
    }
    
  • ServiceUser的实现类

    public class ServiceUserImpl implements ServiceUser{
        private DaoUser daoUser = new DaoUserMyBatisImpl();
    
        @Override
        public void printText() {
            daoUser.printText();
        }
    }
    
    
  • 用UserPrintText测试

    public class UserPrintText {
        @Test
        public void UserPrintTextTest(){
            new ServiceUserImpl().printText();
        }
    }
    

这是我们原来的方式 , 开始大家也都是这么去写的对吧 . 那我们现在修改一下

把Userdao的实现类增加一个

public class DaoUserJdbcImpl implements DaoUser{
    @Override
    public void printText() {
        System.out.println("我是Jdbc的执行!");
    }
}

紧接着我们要去使用DaoUserJdbcImpl的话 , 我们就需要去service实现类里面修改对应的实现

private DaoUser daoUser = new DaoUserJdbcImpl();

每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身

所有我们采用IOC的解决办法


让UserDao的具体调用在Service层,在Service层设置一个setUserDao的接口,让我们的Controller层去实现

/**
 * 实现我们DaoUser的Service层的自我调用
 * @param daoUser 即将设置的UserDao实现类的对象
 * */
public void setDaoUser(DaoUser daoUser);
private DaoUser daoUser ;

@Override
public void setDaoUser(DaoUser daoUser) {
    this.daoUser = daoUser;
}

那么Cotroller层才是控制的主人公

@Test
public void UserPrintTextTest(){
    ServiceUserImpl service = new ServiceUserImpl();

    service.setDaoUser(new DaoUserMyBatisImpl());

    service.printText();
}

我认为IOC(控制反转),就是获得依赖对象的方式反转了,以前是Service层进行调用,现在是Controller进行自定义的设置。Controller层就是来处理用户请求的,如果有个用户,有个按钮来控制使用的是JDBC还是MyBatis,那么通过IOC可以非常轻松的实现

2.2 IOC的本质

控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方

想学Spring?看看这篇文章吧!_第3张图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H0DhMNnS-1631336112019)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210805143707321.png)]

IOC是Spring框架的核心内容,采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)。

2.3 IOC执行流程

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

想学Spring?看看这篇文章吧!_第4张图片

2.4 HelloSpring

编写Pojo

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hello {
    private String str;
}

编写beans.xml


<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">

<bean id="hello" class="com.ZhengXinhao.Pojo.Hello">
    <property name="str" value="Spring"/>
bean>

beans>

测试

@Test
public void HelloTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    Hello hello = (Hello) context.getBean("hello");

    System.out.println(hello);
}

思考

  • Hello 对象是谁创建的 ?

    hello 对象是由Spring创建的

  • Hello 对象的属性是怎么设置的 ?

    hello 对象的属性是由Spring容器设置的

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收

可以通过newClassPathXmlApplicationContext去浏览一下底层源码

2.5 IOC创建对象方式

而我理解的通过xml配置实现IOC的工作流程就是,我们把我们对每个类的配置情况写在beans.xml中交个Spring,在创建我们的ApplicationContext的时候,其实我们的每一个bean对象都被创建放在了我们的Spring中的BeanFactory中了,每一次的getBeans只是从里面调用而已,对象没有改变(它的hashcode没有改变)。所谓的IOC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !

想学Spring?看看这篇文章吧!_第5张图片

  • set方法注入是调用无参构器的,如果我们自定义了一个有参构造器,就会报错

  • 我们也可以用有参构造来创建

    下标法

    <constructor-arg index="0" value="Hello Java Designer!"/>
    

    属性法

    <constructor-arg type="java.lang.String" value="Hello Java Designer!"/>
    

    引用名法 本人最青睐

    <constructor-arg name="str" value="Hello Java Designer!"/>
    

3 Spring配置

3.1 别名

给你的对象引用名起别名,原来的名字依然可以使用

<alias name="hello" alias="helloNew"/>

3.2 bean的配置

name里面的别名,分割方式有很多种


<bean id="userT" class="com.kuang.pojo.UserT" name="user2 u2,u3;u4">
    <property name="name" value="郑信豪"/>
bean>

3.3 Import

这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。

也就是是说导入其他的beans.xml就不用在程序代码在再去添加了

 new ClassPathXmlApplicationContext("beans.xml","bean1.xml","bean2.xml","bean3.xml");

beans.xml(applicationContext.xml)中直接导入

<import resource="bean1.xml"/>
<import resource="bean2.xml"/>
<import resource="bean3.xml"/>

导入之后里面的东西会被整合

4 DI依赖注入

4.1 构造器注入

前面已经说过了

4.2 Set方法注入

通过查找name值的set方法,这里严格检查字母的大小写

  • 依赖注入:Set注入
    • 依赖:bean对象的创建依赖于容器(Spring)!
    • 注入:bean对象中的所有属性,由容器(Spring)来注入!
4.2.1 环境搭建

引用类型

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

待注入对象

public class Student {

    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}
4.2.2 依赖注入
    <bean id="address" class="com.ZhengXinhao.Pojo.Address">
        <property name="address" value="重庆邮电大学"/>
    bean>

    <bean id="student" class="com.ZhengXinhao.Pojo.Student">

        <property name="name" value="郑信豪"/>



        <property name="address">
            <bean class="com.ZhengXinhao.Pojo.Address">
                <property name="address" value="重庆"/>
            bean>
        property>


        <property name="books">
            <array>
                <value>MySqlvalue>
                <value>Javavalue>
            array>
        property>


        <property name="hobbies">
            <list>
                <value>打游戏value>
                <value>学习value>
            list>
        property>


        <property name="games">
            <set>
                <value>王者荣耀value>
                <value>LOLvalue>
            set>
        property>


        <property name="card">
            <map>
                <entry key="student" value="2019212911"/>
                <entry key="phone" value="17382215131"/>
                <entry key="null" value=""/>
            map>
        property>


        <property name="wife">
            <null/>
        property>


        <property name="info">
            <props>
                <prop key="sex">prop>
                <prop key="age">21prop>
            props>
        property>
    bean>

4.3 拓展方式注入

我们可以使用p命名空间和c命名空间进行注入

在这里插入图片描述

使用:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    
    <bean id="user" class="com.kuang.pojo.User" p:name="郑信豪" p:age="20"/>

    
    <bean id="user2" class="com.kuang.pojo.User" c:name="狂神" c:age="22"/>
beans>

<bean id="student" class="com.ZhengXinhao.Pojo.Student" p:address-ref="address">

特别注意,需要导入xml约束

       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"

4.4 Bean的作用域

  • 单例模式(Spring默认机制)
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神" c:age="22" scope="singleton"/>
  • 原型模式:每次从容器中get的时候,都会产生一个新对象!多线程建议使用
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神" c:age="22" scope="prototype"/>
  • 其余的request、session、application、这些只能在web开发中使用到!

5 Bean的自动装配

5.1 环境搭建

创建项目,一个人有两个宠物!

public class People {
    private String name;
    private Cat cat;
    private Dog dog;
}
public class Cat {
    public void shot(){
        System.out.println("miao~");
    }
}
public class Dog {
    public void shot(){
        System.out.println("wang~");
    }
}

5.2 autowire

装配autowire=“byName”

<bean id="people" class="com.ZhengXinhao.Pojo.People" p:name="郑信豪" autowire="byName"/>
<bean id="dog" class="com.ZhengXinhao.Pojo.Dog"/>
<bean id="cat" class="com.ZhengXinhao.Pojo.Cat"/>

也可以

<bean id="people" class="com.ZhengXinhao.Pojo.People" p:name="郑信豪" autowire="byType"/>
<bean id="dog" class="com.ZhengXinhao.Pojo.Dog"/>
<bean id="cat" class="com.ZhengXinhao.Pojo.Cat"/>
  • ByName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
  • ByType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

检查大小写

5.3 注解实现自动装配

jdk1.5支持的注解,Spring2.5就支持注解了!

要使用注解须知:

  1. 导入约束
  2. 配置注解的支持
配置注解的支持

<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
	        https://www.springframework.org/schema/beans/spring-beans.xsd
	        http://www.springframework.org/schema/context
	        https://www.springframework.org/schema/context/spring-context.xsd">
		
		
        <context:annotation-config/>
beans>

@Autowired

直接在属性上使用即可!也可以在set方法上使用!

使用Autowired我们就可以不用编写set方法了,前提是你这个自动配置的属性在IOC(Spring)容器中存在,

默认byType,同类型有多个就会再次byName!

@Nullable

字段标记了了这个注解,说明这个字段可以为null

当然我们也可以使用@Autowired(required = false),里面的required默认为true

@Qualifier

当类型重复了且找不到byName的值,就通过@Qualifier进行匹配

@Resource

在jdk11中已经取消

默认byType,同类型有多个就会再次byName!

6 使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop的包导入了

在这里插入图片描述

使用注解需要导入约束,配置注解的支持!

配置注解的支持

<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
	        https://www.springframework.org/schema/beans/spring-beans.xsd
	        http://www.springframework.org/schema/context
	        https://www.springframework.org/schema/context/spring-context.xsd">
		
		
        <context:annotation-config/>
    	
	    <context:component-scan base-package="com.ZhengXinhao.Pojo"/>
beans>

@Component(“address”)

注册bean,默认id为类名首字母小写

表明这个类要作为组件类,并且告诉Spring要为它注册bean

#就是等同于
<bean id="address" class="com.ZhengXinhao.Pojo.Address">

也可以等同于

@Bean("address")
public UserServiceImpl getService(){
    return new UserServiceImpl();
}

@Value

字段属性的注入

@Value("ZhengXinHao")
private String name;

@Component衍生注解

dao 【@Repository】
service 【@Service】
controller 【@Controller

这三个和@Component一样

@Scope(“singleton”)

设置作用域

小结

  • xml与注解:
  • xml更加万能,适用于任何场合!维护简单方便
  • 注解不是自己类使用不了,维护相队复杂!
  • xml与注解最佳实践:
  • xml用来管理bean;
  • 注解只负责完成属性的注入;
  • 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

7 使用JavaConfig实现配置

我们现在要完全不使用Spring的xml配置了,全权交给Java来做!

JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能

7.1 Set方法配置

@Data
@Component
public class Address {
    @Value("重庆邮电大学")
    private String address;
}
@Data
@Component
public class Student {
    @Value("郑信豪")
    private String name;
    @Autowired
    private Address address;
}
/**
 * @author lenovo
 *  .这个也会被注册到容器中,因为他本来就是一个@Component
 *  .@Configuration代表这是一个配置类,就和我们之前看的beans.xml一样的
 *  .@ComponentScan("com.ZhengXinhao.Pojo")扫描这个包的注解
 *  .@Import(JavaConfig2.class)引入其他的配置文件(JavaConfig)
 */
@Import(JavaConfig2.class)
@Configuration
@ComponentScan("com.ZhengXinhao.Pojo")
public class JavaConfig {
    @Bean("student")
    public Student get_student(){
        return new Student();
    }
}

测试

@Test
public void javaConfigTest(){
    ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);

    Student student = context.getBean("student", Student.class);

    System.out.println(student);
}

7.2 构造器的方法构建

@Data
@Component
public class Address {
    @Value("重庆邮电大学")
    private String address;
}
@Data
@Component
public class Student {
    private String name;
    @Autowired
    private Address address;
    public Student(String name) {
        this.name = name;
    }
}
@Configuration
@ComponentScan("com.ZhengXinhao.Pojo")
public class JavaConfig {
    @Bean("student")
    public Student get_Student(){
        return  new Student("郑信豪");
    }
}

测试

@Test
public void JavaConfigTest(){
    ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);

    Student student = (Student) context.getBean("student");

    System.out.println(student);
}

8 代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPr0Tyij-1631336112025)(E:\学习文件\JAVA学习\学习记录\前端\imgs\image-20210808152824555.png)]

8.1 静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!

抽象角色(出租,结婚)

public interface Rent {
    /**
     * 出租房子的功能
     * */
    public void rent();
}

真实对象(房东,结婚对象)

public class Hostess implements Rent{
    @Override
    public void rent() {
        System.out.println("出租房屋,收钱!");
    }
}

代理对象(房屋中介,婚介所)

public class Proxy implements Rent{
    private Hostess hostess;


    public Proxy(Hostess hostess) {
        this.hostess = hostess;
    }

    @Override
    public void rent() {
        hostess.rent();
        seeHouse();
        sign();
        fee();
    }
    //看房
    private void seeHouse(){
        System.out.println("中介带着看房子!");
    }

    //签合同
    private void sign(){
        System.out.println("和中介签署租赁合同!");
    }

    //收费用
    private void fee(){
        System.out.println("中介收取费用!");
    }
}

客户(你)

public class Client {
    @Test
    public void client(){
        Hostess hostess = new Hostess();
        Proxy proxy = new Proxy(hostess);
        proxy.rent();
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务

  • 公共角色就交给代理角色!实现了业务的分工!

  • 公共业务发生扩展的时候,方便集中管理!

    实现开闭原则软件实体应当对扩展开放,对修改关闭

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

8.2 深入理解

抽象角色(出租,结婚)

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

真实对象(房东,结婚对象)

public class UserServiceImpl implements UserService{
    @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("查询了一个用户!");
    }
}

代理对象(房屋中介,婚介所)

public class UserServiceProxy implements UserService{
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    public void log(String msg){
        System.out.println("[Debug] 使用了一个"+msg+"方法");
    }
}

客户(你)

public class Client {
    @Test
    public void Test(){
        UserServiceImpl userService = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);

        proxy.delete();
    }
}

从代理到AOP

想学Spring?看看这篇文章吧!_第6张图片

8.3 动态代理

  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是我们直接写好的!

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口 — JDK动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javassist

    需要了解两个类:Proxy:生成动态代理实例;InvocationHandler:调用处理程序,并返回结果

抽象对象

public interface Rent {
    /**
     * 出租房屋的能力
     * */
    public void rent();
}

真实对象

public class Hostess implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

动态生成代理对象的类

  • 在这里你需要明白rent.getClass().getInterfaces(),这里是得到实现类的接口的
  • method.invoke(rent, args),这里是调用rent这个实体类里面的方法
  • 这里留下一个疑问,invoke方法是什么时候调用的?什么时候生成我们的动态代理类的,代理的执行方法是什么时候执行的
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现!
        Object result = method.invoke(rent, args);
        seeHose();
        fee();
        return result;
    }

    public void seeHose(){
        System.out.println("中介带着看房子!");
    }

    public void fee(){
        System.out.println("中介收取费用!");
    }
}

测试

@Test
public void Test(){
    //真实角色
    Hostess hostess = new Hostess();

    //代理角色:现在没有
    ProxyInvocationHandler pih = new ProxyInvocationHandler();

    //通过调用程序处理角色来处理我们要调用的接口对象!
    pih.setRent(hostess);
    Rent proxy = (Rent) pih.getProxy(); //这里的proxy就是动态生成的,我们并没有写
    proxy.rent();

    System.out.println(Hostess.class.getInterfaces()[0].getName());
}

我们可以生成工具类

public class ProxyTarget implements InvocationHandler {
    private Object target;

    public ProxyTarget(Object target) {
        this.target = target;
    }

    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法已经执行");
        return method.invoke(target, args);
    }
}

测试

@Test
public void test(){
    UserServiceImpl service = new UserServiceImpl();
    ProxyTarget target = new ProxyTarget(service);
    UserService proxy = (UserService) target.getProxy();
    proxy.add();
}

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共角色就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

9 AOP

9.1 什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngDzNtwW-1631336112028)(E:\学习文件\JAVA学习\学习记录\前端\imgs\image-20210809160633114.png)]

9.2 AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…

  • 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

  • 目标(Target):被通知对象。

  • 代理(Proxy):向目标对象应用通知之后创建的对象。

  • 切入点(PointCut):切面通知执行的“地点”的定义。

  • 连接点(JointPoint):与切入点匹配的执行点。

  • 切入点(Pointcut)
    在哪些类,哪些方法上切入(where

  • 通知(Advice)
    在方法执行的什么实际(**when:**方法前/方法后/方法前后)做什么(**what:**增强的功能)

  • 切面(Aspect)
    切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强!

  • 织入(Weaving)
    把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)

想学Spring?看看这篇文章吧!_第7张图片

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

想学Spring?看看这篇文章吧!_第8张图片

即AOP在不改变原有代码的情况下,去增加新的功能。

9.3 在Spring中使用AOP

导入依赖

<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.7version>
dependency>
9.3.1 接口实现

使用Spring的API接口【主要是SpringAPI接口实现】

核心业务接口

public interface UserService {
    public void add();
    public void delete();
    public String update();
    public void query();
}

核心业务实现类

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加了一个用户!");
    }

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

    @Override
    public String update() {
        System.out.println("修改了一个用户!");
        return "Hello Java Designer!";
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

切面

public class AfterLog implements AfterReturningAdvice{
    /**
     * @param method 要执行的目标对象的方法
     * @param args 参数
     * @param target 目标对象
     * @param returnValue 方法的返回值
     * */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.getName()+"方法被执行,返回值为--->"+"("+returnValue.getClass().getTypeName()+")"+returnValue);
    }
}
public class BeforeLog implements MethodBeforeAdvice {
    /**
     * @param method 要执行的目标对象的方法
     * @param args 参数
     * @param target 目标对象
     * */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法即将被执行");
    }
}

xml配置

expression:表达式,execution(要执行的位置!* * * * *)


配置原则

想学Spring?看看这篇文章吧!_第9张图片



    <bean id="SService" class="com.ZhengXinhao.Service.UserService" abstract="true"/>
    <bean id="service" class="com.ZhengXinhao.Service.UserServiceImpl"/>
    <bean id="after" class="com.ZhengXinhao.Log.AfterLog"/>
    <bean id="before" class="com.ZhengXinhao.Log.BeforeLog"/>

    <aop:config>

        <aop:pointcut id="LogPointCut" expression="execution(* com.ZhengXinhao.Service.UserServiceImpl.*(..))"/>

        <aop:advisor advice-ref="after" pointcut-ref="LogPointCut"/>
        <aop:advisor advice-ref="before" pointcut-ref="LogPointCut"/>
    aop:config>

测试

@Test
public void AopTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    UserService service = (UserService) context.getBean("service");

    service.update();
}
9.3.2 自定义实现

自定义类来实现【主要是切面定义】

自定义方法

public class DiyAspect {
    public void before(){
        System.out.println("=======方法即将被执行========");
    }
    public void after(){
        System.out.println("==========方法执行后========");
    }
}

beans.xml


<bean id="SService" class="com.ZhengXinhao.Service.UserService" abstract="true"/>
<bean id="service" class="com.ZhengXinhao.Service.UserServiceImpl"/>
<bean id="diyAspect" class="com.ZhengXinhao.Diy.DiyAspect"/>

<aop:config>


        <aop:pointcut id="pointcut" expression="execution(* com.ZhengXinhao.Service.UserServiceImpl.*(..))"/>
        <aop:aspect ref="diyAspect">

            <aop:after method="after" pointcut-ref="pointcut"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
        aop:aspect>
    aop:config>

测试

@Test
public void AopTest(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    UserService service = (UserService) context.getBean("service");

    service.update();
}
9.3.3 注解实现

接口

public interface UserService {
    public void add();
    public void delete();
    public String update();
    public void query();
}

实现类

@Component("service")
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增加了一个用户!");
    }

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

    @Override
    public String update() {
        System.out.println("修改了一个用户!");
        return "Hello Java Designer!";
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

切面

@Aspect
@Component
public class AnnotationPointCut {
    @Before("execution(* com.ZhengXinhao.Service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("====方法执行前====");
    }
    @After("execution(* com.ZhengXinhao.Service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("====方法执行后====");
    }

    @Around("execution(* com.ZhengXinhao.Service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");

        System.out.println(joinPoint.getSignature());
        joinPoint.proceed();

        System.out.println("环绕后");
    }
}

配置JavaConfig

@Configuration
@ComponentScan("com.ZhengXinhao")
@EnableAspectJAutoProxy
public class JavaConfig {
}

注意

在注解配置中要用

@EnableAspectJAutoProxy

在xml配置中要用


<aop:aspectj-autoproxy/>

10 整合MyBatis

http://mybatis.org/spring/zh/

10.1 pom.xml

除了Sping和MyBatis必要的包以外,我们还要导入MyBatis-Spring的整合包

        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.13version>
        dependency>
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.7version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.10version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>5.3.9version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-jdbcartifactId>
            <version>5.3.9version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.9.7version>
        dependency>
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatis-springartifactId>
            <version>2.0.6version>
        dependency>

10.2 配置

配置mybatis-config.xml


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>
configuration>

配置spring-mybatis.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">

<!--    配置dataSource-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/k_study?serverTimezone=GMT%2B8&useUnicode=true&useSSL=false&characterRnCoding=utf8"/>
        <property name="password" value="123456"/>
        <property name="username" value="root"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
     </bean>

<!--    配置sqlSessionFactory,导入原来的mysql-config.xml(dataSource、config、mapper)-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/ZhengXinhao/Mapper/BlogMapper.xml"/>
    </bean>

<!--    配置sqlSession,只能用sqlSession的构造方法导入sqlSessionFactory-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>

配置接口

public interface BlogMapper {
    public ArrayList<Blog> selectBlogs();
}

配置接口的Mybatis的对应的Mapper.xml


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ZhengXinhao.Mapper.BlogMapper">
    <select id="selectBlogs" resultType="com.ZhengXinhao.Pojo.Blog">
        select * from k_study.blog
    select>
mapper>

配置实现类让Spring托管

public class BlogMapperImpl implements BlogMapper{
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public ArrayList<Blog> selectBlogs() {
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        return mapper.selectBlogs();
    }
}

配置总的beans配置xml


<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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <import resource="spring-mybatis.xml"/>


    <bean id="blogMapperImpl" class="com.ZhengXinhao.Mapper.BlogMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    bean>


    <bean id="method2" class="com.ZhengXinhao.Mapper.BlogMapperImplExtend">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    bean>
beans>

测试

@Test
public void MyBatisReviewTest() throws IOException {
    ApplicationContext context = new ClassPathXmlApplicationContext("applacation.xml");
    BlogMapper mapper = (BlogMapper) context.getBean("method2");
    ArrayList<Blog> blogs = mapper.selectBlogs();

    for (Blog blog : blogs) {
        System.out.println(blog);
    }
}

在上面的获得sqlSession的方法有不同的的实现方法,第一种就是手动的注入sqlSession,第二种就是继承sqlSessionDaoSupport

public class BlogMapperImplExtend extends SqlSessionDaoSupport implements BlogMapper{
    @Override
    public ArrayList<Blog> selectBlogs() {
        return getSqlSession().getMapper(BlogMapper.class).selectBlogs();
    }
}

11 声明式事务

11.1 回顾事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性。
  • 事务ACID原则:
    • 原子性(atomicity)
      事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用。
    • 一致性(consistency)
      一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中。
    • 隔离性(isolation)
      可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
    • 持久性(durability)
      事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中。

11.2 事务需求环境搭建

依然是交给Spring-MyBatis来管理

接口

public interface BlogMapper {
    /**
     * 查询所有的博客信息
     * @return 返回Blog对象的集合
     * */
    public ArrayList<Blog> selectBlogs();

    /**
     * 删除用户
     * @param id 删除依据
     * */
    public void delete(String id);

    /**
     *添加用户
     * @param blog 准备添加的用户
     * */
    public void add(Blog blog);
}

实现类,需要被Spring托管

@Component("impl")
public class BlogMapperImpl implements BlogMapper{
    @Autowired
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public ArrayList<Blog> selectBlogs() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Blog blog = (Blog) context.getBean("blog");
        add(blog);
        delete("500");
        int i =1/0;//定义一个错误,让程序有异常
        return sqlSession.getMapper(BlogMapper.class).selectBlogs();
    }

    @Override
    public void delete(String id) {
        sqlSession.getMapper(BlogMapper.class).delete(id);
    }

    @Override
    public void add(Blog blog) {
        sqlSession.getMapper(BlogMapper.class).add(blog);
    }
}

这样的话,这就不是一个事务

优化

在beans.xml中配置

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="transaction" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="select*"/>
    tx:attributes>
tx:advice>

<aop:config>
    <aop:pointcut id="TransactionPointCut" expression="execution(* com.ZhengXinhao.Mapper.BlogMapperImpl.*(..))"/>
    <aop:advisor advice-ref="transaction" pointcut-ref="TransactionPointCut"/>
aop:config>

可以配置事务的执行路径

<tx:method name="select*" propagation="REQUIRED"/>

spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!


思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况;
  • 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!

快速配置

pom.xml

	<properties>
   		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
   		<maven.compiler.encoding>UTF-8maven.compiler.encoding>
   	 	<java.version>11java.version>
   	 	<maven.compiler.source>11maven.compiler.source>
   		<maven.compiler.target>11maven.compiler.target>
		properties>
	<dependencies>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13version>
        dependency>
		<dependency>
   			 <groupId>org.springframeworkgroupId>
   			 <artifactId>spring-webmvcartifactId>
   			 <version>5.3.9version>
		dependency>
		<dependency>
  			 <groupId>org.springframeworkgroupId>
 			 <artifactId>spring-jdbcartifactId>
   			 <version>5.3.9version>
		dependency>
        <dependency>
             <groupId>org.projectlombokgroupId>
             <artifactId>lombokartifactId>
             <version>1.18.16version>
        dependency>
        <dependency>
		    <groupId>org.aspectjgroupId>
   			<artifactId>aspectjweaverartifactId>
  			<version>1.9.7version>
		dependency>
    dependencies>


    <build>
        
        <resources>
            <resource>
                <directory>src/main/resourcesdirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                    <include>**/*.tldinclude>
                includes>
                <filtering>falsefiltering>
            resource>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                    <include>**/*.tldinclude>
                includes>
                <filtering>falsefiltering>
            resource>
        resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-surefire-pluginartifactId>
                <version>2.22.1version>
                <configuration>
                    <skipTests>trueskipTests>
                configuration>
            plugin>
        plugins>
    build>

beans.xml


<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">
    
<bean id="hello" class="com.ZhengXinhao.Pojo.Hello">
    <property name="str" value="Spring"/>
bean>
    
beans>

作用域

singleton

prototype

Spring-MyBatis

pom.xml

    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.13version>
    dependency>
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.13version>
    dependency>
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.7version>
    dependency>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.10version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>5.3.9version>
    dependency>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-jdbcartifactId>
        <version>5.3.9version>
    dependency>
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.9.7version>
    dependency>
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatis-springartifactId>
        <version>2.0.6version>
    dependency>

mybatis-config.xml


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>
configuration>

spring-mybatis.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">

<!--    配置dataSource-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/k_study?serverTimezone=GMT%2B8&useUnicode=true&useSSL=false&characterRnCoding=utf8"/>
        <property name="password" value="123456"/>
        <property name="username" value="root"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
     </bean>

<!--    配置sqlSessionFactory,导入原来的mysql-config.xml(dataSource、config、mapper)-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/ZhengXinhao/Mapper/BlogMapper.xml"/>
    </bean>

<!--    配置sqlSession,只能用sqlSession的构造方法导入sqlSessionFactory-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>

Mapper.xml


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ZhengXinhao.Mapper.BlogMapper">
    <select id="selectBlogs" resultType="com.ZhengXinhao.Pojo.Blog">
        select * from k_study.blog
    select>
mapper>

beans.xml


<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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <import resource="spring-mybatis.xml"/>


    <bean id="blogMapperImpl" class="com.ZhengXinhao.Mapper.BlogMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    bean>


    <bean id="method2" class="com.ZhengXinhao.Mapper.BlogMapperImplExtend">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    bean>
beans>

你可能感兴趣的:(java学习路,笔记,spring,java,spring,boot,ioc,aop)