关于java:Spring框架之IOC和AOP底层原理

10次阅读

共计 13981 个字符,预计需要花费 35 分钟才能阅读完成。

​1、1 简介
Spring:春天 –> 软件行业的春天
2002,首次推出了 Spring 框架的雏:interface21 框架!
Spring 框架即以 interface21 框架为根底,通过从新设计,并不断丰富其外延于 2004 年 3 月 24 日公布了 1.0 正式版本
Spring Framework 创始人,驰名作者。Rod 在悉尼大学不仅取得了计算机学位,同时还取得了音乐学位。更令人吃惊的是在回到软件开发畛域之前,他还取得了音乐学的博士学位
Spring 理念:使现有的技术更加的容易应用,自身是一个大杂烩,整合了现有的技术框架!
SSH:Struct2+Spirng+Hibreante
SSM:SpringMVC+Spring+Mybatis
官网:https://spring.io/projects/sp…
官网下载:https://repo.spring.io/releas…
GitHub:https://github.com/spring-pro…
官网外围:https://docs.spring.io/spring…
Maven:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

1.2 长处:
Spring 是一个开源的收费的框架(容器)
Spring 是一个轻量级、非入侵式的框架
管制反转 (IOC)、面向切面编程(AOP)
反对事务的解决,对框架整合的反对
总结一句话:Spring 就是一个轻量级的管制反转(IOC),和面向切面编程(AOP)的框架

1、3 组成

1、4 拓展
Spring 官网:现代化的开发就是基于 java 的开发


Spring Boot
一个疾速开发的脚手架
基于 SpringBoot 能够疾速开发单个微服务
约定大于配置
Spring Cloud
SpringCloud 是基于 SpringBoot 实现的
因为当初大多数公司都在应用 SprigBoot 进行疾速开发,学习 SpringBoot 的前提,须要齐全把握 Spring 以及

SpringMVC!

弊病:Spring 倒退太久,违反了原来的理念!配置非常繁琐,人称“配置天堂”!

2、IOC 实践推导
1、UserDao 接口

public interface UserDao {void getUser();
}

2、UserDaoImpl 实现类

public class UserDaoImpl implements UserDao {public void getUser() {System.out.println("默认获取用户的数据");
    }
}

3、UserService 接口

public interface UserService {void getUser();
}

4、UserServiceImpl 业务实现类

public class UserServiceImpl implements UserService {
    private UserDao userDao ;

    // 利用 Set 接口进行动静实现注入
    public void setUserDao(UserDao userDao) {this.userDao = userDao;}

    public void getUser() {userDao.getUser();
    }
}

在之前的业务中,用户的额需要可能会影响到咱们原来的代码,咱们须要依据用户的需要去批改源代码!如果程序代码量非常大,批改一次的老本代价非常低廉!

咱们应用一个 Set 接口实现,曾经产生了革命性的扭转!

// 利用 Set 接口进行动静实现注入

public void setUserDao(UserDao userDao) {this.userDao = userDao;}

之前,程序是被动创建对象!控制权在程序员手上!
应用了 Set 注入后,程序不再具备主动性,而是变成了被动的接管对象
这种思维,从实质上解决了问题,咱们程序员不必再去治理对象的创立了。零碎的耦合性大大降低,能够更加专一于在业务的实现上!这是 IOC 的原型!
IOC 实质
管制反转 IoC(Inversion of Control),是一种设计思维,DI(依赖注入)是实现 IoC 的一种办法,也有人认为 DI 只是 IoC 的另一种说法。没有 IoC 的程序中 , 咱们应用面向对象编程 , 对象的创立与对象间的依赖关系齐全硬编码在程序中,对象的创立由程序本人管制,管制反转后将对象的创立转移给第三方,集体认为所谓管制反转就是:取得依赖对象的形式反转了。


采纳 XML 形式配置 Bean 的时候,Bean 的定义信息是和实现拆散的,而采纳注解的形式能够把两者合为一体,Bean 的定义信息间接以注解的模式定义在实现类中,从而达到了零配置的目标。

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

3、HelloSpring
1、导人 Spring Maven 依赖

  <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
    </dependencies>

2、编写 Hello 实体类

public class Hello {
    private String str;

    public String getStr() {return str;}

    public void setStr(String str) {this.str = str;}

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }
}

3、编写咱们的 Spring 文件,命名为 beans.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">
    <!-- 应用 Spring 创建对象,在 Spring 中这些都被称为 bean
        类型  变量名 = new 类型()
        Hello hello = new Hello()

        id = 变量名
        class = new 的对象
        property 相当于给对象中的属性设置一个值
    -->
    <bean id="hello" class="com.aostarit.pojo.Hello">
        <property name="str" value="Spring123"/>
    </bean>

</beans>

4、测试

public class MyTest {public static void main(String[] args) {
       // 获取 Spring 的上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        // 咱们的对象当初都在 Spring 容器中了,间接通过 context 去获取就能够获取实例化对象
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello);
    }
}

思考问题?

Hello 对象是谁创立的?
Hello 对象是由 Spring 创立的
Hello 对象的属性是怎么设置的?
Hello 对象的属性是由 Spring 容器设置的
这个过程就叫管制反转

管制:谁来管制对象的创立,传统应用程序的对象是由程序自身来管制创立的,应用 Spring 后,对象是由 Spring 来创立的

反转:程序自身不创建对象,而是变成被动的接管对象

依赖注入:就是利用 set 办法实现注入的

IOC 是一种编程思维,由被动的编程变成被动的接管

能够通过 new  ClassPathXmlApplicationContext 去浏览以下底层源码

OK, 到了当初,咱们彻底不再须要程序中去改变了,要实现不同的操作,只须要在 xml 配置文件中进行批改,所谓的 IOC, 一句话搞定: 对象由 Spring 来创立、治理、拆卸!

4、IOC 创建对象的形式(结构器注入依赖)
1. 应用无参结构创建对象,默认

2. 应用有参结构创建对象

下标赋值:<!-- 第一种通过下标赋值注入 -->
   <bean id="user" class="com.aostarit.pojo.User">
       <constructor-arg index="0" value="文贤波"/>
    </bean>

类型赋值:<bean id="user" class="com.aostarit.pojo.User">
        <constructor-arg type="java.lang.String" value="wxb"/>
</bean>

参数名赋值:<bean id="user" class="com.aostarit.pojo.User">
        <constructor-arg name="name" value="123"/>
 </bean>

总结:在配置文件加载的时候,容器中治理的对象就曾经实现了初始化
5、Spring 配置
5.1 别名:
应用别名也能够获取对象

<!-- 给对象起别名 -->
<alias name="user" alias="userNew"/>

5.2Bean 配置

    <!--
        id :bean 惟一的标识符,也相当于咱们的对象名
        class:bean 所对应的权限命名:包名 + 类名
        name: 也是别名 并且 name 更弱小,能够同时取多个别名
    -->

    <!-- 第二种通过参数名赋值注入 -->
    <bean id="user" class="com.aostarit.pojo.User">
        <constructor-arg name="name" value="user"/>
    </bean>
    <bean id="test" class="com.aostarit.pojo.test" name="test2,test3">
        <constructor-arg name="id" value="test"/>
    </bean>

5.2Import
个别用于团队开发应用,它能够将多个配置文件导入合并为一个

假如当初我的项目中由多集体开发,张三,李四,王五,这三个人复制不同的类开发,不同的类须要注册在不同的 bean 中,咱们能够利用 Import 将所有人的 beans.xml 文件合并为一个总的 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">

    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>
</beans>

应用的时候,就间接应用总的配置文件即可

6、依赖注入(DI)
6.1 结构器注入
前边曾经说过

6.2Set 形式注入
依赖注入:Set 注入
依赖:bean 对象的创立依赖于容器
注入:bean 对象的所有属性,由容器来注入
【环境搭建】

简单类型:

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> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private Properties info;
    private String wife;
}

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

    <bean id="student" class="com.aostarit.pojo.Student">
        <!-- 第一种一般值注入:间接写 value 属性注入 -->
        <property name="name" value="wxb"/>
    </bean>

</beans>

测试类:

 public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("beans1.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());
    }
欠缺注入信息
<?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">

    <bean id="address" class="com.aostarit.pojo.Address">
        <property name="address" value="四川成都"/>
    </bean>

    <bean id="student" class="com.aostarit.pojo.Student">
        <!-- 一般值注入:间接写 value 属性注入 -->
        <property name="name" value="wxb"/>

        <!-- Bean 注入 ref -->
        <property name="address" ref="address"/>

        <!-- 数组注入 -->
        <property name="books">
            <array>
                <value> 西游记 </value>
                <value> 红楼梦 </value>
                <value> 三国演义 </value>
                <value> 水浒传 </value>
            </array>
        </property>

        <!-- list 注入 -->
        <property name="hobbys">
            <list>
                <value> 听歌 </value>
                <value> 写代码 </value>
                <value> 看电影 </value>
            </list>
        </property>

        <!-- map 注入 -->
        <property name="card">
            <map>
                <entry key="身份证" value="1234561234561234"/>
                <entry key="银行卡" value="1111111111111111"/>
            </map>
        </property>

        <!-- set 注入 -->
        <property name="games">
            <set>
                <value>DNF</value>
                <value>CF</value>
                <value>LOL</value>
            </set>
        </property>

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

        <!--propesties -->
        <property name="info">
            <props>
                <prop key="driver">123456</prop>
                <prop key="url"> 男 </prop>
                <prop key="username"> 小明 </prop>
                <prop key="password"> 小明 </prop>
            </props>
        </property>
    </bean>


</beans>

6.3 拓展形式注入
P 命名空间注入:

  <!-- p 命名空间注入,能够间接注入属性的值 -->
    <bean id="user" class="com.aostarit.pojo.User" p:name="wxb"/>

C 命名空间注入:

 <!-- c 命名空间注入,通过结构器注入,constructor-args -->
    <bean id="user" class="com.aostarit.pojo.User" c:name="123" c:age="18"/>

测试

  @Test
    public void test(){ApplicationContext context = new ClassPathXmlApplicationContext("user.xml");
        User user = context.getBean("user",User.class);
        System.out.println(user.toString());
    }

留神点:应用 p 命名或者 c 命名空间注入,须要导入依赖

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

6.4Bean 的作用域



1、单例模式:(Spring 默认机制)

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

2、原型模式:每次从容器中 get 的时候,都会产生一个新对象!

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

3、其余的 request、session、application 这些只能在 web 开发中才会用到

7、Bean 主动拆卸
主动拆卸是 Spring 满足 Bean 依赖的一种形式
Spring 会在上下文中主动寻找,并主动给 bean 拆卸属性
在 Spring 中有三张主动拆卸形式

在 xml 中显示配置
在 java 中显示配置
隐式的主动拆卸 bean(重要)
7.1 测试
环境搭建:一个人有两个宠物!

7.2ByName 主动拆卸

<!--
        byName: 会主动在容器中查找,和本人对象 set 办法前面的值对应的 beanid
     -->
    <bean id="people" class="com.aostarit.pojo.People" autowire="byName">
        <property name="name" value="波少"/>
    </bean>

7.3ByType 主动拆卸

<!--
        byName: 会主动在容器中查找,和本人对象 set 办法前面的值对应的 beanid
        byType: 会主动在容器中查找,和本人对象属性类型雷同的 bean
     -->
    <bean id="people" class="com.aostarit.pojo.People" autowire="byType">
        <property name="name" value="波少"/>
    </bean>

小结:
byname 时,须要保障所有 bean 的 id 惟一,并且这个 bean 须要和主动注入的属性的 set 办法的值统一
bytyp 时,须要保障所有 bean 的 class 惟一,并且这个 bean 须要和主动注入的类的属性统一
7.4 应用注解进行主动拆卸
jdk1.5 开始反对注解,Spring2.5 开始反对注解

要应用注解须知:

导入束缚
配置注解的反对:xmlns:context="http://www.springframework.org/schema/context"
<?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
        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/>

    <bean id="cat" class="com.aostarit.pojo.Cat"/>
    <bean id="dog" class="com.aostarit.pojo.Dog"/>
    <bean id="people" class="com.aostarit.pojo.People"/>

</beans>

@Autowired

间接在属性上应用即可,也能够在 set 形式上应用

应用 @Autowired 咱们能够不必编写 set 办法了,前提式你这个主动拆卸的属性在 IOC(Spring)容器中存在,且合乎名字 byname!

科普:

@Nullable 字段标记了这个注解,阐明这个字段能够为 NULL

public @interface Autowired {boolean required() default true;
}

@Autowired(required=false)如果显示的定义了 required=false,则示意这个对象能够为 null,否则不容许为空

public class People {@Autowired(required = false)
    private Dog dog;
    @Autowired
    private Cat cat;
    private String name;
}

如果 @Autowired 主动拆卸无奈通过一个注解实现的时候,咱们能够应用 @Qualifier(value = "XXX")去配置 @Autowired 注解,给其指定一个惟一的 Bean

public class People {@Autowired(required = false)
    private Dog dog;
    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;
    private String name;

    public Dog getDog() {return dog;}
    public Cat getCat() {return cat;}
}

@Resource 注解

public class People {
    @Resource
    private Dog dog;
    @Resource(name = "cat1")
    private Cat cat;
    private String name;
}

@Resource 和 @Autowired 的区别:

都是用来进行主动拆卸的,都能够放在属性字段上

@Autowired 通过 bytype 的形式实现,而且必须要求这个对象存在【罕用】
@Resource 默认通过 byname 的形式实现,如果找不到名字,则通过 bytype 的形式实现! 如果两个都找不到,就会报错!【罕用】
执行程序不同:@Autowired 通过 bytype 的形式实现、@Resource 默认通过 byname 的形式实现
8、应用注解开发
在 Spring4 之后的开发,要应用注解开发,必须要导入 aop 的包



应用注解须要导入 context 的束缚,减少注解的反对!

1、bean

2、属性如何注入

// 等价于 <bean id="user" class="com.aostarit.pojo.User"/>
@Component
public class User {
    //  <property name="name" value="name"/>
    @Value("value")
    public String name ;

    public String getName() {return name;}

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

3、衍生的注解

@Component 有几个衍生的注解, 在咱们的 web 开发中,会依照 mvc 三层架构分层
dao 层:【@Repository】
service 层:【@Service】
contooller 层:【@Controller】
这四个注解性能都是一样的,都是代表将某个类注册到 Spring 中,拆卸 Bean
4、主动拆卸

@Autowired: 主动拆卸通过类型—-> 名字

如果 @Autowired 不能惟一主动拆卸主属性,则须要通过 @Qualifier(value = "XXX")

@Resource: 主动拆卸通过名字 –> 类型
@Nullable 字段标记了这个注解,阐明这个字段能够为 NULL
@Component 组件: 放在类上,阐明这个类被 Spring 治理了,就是 bean!

5、作用域

@Component
@Scope("singleton")
public class User {
    //  <property name="name" value="name"/>
    @Value("value")
    public String name ;

    public String getName() {return name;}

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

6、小结

xml 与注解

xml 更加万能,应用于任何场合!保护更加简略
注解不是本人的类应用不了,保护绝对简单
最佳实际:

xml 用来治理所有的 bean,
注解只负责实现属性的注入
咱们在应用的过程中,只须要留神一个问题,必须让注解失效,就必须开启注解的反对

<!-- 注解扫描的包门路,Spring 会扫描该门路下所有的注解 -->
    <context:component-scan base-package="com.aostarit"/>

9、应用 Java 的形式配置 Spring
咱们当初齐全不应用 Spring 的 xml 配置了,全权交给 java 来做

javaConfig 是 Spring 的一个子项目,在 Spring4 之后,它成为了一个外围性能

实体类

// 这个注解阐明这个类被 spring 治理了,注册到了容器中
@Component
public class User {
    private String name;

    public String getName() {return name;}

    @Value("wxb")// 注入值
    public void setName(String name) {this.name = name;}

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

配置类

//@Configuration 这个类也会被 spring 治理,注册到容器中,因为 Configuration 原本就是一个 @Component
//@Configuration 代表这是一个配置类,就和咱们之前的 beans.xml 一样
@Configuration
@ComponentScan("com.aostarit.pojo")
@Import(UserConfig.class)
public class BeanConfig {

    // 注册一个 bean: 就相当于咱们之前写的一个 bean 标签
    // 这个办法的名字,就相当于 bean 标签的 id 属性
    // 这个办法的返回值类型,就相当于 bean 标签中的 class 属性
    @Bean
    public User getUser(){return new User();
    }
}

测试类

public class MyTest {public static void main(String[] args) {
        // 如果齐全应用了配置类去做,咱们就只能通过 AnnocationConfig 上下文来获取容器,通过配置的 class 对象加载!ApplicationContext context  = new AnnotationConfigApplicationContext(BeanConfig.class);
        User user = (User) context.getBean("getUser");
        System.out.println(user.getName());
    }
}

这种纯 Java 在 SpringBoot 中随处可见

10、代理模式
为什么要学习代理模式?因为这就是 SpringAOP 得底层实现原理:【SpringAOP 和 SpringMVC】

10.1 动态代理


角色剖析:

形象角色:个别会应用接口或者是抽象类类解决
实在角色:被代理得角色
代理角色:代理实在角色,代理实在角色后,咱们个别会做一些从属操作
客户:拜访代理得角色得人
代码步骤

接口
// 租房
public interface Rent {public void rent();
}
实在角色
// 房东
public class Host implements Rent {public void rent() {System.out.println("房东租房");
    }
}

代理角色
public class Proxy {
    private Host host;

    public Proxy(Host host) {this.host = host;}

    public Proxy() {}

    public  void rent(){seeHouse();
        host.rent();
        hetong();}

    public void seeHouse(){System.out.println("中介代理看房子");
    }

    public void hetong(){System.out.println("签订合同");
    }
}

客户端拜访代理角色
public class Demo01 {public static void main(String[] args) {
        // 房东出租房子
        Host host = new Host();
        // 中介代理模式
        Proxy proxy = new Proxy(host);
        // 你不必面对房东,间接找中介即可
        proxy.rent();}
}

代理模式得益处

能够是实在角色得操作更加纯正,不必去关注一些公共得业务
公共也就交给代理角色,实现了业务得分工
公共业务发送扩大得时候,不便集中管理
代理模式得毛病:

一个实在得角色就会产生一个代理角色,代码量会翻一倍,开发效率会变低
10.2 加深了解
聊聊 AOP


10.3 动静代理
动静代理和动态代理角色一样
动静代理得代理类是动静生成得,不是咱们间接写好得
动静代理分为两大类:基于接口得动静代理,基于类的动静代理
基于接口:JDK 动静代理 [咱们在这里应用]
基于类:cglib
java 字节码实现:javassist
须要理解得两个类:Proxy 代理,InvocationHandler: 调用处理程序
动静代理得益处:
能够是实在角色得操作更加纯正,不必去关注一些公共得业务
公共也就交给代理角色,实现了业务得分工
公共业务发送扩大得时候,不便集中管理
一个动静代理类代理得是一个接口,个别就是对应得一类业务
一个动静代理类能够代理多个类,只有是实现同一个接口即可
11、AOP
11.1、什么是 AOP
       在软件业,AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译形式和运行期间动静代理实现程序性能的对立保护的一种技术。AOP 是 OOP 的连续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 能够对业务逻辑的各个局部进行隔离,从而使得业务逻辑各局部之间的耦合度升高,进步程序的可重用性,同时进步了开发的效率。
​​

正文完
 0