spring框架:IOC、AOP详解

一、IOC

1.IOC的介绍

IOC:(Inverse of Control)控制反转,也叫依赖注入,指的是将对象的创建权交给spring容器创建,利用了工厂模式将对象交给容器管理,只需要在spring配置文件配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。不需要我们自己动手去创建对象,大大的降低了代码之间的耦合度,使资源更加容易管理。

2.IOC容器Bean对象实例化模拟

思路 :
1. 定义 Bean ⼯⼚接⼝,提供获取 bean ⽅法
2. 定义 Bean ⼯⼚接⼝实现类,解析配置⽂件,实例化 Bean 对象
3. 实现获取 Bean ⽅法

2.1定义bean属性对象

package com.itheima.pojo;

public class BeanObject {
    private String id; // bean对象的id属性值
    private String clazz; // bean对象的类路径

    public BeanObject(String id, String clazz) {
        this.id = id;
        this.clazz = clazz;
    }

    public BeanObject() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClazz() {
        return clazz;
    }

    public void setClazz(String clazz) {
        this.clazz = clazz;
    }
}

2.2 添加dom4j坐标依赖

<!-- dom4j -->
<dependency>
 <groupId>dom4j</groupId>
 <artifactId>dom4j</artifactId>
 <version>1.6.1</version>
</dependency>
<!-- XPath -->
<dependency>
 <groupId>jaxen</groupId>
 <artifactId>jaxen</artifactId>
 <version>1.1.6</version>
</dependency>

2.3 准备自定义配置文件

spring.xml

<?xml version="1.0" encoding="utf-8" ?>
<beans>
 <bean id="userService" class="com.itheima.service.UserService"></bean>
 <bean id="accountService" class="com.itheima.service.AccountService"></bean>
</beans>

2.4 定义Bean工厂接口

package com.itheima.spring;

public interface MyFactory {
 // 通过id值获取对象
 public Object getBean(String id);
}

2.5 定义Bean接口的实现类

package com.xxxx.spring;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
    * 模拟Spring的实现
    * 1、通过构造器得到相关配置⽂件
    * 2、通过dom4j解析xml⽂件,得到List 存放id和class
    * 3、通过反射实例化得到对象 Class.forName(类的全路径).newInstance(); 通过Map<id,Class>             
      存储
    * 4、得到指定的实例化对象
*/
 public class MyClassPathXmlApplicationContext implements BeanFactory {
    private Map beans = new HashMap(); // 实例化后的对象放⼊map
    private List<MyBean> myBeans; // 存放已读取bean 配置信息
   /* 1、通过构造器得到相关配置⽂件 */
    public MyClassPathXmlApplicationContext(String fileName) {
   /* 2、通过dom4j解析xml⽂件,得到List (存放id和class) */
      this.parseXml(fileName);
   /* 3、通过反射实例化得到对象Class.forName(类路径).newInstance(); 通过Map存储 */
      this.instanceBean();
   }
 /**
     * 通过dom4j解析xml⽂件,得到List 存放id和class
     * 1、获取解析器
     * 2、得到配置⽂件的URL
     * 3、通过解析器解析xml⽂件(spring.xml)
     * 4、通过xpath语法,获取beans标签下的所有bean标签
     * 5、通过指定语法解析⽂档对象,返回集合
     * 6、判断集合是否为空,遍历集合
     * 7、获取标签元素中的属性
     * 8、得到Bean对象,将Bean对象设置到集合中
     * @param fileName
 */
  private void parseXml(String fileName) {
   // 1、获取解析器
   SAXReader reader = new SAXReader();
   // 2、得到配置⽂件的URL
   URL url = this.getClass().getClassLoader().getResource(fileName);
   try {
   // 3、通过解析器解析xml⽂件(spring.xml)
     Document document = reader.read(url);
    // 4、通过xpath语法,获取beans标签下的所有bean标签
    XPath xPath = document.createXPath("beans/bean");
    // 通过指定语法解析⽂档对象,返回集合
    List<Element> list = xPath.selectNodes(document);
    // 判断集合是否为空,遍历集合
    if (list != null && list.size() > 0) {
      myBeans = new ArrayList<>();
      for(Element el : list) {
      // 获取标签元素中的属性
      String id = el.attributeValue("id"); // id 属性值
      String clazz = el.attributeValue("class"); // class 属性值
      System.out.println(el.attributeValue("id"));
      System.out.println(el.attributeValue("class"));
      // 得到Bean对象
      MyBean bean = new MyBean(id, clazz);
      // 将Bean对象设置到集合中
      myBeans.add(bean);
      }
    }
  } catch (DocumentException e) {
   e.printStackTrace();
 }

 /**
     * 通过反射实例化得到对象 
     * Class.forName(类的全路径).newInstance(); 
     * 通过Map<id,Class>存储
 */
  private void instanceBean() {
   // 判断bean集合是否为空,不为空遍历得到对应Bean对象
    if (myBeans != null && myBeans.size() > 0) {
      for (MyBean bean : myBeans){ 
        try {
           // 通过类的全路径实例化对象
           Object object = Class.forName(bean.getClazz()).newInstance();
           // 将id与实例化对象设置到map对象中
           beans.put(bean.getId(), object);
       } catch (Exception e) {
          e.printStackTrace();
      }
    }
   }
 }
 /**
 * 通过key获取map中的指定value
 * @param id
 * @return
 */
 @Override
 public Object getBean(String id) {
     Object object = beans.get(id);
     return object;
   }
}

2.6 测试自定义IOC容器

 1. 创建与配置文件中对应的Bean对象

package com.itheima.service;
public class UserService {
 public void test(){
 System.out.println("UserService Test...");
 }
}
package com.itheima.service;
public class AccountService {
 public void test(){
 System.out.println("AccountService Test...");
 }
}
2. 测试是否可以获取实例化的 Bean 对象
package com.itheima;
import com.itheima.spring.MyFactory;
import com.itheima.spring.MyClassPathXmlApplicationContext;
import com.itheima.service.AccountService;
import com.itheima.service.UserService;
public class App {
 
 public static void main(String[] args) {
 MyFactory factory = new MyClassPathXmlApplicationContext("spring.xml");
 // 得到实例化对象
 UserService userService = (UserService) factory.getBean("userService");
 userService.test();
 UserService userService2 = (UserService) factory.getBean("userService");
 System.out.println(userService+"=====" + userService2);
 AccountService accountService =
 (AccountService)factory.getBean("accountService");
 accountService.test();
 }
}
Spring 容器在启动的时候读取 xml 配置信息,并对配置的 bean进行 实例化(这⾥模拟的⽐较简
单,仅⽤于帮助⼤家理解),同时通过上下⽂对象提供的 getBean() ⽅法拿到我们配置的 bean
象,从⽽实现外部容器⾃动化维护并创建 bean 的效果。

3、 Spring IOC配置文件加载

 Spring 配置⽂件加载
<?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="userService" class="com.itheima.service.UserService"></bean>
</beans>

4、Spring IOC 容器 Bean 对象实例化

4.1. 构造器实例化

注:通过默认构造器创建空构造方法必须存在,否则创建失败。

1. 设置配置⽂件 spring.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="userService" class="com.itheima.service.UserService"></bean>
</beans>
2. 获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService"); 
userService.test();

4.2  静态⼯⼚实例化(了解)

注: 要有该⼯⼚类及⼯⼚⽅法,⼯⼚⽅法为静态的
1. 定义静态⼯⼚类
package com.itheima.factory;
import com.itheima.service.UserService;

public class StaticFactory {
 /**
 * 定义对应的静态⽅法,返回实例化对象
 * @return
 */
 public static UserService createUserService() {
 return new UserService();
 }
}
2. 设置配置⽂件 spring.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="userService" class="com.itheima.factory.StaticFactory" factorymethod="createUserService"></bean>
</beans>
3. 获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService"); 
userService.test();

当我们指定Spring使⽤静态⼯⼚⽅法来创建Bean实例时,Spring将先解析配置⽂件,并根据配置 ⽂件指定的信息,通过反射调⽤静态⼯⼚类的静态⼯⼚⽅法,并将该静态⼯⼚⽅法的返回值作为 Bean实例,在这个过程中,Spring不再负责创建Bean实例,Bean实例是由⽤户提供的静态⼯⼚⽅ 法提供的。

4.3 实例化⼯⼚实例化(了解)

注: ⼯⼚⽅法为⾮静态⽅法 ,需要配置⼯⼚bean ,并在业务 bean 中配置 factory-bean factory-method 属性
1. 定义⼯⼚类
package com.itheima.factory;
import com.itheima.service.UserService;
/**
* 定义⼯⼚类
*/
public class InstanceFactory {
 /**
 * 定义⽅法,返回实例化对象
 * @return
 */
 public UserService createUserService() {
 return new UserService();
 }
}
2. 设置配置⽂件 spring.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">
 <!--
 实例化⼯⼚
 1.定义实例化⼯⼚bean
 2.引⽤⼯⼚bean 指定⼯⼚创建⽅法(⽅法为⾮静态)
 -->
 <bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean>
 <bean id="userService" factory-bean="instanceFactory" factorymethod="createUserService"></bean>
</beans>
3. 获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService"); 
userService.test();

5、Spring IOC 注⼊

5.1. Spring IOC ⼿动装配(注⼊)

Spring ⽀持的注⼊⽅式共有四种: set 注⼊、构造器注⼊、静态⼯⼚注⼊、实例化⼯⼚注⼊。

5.1.1  set方法注入

注: 属性字段需要提供set ⽅法
四种⽅式,推荐使⽤ set ⽅法注⼊
1. 属性字段提供 set ⽅法
public class UserService {
 // 业务对象UserDao set注⼊(提供set⽅法)
 private UserDao userDao;
 public void setUserDao(UserDao userDao) {
 this.userDao = userDao;
 }
}
2. 配置⽂件的 bean 标签设置 property 标签
<?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">
<!--
 IOC通过property标签⼿动装配(注⼊):
 Set⽅法注⼊
 name:bean对象中属性字段的名称
 ref:指定bean标签的id属性值
 -->
 <bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
 <bean id="userService" class="com.xxxx.service.UserService">
 <!--业务对象 注⼊-->
 <property name="userDao" ref="userDao"/>
 </bean>
</beans>

5.1.2  构造器注入

注: 提供带参构造器
1.java代码
public class UserService {
 private UserDao userDao; // JavaBean 对象
 
 public UserService(UserDao userDao) {
 this.userDao = userDao;
 }
 public void test(){
 System.out.println("UserService Test...");
 userDao.test();
 }
}

2.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">
 <!--
 IOC通过构造器注⼊:
 通过constructor-arg标签进⾏注⼊
 name:属性名称
 ref:指定bean标签的id属性值
 -->
 <bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
 
 <bean id="userService" class="com.xxxx.service.UserService">
 <constructor-arg name="userDao" ref="userDao"></constructor-arg>
 </bean>
</beans>

5.1.3  静态工厂注入

1. 定义静态⼯⼚类

public class StaticFactory {
 // 定义静态⽅法
 public static TypeDao createTypeDao() {
 return new TypeDao();
 }
}

2. Java代码

public class TypeService {
 private TypeDao typeDao;
 
 public void setTypeDao(TypeDao typeDao) {
 this.typeDao = typeDao;
 }
 public void test() {
 System.out.println("TypeService Test...");
 }
}
3. XML 配置
在配置⽂件中设置 bean 标签,指定⼯⼚对象并设置对应的⽅法
<bean id="typeService" class="com.itheima.service.TypeService">
 <property name="typeDao" ref="typeDao"/>
</bean>
<!--
 静态⼯⼚注⼊:
 静态⼯⼚注⼊也是借助set⽅法注⼊,只是被注⼊的bean对象的实例化是通过静态⼯⼚实例化的
-->
<bean id="typeDao" class="com.itheima.factory.StaticFactory" factorymethod="createTypeDao"></bean>

5.1.4. 实例化工厂注入

1. 定义⼯⼚类
public class InstanceFactory {
 public TypeDao createTypeDao() {
 return new TypeDao();
 }
}
2. Java代码
public class TypeService {
 private TypeDao typeDao;
 
 public void setTypeDao(TypeDao typeDao) {
 this.typeDao = typeDao;
 }
 public void test() {
 System.out.println("TypeService Test...");
 }
}
3. XML 配置
声明⼯⼚bean标签,声明bean对象,指明⼯⼚对象和⼯⼚⽅法
<bean id="typeService" class="com.itheima.service.TypeService">
 <property name="typeDao" ref="typeDao"/>
</bean>
<!--
 实例化⼯⼚注⼊:
 实例化⼯⼚注⼊也是借助set⽅法注⼊,只是被注⼊的bean对象的实例化是通过实例化⼯⼚实例化的
-->
<bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean>
<bean id="typeDao" factory-bean="instanceFactory" factory-method="createTypeDao">
</bean>
重点掌握 set 注入和构造器注入,工厂方式了解即可。实际开发中基本使用 set方 式注入 bean

二、AOP

1.AOP的介绍

AOP:(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。 AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。

2.代理模式

aop的代理模式可以分为动态代理和静态代理,动态代理有两种实现方式,通过jdk和cglib两种代理,通过代理类可以为原始类增加额外的功能。

2.1 静态代理

静态代理优缺点

1)优点 :在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展

2)缺点 :因为代理对象需要与目标对象实现一样的接口,所以会很多代理类 ,一旦接口增加方法,目标对象与代理对象都要维护

开发代理对象的原则:

1)代理对象和目标对象实现相同的接口

2)代理对象依赖于目标对象

2.2 动态代理(反射)

1.基于jdk的动态代理

程序运行的过程中,通过jdk提供代理技术动态的为某个类产生动态代理对象的过程。

开发代理对象的原则:

1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用JDK动态代理。

2)代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。

3)动态代理也叫做 :JDK代理、接口代理

2.3 基于CGLib的动态代理

开发代理对象的原则:

1.代理对象无需和原始类对象实现相同的接口

2.代理对象和原始类对象要存在父子类关系

3. AOP应用场景

场景一: 记录日志
场景二: 监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )

4. AOP的特点

1. 降低模块与模块之间的耦合度,提⾼业务代码的聚合度。(⾼内聚低耦合)
2. 提⾼了代码的复⽤性。
3. 提⾼系统的扩展性。(⾼版本兼容低版本)
4. 可以在不影响原有的功能基础上添加新的功能

5. AOP基本概念

5.1  Joinpoint(连接点)

被拦截到的每个点, spring 中指被拦截到的每⼀个⽅法, spring aop ⼀个连接点即代表⼀个⽅法的执⾏。

5.2  Pointcut(切⼊点)

对连接点进⾏拦截的定义(匹配规则定义 规定拦截哪些⽅法,对哪些⽅法进⾏处理), spring 有专⻔的表达式语⾔定义。

5.3 Advice(通知)

拦截到每⼀个连接点即(每⼀个⽅法)后所要做的操作
        1. 前置通知 (前置增强) — before() 执⾏⽅法前通知
        2. 返回通知(返回增强) — afterReturn ⽅法正常结束返回后的通知
        3. 异常抛出通知(异常抛出增强) — afetrThrow()
        4. 最终通知 — after ⽆论⽅法是否发⽣异常,均会执⾏该通知。
        5. 环绕通知 — around 包围⼀个连接点( join point )的通知,如⽅法调⽤。这是最强⼤
的⼀种通知类型。 环绕通知可以在⽅法调⽤前后完成⾃定义的⾏为。它也会选择是
否继续执⾏连接点或直接返回它们⾃⼰的返回值或抛出异常来结束执⾏。

5.4 Aspect(切⾯)

切⼊点与通知的结合,决定了切⾯的定义,切⼊点定义了要拦截哪些类的哪些⽅法,通知则定义了拦 截过⽅法后要做什么,切⾯则是横切关注点的抽象,与类相似,类是对物体特征的抽象,切⾯则是横切 关注点抽象。

5.5  Target(⽬标对象)

被代理的⽬标对象

5.6. Weave(织⼊)

将切⾯应⽤到⽬标对象并⽣成代理对象的这个过程即为织⼊

5.7  Introduction(引⼊)

在不修改原有应⽤程序代码的情况下,在程序运⾏期为类动态添加⽅法或者字段的过程称为引⼊

6.Aop的通知类型

通知描述
前置通知在一个方法执行之前,执行通知。
后置通知在一个方法执行之后,不考虑其结果,执行通知。
返回后通知在一个方法执行之后,只有在方法成功完成时,才能执行通知。
抛出异常后通知在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
环绕通知在一个方法调用之前和之后,执行通知。

  • 36
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值