Spring从0开始最全面详解

Spring从0开始最全面详解

1、spring的概述

  1. spring是什么?

    ​ Spring是分层的Java SE/EE应用 full-stack 轻量级开源框架,以IOC(Inverse Of Control :反转控制)和AOP(Aspect Oriented Programming :面向切面编程)为内核,提供了展现层Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三框架和类库。

  2. spring的两大核心?
    ​ IOC 和 AOP

  3. spring的发展历程和优势
    优势:方便解耦,简化开发(利用了IOC容器)
    AOP编程的支持
    声明事务的支持
    方便程序的测试
    方便集成各种优秀框架
    降低 JavaEE API 的使用难度
    Java 源码是经典学习典范

  4. spring体系结构
    在这里插入图片描述

2、程序的耦合及解耦

耦合:程序间的依赖关系
包括:
​		类之间的依赖
​		方法间的依赖
解耦:
​	降低程序间的依赖关系
实际开发中:
​	应该做到:编译期不依赖,运行期才依赖
解耦的思路:
​	第一步:使用反射来创建对象,而避免使用new 关键字。
​	第二步:通过读取配置文件来获取要创建的对象的全限定类名。

3、IOC概念和spring中的IOC

  1. IOC(控制反转):不用new对象了,用工厂来创建对象,控制权发生了反转
  2. IOC作用:削减计算机程序的耦合(解除代码中的依赖关系)
  3. 获取spring 的ioc核心容器,并根据id获取对象
    ​ ApplicationContext的三个常用实现类
    ​ 1.ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
    ​ 2.FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(但是必须要有访问权限)
    ​ 3.AnnotationConfigApplicationContext:它是用于读取注解创建容器的
  4. 核心容器的两个接口引发的问题:
    ​ ApplicationContext:单例对象适用 一般采用此接口
    ​ 它在构建核心容器时,创建对象采用的策略是采用立即加载的方式,也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
    ​ BeanFactory:多例对象适用
    ​ 它在构建核心容器时,创建对象采用的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
    //1.获取核心容器对象
    //ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\bean.xml");(绝对)
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    //2.根据id获取Bean对象
    UserService service = (UserService) ac.getBean("userService");
    UserDao dao = ac.getBean("userDao",UserDao.class);
    
    System.out.println(service);
    System.out.println(dao);
    

spring中基于XML的IOC环境搭建,基于spring在xml中的配置

  1. 创建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"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<!-- 把对象的创建交给spring来管理 -->
        <bean id="userService" class="com.itheima.service.Imp.UserServiceImpl"></bean>
        <bean id="userDao" class="com.itheima.dao.Imp.UserDaoImpl"></bean>
            
        <!-- 把对象的创建交给spring管理 -->
        <!-- spring对bean的管理细节
                1.创建bean的三种方式
                2.bean对象的作用范围
                3.bean对象的生命周期
        -->
    
        <!-- 创建bean的三种方式 -->
        <!-- 第一种方式:使用默认的无参构造函数创建
                在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
                采用的就是默认无参构造函数创建bean对象,此时如果类中没有没有默认的无参构造,则对象无法创建。-->
        <bean id="userService" class="com.lucky.service.Impl.UserServiceImpl"></bean>
         
    
        <!-- 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器中) -->
    	<bean id="instanceFactory" class="com.lucky.factory.InstanceFactory"></bean> 得到InstanceFactory对象
      	<bean id="userService" factory-bean="instanceFactory" factory-method="getUserService"></bean>  调用里面的方法
    
        <!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器中)-->  <!-- class创建 StaticFactory实体类,在通过静态方法创建实体类-->
        <bean id="userService" class="com.lucky.factory.StaticFactory" 
            factory-method="getUserService"></bean>
    </beans>
    
  2. bean对象的作用范围:

    <!-- bean的作用范围:
             bean标签的scope属性:
                 作用:用于指定bean的作用范围
                 取值:常用的就是单例的和多例的
                 singleton:单例的(默认值)(产生的对象为一个)
                 prototype:多例的
                 request:作用域于web应用的请求范围
                 session:作用于web应用的会话范围
                 global-session:作用与集群环境的会话范围(全局会话范围),当不是集群环境时,他就是session
         -->
        <bean id="userService" class="com.lucky.service.Impl.UserServiceImpl" scope="prototype"></bean>
    
  3. bean对象的生命周期:

<!-- bean对象的生命周期:
                单例对象:
                    出生:当容器创建时产生
                    活着:只要容器还在,对象一直活着
                    死亡:容器销毁,对象灭亡  (销毁时,还没来得及调用销毁对象就已经消失)
                    总结:单例对象的声明周期和容器相同
                多例对象:
                    出生:当我们使用对象时spring框架为我们创建
                    活着:对象只要是在使用过程中就一直活着
                    死亡:当对象时间不用,且没有别的对象引用时,由java的垃圾回收机制回收
-->
    <bean id="userService" class="com.lucky.service.Impl.UserServiceImpl" scope="singleton"
          init-method="init" destroy-method="destroy"></bean>
依赖注入(Dependency Injection)
  1. 依赖关系的 管理:
    以后都交给spring来维护。
    在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明。
  2. 依赖关系的维护:
    就称之为依赖注入。
  3. 依赖注入:
    能注入的数据:
    1. 基本类型和String
    2. 其他bean类型(在配置文件中或者注解配置过的bean)
    3. 复杂类型
    ​ 注入的方式:
    1. 使用构造函数提供
    2. 使用set方法提供
    3. 使用注解提供
<!-- 构造函数依赖 :
	<--1. 构造函数的注入:
			使用的标签:constructor-arg (bean标签内部)
			标签中的属性:
				type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
								type遇到的问题:不知道是否还有其他相同的类型。
					index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。
							index遇到的问题:不知道具体位置的类型是什么。
				name:用于指定给构造函数中指定名称的参数赋值 (最常用)。
                ========================================================================
				value:用于提供基本类型和String类型的数据。
				ref:用于指定其他的bean类型的数据。它指的就是在spring的ioc核心容器中出现过的bean对象。
	  优势:
		在获取bean对象,注入数据是必须的操作,否则对象无法创建成功。
	  弊端:
		改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
     -->
    <bean id="accountService" class="com.lucky.service.Impl.AccountServiceImpl">
       <!-- <constructor-arg type="java.lang.String" value="显示"></constructor-arg>
        <constructor-arg index="1" value="18"></constructor-arg>-->
        <constructor-arg name="name" value="范德萨"></constructor-arg>
        <constructor-arg name="age" value="16"></constructor-arg>
        <constructor-arg name="birthday" ref="date"></constructor-arg>

    </bean>

    <bean id="date" class="java.util.Date"></bean>
        
<!-- set方法注入-->
     <!--使用标签:property (在bean标签内部)
            标签属性:
                name:用于指定给构造函数中指定名称的参数赋值 (最常用)。
                value:用于提供基本类型和String类型的数据。
                ref:用于指定其他的bean类型的数据。它指的就是在spring的ioc核心容器中出现过的bean对象。
        优势:
        	创建对象时没有明确的限制,可以直接使用默认构造函数。
        弊端:
        	如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
	 -->
    <bean id="accountService" class="com.lucky.service.Impl.AccountServiceImpl2">
        <property name="name" value="大数"></property>
        <property name="age" value="48"></property>
        <property name="birthday" ref="date"></property>
    </bean>

spring基于注解的IOC以及ioc的案例

  1. spring中ioc的常用注解

    /* 用于创建对象的:
           他们的作用域就和在XML配置文件中编写一个   <bean></bean> 标签实现的功能是一样的
           @Component:
               作用:用于把当前类对象存入spring容器中
               属性:
                  value:用于指定bean的id。当我们不写时,它的默认值就是当前类名,且首字母改小写
           @Controller:一般用在表现层
           @Service:一般用在业务层
           @Repository:一般用在持久层
           以上三个注解它们的作用和属性与Component是一模一样的。
           它们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。
     用于注入数据的:
           他们的作用域就和在xml配置文件中的bean标签中写一个 <property></property> 标签的作用是一样的
           @Autowired:
             作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
                   如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
                   如果ioc容器中有多个类型匹配时,则会按照创建UserDao的变量名称和注入时的名称匹配
             出现位置:可以是变量上,也可以是方法上
             细节:在使用注解注入时,set方法就不是必须的了
           @Qualifier:
              作用:在按照指定类中注入的基础之上在按照名称注入。他在给成员注入时不能单独使用(必须依赖与Autowired)。但是在给方法参数注入可以单独使用
               属性:value:用于指定注入bean的id。
           @Resource:
              作用:直接按照bean的id注入。它可以独立使用
              属性:
                   name:用于指定bean的id
           以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
          另外,集合类型的注入只能通过XML来实现
     
           @Value:
               作用:用于注入基本数据类型和String类型的数据
               属性:
                   value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
                       SpEL的写法:${表达式}   */
    
    @Component(value = "userService")
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao2 = null;
    }
    
    @Repository
    public class UserDaoImpl implements UserDao {
        
    }
    @Repository(value = "userDao2")
    public class UserDaoImpl2 implements UserDao {
        
    }
    
    //使用Qualifier时,两个必须一起使用
    //@Autowired
    //@Qualifier("userDaoImpl")
    
    @Resource(name = "userDaoImpl")
    private UserDao userDao2 = null;
    
    /* 用于改变作用范围的:
     *      他们的作用就和在bean标签中使用scope属性实现的功能是一样的
     *      @Scope:
     *          作用:用于指定bean的作用范围
     *          属性:value:指定范围的取值。常用取值:singleton prototype
     *   和生命周期相关
     *      他们的作用就和在bean标签中使用 init-method 和 destroy-method 的作用是一样的
     *      @PreDestroy:
     *          作用:用于指定销毁方法
     *      @PostConstruct:
     *          作用:用于指定初始化方法
     * */
         
    @Component(value = "userService")
    @Scope("prototype")
    public class UserServiceImpl implements UserService {
        @Override
        @PostConstruct
        public void init() {
            System.out.println("我初始化了...");
        }
    
        @Override
        @PreDestroy
        public void destroy() {
            System.out.println("我销毁了...");
        }
    }
    
     	@Value(value = "${driver}")
        private String driver;
        
        @Value("username")
        private String username;
    
  2. 改造基于注解的ioc案例,使用纯注解的方式实现

    /**
     * 该类是一个配置类,它的作用和bean.xml是一样的:
     *  spring中的新注解:
     *      @Configuration:
     *          作用:指定当前类是一个配置类
     			细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
     *      @ComponentScan:
     *          作用:用于通过注解指定spring在创建容器时要扫描的包
     *          属性:
     *              value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
     *                  等同于这个配置:<context:component-scan base-package="com.lucky"></context:component-scan>
     	
     */
    @Configuration
    @ComponentScan("com.lucky")  //要扫描的包
    public class ConfigReplaceXml {
    }
    

    注意:ComponentScan扫描时,只会扫描有 @Configuration 注解的类

    Configuration细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。

     /*
     @Bean:
     	作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中。
        属性:
        	 name:用于指定bean的id。当不写时,默认值是当前方法的名称,
    		 细节: 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
    		  查找的方式和Autowired注解的作用是一样的
    */
    	@Bean
        @Scope("prototype")
        public QueryRunner createQueryRunner(DataSource dataSource){
            return new QueryRunner(dataSource);
        }
    
        @Bean
        public DataSource createDataSource(){
            try{
                ComboPooledDataSource dataSource = new ComboPooledDataSource();
                dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
                dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
                dataSource.setUser("root");
                dataSource.setPassword("root");
                return dataSource;
            }catch (Exception e){
                throw new RuntimeException(e);
            }
    
        }
    
    @Test
    public void test2(){
    	ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class);  //创建方式改变,基于注解创建
        QQUserService service = ac.getBean("userService",QQUserService.class);
        QQUser one = service.findOne(2);
        System.out.println(one);
    }
    		  
    

    @Bean注解的细节:细节: 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,没有就会报错

    ​ (1).实现配置类调用另一配置类的方法:

    /* 
    @Import:
          作用:用于导入其它的配置类
          属性:value:用于指定其他配置类的字节码。
          当使用Import的注解之后,有Import注解的类就是父配置类,而导入的都是子配置类
    */
    //第一种
    @ComponentScan({"com.lucky"})
    public class ConfigReplaceXml {
    }
    
    @Configuration
    public class JDBCConfig {
    }
    
    ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class);
    
    //第二种
    @ComponentScan({"com.lucky"})
    public class ConfigReplaceXml {
    }
    
    public class JDBCConfig {
    }
    ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class, JDBCConfig.class);//两个类都加上,为兄弟关系
    
    //第三种(具有父子关系)
    @ComponentScan({"com.lucky"})
    @Import(value = {JDBCConfig.class})
    public class ConfigReplaceXml {
    }
    
    ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigReplaceXml.class);
    

    ​ (2)配置连接数据库的资源文件

    /*
    PropertySource:
    *   作用:用于指定properties文件的位置。
    *   属性:value:指定文件的的名称和路径。
               关键字:classpath:表示类路径下
    */
    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/test
    user=root
    password=root
        
    @ComponentScan({"com.lucky"})
    @Import(value = {JDBCConfig.class})
    @PropertySource("classpath:jdbcConfig.properties")
    public class ConfigReplaceXml {
    }
    
    	@Value(value = "${driver}")
        private String driver;
        @Value(value = "${url}")
        private String url;
        @Value("${user}")
        private String username;
        @Value("${password}")
        private String password;
    

    ​ (3)在注解中springIOC容器中同一个对象有多个实现方法时

    /*当方法重载时,用 @Qualifier 注解可以实现使用的是哪一个方法 */
    @Bean
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier(value = "ds1") DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    
    @Bean(name = "ds1")
        public DataSource createDataSource(){}
    
    @Bean(name = "ds2")
        public DataSource createDataSource2(){}
    

4、动态代理的两种方式

1、动态代理(基于接口实现)

/*基于接口的动态代理:
*   特点:字节码随用随创建,随用随加载
*   作用:不修改源码的基础上对方法增强
*   分类:
*      基于接口的动态代理
*      基于子类的动态代理
*   基于接口的动态代理:
*      涉及的类:Proxy
*      提供者:JDK官方
*   如何创建代理对象:
*      使用Proxy类中的newProxyInstance方法
*   创建代理对象的要求:
*      被代理类最少实现一个接口,如果没有则不能使用
*   newProxyInstance方法的参数:
*       1.类加载器:它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。真实对象.getClass().getClassLoader()
*       2.接口数组:字节码数组,它是用于让代理对象和被代理对象有相同的方法。真实对象.getClass().getInterfaces()
*       3.处理器:new InvocationHandler()  */
Huawei huawei = new HuaWei();
Producer proxyProducer = (Producer) Proxy.newProxyInstance(huaWei.getClass().getClassLoader(), huiWei.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强代码
                Object returnValue = null;
                //1.获取方法执行的参数
                double money = (double) args[0];
                //2.判断当前方法是不是销售
                if("saleProduce".equals(method.getName())){
                    //若是销售的方法名,则要进行处理,代理商要拿到 20%,生产商只能拿到 80%
                    returnValue = method.invoke(huaWei, money * 0.8);
                }
                return returnValue;
            }
        });
        proxyProducer.saleProduce(10000);
  1. 动态代理另一种实现方式(基于子类)
/*基于子类的动态代理:
*   特点:字节码随用随创建,随用随加载
*   作用:不修改源码的基础上对方法增强
*   分类:
*      基于接口的动态代理
*      基于子类的动态代理
*   基于子类的动态代理:
*      涉及的类:Enhancer
*      提供者:第三方cglib库
*   如何创建代理对象:
*      使用Enhancer类中的create方法
*   创建代理对象的要求:
*      被代理类不能时最终类
*   create方法的参数:
*       1.Class:字节码,用于指定被代理对象的字节码,
*       2.Callback:用于指定提供增强的代码,
*          它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
*          此接口的实现类都是谁用谁写
*          我们一般写的都是该接口的子接口的实现类:MethodInterceptor
*/
Huawei huawei = new HuaWei();
Producer cglibProducer = (Producer) Enhancer.create(huaWei.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] ags, MethodProxy methodProxy) throws Throwable {
                //增强代码
                Object returnValue = null;
                //1.获取方法执行的参数
                double money = (double) ags[0];
                //2.判断当前方法是不是销售
                if ("saleProduce".equals(method.getName())) {
                    //若是销售的方法名,则要进行处理,代理商要拿到 20%,生产商只能拿到 80%
                    returnValue = method.invoke(huaWei, money * 0.8);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduce(12000);

5、AOP

​ AOP:Aspect Oriented Programing(面向切面编程)。

spring中基于XML的AOP配置
spring中基于XML的 AOP 配置步骤
     1.把通知 bean 也交给 spring 来管理
     2.使用 aop:config 标签表明开始aop配置
     3.使用 aop:aspect 标签表明配置切面
         id属性  : 是给切面提供一个唯一标识
         ref属性 : 是指定通知类的 id
     4.在 aop:aspect 标签内部使用对应标签来配置通知的类型
         我们现在实例是让 printLog 方法在切入点方法执行之前执行,所以是前置通知
         aop:before : 表示配置前置通知
             method属性:用于指定Logger类中哪个方法是前置通知
             pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法的增强
		切入点表达式的写法:
                    关键字:execution(表达式)
                    表达式:
                        访问修饰符 返回值 包名.包名.包名...类名.方法名.(参数列表)
                    举例:
                        public void com.lucky.service.impl.AccountServiceImpl.save()

                修饰符可以不写:
                    void com.lucky.service.impl.AccountServiceImpl.save()
                返回值可以使用通配符,表示任意返回值:
                    * com.lucky.service.impl.AccountServiceImpl.save()
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个 *.
                    void *.*.*.*.AccountServiceImpl.save()
                包名可以使用 .. 表示当前包及其子包:
                    void *..AccountServiceImpl.save()
                类名和方法名都可以使用 * 来通配
                    * *..*.*()
                参数列表:
                    可以直接写数据类型:
                        基本类型直接写名称      int
                        引用类型写包名.类名的方式 java.lang.String
                    可以使用通配符表示任意的类型,但是必须有参数
                    可以使用 .. 表示有无参数均可,有参数可以是任意类型
                全通配符写法:
                    * *..*.*(..)

                实际开发中切入点表达式的通常写法 :
                    切到业务层实现类下的所有方法
                        * com.lucky.service.impl.*.*(..)
        
			<!-- 所有方法都可 -->
<aop:before method="printLog" pointcut="execution(* *..*.*(..))"></aop:before>
            <!-- 省略修饰符 -->
<aop:before method="printLog" pointcut="execution(void com.lucky.service.impl.AccountServiceImpl.save())"></aop:before>
            <!-- 包名改 *. -->
<aop:before method="printLog" pointcut="execution(void *.*.*.*.AccountServiceImpl.save())"></aop:before>
            <!--  任意包下或子包下有AccountServiceImpl类的 -->
<aop:before method="printLog" pointcut="execution(void *..AccountServiceImpl.save())"></aop:before>

            <!-- 任意包下或子包下的任意类、任意方法,只要是无参就执行 -->
<aop:before method="printLog" pointcut="execution(void *..*.*())"></aop:before>

            <!-- 参数通配符(可..-->
<aop:before method="printLog" pointcut="execution(void *..*.*(..))"></aop:before>

            <!-- 实际开发需要写的 -->
<aop:before method="printLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:before>
四种常用的通知类型
method:指定一个类中的方法   pointcut:指定切入点表达式,该表达式的含义指的是对业务层中哪些方法的增强

<!-- 配置前置通知,在切入点方法执行之前执行 -->
<aop:before method="beforePrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:before>

<!-- 配置后置通知  : 在切入点方法正常之后执行。它和异常通知永远只能执行一个 -->
<aop:after-returning method="afterPrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:after-returning>

<!-- 配置异常通知  : 在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个 -->
<aop:after-throwing method="afterExceptionPrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:after-throwing>

<!-- 配置最终通知  : 无论切入点方法是否正常执行它都会在最后执行-->
<aop:after method="finallyPrintLog" pointcut="execution(* com.lucky.service.impl.*.*(..))"></aop:after>
配置通用表达式
<!-- 配置通用切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
  此标签写在 aop:aspect 标签内部,只能当切面使用。
  他还可以写在 aop:aspect 外面。此时就变成了所有切面可用
-->
<!-- 要配置在 aop:aspect 的外面,必须是在 aop:aspect 的前面 -->
<aop:pointcut id="pt1" expression="execution(* com.lucky.service.impl.*.*(..))"/>
环绕通知
//invoke方法执行的就是环绕通知
/**
 * 环绕通知
 *  问题:当我们配置了环绕 之后,切入点方法没有执行,而通知方法执行了
 *  分析:通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
 *  解决:Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法 proceed() ,此方法就相当于明确调用切入点方法。
 *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
 */
public Object aroundPrintLog(MethodInvocationProceedingJoinPoint pjp){
    System.out.println("环绕通知:打印aroundPrintLog...");
    return null;
}
基于注解的AOP

使用注解时:
XML中要配置:<aop:aspectj-autoproxy proxy-target-class=“true”></aop:aspectj-autoproxy>
@Aspect放在类上:表示当前类是一个切面类

6、事务控制

1、基于XML配置
<!-- spring中基于XML的声明式事务控制配置步骤
     1.配置事务管理器
     2.配置事务的通知
         此时我们需要导入事务的约束 tx 名称空间和约束,同时也需要aop的
         使用 tx:advice 标签配置事务通知
             属性:
                 id                  : 给事务通知起一个唯一标识
                 transaction-manager : 给事务通知提供一个事务管理器引用
     3.配置AOP中的切入点表达式
     4.建立事务通知和切入点表达式的对应关系
     5.配置事务的属性:
         是在事务的通知 tx:advice 标签内部
-->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 2.配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
 <!-- 5.配置事务的属性
         isolation      :用于指定事务的隔离级别。默认方法时DEFAULT,表示使用数据库的默认隔离级别。
         propagation    :用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
         read-only      :用于指定事务的超时时间,默认值是 -1,表示永不超时。如果指定了数值,以秒为单位。
         rollback-for   :用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。
         no-rollback-for:用于指定一个异常,当产生异常时,事务不回滚,产生其他异常时事务回滚。没有默认值,表示任何异常都回滚。
 -->
 <tx:attributes>
     <!-- 写的两个属性,优先级是 2>1 因为下面的只是一部分(查询的)适用,且只能为只读方式;第一个是所有的都适用 -->
     <tx:method name="*" propagation="REQUIRED" read-only="false"/>
     <tx:method name="find*" read-only="true" propagation="SUPPORTS"></tx:method>
 </tx:attributes>
</tx:advice>

<!-- 配置AOP -->
<aop:config>
 <!-- 3.配置切入点表达式 -->
 <aop:pointcut id="pt1" expression="execution(* com.smile.service.impl.*.*(..))"/>
 <!-- 4.建立切入点表达式和事务通知的关系 -->
 <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
2、基于注解配置
 <!--配置spring创建容器时要扫描的包-->
    <context:component-scan base-package="com.smile"></context:component-scan>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- spring中基于 注解 的声明式事务控制配置步骤
            1.配置事务管理器
            2.开启spring对注解事务的支持
            3.在需要事务支持的地方使用 @Transactional
     -->
    <!-- 1.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 2.开启spring对注解事务的支持 -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
@Service("accountService")
//@Transactional   //需要事务支持
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)  //对里面的全部方法配置针对查询的只读形式
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public Account findAccountById(int id) {
        return accountDao.findAccountById(id);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)  //只对该方法针对增删改查的读写形式
    public void transfer(String sourceName, String targetName, double money) {}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
- chapter1:[基本项目构建(可作为工程脚手架),引入web模块,完成一个简单的RESTful API](http://blog.didispace.com/spring-boot-learning-1/) - [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/) ### 工程配置 - chapter2-1-1:[配置文件详解:自定义属性、随机数、多环境配置等](http://blog.didispace.com/springbootproperties/) ### Web开发 - chapter3-1-1:[构建一个较为复杂的RESTful API以及单元测试](http://blog.didispace.com/springbootrestfulapi/) - chapter3-1-2:[使用Thymeleaf模板引擎渲染web视图](http://blog.didispace.com/springbootweb/) - chapter3-1-3:[使用Freemarker模板引擎渲染web视图](http://blog.didispace.com/springbootweb/) - chapter3-1-4:[使用Velocity模板引擎渲染web视图](http://blog.didispace.com/springbootweb/) - chapter3-1-5:[使用Swagger2构建RESTful API](http://blog.didispace.com/springbootswagger2/) - chapter3-1-6:[统一异常处理](http://blog.didispace.com/springbootexception/) ### 数据访问 - chapter3-2-1:[使用JdbcTemplate](http://blog.didispace.com/springbootdata1/) - chapter3-2-2:[使用Spring-data-jpa简化数据访问层(推荐)](http://blog.didispace.com/springbootdata2/) - chapter3-2-3:[多数据源配置(一):JdbcTemplate](http://blog.didispace.com/springbootmultidatasource/) - chapter3-2-4:[多数据源配置(二):Spring-data-jpa](http://blog.didispace.com/springbootmultidatasource/) - chapter3-2-5:[使用NoSQL数据库(一):Redis](http://blog.didispace.com/springbootredis/) - chapter3-2-6:[使用NoSQL数据库(二):MongoDB](http://blog.didispace.com/springbootmongodb/) - chapter3-2-7:[整合MyBatis](http://blog.didispace.com/springbootmybatis/) - chapter3-2-8:[MyBatis注解配置详解](http://blog.didispace.com/mybatisinfo/) ### 事务管理 - chapter3-3-1:[使用事务管理](http://blog.didispace.com/springboottransactional/) - chapter3-3-2:[分布式事务(未完成)] ### 其他内容 - chapter4-1-1:[使用@Scheduled创建定时任务](http://blog.didispace.com/springbootscheduled/) - chapter4-1-2:[使用@Async实现异步调用](http://blog.didispace.com/springbootasync/) #### 日志管理 - chapter4-2-1:[默认日志的配置](http://blog.didispace.com/springbootlog/) - chapter4-2-2:[使用log4j记录日志](http://blog.didispace.com/springbootlog4j/) - chapter4-2-3:[对log4j进行多环境不同日志级别的控制](http://blog
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、付费专栏及课程。

余额充值