Spring内容汇总

0. 简介

       本文用于记录本人的Spring学习内容以及部分自己对于工厂模式和代理模式的理解,参考资料源自以下:
       JAVA设计模式之工厂模式(三种工厂模式)-阿里云开发者社区 (aliyun.com)
       设计模式(四)——搞懂什么是代理模式 - 知乎 (zhihu.com)
       静态代理和动态代理有什么区别?–乐字节java - 知乎 (zhihu.com)
       B站狂神说Java——Spring相关视频
       可能有部分理解不到位的地方,不喜勿喷。

1. 工厂模式:用于对象的创建,使得客户从具体的产品对象中被解耦;简单理解就是对象的创建不通过程序本身来决定,而是由客户来决定。

  • 简单工厂模式:实际上不能算作一种设计模式,它引入了创建者的概念,将实例化的代码从应用代码中抽离,在创建者类的静态方法只处理创建对象的细节,参考如下代码:

    public class SimpleFactory{
        public static Object getInstance(String type){
            if(type.equals("ClassA"))
                return new ClassA();
            else if(type.equals("ClassB"))
                return new ClassB();
            else if(type.equals("ClassC"))
                return new ClassC();
            return null;
        }
    }
    

    这里的ClassA,ClassB,ClassC都是同一类产品,可以是实现了同一接口的类,也可以是继承了同一父类的类,返回类型为Object只是因为我没有写这个接口和父类。简单工厂强调的是,将对象的业务与创建分离开来,即,ClassA,ClassB,ClassC的业务与创建。

    在简单工厂中,如果有新增了产品,则需要对工厂类的静态方法进行改动,不符合开闭原则。

  • 工厂方法模式:定义了一个创建对象的接口,但是由子类决定要实例化的类是哪一个,工厂方法把类的实例化推迟到了子类。

    应用场景:同样是生产泡面的工厂,合味道生产合味道的泡面,康师傅生产康师傅的泡面,在这里面,合味道和康师傅这两个品牌,属于抽象工厂的两个子类,他们生产的都是泡面。

    public abstract class PaoMianFactory{
        public abstract PaoMian createPaoMian();
    }
    
    public class HeWeiDao extends PaoMianFactory{
        
        @Override
        public PaoMian createPaoMian(){
            return new HeWeiDaoPaoMian();
        }
    }
    
    public class KangShiFu extends PaoMianFactory{
        
        @Override
        public PaoMian createPaoMian(){
            return new KangShiFuPaoMian();
        }
    }
    //在这里面,PaoMian也是一个抽象类
    //KangshifuPaoMian和HeWeiDaoPaoMian都是PaoMian的子类
    

    相比于简单工厂,工厂方法模式在增加了产品的情况下,不需要修改原有的代码,符合开闭原则,但是相关类的代码文件量直线上升,新增一个产品需要添加定义两个新的类,很不方便。

  • 抽象工厂模式:在工厂方法的基础上进行扩展,假设,合味道工厂除了生产泡面,还生产饮料,康师傅工厂也是一样。那么此时,根据共产方法模式,我们一共需要新增6个类(1个饮料抽象类,2个饮料子类,1个工厂抽象类,2个工厂子类),此时工厂方法的弊端就体现出来了。因此,我们在工厂方法的基础上,套用简单工厂,形成了抽象工厂模式。

    即,工厂抽象类不仅要生产泡面还要生产饮料(新增一个生产饮料的方法),此时我们就不需要再去写新的工厂类,节省了一半的代码。从康师傅的角度来看,康师傅工厂既生产了泡面,又生产了饮料,和简单工厂很相似,只是从传入不同的参数变成了调用不同的方法;从多个工厂的角度来看,和工厂方法很相似。因此,抽象工厂模式对于新增产品品牌,是开放的,对于新增产品本身,则是关闭的。

2. 代理模式:为其他对象提供一种代理以控制对这个对象的访问

代理模式结构图

在上图中,Subject是一个抽象类或者接口,RealSubject是实现方法类,实现了具体的业务执行,Proxy则是RealSubject的代理,直接和Client接触,即RealSubject不和客户接触。代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强,想要达到这个功能,代理类与被代理类应该共同实现同一个接口或者共同继承某个类,在代理类中选择调用被代理类的实现方法从而实现拓展。

  • 静态代理:以租房为例,我们一般使用租房软件、找中介或者找房东,这里的中介就是代理者,需要定义一个提供了租房方法的接口,租房的实现类,以及中介的类:

    //租房接口
    public interface IRentHouse{
        void rentHouse();
    }
    
    //租房的实现类
    public class RentHouse implements IRentHouse{
        @Override
        public void rentHouse(){
            System.out.println("租了一间房子……");
        }
    }
    
    //中介的类
    public class Proxy implements IRentHouse{
        private IRentHouse rentHouse;
        
        public IRentHouse(RentHouse rentHouse){
            this.rentHouse=rentHouse;
        }
        
        @Override
        public void rentHouse(){
            System.out.println("交中介费");
            rentHouse.rentHouse();
            System.out.println("中介负责维修管理");
        }
    }
    

    定义好之后,通过客户端调用中介类Proxy即可实现租房的功能。在静态代理中,代理与被代理的角色是固定的,如果说存在20个业务,如果要对这20个业务都实现代理,那么就需要创建20个代理角色,与之前的工厂方法模式一样存在着类似的弊端,于是就催生了动态代理。

  • 动态代理:相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法即满足生产需要的同时又达到代码通用的目的

    动态代理的两种实现方式:

    • JDK动态代理
    • CGLIB动态代理

    特点:

    • 代理目标对象不固定
    • 在应用程序执行时动态地创建目标对象
    • 代理对象会增强目标对象的行为

    本文介绍一下JDK动态代理的实现,CGLIB不谈。JDK动态代理需要实现InvocationHandler接口,此接口是JDK提供的动态代理接口,对被代理的方法提供代理。其中Invoke方法是接口InvocationHandler定义必须实现的,它完成了对真实方法的调用。

    //中介的类需要做出相应的修改
    public class InterMediaryProxy implements InvocationHandler{
        private Object obj;
        
        public InterMediaryProxy(Object object){
            this.object=object;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
            System.out.println("代理前");
            Object result = method.invoke(this.obj,args);
            System.out.println("代理后");
            return result;
        }
        
    }
    
    //相关测试代码:
    public static void main(String[] args){
        IRentHouse rentHouse = new RentHouse();
        
        //定义一个Handler
        InvocationHandler handler = new InterMediaryProxy(rentHouse);
        //获得类的class loader
        ClassLoader classLoader = rentHouse.getClass().getClassLoader();
        //动态生成一个代理者
        IRentHouse proxy = (IRentHouse) Proxy.newProxyInstance
            									(classLoader,new class[]{IRentHouse.class},handler);
        proxy.rentHouse();
    }
    

    在这里我们使用了Proxy的类方法,代码如下:

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler handler)    
    //loader:类加载器,interfaces:代码用来代理的接口,即需要被代理的接口
    //handler:一个InvocationHandler对象,即我们自定义的动态代理类
    

    JDK动态代理是针对于接口进行代理的,所以需要编写的接口类仍旧需要编写,其实现子类的相关代码也要编写,只是相比起静态代理,不需要去编写代理类的相关代码,只要拓展的操作是相同的,那么只需要使用同一个动态代理类实现代理即可。实现业务的真正方法是,动态代理类的invoke方法。

    动态代理是否要求,接口只能有一种业务?

3. Spring是什么?

Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。

4. Spring导包:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

5. 什么是IOC容器?

  • IOC容器是指在Spring中负责创建对象,管理对象(依赖注入、装配对象、配置对象并且管理这些对象的整个生命周期)的容器;

  • 其中,IOC是指控制反转,是一种设计思想。在Java开发中,传统设计思路,是由程序主动去创建依赖对象;而在控制反转的设计思想中,有一个专门的容器来创建这些对象,在这里我们简称其为IOC容器,同时,IOC容器也负责管理依赖对象的生命周期;

  • 控制反转是指创建对象的这一个过程,交给了容器来做,而程序本身只是被动地接受已经创建好的对象,相比较于传统的开发,这个过程被反转了,因此称为控制反转;

  • 有点类似于工厂方法模式,将对象创建交托给工厂来做,而程序本身只负责接收以及使用,但是这两者之间还是存在区别的,只是这样类比可能方便理解;

  • 使用IOC的好处:

    • 实现了组件之间的解耦,提高了程序的灵活性和可维护性

    使用IOC的缺点:

    • 创建对象的步骤变复杂了,不直观;
    • 使用了反射来创建对象,所以在效率上有所损耗,但对于程序的灵活性和可维护性而言,消耗所带来的损失比较小;
    • 如果修改了相关的类名,需要到XML配置文件中修改相应的设置。

6. Spring IOC怎么使用

  • 编写一个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);
        }
    }
    
  • 编写配置文件ApplicationContext.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
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="hello" class="xxx.xxx.xxx.Hello">
            <property name="name" value="Spring"></property>
        </bean>
    
    </beans>
    
  • 实例化容器并取得目标对象:

    //实例化容器
    ApplicationContext context = new 
        		ClassPathXmlApplicationContext("ApplicationContext.xml"/*,"services.xml", "daos.xml"*/);
    //这里可以一次性读取多个配置文件
    Hello hello = (Hello)context.getBean("hello");
    hello.show();
    //此时控制台就会输出HelloSpring
    
  • 在Spring的xml配置文件中:

    • bean标签代表的是对象,其中id属性代表的是变量名,class属性则是要创建对象的类名;
    • property标签相当于对象中的属性,name属性是变量名,value属性是要注入的值;
    • 如果自定义类中的某个属性也是自定义类,此时value属性是没有用的,应该使用ref属性引用某个已经被创建的bean;
    • 如果要将自定义的类交给SpringIOC管理,需要给相应的属性都设置set方法,因为在SpringIOC中默认的注入方法是通过set注入的,如果没有设置相应的方法,则会报错。

7. IOC创建对象的方式

  • 默认情况下,走无参构造方法,通过设置property和相应的set方法将属性注入;

  • 如果设置了有参构造的话,可以通过有参构造来实现对象的创建:

    • 配合索引使用:

      <bean id="xxx" class="xxx.xxx.xxx.Object">
      	<constructor-arg index="0" value="xxx" />
          <constructor-arg index="1" value="xxx" />
      </bean>
      
    • 配合类型使用:如果要使用这种方式,不允许构造方法中存在相同的类型参数

      <bean id="xxx" class="xxx.xxx.xxx.Object">
      	<constructor-arg type="int" value="xxx" />
          <constructor-arg type="String" value="xxx" />
      </bean>
      
    • 配合参数名使用:

      <bean id="xxx" class="xxx.xxx.xxx.Object">
      	<constructor-arg name="argFir" value="xxx" />
          <constructor-arg name="argSec" ref="xxx" />
          <!--这里和之前说的一样,遇到了自定义类填充的属性,使用ref属性-->
      </bean>
      
  • Spring创建的对象,默认情况下,都是唯一的,或者说,在Spring中注册的类只能创建一个实例(单例模式,可以通过设置更改);

8. Spring配置文件的标签说明:

  • bean标签:负责实例化类;

  • import标签:负责导入其他的子配置文件,通过统一管理,在客户端只需要读取总配置文件即可;

    <import resource="beans.xml" />
    
  • alias标签:为bean设置别名;

9. DI(Dependency Injection——依赖注入):bean对象的创建依赖于容器,bean对象中的所有属性由容器来注入。

Set注入的复杂对象分析:

  • List——<list />

    <property name="someList">
        <list>
        	<value>xxx</value>
            <ref bean="xxx" />
        </list>
    </property>
    
  • Set——<set />

    <property name="someSet">
    	<set>
        	<value>xxx</value>
            <ref bean="xxx" />
        </set>
    </property>
    
  • Map——<map />

    <property name="someMap">
    	<map>
        	<entry key="an entry" value="just some string"></entry>
        	<entry key="a ref" value-ref="xxx"></entry>
        </map>
    </property>
    
  • Properties——<props />

    <property name="adminEmails">
    	<prop key="adminstrastor">xxx</prop>
        <prop key="support">[emailprotected]</prop>
        <prop key="development">[emailprotected]</prop>
    </property>
    
  • Arrays——<array />

    <property name="arrays">
    	<array>
            <value>xxx</value>
            <ref bean="xxx" />
        </array>
    </property>
    
  • 如果想实现null的注入,有一个标签是<null />,使用这个标签表示空指针注入

  • p命名空间注入:如果想要使用p命名空间需要导入约束

    xmlns:p="http://www.springframework.org/schema/p"
    
    <!--使用p命名空间的情况下,会自动识别当前注入类有哪些属性,从而简化配置文件-->
    <!--假设类User有属性name,age,gender,可以通过以下方式实现注入-->
    <bean id="user" class="xxx.xxx.xxx.User" p:name="张三" p:age="18" p:gender=""></bean>
    <!--对于简单的属性可以使用p命名空间注入,p--》property,p命名空间也可以实现引用-->
    
  • c命名空间注入:如果想要使用c命名空间需要导入约束(constructor)

    xmlns:c="http://www.springframework.org/schema/c"
    
    <bean id="user" class="xxx.xxx.xxx.User" c:name-ref="beanName" c:age-ref="beanAge" c:gender-ref="beanGender"></bean>
    <!--这里面都是使用引用的,如果是简单类型的可以直接注入String,即不用加上-ref-->
    <!--c命名空间和p命名空间的区别在于,c命名空间是通过构造器注入的,p命名空间是通过Set注入的-->
    

Bean的作用域:scope属性——singleton,prototype,request,session,application,websocket

  • singleton(默认作用域):同一个类型创建的bean对象是一样的(即同一块地址)
  • prototype(原型模式):即使是同一个bean对象,每一个getBean都会分配一个新的,即产生新的独立的对象
  • 其余的在Web开发中使用

10. Bean的自动装配:我们先假设一个环境,分别有三个类:People,Cat,Dog

public class People{
    private String name;
    private Dog dog;
    private Cat cat;
    
    //省略GetterAndSetter
}

public class Cat{
    public void shout(){
		System.out.println("miao~");
    }
}

public class Dog{
    public void shout(){
        System.out.println("wang!");
    }
}

相应的bean配置文件如下:

<bean id="dog" class="xxx.xxx.xxx.Dog"></bean>
<bean id="cat" class="xxx.xxx.xxx.Cat"></bean>

<bean id="people" class="xxx.xxx.xxx.People">
	<property name="name" value="张三" />
    <property name="cat" ref="cat" />
    <property name="dog" ref="dog" />
</bean>

如上所示,虽然cat和dog我们在配置文件的前面已经显式地声明过了,但是在装配People类的bean的时候,我们还是需要再配置一遍。然而通过使用autowire属性我们可以省略掉重复的配置:

<bean id="dog" class="xxx.xxx.xxx.Dog"></bean>
<bean id="cat" class="xxx.xxx.xxx.Cat"></bean>

<bean id="people" class="xxx.xxx.xxx.People" autowire="byName">
	<property name="name" value="张三" />
</bean>

<!--
使用autowire="byName",Spring会自动查找并进行配置,但是有一个限制条件,
就是已经配置好的bean的id属性必须和自定义类中的相应的set方法后面的名字保持一致,否则会报错
-->

<!--
autowire="byType"则会自动检查类型然后进行适配,但是如果在装配类中有多个相同类型的属性字段,
即People中有多个Dog属性,或者已经定义的bean中有多个属于Dog类的bean,
那么装配会失败,Spring会报错
-->

11. 在Java中使用注解实现自动装配:

  • 导入支持:在相应的配置文件下导入对注解的支持

    <beans xmlns="……………………………………"
           xmlns:xsi="……………………………………"
           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 />
    </beans>
    
  • 在需要实现自动装配的字段上添加注解:

    public class People{
        @Autowired
        private Cat cat;
        //使用了注解自动配置的情况下,甚至可以不用设置set方法
        //因为注解的底层是通过反射实现的,也可以在set方法上用注解,效果一样
        //但是使用注解的情况需要保证bean的id与属性名保持一致
        //或者类型唯一,将byName,byType的索引方式融合了
        @Autowired
        @Qualifier(value="beanid")
        private Dog dog;
        //在byName匹配不上且byType类型不唯一时,可以通过添加@Qualifier注解的方式
        //指定目标bean的id,以此实现自动装配
        private String name;
        
        
        //java自带的注解@Resource也可以实现和@Autowired一样的效果
        //在注解@Resource中,也有指定beanid的属性,@Resource(name="beanid")
        //@Autowired先byType在byName,@Resource则相反,且@Resource没有提示
    }
    

12. Spring注解开发:

  • 导入AOP的包(因为注解的后台实现用到了AOP编程),这里在webmvc中的包已经包含了AOP的包;

  • 增加约束:

    <beans xmlns="……………………………………"
           xmlns:xsi="……………………………………"
           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 />
    </beans>
    
  • 指定要自动扫描的包,这个包下的注解就会生效:

    <beans ……………………………………>
    	……………………………………
        <context:component-scan base-package"xxx.xxx.xxx" />
    </beans>
    
  • 注解类型:

    • @Component:在Java代码层面将这个类注册到Spring容器中,默认beanid是类名小写

      @Component("user")
      //beanid也可以通过这种方式显式声明
      public class User{
          ……………………
      }
      

      @Component衍生注解:(效果一样只是为了区分SpringMVC中的三层架构)

      • Mapper:@Repository
      • Service:@Service
      • Controller:@Controller
    • @Value:在属性字段为属性赋值,也可以设置在set方法上,效果是一样的

      @Component
      public class User{
          @Value("张三")
          public String name;
          
          @Value("张三")
          public void setName(String name){
              this.name=name;
          }
      }
      
    • @Autowired:上文已经描述

    • @Scope:设置bean的作用域

      @Component
      @Scope("singleton")
      public class User{
          ……………………………………
      }
      

13. 使用JavaConfig实现配置:前面讲的都是如何使用注解完成对于bean在Spring中的注册,以及相关的属性注入。而在Spring中,也可以实现自定义类搭配注解替代配置文件,具体代码如下:

@Component("user")
public class User{
    @Value("张三")
    private String name;
}
@Configuration
@ComponentScan("xxx.xxx.pojo")
//自动扫描pojo包下的注解
@Import(xxx.class)
//导入另外一个配置文件
public class JavaConfig{
    
    @Bean
    public User user(){
        return new User();
    }
    //注册一个bean,相当于我们的bean标签
    //方法名字相当于beanid
    //方法返回值相当bean中的class属性
}
//这里一开始参考资料是没有加上自动扫描的注解的,然后方法名用的是getUser
//测试的时候也是用的getUser,试过getBean传参数"user"但是无法获取
//个人认为这里面因为没有加上自动扫描的注解,所以一开始原先那个类没有注册到Spring中,或者说自定义的JavaConfig中
//后面虽然加上了自动扫描的注解但是方法名也改成了user,所以无法检测到Sping中是有一个bean还是两个bean
//建议自己敲一遍代码确认一下
public void static main(String[] args){
    ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
    //这里因为是使用了注解,所以上下文获取要用AnnotationConfigApplicationContext来获取
    User user = (User)context.getBean("user");
    System.out.println(user.getName());
}

14. AOP:面向切面编程,核心是动态代理。使用AOP织入,需要导入一个依赖包:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

同时在相应的配置文件中写好约束:

<beans xmlns="……………………………………"
       xmlns:xsi="……………………………………"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="……………………………………
        http://www.springframework.org/schema/aop   
        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
	……………………………………
</beans>

实现AOP的方式:

  • 使用Spring的API接口:自定义多个类分别实现自己想要的前置功能和后置功能,然后在Spring中注册,最后使用aop标签进行包装;

    public interface UserService{
        public void add();
        
        public void delete();
    }
    
    public class UserServiceImpl implements UserService{
        public void add(){
            ……………………………………
        }
        
        public void delete(){
            ……………………………………
        }
    }
    
    //自定义的类实现SpringAOP的相关接口
    public class Log implements MethodBeforeAdvice{
        //实现before方法
        public void before(Method method, Object[] args, Object target)throws Throwable{
            //method——被切入的方法
            //args——动态执行时传入的参数
            //target——执行方法的对象
            System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");
        }
    }
    //这里的MethodBeforeAdvice应该是指方法被执行前
    //而BeforeAdvice应该是已经进入了方法但是方法体执行前
    
    public class AfterLog implements AfterReturningAdvice{
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target)throws Throwable{
            System.out.println(………………………………);
            //这里比前面的方法多了一个参数,returnValue,是方法调用后的返回参数
        }
    }
    
    <bean id="userServiceImpl" class="xxx.xxx.xxx.UserServiceImpl" />
    <bean id="log" class="xxx.xxx.xxx.Log" />
    <bean id="afterLog" class="xxx.xxx.xxx.AafterLog" />
    
    <aop:config>
        <!--设置切入点-->
        <aop:pointcut id="pointcut" expression="execution(* xxx.xxx.service.UserServiceImpl.*(..))"></aop:pointcut>
        
        <!--绑定切入点与切入内容-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut" />
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut" />
        <!--这里的切入内容放置顺序,由实现类的实现接口决定-->
    </aop:config>
    

    这里面的execution表达式有以下语法格式:

    execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)

    • 除了返回类型模式,方法名模式和参数模式,其他两个可要可不要。而方法名模式中,需要写清楚该方法属于哪个包下的哪个类。

    • "…"在方法名模式下表示当前包及其子包,在参数模式下表示任意数量任意参数

    • "*"在方法名模式下表示包下的类名,或者任意前缀后缀,或者方法名,及其前缀后缀

      ​ 在参数名模式下表示一个任意参数

    • 上文的execution表达式表示匹配xxx.xxx.service包下的UserServiceImpl类的所有方法

    测试代码:

    ApplicationContext context = new ClassPathXmlApplicationContext("applicationConfig.xml");
    UserService userService = (UserService)context.getBean("userServiceImpl");
    //这里使用接口来接收对象,因为动态代理的对象是接口
    userService.add();
    
  • 自定义类实现:约束没有上一种那么强

    public class DiyPointCut{
        public void before(){
            ……………………………………
        }
        
        public void after(){
            ……………………………………
        }
    }
    
    <bean id="diy" class="xxx.xxx.xxx.DiyPointCut"></bean>
    
    <aop:config>
        <!--切面-->
    	<aop:aspct ref="diy">
            <aop:pointcut id="pointcut" expression="execution(* xxx.xxx.service.UserServiceImpl.*(..))" />
            <aop:before method="before" pointcut-ref="pointcut" />
            <aop:after method="after" pointcut-ref="pointcut" />
        </aop:aspct>
    </aop:config>
    
  • 自定义的类,功能没有SpringAPI强大,从SpringAPI中的方法重写会传递一堆与方法有关的参数就可以看出来。

  • 注解实现AOP:

    • 在配置文件中导入AOP注解支持

      <aop:aspectj-autoproxy />
      
    • 实现自定义类:这里的自定义类,资料中作者也把它一起注册到Spring中,但我觉得应该不用注册,没有实际敲代码测试

      @Aspect				//表示这个类是一个切面
      public class AnnotationPointCut{
          @Before("execution(* xxx.xxx.service.UserServiceImpl.*(..))")
          public void before(){
              ……………………………………
          }
          
          @After("execution(* xxx.xxx.service.UserServiceImpl.*(..))")
          public void after(){
              ……………………………………
          }
          
          @Around("execution(* xxx.xxx.service.UserServiceImpl.*(..))")
          public void around(ProceedingJoinPoint joinPoint){
              ……………………………………
              //这里的joinPoint是连接点的意思
              //这一句代码的作用是让原先的方法正常执行
              joinPoint.proceed();
              ……………………………………
          }
      }
      

15. 整合MyBatis

  • 导入依赖:

    <dependency>
    	<groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
    
  • 配置DataSource和SqlSessionFactory(将数据源和SqlSession创建工厂交给Spring)

    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    	<property name="driverClassName" value="…………………………" />
        <property name="url" value="………………………………" />
        <property name="username" value="………………………………" />
        <property name="password" value="………………………………" />
    </bean>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    	<property name="dataSource" ref="datasource" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <!--也可以直接在这里绑定Mappers映射-->
        <property name="mapperLocations" value="classpath:xxx/xxx/mapper/*.xml" />
    </bean>
    
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    	<!--SqlSessionFactory注册后则可以完成SqlSession的注册-->
        <!--SqlSessionTemplate与SqlSession是一样的,某种程度上比SqlSession还强大(线程安全)-->
        <!--在源代码中,没有设置相应的set方法,这里只能通过构造器注入-->
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>
    
  • 为什么整合MyBatis了,SqlSession注册到了Spring中,反而要实现Dao层的接口了?直接从Spring中获取SqlSession,然后使用getMapper方法获得目标对象调用其方法不可以吗?

    官方文档的建议使用方法:

    public class UserMapperImpl implements UserMapper{
        private SqlSessionTemplate sqlSession;
        
        public void setSqlSession(SqlSessionTemplate sqlSession){
            this.sqlSession = sqlSession;
        }
        
        public List<User> getUser(){
            UserMapper mapper=sqlSession.getMapper(UserMapper.class);
            return mapper.selectUser();
        }
    }
    
    <bean id="userMapperImpl" class="xxx.xxx.xxx.UserMapperImpl">
    	<property name="sqlSession" ref="sqlSession" />
    </bean>
    
  • 整合MyBatis的第二种方式:使用自定义类继承SqlSessionDaoSupport同时实现:

    public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
        public List<User> getUser(){
            UserMapper mapper = getSession().getMapper(UserMapper.class);
            return mapper.selectUser();
        }
    }
    //这个SqlSessionDaoSupport内嵌了SqlSession,所以不需要再在自定义类定义SqlSession
    //因为SqlSessionDaoSupport的SqlSession是通过SqlSessionFactory来初始化的
    //所以这里在注册bean的时候要给自定义类注入SqlSessionFactory
    
    <bean id="userMapperImpl" class="xxx.xxx.xxx.UserMapperImpl">
    	<property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
    

16. 声明式事务:MyBatis—Spring允许MyBatis参与到Spring的事务管理中,直接借助Spring的DataSourceTranscationManager实现事务管理,而不用给MyBatis再去创建一个新的专用事务管理器,核心是SpringAOP。(优点在于,只需要关注逻辑代码的生成,而不用关注事务的自动提交关闭再打开这种重复性代码)

```xml
<beans xmlns="……………………………………"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="……………………………………
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">

<bean id="transactionManager" class="org.springframeword.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
</bean>

<!--结合AOP实现自定义事务管理-->
<!--这里的tx标签是需要引入约束的-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<!--配置相关参数,选择给哪些方法做事务管理-->
    <tx:attributes>
    	<tx:method name="add" propagation="REQUIRED" />
        <!--propagation默认情况下就是REQUIRED-->
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

<!--配置事务切入-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* xxx.xxx.xxx.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
<!--通过上面的配置就可以完成声明式事务管理,主要是为了预防在一次Dao连接中的两次操作不全生效的问题-->
```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值