Spring

Spring

1、动态代理详解

动态代理实现的步骤

  1. 创建接口,定义目标要完成的功能
  2. 创建目标类,实现接口
  3. 创建InvocationHandler接口实现类 ,在invoke方法中完成代理的功能
    • 调用目标方法
    • 增强的功能
  4. 使用Proxy类创建代理类,并把返回值转换成接口。

1、创建接口,定义目标要完成的功能

package com.jfs.proxys.jdkproxy;

public interface Rent {
    void sell();
    void buy();
}

2、创建目标类,实现接口

package com.jfs.proxys.jdkproxy;

public class Host implements Rent {
    @Override
    public void sell() {
        System.out.println("卖家:卖房子");
    }

    @Override
    public void buy() {
        System.out.println("目标类:买房子");
    }

}

3、创建InvocationHandler接口实现类 ,在invoke方法中完成代理的功能

package com.jfs.proxys.jdkproxy;

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

public class ProxyInvocationHandle implements InvocationHandler {

    private Object rent;

    public ProxyInvocationHandle(Object rent) {
        this.rent = rent;
    }
	//proxy是代理对象,method是被代理的方法, args是方法的参数,这些都不要手动输入,jdk自动注入
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增强的功能
        System.out.println("执行了"+method.getName());
        //需要完成的目标类功能
        Object res = method.invoke(rent, args);
        return res;
    }
}

4、通过Proxy类获取代理类对象,转换为相应接口的类型(可以代理接口中的任意方法)

package com.jfs.proxys.jdkproxy;

import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        Rent rent = new Host();
        ProxyInvocationHandle handle = new ProxyInvocationHandle(rent);
      	//第一个参数是目标类的类加载器,第二个参数是目标类实现的接口,第三个参数是InvocationHandler接口实现类
        Rent proxy = (Rent) Proxy.newProxyInstance(rent.getClass().getClassLoader(), 				rent.getClass().getInterfaces(), handle);
        proxy.hashCode();
        proxy.sell();
        proxy.buy();
    }

提示: 执行proxy.sell方法执行InvocationHandler类的invoke()方法 参数method就是sell()方法

2、Aop

2.1 AOP概念

​ 面向切面编程,是基于动态代理实现的,动态代理有jdk动态代理、cglib动态代理

aop就是把动态代理的步骤规范化,让开发人员使用统一的方式去开发。


  1. 分析去哪些功能需要提取出来,找出切面
  2. 确定切面执行的时间,是在方法钱还是在方法后等等
  3. 确定切面在哪个类,哪个方法添加增强功能

2.2 AOP术语

  1. Aspect:切面,表示增强的功能,常见的切面有日志、事务、参数检查、权限验证等等。

  2. Joinpoint:连接点 表示业务方法和切面的位置,某类中的业务方法 。

  3. PointCut:切入点 表示一个或者几个连接点的汇总。

    1. advice:通知 :表示切面执行的时间。

2.3 实现AOP的框架

  1. spring:通常在事务中用的比较多,spring中的aop使用起来比较笨重。

  2. aspectJ:一个专门做aop的开源框架,spring集成了aspectJ的所有功能

    aspectJ实现aop的方式

    • 通过xml注解方式

    • 通过注解的方式

2.4 切入点表达式

execution(访问权限 返回值类型 方法的声明(参数列表) 异常类型)

其中:加粗的字体不能省略

示例

execution(public * *(…)):任意的类型为public的方法

execution(* set*(…)) :任意一个以set方法开头的方法

execution(* com.xyz.service. *. *(…)):com.xyz.service包下面的任意类的任意方法

execution(* com.xyz.service…*. *(…))service包及其子包下所有的方法

execution(* …service.*. *(…))所有包中service包中的所有类和方法

*代表0个或者多个字符

…用在方法参数中代表是任意个参数,用在包后面表示当前包和所有子包

2.5 AspectJ(aop)使用步骤

  1. 创建一个maven项目
  2. 导入依赖
    • spring 依赖
    • aspectj依赖
  3. 创建目标类和其接口
  4. 创建切面类
    • 在切面类上加@Aspect
    • 在方法上面加通知注解,例如@Before,有需要切入点的表达式execution()
  5. 创建spring配置文件,声明对象,把对象统一交给spring容器管理
    • 声明目标对象
    • 申明切面对象
    • 生产aspectj框架中自动代理生成器标签
  6. 创建测试类,通过spring容器获得目标对象,通过代理执行方法,实现aop增强功能。

==注意:==代理对象实际上目标对象修改后的

什么时候考虑使用AOP

  1. 需要增加功能,同时又不能给变原来代码的情况下。
  2. 有很多类需要增强相似的功能,通常有给业务方法增加事务,日志等功能。

2.6 Aop通知类型及其分类

  • 前置通知(Before)

    /**
         * 前置通知
         * public 修饰权限
         * 返回值只能为void
         * 方法名自定义
         * 参数可以没有,也可以有
         * 如果有参数,不是自定义的,有几个参数可以用
         */
    
        /**
         * 参数值:joinPoint ,为了获得目标方法的信息。
         * 参数只能在第一个位置
         */
    	@Before(value = "execution( * *.doSome(..))")
        public void before(JoinPoint joinPoint){
        }
    
  • 后置通知

    /**
         * 后置通知方法
         * 公共方法public
         * 方法没有返回值
         * 方法名自定义
         * 方法有参数,类型推荐Object
         */
    
        /**
         * @AfterReturning:后置通知
         *      属性:1、value:切入点表达式
         *           2、returning:自定义变量,表示目标方法的返回值,要和通知方法的参数名一致
         *      特点:1、在目标方法后执行
         *           2、能够获取方法的返回值
         */
        @AfterReturning(value = "execution(* *..*.dother(..))",returning = "obj" )
        public void after(Object obj){
            System.out.println("目标方法的后置通知");
            System.out.println("方法的返回值是:"+obj);
            Integer obj1 = (Integer) obj;
            obj =obj1+2;
            System.out.println("after通知中obj的值"+obj);
        }
    
  • 环绕通知

    /**
         * 环绕通知定义格式
         *  public 类型
         *  有返回值,推荐为Object
         *  方法名自定义
         *  参数类型是ProceedingJoinPoint 类似InvocationHandler中invoke方法的Method参数。 继承JoinPoint
         *  返回值是目标方法的返回值可以被修改
         *
         *  环绕通知经常做事务,目标方法前开启事务,方法后提交事务
         */
    
        /**
         * @Around
         * 1、参数:value(切入点表达式)
         * 2、方法前后都能增强功能
         * 3、能够控制方法是否能够被执行
         * 4、修改目标方法的返回值
         */
        @Around(value = "execution(* *..*.doSome3(..))")
        public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
            String name=null;
            System.out.println("环绕通知前置通知"+new Date());
            //控制方法能够被执行
            Object[] args = pjp.getArgs();
            if (args!=null &&args.length>0){
                if ("张三".equals(args[0])){
                    name= (String) pjp.proceed();
                }
            }
            System.out.println("环绕通知目标方法后面的方法");
            return name+"的儿子";
        }
    
  • 异常通知

    /**
         * 异常通知
         * public修饰
         * 返回值为void
         * 方法名自定义
         * 参数有一个Exception 也可以加一个JoinPoint
         */
    
        /**
         * @AfterThrowing
         *  属性:value:切入点
         *       throwing 必须和参数名一样
         *  特点:
         *      在有异常时执行
         *      可以作为异常的监控程序
         */
        @AfterThrowing(value = "execution(* *..*.doSome4(..))",throwing = "exception")
        public void myThrow(Exception exception){
            System.out.println("发生了"+exception.getMessage()+",发一个短信给主机");
        }
    
  • 最终通知

    /**
         * 最终通知
         * public修饰
         * 返回值为void
         * 方法名自定义
         * 参数可以有,也可以没有,有的话就是JoinPoint
         */
    
        /**
         * @After(value="execution()")
         * 总是会执行
         * 方法执行执行
         * 一般做资源清除功能
         */
        @After(value = "execution(* *..*.doSome*(..))")
        public void after(){
            System.out.println("最终通知");
        }
    

3、Spring整合mybatis

3.1、Spring整合mybatis实现步骤

主要实现:

  1. 创建druid连接池对象
  2. 创建SqlSessionFactory对象
  3. 创建SqlSession对象
  4. 创建Dao对象

实现步骤

  1. 导入依赖

    • spring
    • mybatis
    • mysql
    • jdbc
    • spring-mybatis
    • aop(aspectj)
    <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.6</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.0.11.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.7</version>
            </dependency>
        </dependencies>
    
  2. 编写配置文件

    1. 配置数据源

      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
              <property name="url" value="jdbc:mysql://47.102.110.12:3306/mybatistest?useSSL=false"/>
              <property name="username" value="root"/>
              <property name="password" value="eb7ecd2c8942412d"/>
      </bean>
      
    2. 注册SqlSessionFactory及SqlSessionTemplate(和sqlSession功能一样)

      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <!--   绑定mybatis的配置文件     -->
              <property name="configLocation" value="classpath:mybatis-config.xml"/>
              <property name="dataSource" ref="dataSource"/>
       </bean>
       <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
              <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
       </bean>
      
    3. 将SqlSession注册到实现类中,调用Mapper进行数据库操作

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      import org.springframework.stereotype.Service;
      
      import java.util.List;
      @Component
      public class ServiceImpl {
          @Autowired
          private SqlSessionTemplate sqlSession;
          public List<User> findUsers(){
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);
              return  mapper.selectUsers();
          }
      }
      
      
    4. 测试

      public class MyTest {
          @Test
          public void test01(){
              ClassPathXmlApplicationContext context = new 		ClassPathXmlApplicationContext("springApplicationConfig.xml");
              ServiceImpl serviceImpl = (ServiceImpl) context.getBean("serviceImpl");
              List<User> users = serviceImpl.findUsers();
              for (User user : users) {
                  System.out.println(user);
              }
          }
      }
      
  3. 测试

3.2、mybatis实现步骤

mybatis官方文档地址:

https://mybatis.org/mybatis-3/zh/configuration.html#mappers

  1. 编写实体类

  2. 编写接口

  3. 编写核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!--  属性配置,属性可以在xml配置文件中使用  -->
        <properties resource="db.properties">
            <property name="username" value="jfs"/>
            <property name="password" value="123"/>
        </properties>
        <!--  mybatis的参数设置  -->
        <settings>
            <setting name="logImpl" value="LOG4J"/>
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <setting name="cacheEnabled" value="true"/>
        </settings>
        <!--  为包下的类起别名,为类名的首字母小写  -->
        <typeAliases>
            <package name="com.jfs.pojo"/>
        </typeAliases>
        <!--  环境配置,可以配置mysql或者oracle  -->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 将包内的映射器接口实现全部注册为映射器 -->
        <mappers>
            <package name="com.jfs.dao"/>
        </mappers>
    </configuration>
    
  4. 编写Mapper.xml映射文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.jfs.dao.TeacherMapper">
        <select id="findTeacherById" resultMap="findTeacher">
         select s.id s_id, s.name s_name ,s.age s_age,t.id t_id ,t.name t_name ,t.age t_age from student s left join stu_teacher st on s.id =st.stu_id
            left join teacher t on st.teacher_id=t.id where t.id =#{id}
        </select>
        <resultMap id="findTeacher" type="teacher">
            <id property="id" column="t_id"/>
            <result property="name" column="t_name"/>
            <result property="age" column="t_age"/>
            <collection property="students" ofType="student">
                <id property="id" column="s_id"/>
                <result property="name" column="s_name"/>
                <result property="age" column="s_age"/>
            </collection>
        </resultMap>
    </mapper>
    

    通常mapper.xml映射文件放在resources文件下和接口相同的名字的包下面,例如接口在cpm.jfs.dao包下

    则Mapper.xml文件在resources文件包下面新建一个com.jfs.dao包,然后放在这下面。

  5. 测试

        public void test01() throws IOException {
            String xml ="mybatis-config.xml";
            InputStream resource = Resources.getResourceAsStream(xml);
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resource);
            //true代表的是开启事务
            SqlSession sqlSession = sessionFactory.openSession(true);
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> users = mapper.selectUsers();
            for (User user : users) {
                System.out.println(user);
            }
        }
    

3.3、mybatis-spring

3.3.1、官方文档地址

http://mybatis.org/spring/zh/index.html

4、声明式事务

4.1、回顾事务

把一组业务当做一个业务来做,要么都成功,要么都失败

4.2、事务的ACID原则

  1. 原子性
  2. 一致性
  3. 持久性
  4. 隔离性

5、Spring事务管理

  1. 声明式事务:AOP
  2. 编程式事务:在代码中进行事务管理

5.1、声明式事务配置

5.1.1、事务的隔离级别
  1. 读未提交
  2. 读已提交
  3. 可重复读
  4. 串行化
5.1.2、事务的超时时间
5.1.3、事务的传播行为
  1. PROPAGATION_REQUIRED

  2. PROPAGATION_SUPPORTS

  3. PROPAGATION_NEW

  4. PROPAGATION_MANDATORY

  5. PROPAGATION_NOT_SUPPORTED

  6. PROPAGATION_NEVER

  7. PROPAGATION_NESTED

    加粗的字体经常使用,需要掌握

5.2事务提交、回滚的时机

  1. 业务方法执行完,没有异常或错误时,执行提交
  2. 业务执行过程中,出现编译时异常和error时,执行回滚
  3. 业务执行过程中,出现编译时异常,执行提交。例如:IoException、FileNotFoundException等。

5.3、Spring事务使用步骤

  1. 指定使用的事务管理器,在spring中通过
  2. 指定哪些类,哪些方法需要使用事务
  3. 指定方法需要的隔离级别、事务传播行为、超时时间

5.4、Spring框架提供的事务方案

5.4.1、@Transactional注解

放在public方法上、给注解属性赋值,表示隔离级别、传播行为、异常信息。

5.4.1.1、@Transactional注解使用步骤
  1. 声明事务管理器对象

    <!--  声明事务管理器  -->
        <bean id="transactionManager" 		     	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
  2. 开启事务注解驱动

    spring使用aop机制,使用Aop的环绕通知

    @Aroud(需要开启事务的方法)

    Object myAroud(){

    ​ 事务管理器对象.commit

    ​ try{

    ​ 管理的对象的方法 ;

    ​ 事务管理器对象.commit;

    ​ }catch(exception e){

    ​ 事务管理器对象.rollback();

    ​ }

    }

     <!-- 开启事务注解驱动   -->
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
  3. 在方法上或者类上加上@Transactional注解

    @Transactional(isolation = Isolation.DEFAULT,
                propagation = Propagation.REQUIRED,
                rollbackFor = {NullPointerException.class,NumOutException.class}
        )
        /**
         * 默认抛出运行时异常,进行回滚。
         */
        public void buyGood(int num,String id) {
    
5.4.1.2、Spring配置文件方式实现事务

适合大型项目,在spring配置文件中声明类,方法需要的事务。

实现步骤
  1. 导入依赖

    <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.3.15</version>
            </dependency>
    
  2. 声明事务管理器对象

    <!--  声明事务管理器  -->
        <bean id="transactionManager" 		                  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
  3. 声明方法需要的事务类型(配置方法的事务属性(隔离级别、传播行为、超时时间))

    <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <!--name:方法名称:
                        1)完整的方法名称
                        2)可以统配符表示多个方法名称
                    isolation:隔离级别
                    propagation:传播行为
                    rollback-for:指定全限定异常名称,发生异常一定回滚。
                -->
                <tx:method name="buy*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.NullPointerException,com.jfs.exception.NumOutException"/>
            </tx:attributes>
        </tx:advice>
    
  4. 配置aop声明哪些类,需要创建代理

    <!--  配置aop  -->
    <aop:config>
        <!--  配置切入点表达式,指定哪些包中的类,需要使用事务      -->
        <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
        <!--  配置增强器:关联advice和pointcut  -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
    </aop:config>
    

6、Spring-Web

6.1、 创建是spring-web项目实现步骤

  1. 创建一个spring-webapp 的maven项目
  2. 加入依赖(拷贝spring08-spring-mybatis项目的),并加入jsp和servlet依赖
  3. 拷贝代码和配置文件
  4. 创建jsp请求
  5. 创建一个servlet接收jsp请求,调用service,service调用dao
  6. 创建一个jsp作为结果页面

**需求:**web项目中容器对象只需要创建一次,把容器对象放到全局作用域ServletContext中。

**实现:**使用监听器,当全局作用域对象被创建出来时,把容器对象放到ServletContext中。

  1. 导入依赖

       <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-web</artifactId>
         <version>5.3.14</version>
       </dependency>
    
  2. 注册监听器

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
  3. 配置监听器加载spring配置文件的路径

       <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:springApplicationConfig.xml</param-value>
       </context-param>  
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值