首页 > 编程语言 > 详细

浅入Spring

时间:2021-03-27 22:10:58      阅读:26      评论:0      收藏:0      [点我收藏+]

IOC:

1、Spring简介

  1. spring是分层的(一站式)轻量级开源框架,他以IoC(Inversion of Control,控制反转),和AOP(Aspect Oriented Programming,面向切面编程)为内核。
  2. Spring是一个企业级开发框架,是软件设计层面的框架,优势在于可以将应用程序进行分层,开发者可以自主选择组件。
  3. Spring框架的优点
    • 非侵入式设计:Spring是一种非侵入式(non-invasive)框架,它可以使应用程序代码对框架的依赖最小化。
    • 方便解耦、简化开发:Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给Spring容器的管理,大大的降低了组件之间的耦合性。
    • 支持AOP:Spring提供了对AOP的支持,它允许将一些通用任务,如安全、事物、日志等进行集中式处理,从而提高了程序的复用性。
    • 支持声明式事务处理:只需要通过配置就可以完成对事物的管理,而无须手动编程。
    • 方便程序的测试:Spring提供了对Junit4的支持,可以通过注解方便的测试Spring程序。
    • 方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts、Hibernate、MyBatis、Quartz等)的直接支持。
    • 降低Jave EE API的使用难度:Spring对Java EE开发中非常难用的一些API(如JDBC、JavaMail等),都提供了封装,使这些API应用难度大大降低。

2、第一个Spring

  • 新建Maven工程添加依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.4</version>
    </dependency>
    
  • 创建实体类Student

    package pojo;
    
    public class Student {
        private long id;
        private String name;
        private int age;
    
        public Student(long id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
          public Student() {
    
        }
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name=‘" + name + ‘\‘‘ +
                    ", age=" + age +
                    ‘}‘;
        }
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  • 通过IOC创建对象,在配置文件中添加需要管理的对象,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="pojo.Student">
        <property name="id" value="1"></property>
        <property name="name" value="wcy"></property>
        <property name="age" value="21"></property>
    </bean>
    </beans>
    

    1、通过bean标签来完成对象的管理

    • id:对象名

    • class对象的模板类。(所有交给IoC容器来管理的类必须有无参构造函数,因为Spring底层是通过反射机制来创建对象,调用的是无参构造)

    2、成员变量通过property标签完成赋值

    • name:成员变量名
    • value:成员变量(基本数据类型,String等可以直接赋值,如果是其他引用类型,不能通过value赋值)
    • ref:将IoC中的另外一个Bean赋给当前的成员变量(DI)
  • 通过IoC容器创建对象

    package test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import pojo.Student;
    
    public class Test {
        public static void main(String[] args) {
            //加载配置文件
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.XML");
            //通过运行时类获取
            Student student = (Student) applicationContext.getBean(Student.class);
            System.out.println(student);
        }
    }
    

3、Spring的两大核心容器

ApplicationContext是BenaFactor的一个子接口,也被称为应用上下文,是另一种常用的Spring核心容器。它不仅包含了BeanFactor的所有功能,还添加了对国际化,资源访问,事件传播等方面的支持。建议使用该接口

  1. BeanFactory(IoC容器)

    使用BeanFactory加载spring.xml

     XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new FileSystemResource("D:\\files\\idea\\JAVA\\spring_2021.3.22\\src\\main\\resources\\spring.xml"));
            Student student =(Student) xmlBeanFactory.getBean("student");
            System.out.println(student);
    

    通过绝对路径拿到spring.xml配置文件

  2. ApplicationContext(IoC容器)

    通过FileSystemXmlApplicationContext创建:通过绝对路径

    ApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\files\\idea\\JAVA\\spring_2021.3.22\\src\\main\\resources\\spring.xml");
            Student student = (Student) applicationContext.getBean("student");
            System.out.println(student);
    

    通过ClassPathXmlApplicationContext创建:通过类路径

     ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
            Student student =(Student)applicationContext.getBean("student");
            System.out.println(student);
    
  3. 从IoC中获取对象

    通过配置文件已经创建了IoC容器,下面通过两种方式从IoC容器中获取对象

    通过Bean的id属性获取Bean

    package test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import pojo.Student;
    
    public class Test {
        public static void main(String[] args) {
            //加载配置文件
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.XML");
            //通过id获取
            Student student = (Student) applicationContext.getBean("student");
            System.out.println(student);
        }
    }
    

    通过运行时类获取Bean

    package test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import pojo.Student;
    
    public class Test {
        public static void main(String[] args) {
            //加载配置文件
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.XML");
            //通过运行时类获取
            Student student = (Student) applicationContext.getBean(Student.class);
            System.out.println(student);
        }
    }
    

    但是这种方式有一种弊端,配置文件中只能有一个实例。否则会抛出异常,因为没有唯一的bean

4、IOC/DI(依赖注入)

Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。

  1. 构造注入

    通过有参构造方法

     <!-- 构造方式注入的三种方式 -->
        <!--下标-->
        <bean id="student2" class="www.sheep.pojo.Student2">
            <constructor-arg index="0" value="1"/>
            <constructor-arg index="1" value="c罗"/>
            <constructor-arg index="2" value="36"/>
        </bean>
        <!--名字-->
        <bean id="student3" class="www.sheep.pojo.Student2">
            <constructor-arg name="id" value="2"/>
            <constructor-arg name="name" value="内马尔"/>
            <constructor-arg name="age" value="29"/>
        </bean>
        <!--数据类型-->
        <bean id="student4" class="www.sheep.pojo.Student2">
            <constructor-arg type="int" value="3"/>
            <constructor-arg type="java.lang.String" value="林语堂"/>
            <constructor-arg type="java.lang.String" value="100"/>
        </bean>
    

    通过有参构造注入(pojo、String[]、List、Map)

    private wangchaoyu wangchaoyu;
    private String[] strings;
    private List<wangchaoyu> list;
    private Map<String,wangchaoyu> map;
    
     		<!-- 引用类型 -->
            <constructor-arg name="wangchaoyu" ref="wangchaoyu"/>
            <!-- 数组类型 -->
            <constructor-arg name="strings">
                <array>
                    <value>三国演义</value>
                    <value>西游记</value>
                    <value>红楼梦</value>
                    <value>水浒传</value>
                </array>
            </constructor-arg>
            <!-- List集合 -->
            <constructor-arg name="list">
                <list>
                    <ref bean="wangchaoyu2"/>
                </list>
            </constructor-arg>
            <!-- Map集合 -->
            <constructor-arg name="map">
                <map>
                    <entry key="功夫" value-ref="wangchaoyu3"/>
                </map>
            </constructor-arg>
    
  2. Set注入

    通过Set方法(通过Set方法注入要写无参构造)

    基本数据类型注入

    <bean id="student2" class="www.sheep.pojo.Student">
            <property name="id" value="1"/>
            <property name="name" value="我们梦想奔赴大海"/>
            <property name="age" value="21"/>
    </bean>
    

    复杂类型注入

    private wangchaoyu wangchaoyu;
    private String[] strings;
    private List<wangchaoyu> list;
    private Map<String,wangchaoyu> map;
    
    	<bean id="student2" class="www.sheep.pojo.Student">
            <!-- javaBean -->
            <property name="wangchaoyu" ref="wangchaoyu"/>
            <!-- String[] -->
            <property name="strings">
                <array>
                    <value>红楼梦</value>
                    <value>西游记</value>
                    <value>三国演义</value>
                    <value>水浒传</value>
                </array>
            </property>
            <!-- List -->
            <property name="list">
                <list>
                    <ref bean="wangchaoyu2"/>
                </list>
            </property>
            <!-- Map -->
            <property name="map">
                <map>
                    <entry key="hello" value-ref="wangchaoyu3"/>
                </map>
            </property>
        </bean>
    
  3. p命名空间注入、c名门空间注入

    导入C命名空间依赖、实体类中必须存在有参构造器。属于:<property/>

    xmlns:c="http://www.springframework.org/schema/c"
    
    <!-- C命名空间注入 -->
    <bean id="wangchaoyu4" class="www.sheep.pojo.wangchaoyu" c:id="1" c:name="梅西" c:age="36"/>
    

    实体类中必须有set方法、实体类中必须有无参构造器(默认存在)。属于:<constructor-arg/>

     xmlns:p="http://www.springframework.org/schema/p"
    
    <!-- p命名空间注入 -->
    <bean id="wangchaoyu5" class="www.sheep.pojo.wangchaoyu" p:id="2" p:name="c罗" c:age="34"/>
    

5、Spring中的Bean

  1. Bean的实例化三种方式

    • 构造器实例化:构造器实例化是指IoC容器通过Bean对应类中默认的无参构造方法来实例化Bean。

    • 静态工厂实例化

      创建一个实体类

      package www.sheep.pojo;
      
      public class Car {
          private long id;
          private String name;
      
          public long getId() {
              return id;
          }
          public void setId(long id) {
              this.id = id;
          }
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
          public Car(long id, String name) {
              this.id = id;
              this.name = name;
          }
          @Override
          public String toString() {
              return "Car{" +
                      "id=" + id +
                      ", name=‘" + name + ‘\‘‘ +
                      ‘}‘;
          }
      }
      

      创建一个静态工厂类

      public class StaticCarFactory {
          private static Map<Long, Car> carMap;
          static {
              carMap = new HashMap<Long,Car>();
              carMap.put(1L,new Car(1L,"宝马"));
              carMap.put(2L,new Car(2L,"奔驰"));
              carMap.put(3L,new Car(3L,"奥迪"));
          }
          public static Car getCar(long id){
              return carMap.get(id);
          }
      }
      

      在spring.xml中配置静态工厂,通过factory-method:调用静态工厂的方法创建对象

      <?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!-- 配置静态工厂创建Car -->
          <bean id="car" class="www.sheep.factory.StaticCarFactory" factory-method="getCar">
              <constructor-arg value="2"/>
          </bean>
      </beans>
      

      创建Handler类

      public class Test3 {
          public static void main(String[] args) {
              ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-staticfactory.xml");
              Car car =(Car) applicationContext.getBean("car");
              System.out.println(car);
          }
      }
      
    • 实例工厂实例化

      创建实体类

      package www.sheep.pojo;
      
      public class Car {
          private long id;
          private String name;
      
          public long getId() {
              return id;
          }
          public void setId(long id) {
              this.id = id;
          }
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
          public Car(long id, String name) {
              this.id = id;
              this.name = name;
          }
          @Override
          public String toString() {
              return "Car{" +
                      "id=" + id +
                      ", name=‘" + name + ‘\‘‘ +
                      ‘}‘;
          }
      }
      

      创建实例工厂

      public class InstanceCarFactory {
          private Map<Long, Car> carMap;
          public InstanceCarFactory(){
              carMap = new HashMap<Long,Car>();
              carMap.put(1L,new Car(1L,"宝马"));
              carMap.put(2L,new Car(2L,"奔驰"));
              carMap.put(3L,new Car(3L,"奥迪"));
          }
          public Car getCar(long id){
              return carMap.get(id);
          }
      }
      

      在spring.xml中配置调用动态工厂的方法创建对象,先创建InstanceCarFactory工厂,在通过InstanceCarFactory调用getCar方法创建对象。

      <?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 http://www.springframework.org/schema/beans/spring-beans.xsd"> 
          <!-- 创建实例工厂bean -->
          <bean id="carFactory" class="www.sheep.factory.InstanceCarFactory"/>
          <!-- 赔偿实例工厂创建Car -->
          <bean id="car2" factory-bean="carFactory" factory-method="getCar">
              <constructor-arg value="1"/>
          </bean>
      </beans>
      
  2. Bean的作用域

    Spring管理的bean是根据scope来生成的,表示bean的作用域,共有7种。(默认单例模式)

    • singleton:单例,表示通过IoC容器获取的bean的唯一的。
    • prototype:原型,表示通过IoC容器获取的bean是不同的。
    • request:请求,表示一次HTTP请求内生效。
    • session:会话,表示一个用户会话内生效。
    • globalSession:在一个全局的HTTPSession中,容器会返回该Bean的同一个实例。仅在使用portlet上下文生效。
    • application:为每个ServletContext对象创建一个实例,仅在Web相关的ApplicationContext中生效。
    • websocket:为每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效。
    	<!-- 单例模式 -->	
    	<!-- singleton模式无论业务代码是否获取IoC容器中的bean时,Spring在加载spring.xml时就会创建bean -->
    	<bean id="student" class="www.sheep.pojo.Student" scope="singleton">
        <!-- 原型模式 -->
        <!-- prototype模式当业务代码获取IoC容器中的bean时,Spring才去调用无参构造创建对应的bean -->
        <bean id="student" class="www.sheep.pojo.Student" scope="prototype">
    
  3. Spring的继承

    Spring的继承关注点在于具体对象,而不在于类,即不同的两个类的实例化对象可以完成继承,IoC容器会自动将父bean的数据类型和属性名与子类的数据类型与属性名进行匹配,同时可以在此基础上添加其他属性, 子 Bean 也可以覆盖从父 Bean 继承过来的配置

    <!-- twoectends继承oneectends -->
    <bean id="oneextends" class="www.sheep.pojo.oneExtends"></bean>
    <bean id="twoectends" class="www.sheep.pojo.twoExtends" parent="oneextends"></bean>
    
  4. Spring的依赖

    与继承类似,依赖也是描述bean和bean之间的一种关系,配置依赖之后,被依赖的bean一定先创建,再创建依赖的bean,A依赖于B,先创建B,再创建A。

    <!-- twoectends依赖于oneectends -->
    <bean id="oneextends" class="www.sheep.pojo.oneExtends">
    <bean id="twoectends" class="www.sheep.pojo.twoExtends" depends-on="twoectends">
    
  5. Bean的装载三种方式

    Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。

    • XML装载:IoC容器通过了通过XML方式装配的两种方式:Set、构造。

    • 注解(Annotation)的装载

      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"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
       
          <context:annotation-config/>
       	<!--在使用注解功能之前要告诉IoC现在需要启用注解相关的
      		功能,通过上下文级别的配置即可开启所有注解相关的功能-->
      </beans>
      

      三个实体类

      public class Tools {
          public void minTool(){
              System.out.println("我是小扳手。。。。。");
          }
      }
      
      public class Utils {
          private int id;
          private String name;
          private String type;
      }
      
      /**
       * @Autowired注解:当bean中的id名与变量名相同时自动装载,当变量名不同时通过数据类型装载。
       * @Autowired
       *     @Qualifier(value = "utils"):当有多个相同类型的bena时可以使用该注解指定与id对应的名称。
       * */
      public class Mankind {
          private String name;
          private String age;
          @Autowired
          private Tools tools;
          @Autowired
          @Qualifier(value = "utils")
          private Utils utils;  
      }
      

      在spring.xml中添加bean

      <?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
              http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
          <context:annotation-config/>
          <!-- Tools -->
          <bean id="tools" class="www.sheep.pojo.Tools"/>
          <!-- Utils -->
          <bean id="utils" class="www.sheep.pojo.Utils">
              <property name="id" value="1"/>
              <property name="name" value="鼠标"/>
              <property name="type" value="学习"/>
          </bean>
          <!-- Utils -->
          <bean id="utils2" class="www.sheep.pojo.Utils">
              <property name="id" value="1"/>
              <property name="name" value="鼠标"/>
              <property name="type" value="学习.com"/>
          </bean>
          <!-- Mankind -->
          <bean id="mankind" class="www.sheep.pojo.Mankind">
              <property name="name" value="梅西"/>
              <property name="age" value="21"/>
              <!-- 没有通过ref的方式,而是通过注解的方式进行装载 -->
          </bean>
      </beans>
      

      Handler

      public class Test5 {
          public static void main(String[] args) {
              ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-annotation.xml");
              Mankind mankind = (Mankind) applicationContext.getBean("mankind");
              mankind.getTools().minTool();
              System.out.println(mankind);
          }
      }
      
    • 自动装载(Autowrie)

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

      自动装载有两种方式:

      byName:通过属性名自动装载

      创建实体类

      public class Person {
          private long id;
          private String name;
          private Car car;
      }
      
      public class Car {
          private long id;
          private String name;
      }
      

      spring.xml

      	<bean id="car" class="www.sheep.pojo.Car">
              <property name="id" value="1"/>
              <property name="name" value="宝马"/>
          </bean>
          <bean id="person" class="www.sheep.pojo.Person" autowire="byName">
              <property name="id" value="1"/>
              <property name="name" value="编程"/>
          </bean>
      

      Handler

      public class Test4 {
          public static void main(String[] args) {
              ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
              Person person = (Person) applicationContext.getBean("person");
              System.out.println(person);
          }
      }
      

      byType:通过属性的数据类型自动装载

      spring.xml

          <!-- 当bean的id不同时根据数据类型装载 -->
      	<bean id="car123" class="www.sheep.pojo.Car">
              <property name="id" value="1"/>
              <property name="name" value="宝马"/>
          </bean>
          <bean id="person" class="www.sheep.pojo.Person" autowire="byType">
              <property name="id" value="1"/>
              <property name="name" value="编程"/>
          </bean>
      
    • Bean的装载总结:

      byName:使用时根据Bean的id自动装载可以有多个Bean。

      byType:使用byType时只能由一个与之对应的Bean如果有多个会抛出异常。

      注解装载和自动装载:都只能对引用数据类型装载(ref)。

6、注解开发

springIoC中的注解

  • @Component:表示将该类描述为Bean,可以作用在任何层。
  • @Repository:与@Component注解一样将该类描述为Bean,作用于DAO层。
  • @Service:与@Component注解一样将该类描述为Bean,作用在(service)控制层。
  • @Controller:与@Component注解一样将该类描述为Bean,作用在控制层,比如SpringMVC的Controller。
  • @Autowired:用于对Bean的属性、set、构造方法进行标注,配置其他注解 完成Bean的注入,默认按类型注入。
  • @Resource:与@Autowirsd一样,不同在于@Resource默认按照名称注入,但@Resource有两个属性name、type。分别是按照名字、类型注入
  • @Qualifier:与@Autowired注解配置使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,实例名称由@Qualifier注解的参数指定。

7、使用Java的方式配置Spring

现在完全不使用Spring的.xml配置,全权交给Java来做!

JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!

使用Java方式配置SPring的简单使用

  1. 创建实体类

    package com.sheep.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    //通过@Component注解将该类交给IoC容器接管,注册到容器中
    @Component
    public class User {
        private  String name;
    
        public String getName() {
            return name;
        }
        public User(){
            System.out.println("Program Sheep!!!");
        }
        @Value("Program Sheep") //属性注入值
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "User{" +
                    "name=‘" + name + ‘\‘‘ +
                    ‘}‘;
        }
    }
    
  2. 创建管理类

    package com.sheep.config;
    
    import com.sheep.pojo.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Component;
    
    //这个也会被IoC容器接管,注册到容器中,应为他本来就是一个@Component。
    //@Configuration 代表一个配置类,就相当于bean.xml,扫描com.sheep.pojo包下的所有bean
    @Configuration
    @ComponentScan("com.sheep.pojo")
    public class SheepConfig {
        @Bean
        public User getUser(){
            return new User();//就是返回要注入到bean的对象
        }
        //注册一个bean,就相当于我之前写的一个bean标签
        //这个方法的名字,就相当于bean标签中的id属性
        //这个方法的返回值,就相当于bean标签中的class属性
    }
    
    
  3. 创建测试类

    import com.sheep.config.SheepConfig;
    import com.sheep.pojo.User;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class MySheepConfig {
        public static void main(String[] args) {
            //如果完全使用了配置类方式去做,就只能通过AnnotationConfigApplicationContext上下文来获取容器,通过配置运行时类加载
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SheepConfig.class);
            User user = (User) applicationContext.getBean("getUser");
            System.out.println(user);
        }
    }
    

    技术分享图片

    全程并没有使用spring.xml方式配置Bean而是使用纯Java代码配置实现的。


AOP:

1、AOP简介

  1. 什么是AOP

    • AOP全称Aspect-Orientend Programming,面向切面编程。
    • AOP是对面向对象编程的一种补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是AOP。
    • 使用AOP,开发人员在编写业务逻辑时可以专心于核心业务,不会再过多的关注于其他业务逻辑的实现。
    • 目前流行的AOP框架有两个,SpringAOP、AspectJ。
  2. AOP的优点

    • 降低模块之间的耦合度
    • 使系统容易扩展
    • 更好的代码复用
    • 非业务代码更加集中,不分散,便于统一管理
    • 业务代码更加纯粹,没有其他代码的影响
  3. AOP术语

    • Aspect(切面):横切关注点被模块化的抽象对象。

    • Advice(通知):切面对象完成的工作

    • Target Object(目标对象):被通知的对象,即被横切的对象

    • Proxy(代理):切面、通知、目标混合之后的对象

    • Joinpoint(连接点):通知要插入业务代码的具体位置

    • Pointcut(切入点):AOP通过切点定位到连接点。

    • Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

2、JDK动态代理AOP

  1. 创建Maven工程,pom.xml添加

       <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>5.3.4</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.3.5</version>
            </dependency>
        </dependencies>
    
  2. 创建一个计数器接口Cal,定义4个方法。

    package com.sheep.utils;
    
    public interface Cal {
        public int add(int num1,int num2);
        public int sud(int num1,int num2);
        public int mul(int num1,int num2);
        public int div(int num1,int num2);
    }
    
  3. 创建接口的实现类Callmpl

    package com.sheep.utils.impl;
    import com.sheep.utils.Cal;
    
    public class CalImpl implements Cal {
        public int add(int num1, int num2) {
            System.out.println("add方法的参数是["+num1+","+num2+"]");
            int result = num1+num2;
            System.out.println("add方法的结果是"+result);
            return result;
        }
        public int sub(int num1, int num2) {
            System.out.println("sud方法的参数是["+num1+","+num2+"]");
            int result = num1-num2;
            System.out.println("sub方法的结果是"+result);
            return result;
        }
        public int mul(int num1, int num2) {
            System.out.println("mul方法的参数是["+num1+","+num2+"]");
            int result = num1*num2;
            System.out.println("mul方法的结果是"+result);
            return result;
        }
        public int div(int num1, int num2) {
            System.out.println("div方法的参数是["+num1+","+num2+"]");
            int result = num1/num2;
            System.out.println("div方法的结果是"+result);
            return result;
        }
    }
    
  4. 测试

    package com.sheep.test;
    import com.sheep.utils.Cal;
    import com.sheep.utils.MyInvocationHandler;
    import com.sheep.utils.impl.CalImpl;
    
    public class Test1 {
        public static void main(String[] args) {
            Cal cal = new CalImpl();
            cal.add(1,1);
            cal.sub(4,2);
            cal.mul(2,3);
            cal.div(6,2);
        }
    }
    

    上述代码中,日志信息和业务逻辑的耦合性很高,不利于系统的维护,使用AOP可以进行优化,如何来实现AOP?使用动态代理的方式来实现。

  5. 使用动态代理

    创建代理类

    package com.sheep.utils;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    public class MyInvocationHandler implements InvocationHandler {
        //接收委托对象
        private Object object = null;
        //返回代理对象
        public Object bind(Object object){
            this.object = object;
            return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method.getName()+"方法的参数是"+ Arrays.toString(args));
            Object result = method.invoke(this.object, args);
            System.out.println(method.getName()+"结果是"+result);
            return result;
        }
    }
    
  6. 测试

    public class Test1 {
        public static void main(String[] args) {
            //原始
    //        Cal cal = new CalImpl();
    //        cal.add(1,1);
    //        cal.sub(4,2);
    //        cal.mul(2,3);
    //        cal.div(6,2);
            //代理对象开发
            Cal cal = new CalImpl();  //创建委托对象
            MyInvocationHandler myInvocationHandler = new MyInvocationHandler();//创建代理对象
            Cal proxy = (Cal) myInvocationHandler.bind(cal);//将委托对象交给代理对象实现
            proxy.add(1,1);
            proxy.sub(2,1);
            proxy.mul(2,3);
            proxy.div(6,2);
        }
    }
    

    以上是通过动态代理实现AOP的过程,比较复杂,不好理解,Spring框架对AOP进行了封装,使用Spring框架可以用面向对象的思想来实现AOP。

    控制台

    add方法的参数是[1, 1]
    add结果是2
    sub方法的参数是[2, 1]
    sub结果是1
    mul方法的参数是[2, 3]
    mul结果是6
    div方法的参数是[6, 2]
    div结果是3
    

3、Spring框架代理类的AOP实现三种方式

AOP是实现方式一

使用Spring的API接口(获取参数)

  1. 添加依赖

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.6</version>
    </dependency>
    
  2. 定义一个接口

    package com.sheep.service;
    
    public interface UserService {
        public void add();
        public void delete();
        public void update();
        public void select();
    }
    
  3. 定义接口实现类

    package com.sheep.service;
    
    public class UserServiceImpl implements UserService{
        @Override
        public void add() {
            System.out.println("增加一个用户");
        }
        @Override
        public void delete() {
            System.out.println("删除一个用户");
        }
        @Override
        public void update() {
            System.out.println("更新一个用户");
        }
        @Override
        public void select() {
            System.out.println("查询一个用户");
        }
    }
    
  4. 配置前置类

    package com.sheep.log;
    import org.springframework.aop.MethodBeforeAdvice;
    import java.lang.reflect.Method;
    
    public class Log implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
        }
    }
    
  5. 配置后置类

    package com.sheep.log;
    import org.springframework.aop.AfterReturningAdvice;
    import java.lang.reflect.Method;
    
    public class AfterLog implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println("执行了"+method.getName()+"方法。返回结果为:"+o);
        }
    }
    
  6. spring.xml中配置AOP约束

    <?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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    </beans>
    
  7. 在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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 注册Bean -->
        <bean id="userService" class="com.sheep.service.UserServiceImpl"/>
        <bean id="log" class="com.sheep.log.Log"/>
        <bean id="afterLog" class="com.sheep.log.AfterLog"/>
    <!-- 方式一使用原生的SpringAPI接口 -->
        <!-- 配置AOP: 需要导入aop的约束-->
        <aop:config>
            <!-- 切入点 -->
            <aop:pointcut id="pointcut" expression="execution(* com.sheep.service.UserServiceImpl.*(..))"/>
            <!-- 执行环绕增强 -->
            <!-- 通知 -->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
    </beans>
    
  8. 测试类

    import com.sheep.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test01 {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
            //动态代理的必须是接口
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.add();
            userService.delete();
            userService.update();
            userService.select();
        }
    }
    

    控制台

    com.sheep.service.UserServiceImpl的add被执行了
    增加一个用户
    执行了add方法。返回结果为:null
    com.sheep.service.UserServiceImpl的delete被执行了
    删除一个用户
    执行了delete方法。返回结果为:null
    com.sheep.service.UserServiceImpl的update被执行了
    更新一个用户
    执行了update方法。返回结果为:null
    com.sheep.service.UserServiceImpl的select被执行了
    查询一个用户
    执行了select方法。返回结果为:null
    

    在方式一中前置通知与后置通知被抽象为一个类,并在xml中配置该类。

AOP是实现方式二

自定义类来实现

  1. 将方法一的前后置类变成一个类前后置变成两个方法,并且不要AfterReturningAdvice接口

    package com.sheep.diy;
    
    public class DiyPointCut {
        public void before(){
            System.out.println("------方法执行前------");
        }
        public void after(){
            System.out.println("------方法执行后------");
        }
    }
    
  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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <bean id="userService" class="com.sheep.service.UserServiceImpl"/>
        <!--===================================================================================-->
        <!-- 方式一使用原生的SpringAPI接口 -->
        <!-- 注册Bean -->
        <bean id="log" class="com.sheep.log.Log"/>
        <bean id="afterLog" class="com.sheep.log.AfterLog"/>
        <!-- 配置AOP: 需要导入aop的约束-->
        <aop:config>
            <!-- 切入点 -->
            <aop:pointcut id="pointcut" expression="execution(* com.sheep.service.UserServiceImpl.*(..))"/>
            <!-- 执行环绕增强 -->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
        <!--===================================================================================-->
        <!-- 方式二:自定义类 -->
        <bean id="diy" class="com.sheep.diy.DiyPointCut"/>
        <aop:config>
            <!-- 自定义切面 -->
            <aop:aspect ref="diy">
                <!-- 切入点 -->
                <aop:pointcut id="point" expression="execution(* com.sheep.service.UserServiceImpl.*(..))"/>
                <!-- 通知点 -->
                    <!-- 前置通知 -->
                <aop:before method="before" pointcut-ref="point"/>
                    <!-- 后置通知 -->
                <aop:after method="after" pointcut-ref="point"/>
            </aop:aspect>
        </aop:config>
        <!--===================================================================================-->
    </beans>
    
  3. 测试类

    import com.sheep.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test01 {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
            //动态代理的必须是接口
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.add();
            userService.delete();
            userService.update();
            userService.select();
        }
    }
    

    控制台

    ------方法执行前------
    增加一个用户
    ------方法执行后------
    ------方法执行前------
    删除一个用户
    ------方法执行后------
    ------方法执行前------
    更新一个用户
    ------方法执行后------
    ------方法执行前------
    查询一个用户
    ------方法执行后------
    

AspectJ 注解实现AOP

案例一:

  1. 自动生成动态

    	<!-- 让Spring容器结合切面类和目标对象自动生成动态代理对象-->
        <aop:aspectj-autoproxy/>
    
  2. 新建切面类使用注解配置(延续AOP三种实现方式的类)

    package com.sheep.diy;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Aspect //表示这个类是一个切面
    @Component //表示将该类交给IoC容器创建(注意在spring.xml中要配置扫描该该类)
    public class Annotation {
        //@Before:表示前置通知的切入点
        @Before("execution(* com.sheep.service.UserServiceImpl.*(..))")
        public void before(){
            System.out.println("-=-=-=方法执行前-=-=-=");
        }
        // @After:表示后置通知的切入点
        @After("execution(* com.sheep.service.UserServiceImpl.*(..))")
        public void after(){
            System.out.println("-=-=-=方法执行后-=-=-=");
        }
        //@Around表示环绕通知切入点
        @Around("execution(* com.sheep.service.UserServiceImpl.*(..))")
        public void around(ProceedingJoinPoint pj) throws Throwable {
            System.out.println("环绕前");
            Object proceed = pj.proceed();		//执行前后置方法
            System.out.println("环绕后");
        }
    }
    
  3. 在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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 注册Bean -->
        <bean id="userService" class="com.sheep.service.UserServiceImpl"/>
        <bean id="log" class="com.sheep.log.Log"/>
        <bean id="afterLog" class="com.sheep.log.AfterLog"/>
    
    <!--    &lt;!&ndash; 方式一使用原生的SpringAPI接口 &ndash;&gt;-->
    <!--    &lt;!&ndash; 配置AOP: 需要导入aop的约束&ndash;&gt;-->
    <!--    <aop:config>-->
    <!--        &lt;!&ndash; 切入点 &ndash;&gt;-->
    <!--        <aop:pointcut id="pointcut" expression="execution(* com.sheep.service.UserServiceImpl.*(..))"/>-->
    <!--        &lt;!&ndash; 执行环绕增强 &ndash;&gt;-->
    <!--        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>-->
    <!--        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>-->
    <!--    </aop:config>-->
    
    <!--    &lt;!&ndash; 方式二:自定义类 &ndash;&gt;-->
    <!--    <bean id="diy" class="com.sheep.diy.DiyPointCut"/>-->
    <!--    <aop:config>-->
    <!--        &lt;!&ndash; 自定义切面 &ndash;&gt;-->
    <!--        <aop:aspect ref="diy">-->
    <!--            &lt;!&ndash; 切入点 &ndash;&gt;-->
    <!--            <aop:pointcut id="point" expression="execution(* com.sheep.service.UserServiceImpl.*(..))"/>-->
    <!--            &lt;!&ndash; 通知点 &ndash;&gt;-->
    <!--                &lt;!&ndash; 前置通知 &ndash;&gt;-->
    <!--            <aop:before method="before" pointcut-ref="point"/>-->
    <!--                &lt;!&ndash; 后置通知 &ndash;&gt;-->
    <!--            <aop:after method="after" pointcut-ref="point"/>-->
    <!--        </aop:aspect>-->
    <!--    </aop:config>-->
    
        <!-- 方式三 -->
        <!-- 将com.sheep.diy包下的所有类交给IOC容器创建 -->
        <context:component-scan base-package="com.sheep.diy*"/>
    <!--    <bean class="com.sheep.diy.Annotation"/>-->
        <!-- 让Spring容器结合切面类和目标对象自动生成动态代理对象-->
        <aop:aspectj-autoproxy/>
    </beans>
    
    
  4. 测试类

    import com.sheep.log.Log;
    import com.sheep.service.UserService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test01 {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
            //动态代理的必须是接口
            UserService userService = (UserService) applicationContext.getBean("userService");
            userService.add();
            userService.delete();
            userService.update();
            userService.select();
        }
    }
    

    控制台

    环绕前
    -=-=-=方法执行前-=-=-=
    增加一个用户
    -=-=-=方法执行后-=-=-=
    环绕后
    环绕前
    -=-=-=方法执行前-=-=-=
    删除一个用户
    -=-=-=方法执行后-=-=-=
    环绕后
    

    可以看出该环绕在方法的最前和最后

案例二:(获取参数)

  1. 创建接口

    package com.sheep.utils;
    
    public interface Cal {
        public int add(int num1,int num2);
        public int sub(int num1,int num2);
        public int mul(int num1,int num2);
        public int div(int num1,int num2);
    }
    
  2. 创建接口实习类(交给IoC容器)

    package com.sheep.utils.impl;
    import com.sheep.utils.Cal;
    import org.springframework.stereotype.Component;
    
    @Component	//将该类交给IoC容器
    public class CalImpl implements Cal {
        public int add(int num1, int num2) {
            //System.out.println("add方法的参数是["+num1+","+num2+"]");
            int result = num1+num2;
            //System.out.println("add方法的结果是"+result);
            return result;
        }
        public int sub(int num1, int num2) {
            int result = num1-num2;
            return result;
        }
        public int mul(int num1, int num2) {
            int result = num1*num2;
            return result;
        }
        public int div(int num1, int num2) {
            int result = num1/num2;
            return result;
        }
    }
    
  3. 使用注解创建切面类

    package com.sheep.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    @Aspect //声明该类为切面类
    @Component //将来类交给IoC容器
    public class LoggerAspect {
    
        //前置通知
        @Before("execution(public int com.sheep.utils.impl.CalImpl.*(..))")
        public void before(JoinPoint joinPoint){
            //获取方法名
            String name = joinPoint.getSignature().getName();
            //获取参数
            String args = Arrays.toString(joinPoint.getArgs());
            System.out.println(name+"方法的参数是"+args);
        }
        //后置通知
        @After("execution(* com.sheep.utils.impl.CalImpl.*(..)))")
        public void after(JoinPoint joinPoint){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"执行完毕");
        }
        //返回值通知
        @AfterReturning(value = "execution(* com.sheep.utils.impl.CalImpl.*(..)))",returning = "result")
        public void afterReturning(JoinPoint joinPoint,Object result){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"方法的结果是"+result);
        }
        //异常通知
        @AfterThrowing(value = "execution(* com.sheep.utils.impl.CalImpl.*(..)))",throwing = "exception")
        public void fterThrowing(JoinPoint joinPoint,Exception exception){
            String name = joinPoint.getSignature().getName();
            System.out.println(name+"方法抛出了"+exception+"异常");
        }
    }
    
  4. 在spring.xml中配置注解扫描、和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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 自动扫描 -->
        <context:component-scan base-package="com.sheep.aop*"/>
        <context:component-scan base-package="com.sheep.utils*"/>
        <!-- 让Spring容器结合切面类和目标对象自动生成动态代理对象-->
        <aop:aspectj-autoproxy/>
    </beans>
    

    控制台

    add方法的参数是[1, 1]
    add方法的结果是2
    add执行完毕
    sub方法的参数是[2, 1]
    sub方法的结果是1
    sub执行完毕
    
  5. 总结:注解开发中常用的注解、通知的执行先后顺序

    @Aspect :表示这个类是一个切面
    @Component :表示将该类交给IoC容器创建(注意在spring.xml中要配置扫描该该类)

    @Before:表示前置通知

    @After:表示后置通知

    @AfterReturning:表示返回值通知

    @AfterThrowing:异常通知

    @Around:表示环绕通知

    前环绕通知 >> 前置通知 >> 返回值通知 >> 后置通知 >> 后环绕通知。

浅入Spring

原文:https://www.cnblogs.com/w-eye/p/14586243.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!