Spring入门笔记【IOC&AOP】

目录

    • 1、Spring
      • 1.1、优点
      • 1.2、组成
      • 1.3、拓展
    • 2、IOC理论推导
    • 3、HelloSpring
      • 3.1、导入Spring相关jar包
      • 3.2、编写相关代码
        • 3.2.1 编写一个Hello实体类
        • 3.2.2 编写spring文件
        • 3.2.3 测试
    • 4、IOC创建对象的方式
    • 5、Spring配置
      • 5.1、别名
      • 5.2、Bean的配置
      • 5.3、import
    • 6、依赖注入
      • 6.1、构造器注入
      • 6.2、Set方式注入【重点】
      • 6.3、拓展方式注入
      • 6.4、bean的作用域
    • 7、Bean的自动装配
      • 7.1、bean添加自动装配属性
      • 7.2、使用注解实现自动装配
    • 8、使用注解开发
    • 9、使用Java的方式配置Spring
    • 10、代理模式
      • 10.1、静态代理
      • 10.2、加深理解
      • 10.3、动态代理
    • 11、AOP
      • 11.1、什么是AOP
      • 11.2、 Aop在Spring中的作用
      • 11.3、使用Spring实现AOP
    • 12、整合Mybatis
      • 12.1、回忆mybatis
      • 12.2、mybatis-spring
    • 13、声明式事务
      • 13.1、回顾事务
      • 13.2、Spring中的事务管理
    • 注解说明

1、Spring

1.1、优点

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级的、非入侵式的框架
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

1.2、组成

Spring入门笔记【IOC&AOP】_第1张图片

1.3、拓展

  • Spring Boot
    • 一个快速开发的脚手架
    • 基于SpringBoot可以快速开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • SpringCloud是基于SpringBoot实现的

弊端:发展太久,违背了原来的理念。配置十分繁琐!

2、IOC理论推导

  • 之前,程序是主动创建对象,控制权在程序员手上
  • 使用了Set注入后,程序不再具有主动性,而是变成了被动的接收对象

这种思想,从本质上解决了问题,程序员不用再管对象的创建。系统的耦合性大大降低,可以更加专注在业务的实现上,这是IOC的原型。

IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

3、HelloSpring

3.1、导入Spring相关jar包

spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.1.10.RELEASEversion>
dependency>

3.2、编写相关代码

3.2.1 编写一个Hello实体类

 public class Hello {
     private String name;
 
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
 
     public void show(){
         System.out.println("Hello"+ name );
     }
 }

3.2.2 编写spring文件



	<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="hello" class="com.kuang.pojo.Hello">
         <property name="name" value="Spring"/>
      bean> 
 beans>

3.2.3 测试

 @Test
 public void test(){
     //解析beans.xml文件 , 生成管理相应的Bean对象
     ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
     //getBean : 参数即为spring配置文件中bean的id .
     Hello hello = (Hello) context.getBean("hello");
     hello.show();
 }

思考问题 ?
Hello 对象是谁创建的 ?

hello 对象是由Spring创建的

Hello 对象的属性是怎么设置的 ?

hello 对象的属性是由Spring容器设置的 ,

这个过程就叫控制反转

控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .

反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想 , 由主动的编程变成被动的接收 .

可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .

OK , 到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配!

4、IOC创建对象的方式

  • 使用无参构造函数创建对象,默认

  • 假设要通过有参构造函数创建对象

    • 下标赋值

      <bean id="user" class="com.da.pojo.User">
      	<construsctor-arg index="0" value="无敌大天才"/>
      bean>
      
    • 类型(不建议使用)

      <bean id="user" class="com.da.pojo.User">
      	<construsctor-arg type="java.lang.String" value="超级无敌大天才"/>
      bean>
      
    • 参数名

      <bean id="user" class="com.da.pojo.User">
      	<construsctor-arg name="name" value="上天入地超级无敌大天才"/>
      bean>
      

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了

5、Spring配置

5.1、别名

<alias name="user" alias="userNew"/>

5.2、Bean的配置


<bean id="userT" class="com.da.pojo.UserT" name="user2 u2,u3;u4">
	<property name="name" value="啊,你说什么?"/>    
bean>

5.3、import

一般用于团队开发使用,它可以将多个配置文件,导入合并为一个

假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的

  • spring-a-beans.xml

  • spring-b-beans.xml

  • spring-c-beans.xml

  • applicationContext.xml

    <import resource="spring-a-beans.xml"/>
    <import resource="spring-b-beans.xml"/>
    <import resource="spring-c-beans.xml"/>
    

使用的时候直接使用总的就可以了

6、依赖注入

6.1、构造器注入


<bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
bean>

6.2、Set方式注入【重点】

  • 依赖注入
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性,由容器来注入

Stringnull

<bean class="ExampleBean">
    <property name="email" value=""/>
bean>
<bean class="ExampleBean">
    <property name="email">
        <null/>
    property>
bean>

propslistmapset


<bean id="moreComplexObject" class="example.ComplexObject">
    
    <property name="adminEmails">
        <props>
            <prop key="administrator">[email protected]prop>
            <prop key="support">[email protected]prop>
            <prop key="development">[email protected]prop>
        props>
    property>
    
    <property name="someList">
        <list>
            <value>a list element followed by a referencevalue>
            <ref bean="myDataSource" />
        list>
    property>
    
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            
        map>
    property>
    
    <property name="someSet">
        <set>
            <value>just some stringvalue>
            <ref bean="myDataSource" />
        set>
    property>
bean>

6.3、拓展方式注入

可以使用 p 命名空间和 c 命名空间进行注入

官方示例:

  • p命名空间注入,可以直接注入属性的值:property
<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="[email protected]"/>
    bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="[email protected]"/>
beans>
  • c命名空间注入,通过构造器注入:construct-args
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="[email protected]"/>
    bean>

    
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="[email protected]"/>

beans>

注意点:p命名和c命名空间不能直接使用,需要导入xml约束

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4、bean的作用域

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

1.单例模式(Spring默认机制)

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

2.原型模式:每次从容器中get的时候,都会产生一个新对象

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

3.其余的request、session、application,这些只能在web开发中使用

7、Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

在Spring中有三种装配的方式:

  1. 在xml中显式配置
  2. 在java中显式配置
  3. 隐式自动装配bean【重要】

7.1、bean添加自动装配属性

  • byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应地beanid
  • byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean
<bean id="user" class="com.da.pojo.User" autoWire="byName">
	<construsctor-arg name="name" value="上天入地超级无敌大天才"/>
bean>
<bean id="user" class="com.da.pojo.User" autoWire="byType">
	<construsctor-arg name="name" value="上天入地超级无敌大天才"/>
bean>

小结:

  • byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
  • bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致

7.2、使用注解实现自动装配

使用注解须知:

  1. 导入约束:context约束
  2. 配置注解的支持:context:annotation-config/

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

beans>

@Autowired

直接在属性上使用即可;也可以在set方法上使用

使用Autowired就可以不写Set方法,前提是自动装配的属性在IOC(Spring)容器中存在,且符合名字byName

科普:

@Nullable	字段标记了这个注解,说明这个字段可以为null;
public @interface Autowired{
    boolean required() default true;
}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!

public class People{
    @AutoWired
    @Qualifier(value="talent")
    private Talent talent;
}

@Resource注解

public class People{
    @Resource(name="talent")
    private Talent talent;
    
    @Resource
    private Dog dog;
}

小结:

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过byType的方式实现,而且必须要求这个对象存在
  • @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到就报错
  • 执行顺序不同:@Autowired通过byType的方式实现;@Resource通过byname的方式实现

8、使用注解开发

  • 在Spring4之后,要使用注解开发,必须保证aop的包导入了

  • 使用注解需要导入context约束,增加注解的支持(注解驱动或自动扫描)

    
    <context:component-scan base-package="com.core"/>
    <context:annotation-config/>
    

小结:

xml与注解:

  • xml更加万能,适用于任何场合,维护简单方便
  • 注解 不是自己的类使用不了,维护相对复杂

xml与注解的最佳实践:

  • xml只用来管理bean
  • 注解只负责完成属性的注入
  • 使用过程中只需要注意:必须让注解生效,就需要开启注解的支持

9、使用Java的方式配置Spring

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

实体类

//这个注解说明这个类被Spring接管了,注册到了容器中
@Component
public class user{
    private String name;
    private String getName(){
        return name;
	}
    
    @Value("datiancai")//属性注入值
    public void setName(String name){
        this.name=name;
	}
    
    @Override
    public String toString(){
        return "User{" + "name='" + name + "\'" + "}";
	}
}

配置文件

//@Configuration代表这是一个配置类,等价于beans.xml
@Configuration
@ComponentScan("com.core")
@Import(datiancaiConfig.class)
public class MatchlessGeniusConfig{
    //注册一个bean,相当于
    //这个方法的名字相当于bean标签的id属性
    //这个方法的返回值,相当于bean标签的class属性
    @Bean
    public User user(){
        return new user();//返回要注入到bean的对象
	}
}

10、代理模式

代理模式的分类:

  • 静态代理
  • 动态代理

10.1、静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色后,一般会做一些附属操作
  • 客户:访问代理对象的人

代码步骤:

  1. 接口

    //租房
    public interface Rent{
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host implements Rent{
        public void rent(){
            system.out.println("房东要出租房子");
        }
    }
    
  3. 代理角色

    public class Proxy implements Rent{
        private Host host;
        public Proxy(Host host){
            this.host = host;
        }
        
        public void rent(){
            seeHouse();
            host.rent();
            hetong();
            fare();
        }
        
        //看房
        public void seeHouse(){
            system.out.println("中介带你看房");
        }
        
        //合同
        public void hetong(){
            system.out.println("签租赁合同");
        }
        //收中介费
        public void fare(){
            system.out.println("收中介费");
        }
    }
    
  4. 客户端访问代理角色

    public class Client{
        public static void main(String[] args) {
            //房东要租房子
            Host host = new Host();
            //代理
            Proxy proxy = new Proxy(host);
            //不用面对房东,直接找中介租房
            proxy.rent();
        }
    }
    

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色;代码量翻倍,效率会变低~

10.2、加深理解

Spring入门笔记【IOC&AOP】_第2张图片

10.3、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是直接写好的
  • 动态代理分为两大类:基于接口的动态代理;基于类的动态代理
    • 基于接口------JDK动态代理
    • 基于类:cglib
    • java字节码实现:JAVAssist

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

动态代理的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

代码步骤:

  1. 代理角色

    public class ProxyInvocaionHandler implements InvocationHandler {
        
        //被代理的接口
        private Object target;
        
        public void settarget(Object target){
            this.target = target;
        }
        //生成得到代理类
        public void getProxy(){
           return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                         target.getClass().getInterfaces(),this);
        }
        
        //处理代理实例,并返回结果
        public Object invoke(Object proxy, Mehtod method, Object[] args) throws Throwable{
            log(method.getName());
            //动态代理的本质,就是使用反射机制实现
            Object result = method.invok(target,args);
            return null;
        }
        
        public void log(String msg){
            System.out.println("执行了"+msg+"方法");
        }
    }   
    
  2. 客户端访问

    public class Client{
        public static void main(String[] args) {
            //真实角色
            UserServiceImpl userService = new UserServiceImpl();
            //代理角色
            ProxyInvocaionHandler pih = new ProxyInvocaionHandler();
    
            pih.setTarget(userService);//设置要代理的对象
            //动态生成代理类
            UserService proxy = (UserService) pih.getProxy();
            
            proxy.add();
        }
    }
    

11、AOP

11.1、什么是AOP

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

Spring入门笔记【IOC&AOP】_第3张图片

11.2、 Aop在Spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志、安全、缓存、事务等…
  • 切面(ASPECT):横切关注点被模块化的特殊对象(它是一个类)
  • 通知(Advice):切面必须要完成的工作(它是类中的一个方法)
  • 目标(Target):被通知对象
  • 代理(Proxy):向目标对象应用通知之后创建的对象
  • 切入点(PointCut):切面通知执行的“地点”的定义
  • 连接点(JointPoint):与切入点匹配的执行点

Spring入门笔记【IOC&AOP】_第4张图片

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
Spring入门笔记【IOC&AOP】_第5张图片
即AOP在不改变原有代码的情况下,去增加新的功能

11.3、使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包

<dependency> 
<groupId> org.aspectjgroupId > 
<artifactId> aspectjweaverartifactId > 
<version> 1.9.4version > 
dependency> 

方式一:使用Spring的API接口


<aop:config>
    
    <aop:pointcut id="pointcut" expression="execution(* com.tiancai.service.UserServiceImpl.*(..))"/>
    
    
    <aop:advisor advice-ref="log" pointcut="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>

方式二:自定义类实现AOP

<bean id="diy" class="com.tiancai.diy.DiyPointCut"/>
<aop:config>
    
    <aop:aspect ref="diy">
        
        <aop:pointcut id="point" expression="execution(* com.tiancai.service.UserServiceImpl.*(..))"/>
        
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" point-ref="point"/>
    aop:aspect>
aop:config>

方式三:使用注解实现

@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.tiancai.service.UserServiceImpl.*(..))")
    public void before(){
       System.out,println("=========方法执行前=========");
    }
    @After("execution(* com.tiancai.service.UserServiceImpl.*(..))")
    public void after(){
       System.out,println("=========方法执行后=========");
    }
    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.tiancai.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pjp){
       System.out,println("环绕前");
       //执行方法
       Object proceed = pjp.proceed();
       System.out,println("环绕后");
    }
}
<bean id="annotationPointCut" class="com.tiancai.diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>

12、整合Mybatis

步骤

  1. 导入相关jar包
    • junit
    • mybatis
    • mysql数据库
    • spring相关
    • aop织入
    • mybatis-spring【new】
  2. 编写配置文件
  3. 测试

12.1、回忆mybatis

  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试

12.2、mybatis-spring

  1. 编写数据源配置
  2. sqlSessionFactory
  3. sqlSessionTemplate
  4. 需要给接口加实现类
  5. 将自己写的实现类注入到Spring中
  6. 测试

13、声明式事务

13.1、回顾事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败
  • 事务在项目开发中,十分重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性

事务ACID原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性
    • 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化地写到存储器中

13.2、Spring中的事务管理

  • 声明式事务:AOP
  • 编程式事务:需要在代码中,进行事务的管理

思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况
  • 如果不在Spring中配置声明式事务,就需要在代码中手动配置事务
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!

注解说明

  • @Autowired:自动装配通过类型、名字

    如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")

  • @Nullable:字段标记了这个注解,说明这个字段可以为null

  • @Resource:自动装配通过名字、类型

  • @Component:组件,放在类上,说明这个类被Spring管理了,就是bean;按mvc三层架构分层

    • dao[@Repository]
    • service[@Service]
    • controller[@Controller]
  • @ComponentScan:定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中

  • @Value:读配置文件的属性

  • @Configuration:声明配置类,可以启动组件扫描

  • @Bean:产生bean对象

  • @Import:导入

  • @Override:重写

你可能感兴趣的:(spring)