Spring框架的全面详解(个人学习总结)

1、什么是Spring框架

Spring框架是一个容器,它是用来整合其他框架的框架。

它的核心是IOC(控制反转)和AOP(面向切面编程)。

它是一个大的家族,由20多个模块构成,在很多领域都提供了非常优秀的支持。

它的最终目标其实就是解耦合。

2、Spring框架的优点

(1)轻量级

它的核心jar包很小,总共3M多,对代码零污染。

(2)面向接口编程

Spring底层是面向接口的开发.
面向接口开发的规范:
    1.类中的成员变量设计为接口
    2.方法的参数设计为接口
    3.方法的返回值设计为接口
    4.调用时接口指向实现类

总结一句话,上接口就是为了更加灵活。

(3)AOP面向切面编程

1.切面就是程序中那些公共的,通用的,重复的功能。例如 :日志,事务,权限验证等。
2.面向切面编程AOP就是将切面单独拿出来开发,在需要的业务功能上自动反织回去。

(4)方便的整合其它框架

3、什么是IOC(控制反转)

控制反转IOC(Inversion of Control)它是一种概念,同时也是一种思想,就是让Spring容器干活的思想。

问题:
1.控制什么?

是创建对象和依赖注入的控制权

2.反转什么?

是由程序员控制权反转给Spring容器控制权

正转:由程序员创建对象和依赖注入

Student s = new Student();      //程序员在创建对象
s.setName("吃切糕的斯派克");   // 程序员依赖注入值
s.setAge(25);                  // 程序员依赖注入值

反转:Spring容器创建对象和依赖注入

 <bean id="stu" class="com.spike.pojo.Student">       ===>Spring容器在创建对象
		<property  name="name" value="吃切糕的斯派克"></property>  ===>Spring容器依赖注入值
		<property  name="age"  value="25"></property>  ===>Spring容器依赖注入值
 </bean>

4、Spring的IOC实现

主要有两种方式

1.基于xml的实现形式
2.基于注解的实现形式

5、基于xml的IOC的实现

先在pom.xml文件中指定 Java 编译版本,可以是jdk8,jdk11或者jdk17,本处采用jdk17。

<properties>
     <maven.compiler.source>17</maven.compiler.source>
     <maven.compiler.target>17</maven.compiler.target>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
(1)创建对象

注意点:1.一定要有无参的构造方法

              2.所有对象的创建和依赖注入是在容器启动时发生

              3.使用<bean>标签创建对象

代码示例

<!--创建Student对象
    参数:
        id:创建的对象的名称
        class:创建的对象的类型
-->
<bean id="stu"  class="com.spike.pojo.Student"></bean>
(2)依赖注入
1.按注入值分为简单类型注入和引用类型注入

简单类型(8种基本类型(封装类型)+String)使用value注入值。
引用类型使用ref注入值。

2.按注入的方式分为setter注入和构造方法注入
A.   setter注入法

注意点:一定要有setXXX()方法
              使用<property>标签赋值

代码示例

 <!--创建School对象-->
	<bean id="school" class="com.spike.pojo.School">
		  <property name="name" value="天津大学"></property>
		  <property name="address" value="天津市南开区"></property>
	</bean>
<!--创建Student-->
	<bean id="stu"  class="com.spike.pojo.Student">
		  <property name="name" value="吃切糕的斯派克"></property>
		  <property name="age" value="25"></property>
		  <property name="school" ref="school"></property>   ===>引用类型注入值
	</bean>
B.  构造方法注入

使用<constructor-arg>标签赋值。

a.使用构造方法参数名称注入值

<!--创建School对象,使用构造方法参数名称注入值
    public School(String name1, String address1) {...}   //构造方法
-->
<bean id="school" class="com.spike.pojo.School">
      <constructor-arg name="name1"  value="天津大学"></constructor-arg>
	  <constructor-arg name="address1"  value="天津市南开区"></constructor-arg>
 </bean>

b.使用构造方法参数下标注入值

<!--创建Student,使用构造方法参数下标注入值,默认下标从0开始
		        public Student(String name, int age, School school){...}
-->
<bean id="stuIndex" class="com.spike.pojo.Student">
	  <constructor-arg index="1" value="25"></constructor-arg>
	  <constructor-arg index="0" value="吃切糕的斯派克"></constructor-arg>
	  <constructor-arg index="2" ref="school"></constructor-arg>
</bean>

c.使用构造方法参数默认顺序注入值

<!--创建Student,使用构造方法参数默认顺序注入值
	  public Student(String name, int age, School school){...}
-->
<bean id="stuSequence" class="com.spike.pojo.Student">
	  <constructor-arg  value="吃切糕的斯派克"></constructor-arg>
	  <constructor-arg  value="25"></constructor-arg>
	  <constructor-arg  ref="school"></constructor-arg>
</bean>

6、创建系统类型的对象

Spring的IOC的功能就是创建对象并依赖注入值,并不在乎是系统类型还是自定义类型。

<!--例如创建Date类型的对象-->
<bean id="myDate"  class="java.util.Date">
      <property name="time"  value="1234567891011"></property>
</bean>
<!--注意setTime()方法不一定给time的成员变量赋值,只是调用setTime()的方法-->

7、Spring创建对象的作用域

Spring创建对象默认的作用域范围是单例的(singleton)。可以设置为非单例(prototype)。

1.默认单例(因为创建的过程很复杂):
<bean id="myDate"  class="java.util.Date" scope="singleton"></bean>
<bean id="myDate"  class="java.util.Date" ></bean>                   
<!--每次取都返回同一个第一次创建好的对象-->
2.设置为非单例
<bean id="myDate"  class="java.util.Date" scope="prototype"></bean>
<!--每次取都返回一个新创建的对象-->

8、引用类型的属性自动注入

前提:引用类型才可以实现自动注入。
           自动注入有两种方式:按类型自动注入 ,按名称自动注入。

<bean id="school" class="com.bjpowernode.s02.School">
      <property name="name" value="天津大学"></property>
      <property name="address" value="天津市南开区"></property>
</bean>
<!--创建Student-->

<!--按名称自动注入-->
<bean id="stu"  class="com.spike.pojo.Student" autowire="byName"> 
      <property name="name" value="吃切糕的斯派克"></property>
      <property name="age" value="25"></property>
 </bean>

<!--按类型自动注入-->
<bean id="stu"  class="com.spike.pojo.Student" autowire="byType"> 
      <property name="name" value="吃切糕的斯派克"></property>
      <property name="age" value="25"></property>
</bean>

 9、基于注解的IOC实现

依赖注入:DI(Dependency Injection)是IOC的实现技术,所以说DI就是IOC。两种不同的说法而已,就是Spring去创建对象并依赖注入。

(1)创建对象的注解

    @Comconpent:创建任意对象,默认对象的名称是类名的驼峰命名法,也可以自定义对象名称
    @Controller:专门用于创建界面层的对象
    @Service:专门用来创建业务逻辑层的对象
    @Repository:专门用来数据访问层的对象

(2)依赖注入的注解

    @Value:简单类型注入
    @Autowired:引用类型按类型注入

    注意:

    @Autowired
    @Qualifier("名称")

    俩注解一起用是引用类型的按名称注入

代码示例

@Component
public class Student {
	    @Value("吃切糕的斯派克")
	    private String  name;
	    @Value("25")
	    private int  age;
	    @Autowired            //===>单独使用是按类型注入 
	    @Qualifier("school")  //===>俩注解一起使用是按名称注入
	    private School school;
}

切记!!!!一定要在applicationContext.xml配置文件中添加包扫描。

<context:component-scan base-package="com.spike.pojo"></context:component-scan>
(3)同源类型

 1.被注入的对象的类型(Student中的School对象)与注入的对象(Bean工厂中创建好的对象)的类型完全一致。
 2.被注入的对象的类型(Student中的School对象--父)与注入的对象(Bean工厂中创建好的对象--子)的类型是父子类类型。
               按类型注入时如果有多个可被注入的对象,再次按名称进行筛选注入
               按名称注入时如果有多个可被注入的对象,直接按名称指定注入
 3.被注入的对象的类型(Student中的School对象--接口)与注入的对象(Bean工厂中创建好的对象--实现类)的类型是接口和实现类。

10、添加包扫描的方式

(1)单个包扫描(推荐使用)
<context:component-scan base-package="com.spike.mapper"></context:component-scan>
<context:component-scan base-package="com.spike.service.impl"></context:component-scan>
<context:component-scan base-package="com.spike.controller"></context:component-scan>
(2)多个包扫描,多个包之间以逗号或分号或空格分隔
<context:component-scan base-package="com.spike.mapper;com.spike.service.impl,com.spike.controller">
(3)扫描根包(不推荐使用,因为多扫无用包,降低容器启动效率)
<context:component-scan base-package="com.spike"></context:component-scan>

11、Spring配置文件的拆分

因为项目越来越大,多人协作开发,要进行配置文件拆分。

(1)按层拆(推荐使用)
<!--applicationContext_mapper.xml-->
    <bean id="uMapper" class="com.spike.mapper.UsersMapperImpl"></bean>
    <bean id="bMapper" class="com.spike.mapper.BookMapperImpl"></bean>
<!--applicationContext_service.xml-->
    <bean id="uService" class="com.spike.service.impl.UsersServiceImpl">
	      <property name="usersMapper" ref="uMapper"></property>
	</bean>
	<bean id="bService" class="com.spike.service.impl.BookServiceImpl">
	      <property name="bookMapper" ref="bMapper"></property>
	</bean>
<!--applicationContext_controller.xml-->
	<bean id="usersController" class="com.spike.controller.UsersController">
	      <property name="usersService" ref="uService"></property>
	</bean>
	<bean id="bookController" class="com.spike.controller.BookController">
	      <property name="bookService" ref="bService"></property>
	</bean>
(2)按功能拆
<!--applicationContext_users.xml-->
    <bean id="uMapper" class="com.spike.mapper.UsersMapperImpl"></bean>
    <bean id="uService" class="com.spike.service.impl.UsersServiceImpl">
	      <property name="usersMapper" ref="uMapper"></property>
	</bean>
	<bean id="usersController" class="com.spike.controller.UsersController">
	      <property name="usersService" ref="uService"></property>
	</bean>
<!--applicationContext_book.xml-->
    <bean id="bMapper" class="com.spike.mapper.BookMapperImpl"></bean>
  	<bean id="bService" class="com.spike.service.impl.BookServiceImpl">
	      <property name="bookMapper" ref="bMapper"></property>
	</bean>
	<bean id="bookController" class="com.spike.controller.BookController">
	      <property name="bookService" ref="bService"></property>
	</bean>

12、配置文件的整合

(1)单个文件导入
<import resource="applicationContext_mapper.xml">
<import resource="applicationContext_service.xml">
<import resource="applicationContext_controller.xml">
(2)批量导入
<import resource="applicationContext_*.xml">

13、IOC实现之基于xml与注解的区别

注解优点:
                     方便
                     直观
                     高效(代码少,没有配置文件的书写那么复杂)。
注解弊端:   以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。


XML方式优点:
                         配置和代码是分离的。
                         在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
XML方式缺点:编写麻烦,效率低,大型项目过于复杂。

14、什么是AOP

AOP(Aspect Orient Programming),面向切面编程。
切面就是程序中那些公共的通用的,重复的代码和功能称为切面。例如:日志,事务 ,权限等。
面向切面编程:将切面单拎出来开发,在需要的方法中自动反织回去.
通过运行期动态代理实现程序功能的统一维护的一种技术。
面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。提高程序的可重用性,同时提高了开发的效率。

15、手写AOP框架

目的:完成业务和切面功能的解耦合
业务:图书或商品购买业务功能
切面:事务或日志或权限

代理设计模式是23种设计模式之一。目标对象不可访问,通过代理对象增强功能访问就是代理设计模式。
代理模式分为静态代理和动态代理。动态代理分为JDK动态代理和CGLib动态代理。

静态代理的设计规范:
                                (1)必须有业务接口
                                (2)代理对象和目标(业务)对象实现同一个业务接口
                                (3)静态代理对象在程序运行前就已经存在
                                (4)静态代理负责业务和切面的整合

JDK动态代理的设计规范:
                                (1)必须有业务接口
                                (2)动态代理对象在程序运行时动态的在内存中构建
                                (3)动态代理通过方法传参进行业务和切面的整合
                                (4)动态代理对象不能代理业务接口外的方法

JDK动态代理涉及到的类和接口
                                (1)Proxy类   java.lang.reflect包下的类
                                         用来生成动态代理对象
                                        public static Object newProxyInstance(ClassLoader loader,
                                                                                                      Class<?>[] interfaces,
                                                                                                      InvocationHandler h)
                                (2)InvocationHandler接口
                                         代理处理器,用来进行切面和业务整合
                     public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
                                (3)Method类
                                         用来传递测试类中正在调用的目标方法buy(),one()
                                         method.invoke();  ===>进行手工调用目标方法

代码示例       手写AOP-------->循序渐进

第一个版本:业务和切面耦合在一起

/**
 * 第一个版本:业务和切面耦合在一起
 */
public class BookServiceImpl {
    public void buy(){
        try {
            //切面
            System.out.println("事务开启............");
            //业务
            System.out.println("图书购买业务功能实现 ...........");
            //切面
            System.out.println("事务提交............");
        } catch (Exception e) {
            System.out.println("事务回滚...........");
        }
    }
}

第二个版本:使用静态子类代理拆分业务和切面

/**
 * 第二个版本:通过子类实现切面功能,父类只做业务实现
 */
public class BookServiceImpl {
    public void buy(){
        //只完成主业务功能
        System.out.println("图书购买功能实现..............");
    }
}
/**
 * 第二个版本:子类完成切面功能(事务)的处理,整合父类的业务功能
 */
public class SubBookServiceImpl extends BookServiceImpl {
    @Override
    public void buy() {
       //子类中提供事务的切面
        try {
            System.out.println("事务开启...........");
            super.buy();//父类中主业务功能实现
            System.out.println("事务提交...........");
        } catch (Exception e) {
            System.out.println("事务回滚...........");
        }
    }
}

 第三个版本:使用静态代理,完成业务和切面的解耦合

/**
 * 第三个版本:接口规定业务功能
 */
public interface Service {
    //购买的业务
    void buy();
}
public class BookServiceImpl implements Service {
    @Override
    public void buy() {
        System.out.println("图书购买业务功能实现 ..............");
    }
}
public class ProductServiceImpl implements Service {
    @Override
    public void buy() {
        System.out.println("商品业务功能实现.........");
    }
}
/**
 * 第三个版本的静态代理类,在本类中完成业务和切面的整合
 * 灵活目标对象的切换
 */
public class Agent implements Service{
    //类中的成员变量设计为接口
    Service target;
    //通过构造方法传入目标对象,方法的参数设计为接口
    public Agent(Service target){
        this.target = target;
    }
    @Override
    public void buy() {
        try {
            //事务切面
            System.out.println("事务开启.............");
            //调用目标对象的业务功能
//            BookServiceImpl bookService = new BookServiceImpl();
//            bookService.buy();
//            ProductServiceImpl productService = new ProductServiceImpl();
//            productService.buy();
            target.buy();//图书对象来了,就是图书功能实现 ,商品对象来了,就是商品功能实现
            //事务切面
            System.out.println("事务提交............");
        } catch (Exception e) {
            System.out.println("事务回滚............");
        }
    }
}
@Test
 public void test02(){
    //调用时接口指向实现类
    Service agent = new Agent(new ProductServiceImpl());
    agent.buy();

}

  第四个版本   使用静态代理,以面向接口的方式进行业务和切面的各种拆分整合

/**
 * 切面接口定义
 */
public interface AOP {
    //JDK8以后的新特性,默认接口实现default
   default void before(){}
   default void after(){}
   default void exception(){}
}
/**
 * 事务的切面实现
 */
public class TransAop implements AOP {
    @Override
    public void before() {
        System.out.println("事务开启............");
    }
    @Override
    public void after() {
        System.out.println("事务提交............");
    }
    @Override
    public void exception() {
        System.out.println("事务回滚............");
    }
}
/**
 * 日志的切面实现 
 */
public class LogAop implements AOP {
    @Override
    public void before() {
        System.out.println("前置日志输出...........");
    }
}
/**
 * 静态代理对象,完成业务和切面的整合
 * 通过接口灵活的进行业务功能切换和切面功能切换
 */
public class Agent implements Service {
    //成员变量设计为接口(面向接口编程)
    Service target;  //灵活切换目标对象
    AOP aop;         //灵活切换切面对象

    //构造方法传入参数(方法的参数设计为接口--->面向接口编程)
    public Agent(Service target,AOP aop){
        this.target = target;
        this.aop = aop;
    }
    @Override
    public void buy() {
        try {
            //切面
            aop.before();  //不同的切面功能
            //业务
            target.buy();  //不同的业务功能
            //切面
            aop.after();
        } catch (Exception e) {
            aop.exception();
        }
    }
}
@Test
 public void test02(){
    //通过静态代理对象完成功能调用
    Service agent = new Agent(new ProductServiceImpl(),new LogAop());
    agent.buy();
}

  第五个版本  使用动态代理,解耦合业务和切面

 public class ProxyFactory {
	    public static Object getAgent(Service yewu,AOP aop){
	        return Proxy.newProxyInstance(
	                yewu.getClass().getClassLoader(), // ClassLoader loader,类加载器,将目标对象加载进JVM被解析执行
	                yewu.getClass().getInterfaces(), //Class<?>[] interfaces, 目标对象实现的所有业务接口
	                // InvocationHandler h 代理处理器,进行业务和切面整合
	                new InvocationHandler() {
	                    @Override
	                    public Object invoke(
	                            //生成的动态代理对象(本次没用)
	                            Object proxy,
	                            //外部测试类中正在调用的目标方法通过method对象传进来,可以手工在此调用  method.invoke();===>buy()
	                            Method method,
	                            //目标方法的参数
	                            Object[] args) throws Throwable {
	                            //进行业务和切面整合
	                        Object obj = null;
	                        try {
	                            //切面
	                            aop.before();
	                            //业务方法  buy()  通过反射调用目标方法  method.invoke(yewu,args)===> buy();
	                         obj = method.invoke(yewu,args);
	                            //切面
	                            aop.after();
	                        } catch (Exception e) {
	                            //切面
	                            aop.exception();
	                        }

	                        return obj;  //目标方法的返回值
	                    }
	                }
	        );
	    }
	}

16、Spring原生AOP的实现

Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice; 
After通知:在目标方法被调用后调用,涉及接口org.springframework.aop.AfterReturningAdvice; 
Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.ThrowsAdvice; 
Around通知:拦截对目标方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor;

17、AspectJ框架

它是专门针对AOP的非常优秀的框架,易学易用。
它是基于java语言开发的,可以无缝扩展功能。
AspetJ 是 Eclipse 的开源项目。

(1)AspectJ常见通知类型

(1)@Before:前置通知
(2)@AfterReturning:后置通知
(3)@Around:环绕通知
(4)@After:最终通知
(5)@Pointcut:给切入点表达式起别名

(2)AspectJ 的切入点表达式(重点

用来指定切入切面的位置
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
简化版公式:
execution(方法返回值 方法声明(参数))
常见符号解析:
    *    代表任意字符(通配符)
    ..   出现在方法的参数中,代表任意参数
         出现在路径中,代表当前路径及其子路径及其所有的类


  代码示例

execution(public * *(..))                       //所有公共访问权限的方法
execution(* set*(..))                          //以set开头的任意方法
execution(* com.spike.service.impl.*.*(..))    //com.spike.service.impl下的所有类中的所有方法
execution(* com.spike.service..*.*(..))        //com.spike.service及其子包和所有类
execution(* *..service.*.*(..))               //以service结尾的包,前面可以有多级包
execution(* *.service.*.*(..))               //service前面只能有一级包
(3)@Before前置通知 

    在目标方法前切入切面功能,不能影响目标方法的返回值。

@Aspect  //交给AspectJ框架去处理切面功能
@Component
public class MyAspect {
	    /**
	     * 实现切面功能要依靠切面方法
	     * 前置通知切面方法的规范:
	     * 1)访问权限是public
	     * 2)切面方法没有返回值void
	     * 3)切面方法名称自定义
	     * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
	     * 5)使用@Before注解声明是前置通知
	     *   参数:
	     *      value:指定切入点表达式
	     *   目标方法:
	     *      public String doSome(String name, int age)
	     */
	    // @Before(value = "execution(public String com.spike.service.SomeServiceImpl.doSome(String,int))")
	    //@Before(value = "execution(public String com.spike.service.SomeServiceImpl.*(String,int))")
	    //@Before(value = "execution(public String com.spike.service.SomeServiceImpl.*(..))")
	    //@Before(value = "execution(public * com.spike.service.SomeServiceImpl.*(..))")
	    //@Before(value = "execution(public * com.spike.service.*.*(..))")
	    //@Before(value = "execution(public * com.spike.service..*.*(..))")
	    @Before(value = "execution(public * *..service..*.*(..))")
	    public void myBefore(){
	        System.out.println("前置通知功能实现");
	 }
}
(4)@AfterReturning后置通知

    在目标方法执行后切入切面功能,在切面方法中可以得到目标方法的返回值。
    目标方法的返回值是通过切面方法的参数传入,所以受传参的值传递和引用传递的影响,值传递      (String+8种基本类型)不能影响目标方法的返回值,但引用传递可以改变目标方法的返回值。

@Aspect
@Component
public class MyAspect {
	    /**
	     * 后置通知切面方法的规范
	     * 1)访问权限是public
	     * 2)切面方法没有返回值void
	     * 3)切面方法名称自定义
	     * 4)切面方法可以没有参数,如果有参数就是目标方法的返回值
	     * 5)使用@AfterReturning注解声明是后置通知
	     *   参数
	     *      value:指定切入点表达式
	     *      returning:指定返回的目标方法的返回值的名称,此名称与切面方法参数名称一致
	     */
	    @AfterReturning(value = "execution(* com.spike.service.*.*(..))",returning ="obj" )
	    public void myAfterReturning(Object obj){
	        //先判断是否有返回值
	        if(obj != null){
	            //再判断是否是String类型
	            if(obj instanceof  String){
	                String s = (String) obj;
	                System.out.println("在切面方法中目标方法的返回值是:"+s.toUpperCase());
	            }
	            //再判断是否是Student类型
	            if(obj instanceof Student){
	                Student stu = (Student) obj;
	                stu.setName("吃切糕的斯派克");
	                System.out.println("在切面方法中目标方法的返回值是:"+stu);
	            }
	        }
	        System.out.println("后置通知功能实现");
	    }
	}
(5)@Around环绕通知

  是通过拦截目标方法,在其前后切入切面功能,它是功能最强大的通知,事务就是使用的环绕通知
  在切面方法中可以随意改变目标方法的返回值。
  切面方法的参数就是目标方法本身。
  切面方法的返回值就是目标方法的返回值。
  切面方法中可以控制目标方法的访问。

//需求:根据目标方法的第一个参数是吃切糕的斯派克,则可访问目标方法,否则拒绝访问目标方法.
@Aspect
@Component
public class MyAspect {
	    /**
	     * 环绕通知切面方法规范
	     * 1)访问权限是public
	     * 2)切面方法有返回值,其返回值就是目标方法的返回值
	     * 3)切面方法名称自定义
	     * 4)切面 法有参数,参数就是目标方法本身
	     * 5)要回避异常Throwable
	     * 6)使用@Around注解声明是环绕通知
	     * 参数:
	     * value:指定切入点表达式
	     */
	    @Around(value = "execution(* com.spike.service.*.*(..))")
	    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
	        //取出目标方法的参数
	        Object[] params = pjp.getArgs();
	        if (params.length > 1) {
	            String name = (String) params[0];
	            if ("吃切糕的斯派克".equals(name)) {
	                //前切功能
	                System.out.println("环绕通知中的前置功能");
	                //调用目标方法
	                Object obj = pjp.proceed();
	                //后切功能
	                System.out.println("环绕通知中的后置功能");
	                return obj.toString().toUpperCase();
	            }
	        }
	        System.out.println("您无权访问目标方法");
	        return null;
	    }
	}
(6)@After最终通知

是在目标方法执行后切入切面,不管目标方法是否正常执行,切面方法中的功能都会执行,它有点类似于finally,是程序的一个出口,用来进行善后处理,关闭流,清空对象等。

@Aspect
	@Component
	public class MyAspect {
	    /**
	     * 实现切面功能要依靠切面方法
	     * 最终通知切面方法的规范:
	     * 1)访问权限是public
	     * 2)切面方法没有返回值void
	     * 3)切面方法名称自定义
	     * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
	     * 5)使用@After注解声明是最终通知
	     *   参数:
	     *      value:指定切入点表达式
	     *
	     */
	    @After(value = "execution(* com.spike.service.*.*(..))")
	    public void myAfter(){
	        System.out.println("最终通知功能实现");
	    }
	}
(7)@Pointcut切入点表达式起别名

如果多个切面切入到同一个切入点,则可以使用@Pointcut注解起别名,使用一个无参无返空实现的方法名称作为别名。

@Aspect
@Component
public class MyAspect {
	    /**
	     * 实现切面功能要依靠切面方法
	     * 最终通知切面方法的规范:
	     * 1)访问权限是public
	     * 2)切面方法没有返回值void
	     * 3)切面方法名称自定义
	     * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
	     * 5)使用@After注解声明是最终通知
	     *   参数:
	     *      value:指定切入点表达式
	     *
	     */
	    @After(value = "mycut()")
	    public void myAfter(){
	        System.out.println("最终通知功能实现");
	    }
	    @Before(value = "mycut()")
	    public void myBefore1(){
	        System.out.println("前置通知功能实现1");
	    }
	    @Before(value = "mycut()")
	    public void myBefore2(){
	        System.out.println("前置通知功能实现2");
	    }
	    @AfterReturning(value = "mycut()")
	    public void myAfterReturning(){
	        System.out.println("后置通知功能实现");
	    }
	    @Pointcut(value = "execution(* com.spike.service.*.*(..))")
	    public void mycut(){}
	}
(8)AspectJ框架切换动态代理模式

     AspectJ框架默认使用的是JDK动态代理,可以设置为CGLib动态代理。
  (1)JDK动态代理必须实现接口

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>    ===>JDK动态代理

    代理对象必须使用接口来接。
    SomeService service = (SomeService) ac.getBean("someServiceImpl");
    动态代理对象的类型是:class jdk.proxy2.$Proxy19
    如果使用实现类来接动态代理对象则报错:class jdk.proxy2.$Proxy19 cannot be cast to class        com.spike.service.SomeServiceImpl 

  (2)设置为CGLib动态代理,可以不实现接口,它是通过动态生成子类代理重写父类中业务方法来扩展功能。

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> ===>CGLib动态代理

    可以使用接口或实现类来接动态代理对象
    SomeServiceImpl service = (SomeServiceImpl) ac.getBean("someServiceImpl");
    SomeService service = (SomeService) ac.getBean("someServiceImpl");

18、Spring的事务

事务的处理是由Spring来控制,Spring事务控制的方式有两种:编程式事务和声明式事务


  (1)编程式事务(注解式事务)

1.在配置文件中添加事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!--必须配置数据源-->
	<property name="dataSource" ref="dataSource"></property>
</bean>

2.在配置文件中添加事务的注解驱动

<tx:annotation-driven></tx:annotation-driven>

3.在类上或方法上添加@Transactional注解声明事务

@Transactional
public Integer saveAccounts(Accounts accounts) {
        Integer num = accountsMapper.saveAccounts(accounts);
        System.out.println(accounts.getaName()+"帐户增加成功!num="+num);
        //手工抛出异常
        System.out.println(1/0);
        return num;
}

4.@Transactional注解参数详解

@Transactional(
            noRollbackForClassName = "ArithmeticException", //发生指定的异常不回滚事务,使用异常的名称
            noRollbackFor = ArithmeticException.class,  //发生指定的异常不回滚事务,使用异常的类型
            rollbackForClassName = "NullPointerException",  //发生指定的异常必须回滚事务,使用异常的名称
            rollbackFor = NullPointerException.class, //发生指定的异常必须回滚事务,使用异常的类型
            isolation = Isolation.DEFAULT ,  //使用数据库默认的事务隔离级别
            timeout = -1,  //设置连接超时时间,默认-1,永不超时
            readOnly = false,  //查询时必须设置只读属性为true,默认是false
            propagation = Propagation.REQUIRED  //事务的传播特性
    )

(2)事物的传播特性

事务的传播特性可以决定多个事务之间的互斥,归并,加入等。
  常用
  PROPAGATION_REQUIRED:必被包含事务
  PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
  PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不    单开事务
  PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
  PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
  不常用
  PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
  PROPAGATION_NESTED:嵌套事务

(3)声明式事务(一般推荐使用)

只需要在配置文件中声明,不需要硬编码实现事务管理。
  需要方法有命名规范:
  增加:  insert   save   add   create
  更新:  update   change  modify   set  
  删除:  delete   drop   remove  clear
  查询:  select   search  find  query get

代码示例 

 <!--    基于注解的开发添加包扫描-->
    <context:component-scan base-package="com.spike.service.impl"></context:component-scan>
	<!--    添加事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
	<!--    添加事务的切面-->
    <tx:advice id="myadive" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*select*" read-only="true"/>
            <tx:method name="*find*" read-only="true"/>
            <tx:method name="*get*" read-only="true"/>
            <tx:method name="*search*" read-only="true"/>
            <tx:method name="*query*" read-only="true"/>
            <tx:method name="*insert*" propagation="REQUIRED" />
            <tx:method name="*add*" propagation="REQUIRED"/>
            <tx:method name="*save*" propagation="REQUIRED" />
            <tx:method name="*create*" propagation="REQUIRED"/>
            <tx:method name="*update*" propagation="REQUIRED"/>
            <tx:method name="*change*" propagation="REQUIRED"/>
            <tx:method name="*modify*" propagation="REQUIRED"/>
            <tx:method name="*set*" propagation="REQUIRED"/>
            <tx:method name="*delete*" propagation="REQUIRED"/>
            <tx:method name="*drop*" propagation="REQUIRED"/>
            <tx:method name="*remove*" propagation="REQUIRED"/>
            <tx:method name="*clear*" propagation="REQUIRED"/>
            <tx:method name="*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
	<!--    切面和切入点绑定-->
    <aop:config>
        <aop:pointcut id="mycut" expression="execution(* com.spike.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="myadive" pointcut-ref="mycut"></aop:advisor>
    </aop:config>
(4)事务管理器 

但凡要进行事务的处理,必须添加事务管理器。
事务管理器是根据不同的技术或框架来生成提交和回滚的对象。
  JDBC:       Connection       con.commit();     con.rollback();
  MyBatis:    SqlSession     sqlSession.commit();     sqlSession.rollback();
  Hibernate:  Session          session.commit();      session.rollback();

  MyBatis框架的事务管理器是:DataSourceTransactionManager

19、Spring中常见的设计模式

(1)工厂模式:Spring通过工厂模式BeanFactory,ApplicationContext创建Bean对象。
(2)代理设计模式:SpringAOP的实现,底层使用了动态代理模式。
(3)单例模式:Spring中的Bean默认都是单例的。
(4)模板方法模式:Spring中jdbcTemplate,hibernateTemplate等以Template结尾的类都用到了模板模式。
(5)装饰模式:我们的项目需要连接多个数据库,而不同的客户在访问时可能会访问不同的数据库,这种模式可以让我们根据用户的需求动态的切换数据库。
(6)观察者模式:Spring的事件驱动是观察者模式的应用。
(7)适配器模式:SpringAOP的增强功能使用到了适配器模式。

笔者的话

写到这里已经2w字了,可能还有些知识点未列出,笔者在后续会进行更新。同时也恭喜你成功的学到了最后👍,如果你将本文的这些知识点都理解透了,那么你对Spring框架的理解其实已经非常好了💪,可以奖励自己一个🍗。

笔者码字不易,如果你觉得本文对你有一点点帮助的话,请给笔者点个赞。🌹🌹🌹

最后声明:本文为笔者原创内容,如需转载请附上原文出处链接。

  • 33
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring是一个轻量级的、非侵入式的IOC和AOP的一站式的Java开发框架。它于2003年兴起,旨在简化企业级应用开发。Spring的核心功能的jar包大小较小,不需要业务代码继承或实现Spring中的任何类或接口。Spring通过控制反转(IOC)将对象的创建权交给框架,而面向切面编程(AOP)是一种编程思想,是面向对象编程(OOP)的一种补充。Spring还提供了数据访问功能和Web功能,并可以很好地管理其他框架。\[2\] 在Spring中,可以使用注解来描述Bean,其中@Repository是一个泛化的概念,表示一个组件(Bean),可以应用在任何层次。只需将该注解标注在相应的类上即可。\[3\] 总之,Spring是一个功能强大的框架,提供了IOC和AOP的支持,可以简化企业级应用开发,并且具有灵活的注解机制来描述Bean。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [Spring详解(超全面)](https://blog.csdn.net/ailaohuyou211/article/details/130394148)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [spring全面详解](https://blog.csdn.net/weixin_71243923/article/details/128267166)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值