Spring框架之IOC和AOP底层原理

本文内容纲要:Spring框架之IOC和AOP底层原理

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/spring-framework

官网下载:https://repo.spring.io/release/org/springframework/spring/

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

官网核心:https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#spring-core

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组成

Image

1、4拓展

Spring官网:现代化的开发就是基于java的开发

Image

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的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

Image

采用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的作用域

Image

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的包

Image

使用注解需要导入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静态代理

Image

角色分析:

抽象角色:一般会使用接口或者是抽象类类解决

真实角色:被代理得角色

代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作

客户:访问代理得角色得人

代码步骤

接口

//租房

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

Image

10.3动态代理

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

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

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

基于接口:JDK动态代理[我们在这里使用]

基于类:cglib

java字节码实现:javassist

需要了解得两个类:Proxy代理,InvocationHandler:调用处理程序

动态代理得好处:

可以是真实角色得操作更加纯粹,不用去关注一些公共得业务

公共也就交给代理角色,实现了业务得分工

公共业务发送扩展得时候,方便集中管理

一个动态代理类代理得是一个接口,一般就是对应得一类业务

一个动态代理类可以代理多个类,只要是实现同一个接口即可

11、AOP

11.1、什么是AOP

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

Image

Image

Image

本文内容总结:Spring框架之IOC和AOP底层原理

原文链接:https://www.cnblogs.com/swpu-wxb/p/15114393.html

以上是 Spring框架之IOC和AOP底层原理 的全部内容, 来源链接: utcz.com/z/296708.html

回到顶部