spring IOC和AOP简介

Spring

IOC

IOC(接口)
1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2. Spring提供IOC容器实现两种方式:(两个接口)
1. BeanFactory:IOC容器最基本的实现方式,是Spring内部的使用接口,不提供开发人员进行使用 在配置文件时不会创建对象,获取对象的时候才会去创建对象
2. ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用 加载配置文件时就会把配置文件对象进行创建
3. ApplicationContext接口有实现类 1.FileSystemXmlApplicationContext(绝对路径) 2.ClassPathXmlApplicationContext(src下文件)

Bean管理
1. 什么是Bean管理,Bean管理指的是两个操作
1. Spring创建对象
2. Spring注入属性
2. Bean管理操作两种方式
1. 基于XML配置文件方式实现
2. 基于注解方式实现
基于xml文件配置

  1. 通过set方法注入

        <bean id="book" class="com.original.spring5.Book">
            <property name="name" value="asd"></property>
            <property name="id" value="1"></property>
        </bean>
    
  2. 通过有参构造

        <bean id="orders" class="com.original.spring5.Orders">
            <constructor-arg name="name" value="asd"></constructor-arg>
            <constructor-arg name="address" value="XiAn"></constructor-arg>
        </bean>
    
  3. p名称空间注入
    使用p名称空间注入,可以简化基于xml配置方式

    1. 添加约束
    <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"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    1. 注册bean
    <bean id="book" class="com.original.spring5.Book" p:id="11" p:name="pname"></bean>
    
  4. xml注入其它类型的属性

    1. 注入属性字面量

      1. null值
      <bean id="book" class="com.original.spring5.Book">
          <property name="name" value="asd"></property>
          <property name="id" value="1"></property>
          <property name="address">
              <null/>
          </property>
      </bean>
      
      1. 属性值包含特殊符号
      1.把<>转义   &gt;
      2.把带特殊符号的写进CDATA <![DATA[]]>
      <property name="address">
          <value>
              <![CDATA[<<南靖>>]]>
          </value>
      </property>
      
    2. 注入属性-外部bean

      public class UserService {
          private UserDao userDao;
      
          public void setUserDao(UserDao userDao) {
              this.userDao = userDao;
          }
      
          public void add() {
              userDao.add();
          }
      }
      // dao
      public class UserDaoImpl implements UserDao {
          @Override
          public void add() {
              System.out.println("UserDaoImpl add");
          }
          
      }
      // xml 配置
      <bean id="userService" class="com.original.service.UserService">  
      <property name="userDao" ref="userDaoImpl"></property>
      </bean>
      
      <bean id="userDaoImpl" class="com.original.dao.impl.UserDaoImpl">  
      </bean>
      
    3. 注入属性-内部bean

      1. 一对多关系:部门和员工,一个部门有多个员工,一个员工属于一个部门
      @Setter
      @ToString
      public class Dept {
          private String dname;
      }
      @Setter
      @ToString
      public class Emp {
          private String name;
      
          private String gender;
      
          private Dept dept;
      }
      <!-- 内部bean的操作 -->
      <bean id="emp" class="com.original.model.Emp">
          <!-- 设置两个普通属性 -->
          <property name="name" value="xiaocao"></property>
          <property name="gender" value="woman"></property>
          <!-- 对象属性类型 可以ref也可以内部创建bean -->
          <property name="dept">
              <bean id="dept" class="com.original.model.Dept">
                  <property name="dname" value="开发部"></property>
              </bean>
          </property>
      </bean>
      
    4. 注入属性-级联赋值

      1. 第一种写法
      <!-- 内部bean的操作 -->
      <bean id="emp" class="com.original.model.Emp">
          <!-- 设置两个普通属性 -->
          <property name="name" value="xiaocao"></property>
          <property name="gender" value="woman"></property>
          <!-- 级联赋值 -->
          <property name="dept" ref="dept">
          </property>
      </bean>
      
      <bean id="dept" class="com.original.model.Dept">
          <property name="dname" value="财务部"></property>
      </bean>
      
      1. 第二种写法 - 依赖emp中dept的get方法
      <bean id="emp" class="com.original.model.Emp">
      <!-- 设置两个普通属性 -->
      <property name="name" value="xiaocao"></property>
      <property name="gender" value="woman"></property>
      <!-- 级联赋值 -->
      <property name="dept" ref="dept"></property>
      <property name="dept.dname" value="技术部"></property>
      </bean>
      
      <bean id="dept" class="com.original.model.Dept">
          <property name="dname" value="财务部"></property>
      </bean>
      
  5. IOC操作Bean管理(xml注入集合属性)

    1. 注入数组类型属性
    2. 注入List集合属性
    3. 注入Map集合类型属性
    4. Set
    5. 在集合里面设置对象属性值
    6. 把集合注入部分提取出来
     <?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:util="http://www.springframework.org/schema/util"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
             https://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/util
             https://www.springframework.org/schema/util/spring-util.xsd">
    
         <!-- 提取list集合属性类型注入 -->
         <util:list id="bookList">
             <value>易筋经</value>
             <value>易筋经2</value>
             <value>易筋经3</value>
         </util:list>
    
    
         <bean id="book" class="com.original.base.collectiontype.Book">
             <property name="list" ref="bookList"></property>
         </bean>
     </beans>
    
  6. IOC操作Bean管理(FactoryBean)

    1. Spring有两种类型bean,一种普通bean,另外一种工厂bean(FactoryBean)
    2. 普通bean:在配置文件中定义bean类型就是返回类型
    3. 工厂bean:在配置文件定义bean类型和返回类型不一样
      1. 第一步创建类,让这个类作为工厂bean,实现接口FactoryBean
      2. 实现接口里面的方法没在实现的方法中定义返回的bean类型
  7. IOC操作Bean管理(bean作用域)

    1. 在Spring里面,设置创建bean实例是单实例还是多实例
    2. 在Spring里面,默认情况下,我们创建的bean是单实例
    3. 如何设置单实例还是多实例
      1. 在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
      2. 默认值 singleton 单实例
      3. prototype 多实例
      4. singleton和prototype区别
        1. singleton 单实例, prototype多实例
        2. 设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象
        3. 设置scope值是prototype时候,不是在加载spring配置文件的时候创建bean,是在调用getBean方法时创建
      5. Request 基本不用 一次请求
      6. Session 基本不用 一次会话
  8. IOC操作Bean管理(bean生命周期)

    1. 生命周期
      1. 从对象的创建到对象销毁的过程
    2. bean生命周期
      1. 通过构造器创建bean实例(无参构造)
      2. 为bean的属性设置值和对其他bean引用(调用set方法)
      3. 调用bean的初始化的方法(需要进行配置初始化的方法)
      4. bean可以使用了(对象获取到了)
      5. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
      public class Orders {
          public Orders() {
              System.out.println("第一步,调用无参构造创建bean实例");
          }
      
          private String oname;
      
          public void setOname(String oname) {
              System.out.println("set 方法设置属性值");
              this.oname = oname;
          }
      
          public void initMethod() {
              System.out.println("第三部 执行初始化方法");
          }
      
          public void destoryMethod() {
              System.out.println("第五步 执行销毁的方法");
          }
      }
      
      <bean id="orders" class="com.original.bean.Orders" init-method="initMethod" destroy-method="destoryMethod">  
          <property name="oname" value="手机"></property>
      </bean>
      
      @Test
      public void test() {
          ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
          Orders orders = applicationContext.getBean("orders", Orders.class);
          System.out.println("获取bean对象");
          ((ClassPathXmlApplicationContext) applicationContext).close();
      }
      
    3. bean的后置处理器,总共七步
      1. 通过构造器创建bean实例(无参构造)
      2. 为bean的属性设置值和对其他bean引用(调用set方法)
      3. 把bean的实例传递bean后置处理器的方法 postProcessAfterInitialization
      4. 调用bean的初始化的方法(需要进行配置初始化的方法)
      5. 把bean的实例传递bean后置处理器的方法 postProcessBeforeInitialization
      6. bean可以使用了(对象获取到了)
      7. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
    4. 后置处理器的实现
      1. 创建类,实现接口BeanPostProcessor,创建后置处理器
      public class MyBeanPostProcessor implements BeanPostProcessor {
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("初始化之前 postProcessAfterInitialization");
              return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
          }
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              System.out.println("初始化之后 postProcessBeforeInitialization");
              return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
          }
      }
      
      1. xml中配置bean
       <!-- 配置后置处理器 -->
      <bean id="myBeanPost" class="com.original.bean.MyBeanPostProcessor"></bean>
      
  9. 操作bean管理(xml自动装配)

    1. 什么是自动装配
      1. 根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
      2. xml中配置自动装配
      <!-- 配置自动装配 
      
      autowire属性:
          1. byName 根据属性名称注入,注入值bean的id值和类属性名称一致
          2. byType 根据属性类型注入,多个类型bean存在时,就会出现问题,相同类型的bean不能定义多个
      -->
      
      <bean id="emp" class="com.original.demo.autowire.Emp" autowire="byType">
          <!-- <property name="dept" ref="dept"></property> -->
      </bean>
      
      <bean id="dept" class="com.original.demo.autowire.Dept"></bean>
      
  10. IOC操作bean管理(外部属性文件)

    1. 直接配置数据库信息
      1. 配置德鲁伊连接池
      2. 引入德鲁伊连接池
    2. 引入外部属性文件配置数据库连接池
      1. 创建外部属性文件,写入数据库连接信息
      prop.driverClass=com.mysql.jdbc.Driver
      prop.url=jdbc:mysql://localhost:3306/userdb
      prop.username=root
      prop.password=root
      
      1. 把外部properties属性文件引入到bean.xml中
        1. 引入context命名空间
        xmlns:context="http://www.springframework.org/schema/context"
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        
        1. 完整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"
            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">
        
            <!-- 直接配置连接池 -->
            <!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql://localhost:3306/userdb"></property>
                <property name="username" value="root"></property>
                <property name="password" value="root"></property>
            </bean> -->
        
            <!-- 引入外部properties文件 -->
            <context:property-placeholder location="classpath:jdbc.properties"/>
        
            <!-- 配置连接池 -->
            <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${prop.driverClass}"></property>
                <property name="url" value="${prop.url}"></property>
                <property name="username" value="${prop.username}"></property>
                <property name="password" value="${prop.password}"></property>
            </bean>
        </beans>
        
  11. IOC操作bean管理(基于注解方式)

    1. 什么是注解
      1. 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)
      2. 使用注解,注解在类上面、属性方面、方法上面
      3. 使用注解目的:简化xml配置
    2. Spring针对Bean管理中创建对象提供注解,下面四个注解功能是一样的,都可以用来创建bean实例
      1. @Component
      2. @Service
      3. @Controller
      4. @Repository
    3. 基于注解方式完成对象创建
      1. 开启组件扫描 xml配置方式
      <!-- 
          如果配置多个包,多个包之间可以用逗号隔开
      -->
      <context:component-scan base-package="com.original"></context:component-scan>
      
      1. 创建类,在类上添加注解 @Component(value="userService") // value等价于xml方式的id,省略默认为首字母小写
    4. 开启组件扫描的细节配置
      1. use-default-filters="false" 关闭默认扫描规则
        <!-- 设置那些内容进行扫描 -->
        <context:component-scan base-package="com.original" use-default-filters="false">
            <context:include-filter type="annotation" expression="" />
        </context:component-scan>
        <!-- 设置哪些内容不进行扫描 这个不需要关闭默认规则-->
        <context:component-scan base-package="com.original">
            <context:excluder-filter type="annotation" expression="" />
        </context:component-scan>
        
    5. 基于注解方式实现属性注入
      1. @AutoWired:根据属性类型进行自动装备
        1. 创建对象,在Service 和 Dao上加上注解
      2. @Qualifier:根据属性名称进行注入
        1. 这个注解的属性,和上面@Autowired一起使用
        @Autowired // 根据类型注入
        @Qualifier(value = "userDaoImpl")
        
      3. @Resource:可以根据类型注入,可以根据名称注入 - javax提供的
      // 根据类型注入
      // @Resource
      // 根据名称注入
      @Resource(name = "userDaoImpl")
      private UserDao userDao;
      
      1. @Vaule:注入普通类型属性
      @Value(value = "abc")
      private String name;
      
    6. 完全注解开发
      1. 创建配置类,替代xml配置文件
      @Configuration // 作为配置类,替代xml配置文件
      @ComponentScan(basePackages = { "com.original" })
      public class SpringConfig {
      
      }
      // 测试类AnnotationConfigApplicationContext
      ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
      

AOP

动态代理
1. 有两种情况动态代理
1. 有接口情况,使用JDK动态代理,创建接口实现类代理对象,增强类的方法
Proxy对象,调用newProxyInstance方法
方法有三个参数
1. 类加载器
2. 增强方法所在的类,这个类实现的接口,支持多个接口
3. 实现这个接口InvocathonHandler,创建代理对象,写增强方法
代码实现:
1. 创建业务代码接口
```
public interface ProxyTestDao {
int add(int a, int b);

                String update(String id);
            }
            ```
            2. 实现接口
            ```
            public class ProxyTestDaoImpl implements ProxyTestDao {
                @Override
                public int add(int a, int b) {
                    // TODO Auto-generated method stub
                    return a + b;
                }

                @Override
                public String update(String id) {
                    // TODO Auto-generated method stub
                    return id;
                }

            }
            ```
            3. 实现InvocathonHandler接口,创建代理对象
            ```
            // 创建代理对象代码
            class OwnerProxy implements InvocationHandler {
                // 1. 创建的是谁的代理对象,需要把谁传过来
                private Object obj;

                // 有参数的构造
                public OwnerProxy(Object obj) {
                    this.obj = obj;
                }

                // 增强的逻辑
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 方法之前
                    System.out.println("方法执行之前" + method.getName() + " : 传递的参数..." + Arrays.toString(args));
                    // 被增强的方法执行
                    Object res = method.invoke(obj, args);
                    // 方法
                    System.out.println("方法之后执行" + obj);

                    return res;
                }
            }
            ```
            4. 调用
            ```
            public class JDKProxy {
                public static void main(String[] args) {
                    // 创建接口代理类代理对象
                    // 写匿名内部类或者实现接口InvocationHandler
                    Class[] interfaces = { ProxyTestDao.class };
                    ProxyTestDaoImpl impl = new ProxyTestDaoImpl();
                    ProxyTestDao dao = (ProxyTestDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces,
                            new OwnerProxy(impl));
                    int res = dao.add(1, 2);
                    System.out.println("end :" + res);
                }
            }
            ```
    2. 没接口情况,使用CGLIB动态代理,CGLIB创建当前类子类的代理对象
  1. AOP 术语
    1. 连接点:一些类里面哪些类可以被增强,这些方法称为连接点
    2. 切入点:实际被真正增强的方法,称为切入点
    3. 通知(增强):实际增强的逻辑部分称为通知,通知有多种类型
      1. 前置通知
      2. 后置通知
      3. 环绕通知
      4. 异常通知
      5. 最终通知
    4. 切面:是动作,把通知应用到切入点过程
  2. AOP 操作
    1. Spring框架一般都是基于AspectJ实现AOP操作
    2. AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
    3. 基于AspectJ实现AOP操作
      1. 基于xml配置文件
      2. 基于注解方式实现(使用)
  3. 引入AspectJ依赖
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.8.RC2</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.4</version>
    </dependency>
  1. 切入点表达式
    1. 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
    2. 语法结构:execution([权限修饰符][返回类型][类全路径]方法名称) 举例
      1. 对com.original.dao.UserDao类里面的add方法进行增强
      execution(*com.original.dao.UserDao.add(..))
      
      1. 对com.original.dao.UserDao类里面的所有方法进行增强
      execution(*com.original.dao.UserDao.*(..))
      
      1. 对com.original.dao包里面的所有类所有方法
      execution(*com.original.dao.*.*(..))
      
  2. AOP操作(AspectJ注解)
    1. 创建类,在类里面定义方法
    public class User {
        public void add() {
            System.out.println("user add");
        }
    }
    
    1. 创建增强类(编写增强逻辑)在增强类里面创建方法,不同的方法代表不同的通知
    public class UserProxy {
        // 前置通知
        public void before() {
            System.out.println("before......");
        }
    }
    
    1. 进行通知的配置
      1. 在spring配置文件中,开启注解扫描
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:aop="http://www.springframework.org/schema/aop"
          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
              http://www.springframework.org/schema/context
              https://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/aop
              https://www.springframework.org/schema/aop/spring-aop.xsd">
          <context:component-scan base-package="com.original.aop.ano"></context:component-scan>
      </beans>
      
      1. 使用注解创建User和UserProxy对象
      2. 在增强类上面添加注解@Aspect
      3. 在spring配置文件中生成代理对象
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      
      1. 配置不同类型的通知
        1. 在增强类里面,在作为通知方法上面
        @Component
        @Aspect
        public class UserProxy {
        
            // 前置通知
            @Before(value = "execution(* com.original.aop.ano.User.add(..))")
            public void before() {
                System.out.println("before......");
            }
        
            // 最终通知
            @After(value = "execution(* com.original.aop.ano.User.add(..))")
            public void after() {
                System.out.println("after......");
            }
        
            // 后置通知(返回通知)
            @AfterReturning(value = "execution(* com.original.aop.ano.User.add(..))")
            public void afterReturning() {
                System.out.println("AfterReturning......");
            }
        
            // 异常通知
            @AfterThrowing(value = "execution(* com.original.aop.ano.User.add(..))")
            public void afterThrowing() {
                System.out.println("AfterThrowing......");
            }
        
            // 环绕通知
            @Around(value = "execution(* com.original.aop.ano.User.add(..))")
            public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
                System.out.println("环绕之前......");
                // 被增强的方法
                proceedingJoinPoint.proceed();
                System.out.println("环绕之后......");
            }
        }
        
      2. 公共切入点抽取
      @Pointcut(value = "execution(* com.original.aop.ano.User.add(..))")
      public void pointdemo() {
      }
      
      // 前置通知
      @Before(value = "pointdemo()")
      public void before() {
          System.out.println("before......");
      }
      
      1. 有多个增强类对同一个方法增强,设置优先级
        1. 在增强类上面添加注解Order(值)值越高优先级越高 @Order(1)
      2. xml 方式 (省略)
posted @ 2021-11-19 01:24  杨大德  阅读(107)  评论(0)    收藏  举报