Spring ——IoC与AOP讲解

目录

  • 【spring.xml配置文件】
  • 【pom.xml文件】
  • 一、两大核心机制
  • 二、IOC控制反转
    • (1)配置文件详解
    • (2)IoC底层原理
  • 三、IoC特性
    • (1) scope作用域
    • (2)Spring的继承
    • (3)Spring的依赖
    • (4)Spring的工厂方法
    • (5)IoC的自动装载(Autowire)
  • 四、AOP 特性
    • (1)AOP作用
    • (2)AOP 的优点
    • (3)AOP原理
    • (4)AOP实现

Spring 是一个企业级开发框架,是软件设计层面的框架,优势在于可以将应用程序进行分层,开发者可以⾃主选择组件。

【spring.xml配置文件】


<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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">
   
beans>

【pom.xml文件】


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <groupId>com.southwindgroupId>
    <artifactId>aispringiocartifactId>
    <version>1.0-SNAPSHOTversion>
    <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.0.11.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aopartifactId>
            <version>5.0.11.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aspectsartifactId>
            <version>5.0.11.RELEASEversion>
        dependency>   

    dependencies>
project>

一、两大核心机制

  • IoC(控制反转)/ DI(依赖注入)
  • AOP(⾯向切面编程)

二、IOC控制反转

传统程序开发,需要调用者创建被调用者的实例(对象由调用者new出来的)
而在Spring框架中,创建对象交由IoC容器完成推送给调用者

  • 实体类Person
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private int age;
}
  • 在配置文件中设置IoC

    • 无参构造
    
    <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="person" class="com.lin.entity.Person">
            <property name="name" value="张三">property>
            <property name="age" value="18">property>
        bean>
    beans>
    
    • 有参构造
    	<bean id="person1" class="com.lin.entity.Person">
            <constructor-arg name="name" value="李四">constructor-arg>
            <constructor-arg name="age" value="10">constructor-arg>
        bean>
    
  • 从IoC中获取对象

public class Test1 {
    public static void main(String[] args) {
        // 加载配置文件
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Person people = (Person)applicationContext.getBean("person");
        System.out.println(people);
    }
}

也可通过运行时类获取 bean,这种⽅式存在⼀个问题,配置⽂件中⼀个数据类型的对象只能有⼀个实例,否则会抛出异常,因为没有唯一的 bean

java Person people = applicationContext.getBean(Person.class);

(1)配置文件详解

  • 通过< bean >标签完成对象的管理
    • id:对象名
    • class:对象的模板类(所有交给 IoC 容器来管理的类必须有无参构造,因为 Spring 底
      层是通过反射机制来创建对象,调⽤的是无参构造)
  • 对象的成员变量通过< property >标签来赋值
    • name:成员变量名

    • value:成员变量(基本数据类型,String 可以直接赋值,如果是其他引用类型,不能通过 value 赋值)

    • ref :将 IoC 中的另外一个 < bean > 赋给当前的成员变量量(DI 依赖注入)

      • 一对一关系
          <bean id="person" class="com.lin.entity.Person">
              <property name="name" value="张三">property>
              <property name="age" value="18">property>
              <property name="student" ref="student">property>
          bean>
          <bean id="student" class="com.lin.entity.Student">
              <property name="name" value="小明">property>
              <property name="id" value="1">property>
          bean>
      
      • 一对多关系
          <bean id="person2" class="com.lin.entity.Person">
              <property name="name" value="王五">property>
              <property name="age" value="20">property>
              <property name="student">
                  <list>
                      <ref bean="student1">ref>
                      <ref bean="student2">ref>
                  list>
              property>
          bean>
      

(2)IoC底层原理

仿写:解析xml + 利用反射机制创建对象

public class ClassPathXmlApplicationContext implements ApplicationContext {
    private Map<String,Object> map= new HashMap<String,Object>();

    public ClassPathXmlApplicationContext(String path){
        try {
            // 利用DOM4j,读取xml文件
            SAXReader reader = new SAXReader();
            Document document = reader.read("./src/main/resources/"+path);
            // 读取根节点
            Element rootElement = document.getRootElement();
            // 获取迭代器
            Iterator<Element> iterator = rootElement.elementIterator();
            while (iterator.hasNext()){
                Element element = iterator.next();
                // 获取id和class
                String id = element.attributeValue("id");
                String className = element.attributeValue("class");
                // 通过反射创建对象
                Class aClass = Class.forName(className);
                // 获取构造方法,创建对象
                Constructor constructor = aClass.getConstructor();
                Object object = constructor.newInstance();
                // 给对象赋值
                Iterator<Element>beanIterator = element.elementIterator();
                while (beanIterator.hasNext()){
                    Element property = beanIterator.next();
                    String name = property.attributeValue("name");
                    String valueStr = property.attributeValue("value");
                    // 获取set方法名称
                    String methodName= "set"+name.substring(0,1).toUpperCase()+name.substring(1);
                    // 得到set方法
                    Field field = aClass.getDeclaredField(name);
                    Method method = aClass.getDeclaredMethod(methodName, field.getType());
                    // 根据成员变量类型,转换value类型
                    Object value = null;
                    if (field.getType().getName()=="int"){
                        value = Integer.parseInt(valueStr);
                    }
                    if (field.getType().getName()=="java.lang.String"){
                        value = valueStr;
                    }
                    // 执行set方法,为成员变量赋值
                    method.invoke(object, value);
                }
                // 添加到map集合
                map.put(id, object);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String name) {
        return map.get(name);
    }
}

三、IoC特性

(1) scope作用域

Spring 管理的 bean 是根据 scope 来生成的,表示 bean 的作用域,共4种,默认值是singleton。

  • singleton:单例,表示通过 IoC 容器获取的 bean 是唯一的。
  • prototype:原型,表示通过 IoC 容器器获取的 bean 是不同的。
  • request:请求,表示在⼀次 HTTP 请求内有效。
  • session:回话,表示在⼀个用户会话内有效。

request 和 session 只适用于 Web 项目,⼤多数情况下,使用单例和原型较多。

  • 两者区别:
    • prototype 模式:当业务代码获取 IoC 容器器中的 bean 时,Spring 才去调用⽆无参构造创建对应的 bean。
    • singleton 模式:无论业务代码是否获取 IoC 容器器中的 bean,Spring 在加载 spring.xml 时就会创建 bean。

(2)Spring的继承

与 Java 的继承不同,Java 是类层面的继承,⼦类可以继承⽗父类的内部结构信息;Spring 是对象层⾯的继承,子对象可以继承父对象的属性值。

<bean id="people" class="com.lin.entity.Person" parent="person">
  1. 两个不同类之间的实例对象也可以继承关系
  2. ⼦对象必须包含父对象的所有属性,同时可以在此基础上添加其他的属性

(3)Spring的依赖

A依赖B,先创建B,再创建A

<bean id="A" class="com.lin.entity.Person" depends-on="B">bean>

(4)Spring的工厂方法

IoC 通过工厂模式创建 bean 的方式有两种:

  • 静态工厂方法
  • 实例工厂方法
  • 静态工厂方法:
public class StaticCarFactory {
    private static Map<Integer, Car> carMap;
    static {
        carMap=new HashMap<Integer, Car>();
        carMap.put(1, new Car(1, "大众"));
        carMap.put(2, new Car(2, "宝马"));
    }
    public static Car getCar(Integer id){
        return carMap.get(id);
    }
}
    <bean id="car" class="com.lin.factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg name="id" value="1">constructor-arg>
    bean>
public class Test3 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-factory.xml");
        Car car1 =(Car) applicationContext.getBean("car1");
        System.out.println(car1);
    }
}
  • 实例工厂方法
public class InstanceCarFactory {
    private Map<Integer, Car> carMap;
    public InstanceCarFactory(){
        carMap=new HashMap<Integer, Car>();
        carMap.put(1, new Car(1, "大众"));
        carMap.put(2, new Car(2, "宝马"));
    }
    public Car getCar(Integer id){
        return carMap.get(id);
    }

}
	<bean id="carFactory" class="com.lin.factory.InstanceCarFactory">bean>

    <bean id="car2" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg name="id" value="2">constructor-arg>
    bean>
public class Test4 {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-factory.xml");
        Car car2 = (Car) applicationContext.getBean("car2");
        System.out.println(car2);
    }
}

(5)IoC的自动装载(Autowire)

IoC 负责创建对象,DI 负责完成对象的依赖注入,通过配置 property 标签的 ref 属性来完成,同时Spring 提供了另外一种更加简便的依赖注入方式:自动装载,不需要手动配置 property,IoC 容器器会⾃动选择 bean 完成注⼊入。

  • 自动装载有两种⽅方式:
    • byName:通过属性名自动装载(属性名与 bean 中id匹配)
    • byType:通过属性的数据类型自动装载(数据类型与bean中class匹配)
    <bean id="person" class="com.lin.entity.Person" autowire="byName">bean>
    
    <bean id="person" class="com.lin.entity.Person" autowire="byType">bean>

四、AOP 特性

AOP:Aspect Oriented Programming ⾯向切面编程。
AOP 是对面向对象编程的⼀个补充,在运行时,动态地将代码切⼊到类的指定⽅法、指定位置上的编程思想就是⾯向切面编程。将不同⽅法的同⼀个位置抽象成⼀个切面对象,对该切面对象进行编程就是AOP。

(1)AOP作用

  • 对于日志记录,性能统计,安全控制,事务处理,异常处理这样的非核心功能,单独被抽取出来,与业务代码分离,横切在核心业务代码之上
    Spring ——IoC与AOP讲解_第1张图片

(2)AOP 的优点

降低模块之间的耦合度。
使系统更容易扩展。
更好的代码复用。
⾮业务代码更加集中,不分散,便于统⼀管理。
业务代码更加简洁纯粹,不参杂其他代码的影响。

(3)AOP原理

动态代理:
动态代理类

public class ProxyFactory {
    // 获取代理对象
    public static Object getProxy(Object object){
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(object);
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),myInvocationHandler );
    }
}

class MyInvocationHandler implements InvocationHandler {
    // 委托对象
    private Object object;

    public MyInvocationHandler(Object object) {
        this.object = object;
    }
    // 代理对象执行方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("正在执行"+method.getName()+"方法的参数为:"+ Arrays.toString(args));
        // 委托对象执行方法
        Object invoke = method.invoke(object, args);
        System.out.println("结果为:"+invoke.toString());
        return invoke;
    }
}

接口的实现类

public class CalculateImpl implements Calculate{

    public int add(int a,int b) {
        return a+b;
    }

    public int sub(int a,int b){
        return a-b;
    }
}

测试

public class ProxyTest {
    public static void main(String[] args) {
        // 委托对象
        Calculate calculate = new CalculateImpl();
        // 代理对象
        Calculate proxy = (Calculate) ProxyFactory.getProxy(calculate);
        proxy.add(10, 5);
        proxy.sub(10, 5);

    }
}

(4)AOP实现

Spring 框架对 AOP 进⾏行了封装,使⽤Spring 框架可以用面向对象的思想来实现 AOP。
Spring 框架中不需要创建 InvocationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象中完成即可,Spring 框架底层会自动根据切面类以及目标类⽣生成一个代理对象。

  1. 在切面类添加注解:
    @Component :将给类的对象注入到 IoC 容器
    @Aspect :表示是切面类
    @Before :在执行业务代码前执行
    @After :在执行业务代码后执行

    @Aspect
    @Component
    public class LoggerAspect {
        /**
         *业务执行前操作
         */
        @Before("execution(public int com.lin.aop.CalculateImpl.*(..))")
        public void before(JoinPoint joinPoint){
            // 获取方法名
            String name = joinPoint.getSignature().getName();
            // 获取参数
            String args = Arrays.toString(joinPoint.getArgs());
            System.out.println("正在执行"+name+"方法的参数为:"+ args);
        }
    
        /**
         * 业务执行后操作
         */
        @After("execution(public int com.lin.aop.CalculateImpl.*(..))")
        public void after(JoinPoint joinPoint){
            System.out.println(joinPoint.getSignature().getName()+"方法执行完毕");
        }
    
        /**
         *对业务返回值进行操作
         */
        @AfterReturning(value = "execution(public int com.lin.aop.CalculateImpl.*(..))",returning = "result")
        public void afterReturning(Object result){
            System.out.println("结果为"+result);
        }
    
        /**
         *处理业务异常
         */
        @AfterThrowing(value = "execution(public int com.lin.aop.CalculateImpl.*(..))",throwing = "exception")
        public void afterThrowing(Exception exception){
            System.out.println("异常为"+exception);
        }
    }
    
  2. 在业务方法也需要添加注解:
    @Component

    @Component
    public class CalculateImpl implements Calculate {
    
        public int add(int a, int b) {
            return a + b;
        }
    
        public int sub(int a, int b) {
            return a - b;
        }
    }
    
  3. 配置xml文件

    
    <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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
           
        
        <context:component-scan base-package="com.lin">context:component-scan>
        
        <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
        
    beans>
    

    context:component-scan 将 com.lin 包中的所有类进行扫描,如果该类添加了@Component ,则将该类扫描到 IoC 容器中,即 IoC 管理它的对象。
    aop:aspectj-autoproxy 让 Spring 框架结合切面类和目标类自动生成动态代理对象。

  4. 测试

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop.xml");
            Calculate calculate = (Calculate) context.getBean("calculateImpl");
            calculate.add(10, 5);
            calculate.sub(20, 10);
        }
    }
    

    bean名称默认为业务类名首字母小写,也可以@Component(“XXX”)指定(相当于在xml文件中配置bean)

你可能感兴趣的:(Spring与Spring,MVC)