Spring学习笔记

Spring 框架的概述以及Spring中基于XML的IOC配置

/**
 * 获取spring的IoC核心容器,并根据id获取对象     * ApplicationContext的三个常用实现类:
 *      ClassPathXmlApplicationContext: 它可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话加载不了。
 *      FileSystemXmlApplicationContext: 它可以加载磁盘任意路径下的配置文件(必须有访问权限)
 *      AnnotationConfigApplicationContext: 它是用于读取注解创建容器的
 *      
 * 核心容器的两个接口引发出的问题:
 * ApplicationContext: 单例对象适用
 *      它在构建核心容器时,创建对象采取的策略是立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象
 * BeanFactory:  多例对象适用
 *      它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象,什么时候才真正的创建对象。
 * @param args
 */
<!-- Spring对bean的管理细节
     1,创建bean的三种方式
     2,bean对象的作用范围
     3,bean对象的生命周期
-->
<!-- 第一种方式:使用默认构造函数。
        在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其它属性和标签时,
        采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
 <bean id="accountService" class="com.jsp.service.impl.AccountServiceImpl"></bean>
 -->
<!-- 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入Spring容器)
        在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其它属性和标签时,
        采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

<bean id="instanceFactory" class="com.jsp.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
-->

<!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)-->
<bean id="accountService" class="com.jsp.factory.StaticFactory" factory-method="getAccountService"></bean>
<!-- bean的作用范围调整
    bean标签的scope属性:
        作用:用于指定bean的作用范围
        取值:
            singleton:单例的(默认值)
            prototype:多例的
            request:作用于web应用的请求范围
            session:作用于web应用的会话范围
            gloabl-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
-->
<bean id="accountService" class="com.jsp.service.impl.AccountServiceImpl" scope="prototype"></bean>

Spring中基于注解的IOC和IOC的案例

 *  用于创建对象
 *      就和xml配置文件中编写bean标签实现的功能是一样的
 *      @Component*          作用:用于把当前类对象存入spring容器
 *          属性:
 *               value:用于指定bean的id。当我们不写时,它默认值是当前类名,且首字母小写
 *      @Controller:一般用在表现层
 *      @Service:一般用在业务层
 *      @Repository:一般用在持久层
 *      以上三个注解他们的作用和属性与Component是一样的,
 *      他们三个是Spring框架为我们提供明确的三层使用的注解,
 *      使我们的三层对象更加清晰。
 *
 *  用于注入数据
 *      就和xml配置文件中的bean标签中写一个property标签作用一样
 *      @Autowried:
 *          作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
 *          如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
 *          如果ioc容器中由多个类型匹配时,
 *          出现位置:可以是成员变量上,也可以是方法上。
 *          细节:在使用注解注入时,set方法就不是必须的了
 *      @Qualifier*          作用:在按照类型注入的基础之上,再按照名称注入。它在给类成员注入时不能单独使用。
 *          但是在给方法参数注入时可以
 *          属性:value:用于注入指定bean的id
 *      @Resource:
 *         作用:直接按照bean的id注入。它可以独立使用
 *         属性:name:用于指定bean的id
 *      以上三个注入都只能注入其它bean类型的数据,而基本类型和String类型无法使用上述注解实现
 *      另外,集合类型的注入只能通过xml来实现
 *
 *      @Value*          作用:用于注入基本类型和String类型的数据
 *          属性:value:用于指定数据的值。它可以使用spring中的SpEL(也就是spring中的el表达式)
 *          SpEL的写法:${表达式}
 *
 *  用于改变作用范围
 *      就和集合在bean标签中使用scope属性实现的功能一样
 *      @Scope*          作用:用于指定bean的作用范围
 *          属性:value:指定范围的取值。常用取值:singleton prototype
 *  和生命周期相关
 *      在bean标签中使用init-method和destroy-method的作用一样
 *      @PreDestroy:用于指定销毁方法
 *      @PostConstruct:用于指定初始化方法
* spring中的新注解:
 * Configuration:
 *      作用:指定当前类是一个配置类
 *      细节:当配置作为AnnotationConfigApplicationContext对象创建参数时,该注解可以不写。
 * ComponentScan
 *      作用:用于通过注解指定Spring在创建容器时要扫描的包。
 *      属性:
 *          value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包
 *          我们使用此注解就等同于在xml中配置了:
 *          <context:component-scan base-package="com.jsp"></context:component-scan>
 * Bean
 *      作用:用于把当前方法的返回值作为bean对象存入spring容器中
 *      属性:
 *          name:用于指定的bean的id。当不写时默认值是当前方法的名称
 *      细节:
 *          当我们使用注解配置方法时,如果方法有参数,Spring框架会去容器中查找有没有可以用的Bean对象。
 *          查找的方式和Autowired注解的作用是一样的
 *
 * Import
 *      作用:用于导入其它配置类
 *      属性:
 *          value:用于指定其他配置类的字节码。当我们使用Import注解之后,
 *          有Import注解的类就是父配置类,而导入的都是子配置类
 * PropertySource
 *      作用:用于指定properties文件的位置
 *      属性:
 *          value:指定文件的名称和路径
 *          关键字:classpath,表示类路径下
@Configuration
@ComponentScan(basePackages = "com.jsp")
public class SpringConfiguration {
    /**
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name = "runner")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy");
            ds.setUser("root");
            ds.setPassword("maple");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

@Configuration,ComponentScan,@Bean三个注解配置相当于之前xml配置文件。

Test测试类:

public class AccountServiceTest {
    @Test
    public void testFindAll() {
        //获取容器
        //ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        //1.获取容易
        //ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test");
        account.setMoney(12345f);
        //1.获取容易
        //ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        as.saveAccount(account);

    }

    @Test
    public void testUpdate() {
        //1.获取容易
        //ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        //1.获取容器
        //ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        as.deleteAccount(4);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYfHZakG-1575855156098)(/Users/mac/Desktop/注解配置–方法所使用的对象.png)]

通过注解配置,test类中使用AnnotationConfigApplicationContext类通过SpringConfiguration反射得到容器

junit单元测试中,没有main方法也能执行

junit集成了一个main方法

该方法就会判断当前测试类中哪些方法有@Test注解

junit就让有Test注解的方法执行

junit不会管我们是否采用spring框架,在执行测试方法时,junit根本不知道是不是使用了spring框架,所有也就不会为我们读取配置文件/配置类创建spring核心容器。

由以上可知:当测试方法执行时,没有IOC容器,就算写了Autowired注解,也无法实现注入。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gxNYknO-1575855156099)(/Users/mac/Desktop/junit整合spring框架坐标配置.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JOThk5mq-1575855156102)(/Users/mac/Desktop/junit版本.png)]

package com.jsp.test;
import com.jsp.dao.IAccountDao;
import com.jsp.domain.Account;
import com.jsp.service.IAccountService;
import config.SpringConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**
 * 使用junit单元测试,测试配置
 * Spring整合junit的配置
 *      1,导入spring整合junit的jar(坐标)
 *      2,使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的。
 *           @RunWith
 *      3,告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
 *           @ContextConfiguration:
 *                  location:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                  classes:指定注解类所在的位置
 *      当使用spring 5.x版本的时候,要求junit的jar包必须是4.12及以上
 * @author mac

 * @create 2019/11/2
 *

 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
    
    @Autowired
    private IAccountService as = null;
    
    @Test
    public void testFindAll() {
        //执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test");
        account.setMoney(12345f);
        //3.执行方法
        as.saveAccount(account);

    }

    @Test
    public void testUpdate() {
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        //3.执行方法
        as.deleteAccount(4);
    }
}

Spring中的aop和基于XML以及注解的AOP配置

动态代理:

第一种基于接口:

package com.jsp.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 模拟一个消费者
 * @author mac

 * @create 2019/12/8
 *

 */
public class Client {
    public static void main(String[] args) {
        final Producer producer = new Producer();

        /**
         * 动态代理:
         *      特点:字节码随用随创建,随用随加载
         *      作用:不修改源码基础上对方法增强
         *      分类:
         *          1,基于接口的动态代理
         *          2,基于子类的动态代理
         *
         * 基于接口的动态代理:
         *      涉及的类:Proxy
         *      提供者:JDK官方
         * 如何创建代理对象:
         *      使用Proxy中的newProxyInstance方法
         * 创建代理对象的要求:
         *      被代理类最少实现一个接口,如果没有则不能使用
         * newProxyInstance方法的参数:
         *      ClassLoader:类加载器
         *          他是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
         *      Class[]:字节码数组
         *          用于让代理对象和被代理对象有相同的方法,固定写法
         *      InvocationHandler:用于提供增强的代理
         *          他是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
         *          此接口的实现类都是谁用谁写
         */
        IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 作用:执行被代理对象的任何接口方法都会经过该方法
                     * @param proxy  代理对象的引用
                     * @param method 表示当前执行的方法
                     * @param args   当前执行方法所需的参数
                     * @return       和被代理对象有相同的返回值
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //提供增强的代码
                        Object returnValue = null;
                        //1,获取方法执行的参数
                        Float money = (Float) args[0];
                        //2,判断当前方法是不是销售
                        if ("saleProduct".equals(method.getName())) {
                            returnValue = method.invoke(producer,money*0.8f);
                        }
                        return returnValue;
                    }
                });
        proxyProducer.saleProduct(10000f);

    }
}

第二种基于子类:需要借助于第三方cglib库

​ pom.xml

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.1_3</version>
    </dependency>
</dependencies>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RH3AkvvS-1575855156104)(/Users/mac/Desktop/动态代理cglib库.png)]

package com.jsp.cglib;
import com.jsp.proxy.IProducer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 模拟一个消费者
 * @author mac

 * @create 2019/12/8
 *

 */
public class Client {
    public static void main(String[] args) {
        final Producer producer = new Producer();

        /**
         * 动态代理:
         *      特点:字节码随用随创建,随用随加载
         *      作用:不修改源码基础上对方法增强
         *      分类:
         *          1,基于接口的动态代理
         *          2,基于子类的动态代理
         *
         * 基于子类的动态代理:
         *      涉及的类:Enhancer
         *      提供者:第三方cglib库
         * 如何创建代理对象:
         *      使用Enhancer类中的create方法
         * 创建代理对象的要求:
         *      被代理类不能是最终类
         * create方法的参数:
         *      Class:字节码
         *          他是用于指定被代理对象的字节码
         *
         *      Callback:用于提供增强的代理
         *          他是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的
         *          此接口的实现类都是谁用谁写
         *          我们一般写的都是该接口的子接口实现类:MethodInterceptor
         */
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *          以上三个参数和基于接口的动态代理中invoke方法的参数一样
             * @param methodProxy : 当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //提供增强的代码
                Object returnValue = null;
                //1,获取方法执行的参数
                Float money = (Float)args[0];
                //2,判断当前方法是不是销售
                if ("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer,money*0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(12000f);
    }
}

Spring中的JdbcTemlate以及Spring事务控制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值